diff --git a/src/app/sonuc/loading.tsx b/src/app/sonuc/loading.tsx deleted file mode 100644 index b624dcf..0000000 --- a/src/app/sonuc/loading.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Skeleton } from "@/components/ui/skeleton"; - -// /sonuc senkron SQLite sorgularıyla açılır; yavaş bağlantıda beyaz ekran -// yerine sayfanın iskeletini göster. -export default function SonucLoading() { - return ( -
- - - - -
- {Array.from({ length: 8 }).map((_, i) => ( - - ))} -
-
- ); -} diff --git a/src/app/sonuc/page.tsx b/src/app/sonuc/page.tsx index 36d500e..9f033ef 100644 --- a/src/app/sonuc/page.tsx +++ b/src/app/sonuc/page.tsx @@ -1,15 +1,16 @@ +import { Suspense } from "react"; import type { Metadata } from "next"; -import { rankWindowFacets, searchByRank } from "@/lib/db"; import { PUAN_TURLERI, type PuanTuruKey } from "@/types/yokatlas"; -import { getSession } from "@/lib/session"; -import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; -import { tadimlikSec } from "@/lib/tadimlik-havuzu"; -import { SihirbazBolumu } from "@/features/sihirbaz/components/sihirbaz-cagri-karti"; -import { TadimlikSatiri } from "@/features/rapor/components/tadimlik-satiri"; -import { ProgramTablosu } from "@/features/liste/components/program-tablosu"; +import { + SonucProgramTablosu, + SonucProgramTablosuSkeleton, +} from "@/features/liste/components/sonuc-program-tablosu"; import { ManuelHarita } from "@/features/liste/components/manuel-harita"; -import { SiteFooter } from "@/components/site-footer"; +import { TadimlikBolumu } from "@/features/rapor/components/tadimlik-bolumu"; +import { SonucSihirbazi } from "@/features/sihirbaz/components/sihirbaz-bolumu"; import { SiraGerekli } from "@/features/sihirbaz/components/sira-gerekli"; +import { SiteFooter } from "@/components/site-footer"; +import { Skeleton } from "@/components/ui/skeleton"; export const metadata: Metadata = { title: "Tercih Listeni Kur", @@ -19,7 +20,7 @@ export const metadata: Metadata = { robots: { index: false, follow: true }, }; -export default async function SonucPage({ +export default function SonucPage({ searchParams, }: { searchParams: Promise<{ @@ -29,76 +30,73 @@ export default async function SonucPage({ hazir?: 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 ( - <> - - - - ); - } - - // Sihirbaz için: kullanıcının kayıtlı raporu var mı? Listenin kendisi - // /listem'de yaşar; burada yalnızca CTA'nın hangi hâlde render edileceği - // için varlık bilgisi gerekir. - const session = await getSession(); - const raporSatiri = session - ? await kullanicininRaporu(session.user.id) - : null; - - const facetler = rankWindowFacets(sira, turKey); - // Yapay Zeka'sız ham liste: yalnızca sıralamayla ulaşılabilen programlar (dilimli) - const hamSonuclar = searchByRank(sira, turKey, { limitPerBucket: 20 }); - - // Girişsiz tadımlık (İP-B): SSR'da kovaya uyan "genel" satır seçilir; - // sihirbaz kategorisi client'ta okunup /api/tadimlik ile kişiselleştirilir. - // Girişli kullanıcı gerçek ürünü zaten görebildiği için tadımlık almaz. - const tadimlik = session ? null : await tadimlikSec(sira, turKey); - return ( -
-
- {/* Sayfa hiyerarşisi: önce Türkiye haritası, sonra liste. Manuel - liste store'una abone olduğundan tablodan "+" ile eklenenler - buraya canlı yansır. */} - + // Tablo değişken boylu olduğundan altındaki tadımlık, sihirbaz, dipnot ve + // footer aynı sınırda gruplu — bugünkü tek await'li akışla birebir aynı + // anda beliriyorlar, ara durumda zıplama yok. + + + + + + +
+ } + > + {searchParams.then((params) => { + const sira = Number.parseInt(params.sira ?? "", 10); + const turKey: PuanTuruKey = + params.tur && params.tur in PUAN_TURLERI + ? (params.tur as PuanTuruKey) + : "say"; - {/* Yapay Zeka'dan bağımsız ham tablo — haritanın hemen altında, sayfanın ana - gövdesi; puan türü navbar'daki sıralama rozetinden değiştirilir */} - + if (!Number.isFinite(sira) || sira < 1 || sira > 4_000_000) { + return ( + <> + + + + ); + } - {/* Girişsiz tadımlık: gerçek rapor satırı formatında tek örnek. - Havuzda içerik yoksa bileşen hiç render edilmez. */} - {!session ? ( - - ) : null} + return ( +
+
+ {/* Sayfa hiyerarşisi: önce Türkiye haritası, sonra liste. Manuel + liste store'una abone olduğundan tablodan "+" ile eklenenler + buraya canlı yansır. */} + - {/* Sihirbaz akışı (CTA + modal) — üretim ve liste /listem'de yaşar, - burada yalnızca funnel kartları render edilir */} - + {/* Yapay Zeka'dan bağımsız ham tablo — haritanın hemen altında, + sayfanın ana gövdesi; puan türü navbar'daki sıralama + rozetinden değiştirilir */} + -

- 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. -

-
- -
+ {/* Girişsiz tadımlık: gerçek rapor satırı formatında tek örnek. + Havuzda içerik yoksa bileşen hiç render edilmez. */} + + + {/* Sihirbaz akışı (CTA + modal) — üretim ve liste /listem'de + yaşar, burada yalnızca funnel kartları render edilir */} + + +

+ 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. +

+ + +
+ ); + })} + ); } diff --git a/src/features/liste/components/sonuc-program-tablosu.tsx b/src/features/liste/components/sonuc-program-tablosu.tsx new file mode 100644 index 0000000..dae1317 --- /dev/null +++ b/src/features/liste/components/sonuc-program-tablosu.tsx @@ -0,0 +1,27 @@ +import { siraylaProgramAra } from "../liste-queries"; +import { ProgramTablosu } from "./program-tablosu"; +import { Skeleton } from "@/components/ui/skeleton"; +import type { PuanTuruKey } from "@/types/yokatlas"; + +// /sonuc'un ham tablosunun sunucu sarmalayıcısı: okuma burada yaşar, sayfa +// yalnızca kompoze eder. +export function SonucProgramTablosu({ + sira, + tur, +}: { + sira: number; + tur: PuanTuruKey; +}) { + const sonuclar = siraylaProgramAra(sira, tur); + return ; +} + +export function SonucProgramTablosuSkeleton() { + return ( +
+ {Array.from({ length: 8 }).map((_, i) => ( + + ))} +
+ ); +} diff --git a/src/features/liste/liste-queries.ts b/src/features/liste/liste-queries.ts new file mode 100644 index 0000000..027f78d --- /dev/null +++ b/src/features/liste/liste-queries.ts @@ -0,0 +1,12 @@ +import "server-only"; +import { searchByRank } from "@/lib/db"; +import type { PuanTuruKey, RankResults } from "@/types/yokatlas"; + +// /sonuc'un ham tablosu: yalnızca sıralamayla ulaşılabilen programlar, +// hayal/dengeli/garanti dilimli, dilim başına ilk 20 satır. +export function siraylaProgramAra( + sira: number, + tur: PuanTuruKey, +): RankResults { + return searchByRank(sira, tur, { limitPerBucket: 20 }); +} diff --git a/src/features/rapor/components/tadimlik-bolumu.tsx b/src/features/rapor/components/tadimlik-bolumu.tsx new file mode 100644 index 0000000..e0ef53f --- /dev/null +++ b/src/features/rapor/components/tadimlik-bolumu.tsx @@ -0,0 +1,22 @@ +import { getSession } from "@/lib/session"; +import { tadimlikSec } from "../rapor-queries"; +import { TadimlikSatiri } from "./tadimlik-satiri"; +import type { PuanTuruKey } from "@/types/yokatlas"; + +// Girişsiz tadımlık (İP-B): SSR'da kovaya uyan "genel" satır seçilir; +// sihirbaz kategorisi client'ta okunup /api/tadimlik ile kişiselleştirilir. +// Girişli kullanıcı gerçek ürünü zaten görebildiği için tadımlık almaz — +// bu bileşen o durumda hiç render edilmez (skeleton bilerek yok; sayfadaki +// gruplu Suspense sınırı yer ayırmaz). +export async function TadimlikBolumu({ + sira, + tur, +}: { + sira: number; + tur: PuanTuruKey; +}) { + const session = await getSession(); + if (session) return null; + const ilk = await tadimlikSec(sira, tur); + return ; +} diff --git a/src/features/sihirbaz/components/sihirbaz-bolumu.tsx b/src/features/sihirbaz/components/sihirbaz-bolumu.tsx new file mode 100644 index 0000000..8f813b2 --- /dev/null +++ b/src/features/sihirbaz/components/sihirbaz-bolumu.tsx @@ -0,0 +1,41 @@ +import { getSession } from "@/lib/session"; +import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; +import { getSihirbazFacetleri } from "../sihirbaz-queries"; +import { SihirbazBolumu } from "./sihirbaz-cagri-karti"; +import type { PuanTuruKey } from "@/types/yokatlas"; + +// /sonuc'taki sihirbaz funnel'ının sunucu sarmalayıcısı: session, rapor-var-mı +// ve SSR facetleri burada okunur; client orkestratör (SihirbazBolumu, +// sihirbaz-cagri-karti.tsx) yalnızca çözülmüş değerleri alır. Üretim ve +// listenin kendisi /listem'de yaşar. +export async function SonucSihirbazi({ + sira, + tur, + otomatikAc, + secimlerHazir, +}: { + sira: number; + tur: PuanTuruKey; + otomatikAc: boolean; + secimlerHazir: boolean; +}) { + // Kullanıcının kayıtlı raporu var mı? Yalnızca CTA'nın hangi hâlde render + // edileceği için varlık bilgisi gerekir. + const session = await getSession(); + const raporSatiri = session + ? await kullanicininRaporu(session.user.id) + : null; + const facetler = getSihirbazFacetleri(sira, tur); + + return ( + + ); +} diff --git a/src/features/sihirbaz/sihirbaz-queries.ts b/src/features/sihirbaz/sihirbaz-queries.ts new file mode 100644 index 0000000..8991a86 --- /dev/null +++ b/src/features/sihirbaz/sihirbaz-queries.ts @@ -0,0 +1,12 @@ +import "server-only"; +import { rankWindowFacets } from "@/lib/db"; +import type { PuanTuruKey, SihirbazFacetleri } from "@/types/yokatlas"; + +// Sihirbaz adımlarının SSR başlangıç facetleri; adım geçişleri client'ta +// /api/facetler ile yeniden hesaplanır (kademeli süzme orada). +export function getSihirbazFacetleri( + sira: number, + tur: PuanTuruKey, +): SihirbazFacetleri { + return rankWindowFacets(sira, tur); +}