refactor: /sonuc senkron kompozisyona geçti (Faz 9)

- liste-queries.ts (siraylaProgramAra) ve sihirbaz-queries.ts
  (getSihirbazFacetleri) eklendi — /sonuc'un SQLite okumaları feature
  sorgularına indi
- Yeni sunucu sarmalayıcıları: SonucProgramTablosu (+skeleton),
  TadimlikBolumu (girişliyse null — gruplu sınırda skeleton bilerek yok),
  SonucSihirbazi (session + rapor-varlığı + facetler içeride; client
  orkestratöre çözülmüş değerler iner)
- Sayfa senkron: searchParams.then() içinde sıra doğrulaması; geçersiz sıra
  SiraGerekli'ye düşer; geçerli akışta tablo+tadımlık+sihirbaz+dipnot+footer
  tek gruplu Suspense'te (bugünkü tek await'li akışla aynı anda belirir)
- sonuc/loading.tsx silindi; geometrisi sayfadaki fallback'te yaşıyor
- Davranış değişikliği yok (getSession React cache ile tek okuma)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-08-08 01:42:32 +03:00
parent fbd184e2be
commit 22e2fde058
7 changed files with 187 additions and 94 deletions

View File

@@ -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 <ProgramTablosu sonuclar={sonuclar} adaySira={sira} tur={tur} />;
}
export function SonucProgramTablosuSkeleton() {
return (
<div className="mt-8 flex flex-col gap-2">
{Array.from({ length: 8 }).map((_, i) => (
<Skeleton key={i} className="h-14 w-full rounded-xl" />
))}
</div>
);
}

View File

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

View File

@@ -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 <TadimlikSatiri sira={sira} tur={tur} ilk={ilk} />;
}

View File

@@ -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 (
<SihirbazBolumu
sira={sira}
tur={tur}
facetler={facetler}
girisliMi={Boolean(session)}
raporVar={Boolean(raporSatiri?.result)}
otomatikAc={otomatikAc}
secimlerHazir={secimlerHazir}
/>
);
}

View File

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