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);
+}