179 lines
6.2 KiB
TypeScript
179 lines
6.2 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { ArrowRight } from "lucide-react";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Input } from "@/components/ui/input";
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogTitle,
|
||
} from "@/components/ui/dialog";
|
||
import { SihirbazAdimlar } from "@/components/sihirbaz-adimlar";
|
||
import { authClient } from "@/lib/auth-client";
|
||
import type { SihirbazFacetleri } from "@/lib/db";
|
||
import { SIHIRBAZ_STORAGE_KEY, type SihirbazSecimleri } from "@/lib/sihirbaz";
|
||
|
||
// lib/db sunucu tarafı; etiketler client bundle'a sızmasın diye burada.
|
||
const PUAN_TURLERI_SECENEK = [
|
||
{ deger: "say", etiket: "Sayısal" },
|
||
{ deger: "ea", etiket: "Eşit Ağırlık" },
|
||
{ deger: "soz", etiket: "Sözel" },
|
||
{ deger: "dil", etiket: "Dil" },
|
||
{ deger: "tyt", etiket: "TYT" },
|
||
] as const;
|
||
|
||
export function HeroForm() {
|
||
const [siralama, setSiralama] = useState("");
|
||
const [tur, setTur] = useState<string>("say");
|
||
const [sira, setSira] = useState<number | null>(null);
|
||
const [facetler, setFacetler] = useState<SihirbazFacetleri | null>(null);
|
||
const [yukleniyor, setYukleniyor] = useState(false);
|
||
const [modalAcik, setModalAcik] = useState(false);
|
||
const [hata, setHata] = useState<string | null>(null);
|
||
const router = useRouter();
|
||
const { data: oturum } = authClient.useSession();
|
||
const girisli = Boolean(oturum?.user);
|
||
|
||
// Yazarken binlik ayracıyla biçimle (85000 → 85.000)
|
||
function siralamaDegisti(ham: string) {
|
||
setHata(null);
|
||
const rakamlar = ham.replace(/\D/g, "").slice(0, 7);
|
||
setSiralama(
|
||
rakamlar ? Number(rakamlar).toLocaleString("tr-TR") : "",
|
||
);
|
||
}
|
||
|
||
async function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
const value = Number(siralama.replace(/\./g, ""));
|
||
if (!value || value < 1 || value > 4_000_000) {
|
||
setHata("Geçerli bir başarı sıralaması gir (ör. 85.000).");
|
||
return;
|
||
}
|
||
setYukleniyor(true);
|
||
try {
|
||
const res = await fetch(`/api/facetler?sira=${value}&tur=${tur}`);
|
||
if (!res.ok) throw new Error();
|
||
const veri = (await res.json()) as { facetler: SihirbazFacetleri };
|
||
setSira(value);
|
||
setFacetler(veri.facetler);
|
||
setModalAcik(true);
|
||
} catch {
|
||
setHata("Bir şeyler ters gitti, tekrar dener misin?");
|
||
} finally {
|
||
setYukleniyor(false);
|
||
}
|
||
}
|
||
|
||
// Seçimler tamam: sakla ve herkes doğrudan /sonuc'a gitsin. Girişli
|
||
// kullanıcıda orada otomatik üretim başlar; girişsiz kullanıcı önce
|
||
// ücretsiz tabloyu görür, giriş CTA'sı ödülün kaydedilmesi olarak
|
||
// oradan gelir (değer önce — giriş duvarı sihirbazın hemen arkasında değil).
|
||
function secimleriTamamla(secimler: SihirbazSecimleri) {
|
||
if (!sira) return;
|
||
try {
|
||
localStorage.setItem(
|
||
SIHIRBAZ_STORAGE_KEY,
|
||
JSON.stringify({ sira, tur, secimler }),
|
||
);
|
||
} catch {}
|
||
setModalAcik(false);
|
||
router.push(
|
||
girisli
|
||
? `/sonuc?sira=${sira}&tur=${tur}&sihirbaz=1`
|
||
: `/sonuc?sira=${sira}&tur=${tur}&hazir=1`,
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="flex w-full max-w-md flex-col items-center gap-3">
|
||
<div
|
||
role="radiogroup"
|
||
aria-label="Puan türü"
|
||
className="flex flex-wrap justify-center gap-1.5"
|
||
>
|
||
{PUAN_TURLERI_SECENEK.map((t) => (
|
||
<button
|
||
key={t.deger}
|
||
type="button"
|
||
role="radio"
|
||
aria-checked={tur === t.deger}
|
||
onClick={() => setTur(t.deger)}
|
||
className={
|
||
tur === t.deger
|
||
? "cursor-pointer rounded-full border border-primary bg-primary px-3 py-1.5 text-xs font-medium text-white transition-colors duration-200"
|
||
: "cursor-pointer rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs text-slate-600 transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50"
|
||
}
|
||
>
|
||
{t.etiket}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
<form
|
||
onSubmit={handleSubmit}
|
||
className="flex w-full flex-col gap-3 sm:flex-row"
|
||
>
|
||
<label htmlFor="siralama" className="sr-only">
|
||
YKS başarı sıralaması
|
||
</label>
|
||
<Input
|
||
id="siralama"
|
||
inputMode="numeric"
|
||
autoComplete="off"
|
||
placeholder="YKS başarı sıralaman (ör. 85.000)"
|
||
value={siralama}
|
||
onChange={(e) => siralamaDegisti(e.target.value)}
|
||
aria-invalid={hata ? true : undefined}
|
||
aria-describedby={hata ? "siralama-hata" : undefined}
|
||
// flex-1 yalnızca yatay dizilimde (sm+): mobilde form flex-col olduğu
|
||
// için flex-1 inputun yüksekliğini eziyordu. Mobilde geniş padding
|
||
// + büyük yazı; sm ve üzeri eski görünüm.
|
||
className="h-14 bg-background px-6 py-3 text-lg sm:h-12 sm:flex-1 sm:px-2.5 sm:py-1 sm:text-base"
|
||
/>
|
||
<Button
|
||
type="submit"
|
||
size="lg"
|
||
disabled={yukleniyor}
|
||
className="h-12 cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
|
||
>
|
||
{yukleniyor ? "Hazırlanıyor…" : "Listemi oluştur"}
|
||
<ArrowRight className="size-4" aria-hidden />
|
||
</Button>
|
||
</form>
|
||
|
||
{hata ? (
|
||
<p
|
||
id="siralama-hata"
|
||
role="alert"
|
||
className="self-start text-sm font-medium text-red-600"
|
||
>
|
||
{hata}
|
||
</p>
|
||
) : null}
|
||
|
||
<Dialog open={modalAcik} onOpenChange={setModalAcik}>
|
||
<DialogContent className="max-h-[92dvh] gap-0 overflow-y-auto p-6 text-left sm:max-w-3xl">
|
||
<DialogTitle className="sr-only">Tercih listesi sihirbazı</DialogTitle>
|
||
<DialogDescription className="sr-only">
|
||
Sıralamana uygun seçimlerini yap, tercih planına geç.
|
||
</DialogDescription>
|
||
{facetler ? (
|
||
<SihirbazAdimlar
|
||
key={`${sira}-${tur}`}
|
||
facetler={facetler}
|
||
sonButonEtiketi={
|
||
girisli ? "Listemi oluştur (3 kredi)" : "Ücretsiz tablonu gör"
|
||
}
|
||
onTamamla={secimleriTamamla}
|
||
/>
|
||
) : null}
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
);
|
||
}
|