All checks were successful
Deploy / deploy (push) Successful in 9m29s
- Modified redirects in next.config.ts to change the destination for "/sohbet" to "/listem". - Added new dependencies in package.json: marked, motion, react-markdown, remark-breaks, remark-gfm, shiki, and use-stick-to-bottom. - Updated pnpm-lock.yaml to reflect the new package versions and dependencies. - Removed obsolete stack files for Flutter, Nuxt, Nuxt.js, React Native, and Svelte from the UI/UX Pro Max skill data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
176 lines
6.3 KiB
TypeScript
176 lines
6.3 KiB
TypeScript
import Link from "next/link";
|
||
import type { Metadata } from "next";
|
||
import { ArrowLeft } from "lucide-react";
|
||
import { eq } from "drizzle-orm";
|
||
import {
|
||
PUAN_TURLERI,
|
||
rankWindowFacets,
|
||
searchByRank,
|
||
type PuanTuruKey,
|
||
} from "@/lib/db";
|
||
import { getSession, getCurrentUser } from "@/lib/session";
|
||
import { appDb, schema } from "@/lib/appdb";
|
||
import type { RaporSonuc } from "@/lib/ai/rapor";
|
||
import type { RaporParams } from "@/lib/rapor-havuzu";
|
||
import { raporMaskele } from "@/lib/rapor-maske";
|
||
import { SihirbazBolumu } from "./sihirbaz-cagri-karti";
|
||
import { FunnelBanner } from "./funnel-banner";
|
||
import type { MevcutRapor } from "./liste-paneli";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Button } from "@/components/ui/button";
|
||
import { ProgramTablosu } from "@/components/program-tablosu";
|
||
|
||
export const metadata: Metadata = {
|
||
title: "Tercih Listeni Kur — KolayTercih",
|
||
};
|
||
|
||
const TUR_LABELS: Record<PuanTuruKey, string> = {
|
||
say: "Sayısal",
|
||
ea: "Eşit Ağırlık",
|
||
soz: "Sözel",
|
||
dil: "Dil",
|
||
tyt: "TYT (Önlisans)",
|
||
};
|
||
|
||
export default async function SonucPage({
|
||
searchParams,
|
||
}: {
|
||
searchParams: Promise<{
|
||
sira?: string;
|
||
tur?: string;
|
||
sihirbaz?: string;
|
||
acildi?: string;
|
||
}>;
|
||
}) {
|
||
const params = await searchParams;
|
||
const sira = Number.parseInt(params.sira ?? "", 10);
|
||
const turKey: PuanTuruKey =
|
||
params.tur && params.tur in PUAN_TURLERI
|
||
? (params.tur as PuanTuruKey)
|
||
: "say";
|
||
|
||
if (!Number.isFinite(sira) || sira < 1 || sira > 4_000_000) {
|
||
return (
|
||
<main className="mx-auto flex max-w-2xl flex-col items-center px-4 py-24 text-center">
|
||
<h1 className="font-heading text-2xl font-bold">
|
||
Geçerli bir sıralama gerekli
|
||
</h1>
|
||
<p className="mt-3 text-slate-600">
|
||
Listeni kurabilmek için ana sayfadan YKS başarı sıralamanı gir.
|
||
</p>
|
||
<Button asChild className="mt-6 cursor-pointer">
|
||
<Link href="/">
|
||
<ArrowLeft className="size-4" aria-hidden />
|
||
Ana sayfaya dön
|
||
</Link>
|
||
</Button>
|
||
</main>
|
||
);
|
||
}
|
||
|
||
// Sihirbaz için: kullanıcı durumu, kayıtlı rapor ve dinamik facet'ler
|
||
const session = await getSession();
|
||
const user = session ? await getCurrentUser() : null;
|
||
const raporSatiri = user
|
||
? await appDb.query.reports.findFirst({
|
||
where: eq(schema.reports.userId, user.id),
|
||
})
|
||
: null;
|
||
|
||
const facetler = rankWindowFacets(sira, turKey);
|
||
// AI'sız ham liste: yalnızca sıralamayla ulaşılabilen programlar (dilimli)
|
||
const hamSonuclar = searchByRank(sira, turKey, { limitPerBucket: 20 });
|
||
// Paketsize giden rapor sunucuda maskelenir: blur yalnızca CSS olduğundan
|
||
// kilitli satırların gerçek içeriği client'a hiç inmez.
|
||
const hasPaket = Boolean(user?.hasPaket);
|
||
const mevcutRapor: MevcutRapor | null = raporSatiri?.result
|
||
? {
|
||
rapor: hasPaket
|
||
? (raporSatiri.result as RaporSonuc)
|
||
: raporMaskele(raporSatiri.result as RaporSonuc),
|
||
params: raporSatiri.params as RaporParams,
|
||
revisionCount: raporSatiri.revisionCount,
|
||
}
|
||
: null;
|
||
|
||
return (
|
||
<div className="min-h-screen bg-slate-50 text-slate-900">
|
||
<main className="mx-auto max-w-4xl px-4 py-12">
|
||
<div className="flex items-center justify-between gap-3">
|
||
<Badge variant="secondary" className="bg-primary/10 text-primary">
|
||
{sira.toLocaleString("tr-TR")}. sıra · {TUR_LABELS[turKey]}
|
||
</Badge>
|
||
<Button
|
||
asChild
|
||
size="sm"
|
||
variant="outline"
|
||
className="cursor-pointer rounded-full"
|
||
>
|
||
<Link href="/">
|
||
<ArrowLeft className="size-4" aria-hidden />
|
||
Yeni arama
|
||
</Link>
|
||
</Button>
|
||
</div>
|
||
<h1 className="mt-4 font-heading text-3xl font-bold sm:text-4xl">
|
||
{sira.toLocaleString("tr-TR")}. sıradaki bir aday için tercih planı
|
||
</h1>
|
||
<p className="mt-2 max-w-2xl text-slate-600">
|
||
Birkaç adımda ilgi alanlarını ve şehir tercihini seç; yapay zekâ
|
||
gerçek YÖK Atlas verisiyle 24 tercihlik dengeli listeni kursun. Her
|
||
satır yerleşme riskine göre renklenir:{" "}
|
||
<span className="font-semibold text-emerald-600">yeşil güvenli</span>,{" "}
|
||
<span className="font-semibold text-amber-600">sarı az riskli</span>,{" "}
|
||
<span className="font-semibold text-red-600">kırmızı riskli</span>.
|
||
</p>
|
||
|
||
{/* Puan türü seçimi */}
|
||
<nav className="mt-6 flex flex-wrap gap-2" aria-label="Puan türü">
|
||
{(Object.keys(PUAN_TURLERI) as PuanTuruKey[]).map((key) => (
|
||
<Button
|
||
key={key}
|
||
asChild
|
||
size="sm"
|
||
variant={key === turKey ? "default" : "outline"}
|
||
className="cursor-pointer"
|
||
>
|
||
<Link href={`/sonuc?sira=${sira}&tur=${key}`}>
|
||
{TUR_LABELS[key]}
|
||
</Link>
|
||
</Button>
|
||
))}
|
||
</nav>
|
||
|
||
{/* Satın alım funnel'ı: değişen cümlelerle adayı sihirbaza (rapor
|
||
varsa /listem paywall'ına) iten banner — paketli kullanıcıya
|
||
satacak bir şey kalmadığından gösterilmez */}
|
||
{!hasPaket ? <FunnelBanner hedefListem={Boolean(mevcutRapor)} /> : null}
|
||
|
||
{/* AI'dan bağımsız ham tablo + manuel liste haritası — sayfanın ana
|
||
gövdesi; + ile manuel 24'lük liste kurulur */}
|
||
<ProgramTablosu sonuclar={hamSonuclar} adaySira={sira} />
|
||
|
||
{/* Sihirbaz akışı (CTA + modal + AI listesi) — blurlu AI paneli
|
||
sayfanın en altında yaşar */}
|
||
<SihirbazBolumu
|
||
sira={sira}
|
||
tur={turKey}
|
||
facetler={facetler}
|
||
girisliMi={Boolean(session)}
|
||
hasPaket={hasPaket}
|
||
kredi={user?.creditBalance ?? 0}
|
||
mevcutRapor={mevcutRapor}
|
||
otomatikAc={params.sihirbaz === "1"}
|
||
acilis={params.acildi === "1"}
|
||
/>
|
||
|
||
<p className="mt-10 text-center text-xs leading-relaxed text-slate-500">
|
||
Veriler resmî YÖK Atlas kaynağından derlenmiştir. KolayTercih bir karar
|
||
destek aracıdır; yerleşme garantisi vermez, tercih sorumluluğu adaya
|
||
aittir.
|
||
</p>
|
||
</main>
|
||
</div>
|
||
);
|
||
}
|