diff --git a/src/app/api/soru/route.ts b/src/app/api/soru/route.ts index 7fe0123..4806bbc 100644 --- a/src/app/api/soru/route.ts +++ b/src/app/api/soru/route.ts @@ -8,7 +8,7 @@ import { krediBittiIsaretle, } from "@/lib/credits"; import { anahtarVar, SORUMLULUK_REDDI } from "@/lib/ai/client"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; +import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; import { sohbetAkisi } from "@/lib/ai/cagri"; import { listeOzetiCikar } from "@/lib/ai/rapor"; import type { RaporSonuc } from "@/features/rapor/types/rapor"; diff --git a/src/app/listem/loading.tsx b/src/app/listem/loading.tsx deleted file mode 100644 index 74218fb..0000000 --- a/src/app/listem/loading.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Skeleton } from "@/components/ui/skeleton"; - -export default function ListemLoading() { - return ( -
- - - -
-
- {Array.from({ length: 10 }).map((_, i) => ( - - ))} -
- -
-
- ); -} diff --git a/src/app/listem/page.tsx b/src/app/listem/page.tsx index 12fcd9d..8ac28d0 100644 --- a/src/app/listem/page.tsx +++ b/src/app/listem/page.tsx @@ -1,21 +1,9 @@ +import { Suspense } from "react"; import type { Metadata } from "next"; -import { asc, eq } from "drizzle-orm"; -import { redirect } from "next/navigation"; -import { MessageCircleQuestion, Sparkles } from "lucide-react"; -import { verifySession, getCurrentUser } from "@/lib/session"; -import { appDb, schema } from "@/lib/appdb"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; -import { URUNLER, MAX_REVIZYON } from "@/lib/credits"; -import type { RaporSonuc } from "@/features/rapor/types/rapor"; -import type { RaporParams } from "@/features/rapor/types/rapor"; -import { ViewportPortal } from "@/components/viewport-portal"; -import { SiteFooter } from "@/components/site-footer"; -import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske"; -import type { MaskeliRapor } from "@/features/rapor/types/rapor"; -import type { Mesaj } from "./sohbet-client"; -import { ListemGovde } from "./listem-govde"; -import { ListemBosCta } from "./listem-bos-cta"; -import { ListeUretici } from "./liste-uretici"; +import { + ListemIcerik, + ListemIcerikSkeleton, +} from "@/features/rapor/components/listem-icerik"; // listeRevize aksiyonu 2 AI denemesi yapabiliyor (deneme başına 60 sn abort // sınırı, bkz. lib/ai/cagri.ts) → en kötü ~120 sn. Platform limiti bundan @@ -29,126 +17,16 @@ export const metadata: Metadata = { robots: { index: false, follow: false }, }; -const PAKET_FIYATI = (URUNLER.paket.amountKurus / 100).toLocaleString("tr-TR", { - maximumFractionDigits: 0, -}); - -export default async function ListemPage({ +export default function ListemPage({ searchParams, }: { searchParams: Promise<{ acildi?: string; uret?: string }>; }) { - // Girişsiz gelene callback'i ?uret=1 ile ver: girişten dönüşte bekleyen - // seçimlerle üretim kaldığı yerden başlasın. - const ilkParams = await searchParams; - const geriYol = ilkParams.uret === "1" ? "/listem?uret=1" : "/listem"; - const session = await verifySession(geriYol); - const [user, params, satir, sohbetGecmisi] = await Promise.all([ - getCurrentUser(), - searchParams, - kullanicininRaporu(session.user.id), - appDb - .select({ - id: schema.chatMessages.id, - role: schema.chatMessages.role, - content: schema.chatMessages.content, - }) - .from(schema.chatMessages) - .where(eq(schema.chatMessages.userId, session.user.id)) - .orderBy(asc(schema.chatMessages.createdAt)) - .limit(100), - ]); - if (!user) redirect(`/giris?callback=${encodeURIComponent(geriYol)}`); - - const { acildi } = params; - const kilitAcilisi = Boolean(user.hasPaket && acildi === "1"); - const raporVar = Boolean(satir?.result); - - if (!raporVar) { - const bosDurum = ( -
- -

- Henüz Yapay Zeka listen yok -

- -
- ); - return ( - <> -
- {/* ?uret=1: sihirbazdan yönlendirildi — bekleyen seçimlerle üretim - burada koşar, animasyon ve hata/kredi durumları burada yaşar. */} - {params.uret === "1" ? ( - - ) : ( - bosDurum - )} -
- - - ); - } - - const rapor: MaskeliRapor = user.hasPaket - ? (satir!.result as RaporSonuc) - : raporMaskele(satir!.result as RaporSonuc); - const raporParams = satir!.params as RaporParams; - // Danışman özeti sohbetin açılış mesajı olarak gösterilir; sohbet - // /api/soru üzerinden zaten liste + seçim bağlamıyla çalışıyor. - // Paketsizde rapor zaten maskeli: 1 uyarı açık, kalanının yalnızca sayısı var. - const danismanOzeti = rapor.genelDegerlendirme - ? { - metin: rapor.genelDegerlendirme, - uyarilar: rapor.uyarilar, - kilitli: !user.hasPaket, - kilitliUyariSayisi: user.hasPaket ? undefined : rapor.kilitliUyariSayisi, - } - : undefined; - return ( - <> -
- {kilitAcilisi ? ( -
- - Paketin aktif — listendeki tercihlerin tamamı, gerekçeler, riskler ve - PDF artık açık. -
- ) : null} - {/* Başlık ve revizyon/PDF aksiyonları listenin kendi başlık satırında - (bkz. listem-govde.tsx) — sayfa üstünde ayrı bir header yok. */} - - - - - - Danışmana sor - - -
- - + }> + {searchParams.then(({ acildi, uret }) => ( + + ))} + ); } diff --git a/src/app/odeme/sonuc/page.tsx b/src/app/odeme/sonuc/page.tsx index 0fad9b3..e4ff68f 100644 --- a/src/app/odeme/sonuc/page.tsx +++ b/src/app/odeme/sonuc/page.tsx @@ -4,7 +4,7 @@ import { eq } from "drizzle-orm"; import { ArrowRight, CheckCircle2, XCircle } from "lucide-react"; import { verifySession } from "@/lib/session"; import { appDb, schema } from "@/lib/appdb"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; +import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; import { odemeyiSonuclandir } from "@/lib/odeme"; import { Button } from "@/components/ui/button"; import { PagePixelDivider } from "@/components/pixel-decor"; diff --git a/src/app/rapor/yazdir/loading.tsx b/src/app/rapor/yazdir/loading.tsx deleted file mode 100644 index 87072d2..0000000 --- a/src/app/rapor/yazdir/loading.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Skeleton } from "@/components/ui/skeleton"; - -// /rapor/yazdir rapor sorgusunu bekler — belge düzeninde iskelet. -export default function Loading() { - return ( -
-
- - -
-
- {Array.from({ length: 8 }).map((_, i) => ( - - ))} -
-
- ); -} diff --git a/src/app/rapor/yazdir/page.tsx b/src/app/rapor/yazdir/page.tsx index 36cca95..b87ea37 100644 --- a/src/app/rapor/yazdir/page.tsx +++ b/src/app/rapor/yazdir/page.tsx @@ -1,142 +1,19 @@ +import { Suspense } from "react"; import type { Metadata } from "next"; -import Image from "next/image"; -import { redirect } from "next/navigation"; -import { verifySession, getCurrentUser } from "@/lib/session"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; -import { PUAN_TURLERI } from "@/types/yokatlas"; -import type { RaporSonuc } from "@/features/rapor/types/rapor"; -import type { RaporParams } from "@/features/rapor/types/rapor"; -import { YazdirButonu } from "./yazdir-butonu"; +import { + RaporYazdir, + RaporYazdirSkeleton, +} from "@/features/rapor/components/rapor-yazdir"; export const metadata: Metadata = { title: "Tercih Raporu (Yazdır)", robots: { index: false, follow: false }, }; -const DILIM_LABEL: Record = { - hayal: "Hayal (Riskli)", - dengeli: "Dengeli (Az riskli)", - garanti: "Garanti (Güvenli)", -}; - -export default async function YazdirPage() { - const session = await verifySession("/rapor/yazdir"); - // Kullanıcı satırı ile rapor sorgusu bağımsız — paralel çöz - const [user, satir] = await Promise.all([ - getCurrentUser(), - kullanicininRaporu(session.user.id), - ]); - if (!user) redirect("/giris"); - if (!user.hasPaket) redirect("/paket"); - if (!satir?.result) redirect("/sonuc"); - - const rapor = satir.result as RaporSonuc; - const params = satir.params as RaporParams; - const tarih = new Date(satir.updatedAt).toLocaleDateString("tr-TR", { - day: "numeric", - month: "long", - year: "numeric", - }); - +export default function YazdirPage() { return ( -
- - -
-

- {rapor.tercihler.length} Tercihlik Kişisel Liste ve Risk Raporu -

-

- Başarı sıralaması: {params.sira.toLocaleString("tr-TR")} · Puan - türü: {PUAN_TURLERI[params.tur]} - {params.il ? ` · İl: ${params.il}` : ""} · {tarih} -

- {rapor.kapsam?.uyarlandi ? ( -

- Not: Tercihlerine göre {rapor.kapsam.toplam} uygun program bulundu; - liste {rapor.kapsam.dagilim.hayal} hayal ·{" "} - {rapor.kapsam.dagilim.dengeli} dengeli ·{" "} - {rapor.kapsam.dagilim.garanti} garanti olarak kuruldu. -

- ) : null} -
- -
-

- Genel değerlendirme -

-

- {rapor.genelDegerlendirme} -

-
- - - - - - - - - - - - - - {rapor.tercihler.map((t) => { - const p = rapor.programlar[t.programId]; - return ( - - - - - - - - - ); - })} - -
#Program / ÜniversiteDilimGerekçeRisk notuTrend
{t.sira} - {p?.isim} -
- - {p?.universite} · {p?.il ?? "—"} - -
{DILIM_LABEL[t.dilim]}{t.gerekce}{t.riskNotu}{t.trendOzeti}
- -
-

- Bu rapor KolayTercih tarafından resmî YÖK Atlas verisi (2021–2025 - taban sıralamaları, kontenjan ve yerleşme istatistikleri) kullanılarak - üretilmiştir. Taban sıralamaları her yıl değişir; bu rapor yerleşme - garantisi vermez. Tercih listesinin ÖSYM sistemine girilmesi ve nihai - sorumluluğu adaya aittir. -

-
-
- - - Kolay - - Tercih - - -
- - kolaytercih.com - -
-
-
+ }> + + ); } diff --git a/src/app/sonuc/page.tsx b/src/app/sonuc/page.tsx index b995e3c..36d500e 100644 --- a/src/app/sonuc/page.tsx +++ b/src/app/sonuc/page.tsx @@ -2,10 +2,10 @@ 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 "@/lib/rapor-kaydi"; +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 "./tadimlik-satiri"; +import { TadimlikSatiri } from "@/features/rapor/components/tadimlik-satiri"; import { ProgramTablosu } from "@/features/liste/components/program-tablosu"; import { ManuelHarita } from "@/features/liste/components/manuel-harita"; import { SiteFooter } from "@/components/site-footer"; diff --git a/src/app/listem/liste-uretici.tsx b/src/features/rapor/components/liste-uretici.tsx similarity index 99% rename from src/app/listem/liste-uretici.tsx rename to src/features/rapor/components/liste-uretici.tsx index 3a95942..fe9caf8 100644 --- a/src/app/listem/liste-uretici.tsx +++ b/src/features/rapor/components/liste-uretici.tsx @@ -11,7 +11,7 @@ import { useRouter } from "next/navigation"; import { ArrowRight, Lock, RefreshCcw } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; -import { listeOlustur } from "@/app/sonuc/actions"; +import { listeOlustur } from "@/features/rapor/rapor-actions"; import { olay, siraKovasi } from "@/lib/analitik"; import { sonucHref, type TercihProfili } from "@/features/sihirbaz/sihirbaz-sabitler"; import { tercihProfiliOku, tercihProfiliSil } from "@/features/sihirbaz/sihirbaz-profil"; diff --git a/src/app/listem/listem-bos-cta.tsx b/src/features/rapor/components/listem-bos-cta.tsx similarity index 100% rename from src/app/listem/listem-bos-cta.tsx rename to src/features/rapor/components/listem-bos-cta.tsx diff --git a/src/app/listem/listem-govde.tsx b/src/features/rapor/components/listem-govde.tsx similarity index 99% rename from src/app/listem/listem-govde.tsx rename to src/features/rapor/components/listem-govde.tsx index 8ef3ef7..f7b55f4 100644 --- a/src/app/listem/listem-govde.tsx +++ b/src/features/rapor/components/listem-govde.tsx @@ -16,7 +16,7 @@ import { ShieldCheck, } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { RaporListesi } from "@/components/rapor-listesi"; +import { RaporListesi } from "./rapor-listesi"; import { useGorunumOlayi } from "@/components/use-gorunum-olayi"; import { TercihHaritasi } from "@/features/liste/components/tercih-haritasi"; import { pinleriTuret } from "@/lib/harita-pinler"; diff --git a/src/features/rapor/components/listem-icerik.tsx b/src/features/rapor/components/listem-icerik.tsx new file mode 100644 index 0000000..e4b0fd0 --- /dev/null +++ b/src/features/rapor/components/listem-icerik.tsx @@ -0,0 +1,150 @@ +import { redirect } from "next/navigation"; +import { MessageCircleQuestion, Sparkles } from "lucide-react"; +import { verifySession, getCurrentUser } from "@/lib/session"; +import { kullanicininRaporu, sohbetGecmisiGetir } from "../rapor-queries"; +import { URUNLER, MAX_REVIZYON } from "@/lib/credits"; +import type { + MaskeliRapor, + RaporParams, + RaporSonuc, +} from "../types/rapor"; +import { ViewportPortal } from "@/components/viewport-portal"; +import { SiteFooter } from "@/components/site-footer"; +import { Skeleton } from "@/components/ui/skeleton"; +import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske"; +import type { Mesaj } from "./sohbet-client"; +import { ListemGovde } from "./listem-govde"; +import { ListemBosCta } from "./listem-bos-cta"; +import { ListeUretici } from "./liste-uretici"; + +const PAKET_FIYATI = (URUNLER.paket.amountKurus / 100).toLocaleString("tr-TR", { + maximumFractionDigits: 0, +}); + +export async function ListemIcerik({ + uret, + acildi, +}: { + uret: boolean; + acildi: boolean; +}) { + // Girişsiz gelene callback'i ?uret=1 ile ver: girişten dönüşte bekleyen + // seçimlerle üretim kaldığı yerden başlasın. + const geriYol = uret ? "/listem?uret=1" : "/listem"; + const session = await verifySession(geriYol); + const [user, satir, sohbetGecmisi] = await Promise.all([ + getCurrentUser(), + kullanicininRaporu(session.user.id), + sohbetGecmisiGetir(session.user.id), + ]); + if (!user) redirect(`/giris?callback=${encodeURIComponent(geriYol)}`); + + const kilitAcilisi = Boolean(user.hasPaket && acildi); + const raporVar = Boolean(satir?.result); + + if (!raporVar) { + const bosDurum = ( +
+ +

+ Henüz Yapay Zeka listen yok +

+ +
+ ); + return ( + <> +
+ {/* ?uret=1: sihirbazdan yönlendirildi — bekleyen seçimlerle üretim + burada koşar, animasyon ve hata/kredi durumları burada yaşar. */} + {uret ? ( + + ) : ( + bosDurum + )} +
+ + + ); + } + + const rapor: MaskeliRapor = user.hasPaket + ? (satir!.result as RaporSonuc) + : raporMaskele(satir!.result as RaporSonuc); + const raporParams = satir!.params as RaporParams; + // Danışman özeti sohbetin açılış mesajı olarak gösterilir; sohbet + // /api/soru üzerinden zaten liste + seçim bağlamıyla çalışıyor. + // Paketsizde rapor zaten maskeli: 1 uyarı açık, kalanının yalnızca sayısı var. + const danismanOzeti = rapor.genelDegerlendirme + ? { + metin: rapor.genelDegerlendirme, + uyarilar: rapor.uyarilar, + kilitli: !user.hasPaket, + kilitliUyariSayisi: user.hasPaket ? undefined : rapor.kilitliUyariSayisi, + } + : undefined; + + return ( + <> +
+ {kilitAcilisi ? ( +
+ + Paketin aktif — listendeki tercihlerin tamamı, gerekçeler, riskler ve + PDF artık açık. +
+ ) : null} + {/* Başlık ve revizyon/PDF aksiyonları listenin kendi başlık satırında + (bkz. listem-govde.tsx) — sayfa üstünde ayrı bir header yok. */} + + + + + + Danışmana sor + + +
+ + + ); +} + +export function ListemIcerikSkeleton() { + return ( +
+ + + +
+
+ {Array.from({ length: 10 }).map((_, i) => ( + + ))} +
+ +
+
+ ); +} diff --git a/src/components/rapor-listesi.tsx b/src/features/rapor/components/rapor-listesi.tsx similarity index 100% rename from src/components/rapor-listesi.tsx rename to src/features/rapor/components/rapor-listesi.tsx diff --git a/src/features/rapor/components/rapor-yazdir.tsx b/src/features/rapor/components/rapor-yazdir.tsx new file mode 100644 index 0000000..18b93ea --- /dev/null +++ b/src/features/rapor/components/rapor-yazdir.tsx @@ -0,0 +1,153 @@ +import Image from "next/image"; +import { redirect } from "next/navigation"; +import { verifySession, getCurrentUser } from "@/lib/session"; +import { kullanicininRaporu } from "../rapor-queries"; +import { PUAN_TURLERI } from "@/types/yokatlas"; +import type { RaporParams, RaporSonuc } from "../types/rapor"; +import { Skeleton } from "@/components/ui/skeleton"; +import { YazdirButonu } from "./yazdir-butonu"; + +const DILIM_LABEL: Record = { + hayal: "Hayal (Riskli)", + dengeli: "Dengeli (Az riskli)", + garanti: "Garanti (Güvenli)", +}; + +export async function RaporYazdir() { + const session = await verifySession("/rapor/yazdir"); + // Kullanıcı satırı ile rapor sorgusu bağımsız — paralel çöz + const [user, satir] = await Promise.all([ + getCurrentUser(), + kullanicininRaporu(session.user.id), + ]); + if (!user) redirect("/giris"); + if (!user.hasPaket) redirect("/paket"); + if (!satir?.result) redirect("/sonuc"); + + const rapor = satir.result as RaporSonuc; + const params = satir.params as RaporParams; + const tarih = new Date(satir.updatedAt).toLocaleDateString("tr-TR", { + day: "numeric", + month: "long", + year: "numeric", + }); + + return ( +
+ + +
+

+ {rapor.tercihler.length} Tercihlik Kişisel Liste ve Risk Raporu +

+

+ Başarı sıralaması: {params.sira.toLocaleString("tr-TR")} · Puan + türü: {PUAN_TURLERI[params.tur]} + {params.il ? ` · İl: ${params.il}` : ""} · {tarih} +

+ {rapor.kapsam?.uyarlandi ? ( +

+ Not: Tercihlerine göre {rapor.kapsam.toplam} uygun program bulundu; + liste {rapor.kapsam.dagilim.hayal} hayal ·{" "} + {rapor.kapsam.dagilim.dengeli} dengeli ·{" "} + {rapor.kapsam.dagilim.garanti} garanti olarak kuruldu. +

+ ) : null} +
+ +
+

+ Genel değerlendirme +

+

+ {rapor.genelDegerlendirme} +

+
+ + + + + + + + + + + + + + {rapor.tercihler.map((t) => { + const p = rapor.programlar[t.programId]; + return ( + + + + + + + + + ); + })} + +
#Program / ÜniversiteDilimGerekçeRisk notuTrend
{t.sira} + {p?.isim} +
+ + {p?.universite} · {p?.il ?? "—"} + +
{DILIM_LABEL[t.dilim]}{t.gerekce}{t.riskNotu}{t.trendOzeti}
+ +
+

+ Bu rapor KolayTercih tarafından resmî YÖK Atlas verisi (2021–2025 + taban sıralamaları, kontenjan ve yerleşme istatistikleri) kullanılarak + üretilmiştir. Taban sıralamaları her yıl değişir; bu rapor yerleşme + garantisi vermez. Tercih listesinin ÖSYM sistemine girilmesi ve nihai + sorumluluğu adaya aittir. +

+
+
+ + + Kolay + + Tercih + + +
+ + kolaytercih.com + +
+
+
+ ); +} + +// Rapor sorgusu beklenirken belge düzeninde iskelet. +export function RaporYazdirSkeleton() { + return ( +
+
+ + +
+
+ {Array.from({ length: 8 }).map((_, i) => ( + + ))} +
+
+ ); +} diff --git a/src/app/listem/revizyon-kutusu.tsx b/src/features/rapor/components/revizyon-kutusu.tsx similarity index 98% rename from src/app/listem/revizyon-kutusu.tsx rename to src/features/rapor/components/revizyon-kutusu.tsx index 6fef1c1..ba1a554 100644 --- a/src/app/listem/revizyon-kutusu.tsx +++ b/src/features/rapor/components/revizyon-kutusu.tsx @@ -9,7 +9,7 @@ import { useRouter } from "next/navigation"; import { RefreshCcw } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; -import { listeRevize } from "@/app/sonuc/actions"; +import { listeRevize } from "@/features/rapor/rapor-actions"; import { olay } from "@/lib/analitik"; export function RevizyonKutusu({ kalanHak }: { kalanHak: number }) { diff --git a/src/app/listem/sohbet-client.tsx b/src/features/rapor/components/sohbet-client.tsx similarity index 100% rename from src/app/listem/sohbet-client.tsx rename to src/features/rapor/components/sohbet-client.tsx diff --git a/src/app/sonuc/tadimlik-satiri.tsx b/src/features/rapor/components/tadimlik-satiri.tsx similarity index 99% rename from src/app/sonuc/tadimlik-satiri.tsx rename to src/features/rapor/components/tadimlik-satiri.tsx index 3e55470..7c86e30 100644 --- a/src/app/sonuc/tadimlik-satiri.tsx +++ b/src/features/rapor/components/tadimlik-satiri.tsx @@ -14,7 +14,7 @@ import { ArrowRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { UniLogo } from "@/components/uni-logo"; import { SectionEyebrow } from "@/components/pixel-decor"; -import { RaporSatirDetayi } from "@/components/rapor-listesi"; +import { RaporSatirDetayi } from "./rapor-listesi"; import { ProgramSiraGecmisi, ProgramTabanTrendi, diff --git a/src/app/rapor/yazdir/yazdir-butonu.tsx b/src/features/rapor/components/yazdir-butonu.tsx similarity index 100% rename from src/app/rapor/yazdir/yazdir-butonu.tsx rename to src/features/rapor/components/yazdir-butonu.tsx diff --git a/src/app/sonuc/actions.ts b/src/features/rapor/rapor-actions.ts similarity index 99% rename from src/app/sonuc/actions.ts rename to src/features/rapor/rapor-actions.ts index 2164d7c..741f113 100644 --- a/src/app/sonuc/actions.ts +++ b/src/features/rapor/rapor-actions.ts @@ -3,7 +3,7 @@ import { and, eq, sql } from "drizzle-orm"; import { getSession, getCurrentUser } from "@/lib/session"; import { appDb, schema } from "@/lib/appdb"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; +import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; import { PUAN_TURLERI, type PuanTuruKey } from "@/types/yokatlas"; import { spendCredits, diff --git a/src/lib/rapor-kaydi.ts b/src/features/rapor/rapor-queries.ts similarity index 63% rename from src/lib/rapor-kaydi.ts rename to src/features/rapor/rapor-queries.ts index 25a6881..fec8e7b 100644 --- a/src/lib/rapor-kaydi.ts +++ b/src/features/rapor/rapor-queries.ts @@ -1,8 +1,14 @@ import "server-only"; -import { eq } from "drizzle-orm"; +import { asc, eq } from "drizzle-orm"; import { appDb, schema } from "@/lib/appdb"; import { anahtarVar } from "@/lib/ai/client"; -import type { RaporSonuc } from "@/features/rapor/types/rapor"; +import type { RaporSonuc } from "./types/rapor"; + +// Rapor feature'ının sorgu kapısı: kayıtlı rapor, sohbet geçmişi ve girişsiz +// tadımlık seçimi buradan okunur. + +// Girişsiz tadımlık satırı — batch üretilmiş havuzdan seçilir, LLM'e gitmez. +export { tadimlikSec } from "@/lib/tadimlik-havuzu"; // Kullanıcının kayıtlı raporunu okuyan TEK kapı. Rapor tablosunu okuyan her // yer (sayfalar, action'lar, soru API'si) buradan geçmeli; doğrudan @@ -32,4 +38,18 @@ export async function kullanicininRaporu(userId: string) { }); if (satir?.result && bayatMockMu(satir.result as RaporSonuc)) return null; return satir ?? null; -} \ No newline at end of file +} + +/** Kullanıcının danışman sohbet geçmişi, eskiden yeniye (en fazla 100 mesaj). */ +export function sohbetGecmisiGetir(userId: string) { + return appDb + .select({ + id: schema.chatMessages.id, + role: schema.chatMessages.role, + content: schema.chatMessages.content, + }) + .from(schema.chatMessages) + .where(eq(schema.chatMessages.userId, userId)) + .orderBy(asc(schema.chatMessages.createdAt)) + .limit(100); +} diff --git a/src/features/rapor/types/rapor.ts b/src/features/rapor/types/rapor.ts index 14953dd..85045e3 100644 --- a/src/features/rapor/types/rapor.ts +++ b/src/features/rapor/types/rapor.ts @@ -31,7 +31,7 @@ export type RaporSonuc = { uyarilar?: string[]; kapsam?: RaporKapsam; // Dev mock üretimi işareti: gerçek AI aktifken bayat mock kayıtları - // ayıklamak için (bkz. src/lib/rapor-kaydi.ts) + // ayıklamak için (bkz. features/rapor/rapor-queries.ts) devMock?: boolean; // Render için havuzdan zenginleştirilmiş program bilgisi // efektifSira: COALESCE(sira2025, sira2024) — eski kayıtlı raporlarda yok diff --git a/src/lib/dev/actions.ts b/src/lib/dev/actions.ts index 1bd3608..f90071d 100644 --- a/src/lib/dev/actions.ts +++ b/src/lib/dev/actions.ts @@ -7,7 +7,7 @@ import { eq } from "drizzle-orm"; import { headers } from "next/headers"; import { appDb, schema } from "@/lib/appdb"; -import { kullanicininRaporu } from "@/lib/rapor-kaydi"; +import { kullanicininRaporu } from "@/features/rapor/rapor-queries"; import { auth } from "@/lib/auth"; import { getCurrentUser } from "@/lib/session"; import { URUNLER } from "@/lib/credits";