Files
kolaytercih/src/components/arama-funnel-karti.tsx
bilalgursen e62c6f07df
Some checks failed
Deploy / deploy (push) Failing after 1h28m6s
Update package.json scripts, enhance layout with new components, and improve API error handling
- Added new scripts for database operations and temporary production in package.json.
- Integrated KaydetBannerLazy component into the layout for improved user notifications.
- Enhanced API error handling in the soru route to mark credit exhaustion.
- Updated the IletisimPage for better button styling and user experience.
- Refactored ListemPage to streamline user flow and improve session handling.
- Removed unused ListePaneli component to clean up the codebase.
2026-08-06 02:27:42 +03:00

151 lines
5.6 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 { 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 { TypingAnimation } from "@/components/ui/typing-animation";
import { SIHIRBAZ_AC_EVENT, sonucHref } from "@/lib/sihirbaz";
import {
profilToplamaAc,
useTercihProfili,
} from "@/components/manuel-liste/profil-kapisi-store";
const FUNNEL_METINLERI = [
"Sıralamana en uygun listeyi birlikte kuralım.",
"Geleceğin 24 satırlık bir plana sığar.",
"Gerçek YÖK Atlas verisiyle güvenle seç.",
"Her tercihinin bir nedeni olsun.",
"Riskleri biz hesaplayalım, kararı sen ver.",
"Sana uygun bölümler bir adım uzağında.",
"Dengeli bir liste, rahat bir tercih dönemi.",
"Tercihlerini şansa bırakma.",
"Doğru strateji, doğru yerleşme.",
"Hayalindeki bölüm için doğru plan.",
"Verilerle konuşan bir tercih listesi.",
"Üç adımda sana özel 24 tercih.",
"Tercih dönemini stressiz geçir.",
"Geçen yılın verisi, senin avantajın.",
"Güvenli ve iddialı tercihleri dengele.",
"Sıralaman ne söylüyor? Birlikte bakalım.",
"Her satırı senin için hesaplıyoruz.",
"Akıllı tercih, içi rahat bir bekleyiş.",
"Yerleşme ihtimalini görerek seç.",
"Sana özel liste birkaç dakikada hazır.",
"İlgi alanların ve şehrin, tek listede.",
"Boş kalan tercih, kaçan fırsattır.",
"Uzman işi bir liste, senin onayınla.",
"Kararını veriye dayandır.",
"Doğru tercih, doğru bilgiyle başlar.",
"Listeni bugün kur, için rahat olsun.",
"Yerleşme riskin renklerle önünde.",
"En önemli 24 kararını birlikte verelim.",
"Senin sıralaman, sana özel plan.",
"İyi bir liste, iyi bir başlangıçtır.",
];
/** 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?sira= geçerliyken monte;
// sırasız /sonuc'ta (SiraGerekli görünümü) olay boşa gider — kapıya düş.
if (pathname === "/sonuc" && searchParams.get("sira")) {
window.dispatchEvent(new Event(SIHIRBAZ_AC_EVENT));
return;
}
if (profil) {
router.push(sonucHref(profil, { sihirbaz: "1" }));
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 mt-5 overflow-hidden rounded-2xl border border-blue-400/20 bg-[#0b1f3a] px-4 py-5 text-white shadow-[0_20px_50px_-28px_rgba(15,71,150,0.65)] sm:px-5"
>
<div
aria-hidden
className="pointer-events-none absolute -right-12 -top-16 size-44 rounded-full bg-blue-500/25 blur-3xl"
/>
<div
aria-hidden
className="pointer-events-none absolute -bottom-14 left-1/3 size-36 rounded-full bg-cyan-400/10 blur-3xl"
/>
<div className="relative 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-cyan-300" aria-hidden />
{etiket}
</span>
))}
</div>
</div>
<Button
size="lg"
onClick={sihirbazaGit}
className="w-full shrink-0 cursor-pointer bg-orange-500 font-semibold text-white shadow-[0_8px_24px_rgba(249,115,22,0.28)] transition-[background-color,transform,box-shadow] duration-200 hover:bg-orange-400 hover:shadow-[0_10px_28px_rgba(249,115,22,0.36)] active:scale-[0.98] focus-visible:ring-white/70 sm:w-auto"
>
Yapay Zeka listemi kur
<ArrowRight className="size-4" aria-hidden />
</Button>
</div>
</section>
);
}