Files
kolaytercih/src/components/arama-funnel-karti.tsx
bilalgursen 613d9e012a fix(tasarim): piksel zemin yalnız CTA yüzeylerinde, kabın ölçüsüne göre, küçük kare yok
Bilal (24 Eyl 2026): ana sayfa CTA'sında doku hiç görünmüyordu, bazı
kartlarda çok azdı; sonra "çok fazla piksel, küçük olanlar hiç olmasın"
ve "/sonuc'taki kartın arkasına da piksel koymuşsun; pikseller CTA'larda
olacak sadece".

- Kök neden: PixelMasonry sabit 420×120 çizimi kaplayacak şekilde
  ölçekleniyordu; yüksek kartlarda görünen kısım boş banda düşüyordu.
  Artık kabın gerçek ölçüsüne göre üretiliyor (ResizeObserver), yalnız
  kenar çerçevesinde, orta boş; `haric` verilen öğenin (24'lük piksel
  tablosu) çevresi her genişlikte boş. Sunucuda boş, ölçülünce belirir.
- Küçük (tek hücre) kare yok: 4×4 makro hücrede 2–4 hücrelik kareler,
  iri olan seyrek ve silik. Ölçüm: kapanış kartı 1280 px'te 58 kare
  (tabloya binen 0), 375 px'te 27 kare.
- Beyaz içerik kartlarından zemin geri alındı (tadımlık, /listem üretim
  ve kilit kartları, boş durum, sıralama-yok, meraklısına, paket kartları,
  kaydet bandı, katalog üstü kompakt satır, /sonuc "listen hazır").
  Kalanlar: CtaBandi, PikselCtaYuzeyi, arama penceresi kartı.
  PikselZemin'in açık tonu kaldırıldı. DESIGN.md §10.8 güncellendi.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 12:38:47 +03:00

114 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { PikselZemin } from "@/components/piksel-zemin";
import { useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import {
ArrowRight,
ChartNoAxesCombined,
Database,
ListChecks,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { FUNNEL_METINLERI } from "@/lib/funnel-metinleri";
import { TypingAnimation } from "@/components/ui/typing-animation";
import { SIHIRBAZ_AC_EVENT, sonucHref } from "@/features/sihirbaz/sihirbaz-sabitler";
import {
profilToplamaAc,
useTercihProfili,
} from "@/features/liste/hooks/use-tercih-profili";
/** Fisher-Yates ile kopya üzerinde karıştırır; her açılışta farklı sıra. */
function karistir<T>(dizi: readonly T[]): T[] {
const kopya = [...dizi];
for (let i = kopya.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[kopya[i], kopya[j]] = [kopya[j], kopya[i]];
}
return kopya;
}
const KANITLAR = [
{ ikon: ListChecks, etiket: "24 tercih" },
{ ikon: ChartNoAxesCombined, etiket: "Risk analizi" },
{ ikon: Database, etiket: "YÖK Atlas verisi" },
];
/**
* Arama modalının açılış ekranında yaşayan satın alım funnel kartı.
* Rastgele sırayla yazılıp silinen funnel metinleriyle adayı sihirbaza
* iter: /sonuc üzerindeyse oradaki orkestratörü olayla açar, başka
* sayfadaysa profile göre /sonuc'a (sihirbaz açık) yönlendirir ya da
* sıralama kapısını açar. Modal yalnızca açıkken mount olduğundan
* karıştırma her açılışta yenilenir ve hydration sorunu yaratmaz.
*/
export function AramaFunnelKarti({ onSec }: { onSec: () => void }) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const profil = useTercihProfili();
const [metinler] = useState(() => karistir(FUNNEL_METINLERI));
function sihirbazaGit() {
onSec();
// Orkestratör (SihirbazBolumu) yalnızca /sonuc tabloyu render ederken
// monte: profil çerezi (client'ta aynası localStorage profili) ya da eski
// linklerdeki ?sira= geçerliyken. Profilsiz /sonuc'ta (SiraGerekli
// görünümü) olay boşa gider — kapıya düş.
if (pathname === "/sonuc" && (profil || searchParams.get("sira"))) {
window.dispatchEvent(new Event(SIHIRBAZ_AC_EVENT));
return;
}
if (profil) {
router.push(sonucHref({ sihirbaz: "1", kaynak: "arama" }));
return;
}
// Yapay Zeka niyetli CTA: kapı tamamlanınca /sonuc'ta üretim akışı devam
// etmeli ("sonuc" hedefi ücretsiz tabloda bırakıyor, liste hiç kurulmuyordu).
profilToplamaAc("sihirbaz");
}
return (
<section
aria-label="Tercih listeni kur"
className="relative isolate mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-slate-950 px-4 py-5 text-white sm:px-5"
>
<PikselZemin seed={61} />
<div className="grid items-center gap-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:gap-5">
<div className="min-w-0">
<TypingAnimation
as="h3"
words={metinler}
loop
typeSpeed={38}
deleteSpeed={18}
pauseDelay={2600}
className="min-h-[2.75em] font-heading text-base font-bold leading-snug tracking-[-0.02em] sm:min-h-[1.4em] sm:text-lg"
/>
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-2">
{KANITLAR.map(({ ikon: Ikon, etiket }) => (
<span
key={etiket}
className="inline-flex items-center gap-1.5 text-xs font-medium text-slate-200"
>
<Ikon className="size-3.5 text-orange-500" aria-hidden />
{etiket}
</span>
))}
</div>
</div>
<Button
size="lg"
onClick={sihirbazaGit}
className="w-full shrink-0 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600 focus-visible:ring-white/70 sm:w-auto"
>
Yapay Zeka listemi kur
<ArrowRight className="size-4" aria-hidden />
</Button>
</div>
</section>
);
}