Update package.json scripts, enhance layout with new components, and improve API error handling
Some checks failed
Deploy / deploy (push) Failing after 1h28m6s

- Added new scripts for database operations and temporary production in package.json.
- Integrated KaydetBannerLazy component into the layout for improved user notifications.
- Enhanced API error handling in the soru route to mark credit exhaustion.
- Updated the IletisimPage for better button styling and user experience.
- Refactored ListemPage to streamline user flow and improve session handling.
- Removed unused ListePaneli component to clean up the codebase.
This commit is contained in:
bilalgursen
2026-08-06 02:27:42 +03:00
parent 488845ae1f
commit e62c6f07df
77 changed files with 4132 additions and 1659 deletions

View File

@@ -0,0 +1,83 @@
// Girişsiz yapay zekâ tadımlığının sunucu katmanı: batch üretilmiş
// (scripts/tadimlik-uret.ts) satırlar app.db'deki tadimlik_havuzu'ndan
// SEÇİLİR — anonim istek LLM'e asla gitmez, seçim her zaman sunucuda.
import { and, eq } from "drizzle-orm";
import { appDb, schema } from "./appdb";
import { turkishSlugify } from "./slug";
import type { PuanTuruKey } from "./db";
import type { Dilim } from "./rapor-havuzu";
import type { RaporSonuc } from "./ai/rapor";
/** Sihirbaz kategorisi seçilmemiş kullanıcı için kova×tür başına fallback satır. */
export const GENEL_KATEGORI = "genel";
/**
* Havuz anahtarındaki sıra kovaları. Eşikler analitik.ts'teki siraKovasi()
* ile birebir aynı tutulmalı (oradaki Türkçe etiketler DB anahtarı olamaz).
* temsiliSira: batch üretimde "bu kovadaki tipik aday" olarak kullanılan sıra.
*/
export const KOVALAR = [
{ slug: "0-10k", temsiliSira: 5_000, max: 10_000 },
{ slug: "10-50k", temsiliSira: 30_000, max: 50_000 },
{ slug: "50-100k", temsiliSira: 75_000, max: 100_000 },
{ slug: "100-250k", temsiliSira: 175_000, max: 250_000 },
{ slug: "250-500k", temsiliSira: 375_000, max: 500_000 },
{ slug: "500k-plus", temsiliSira: 750_000, max: Number.POSITIVE_INFINITY },
] as const;
export type Kova = (typeof KOVALAR)[number];
export function kovaBul(sira: number): Kova {
return KOVALAR.find((k) => sira <= k.max) ?? KOVALAR[KOVALAR.length - 1];
}
/** "Mühendislik & Teknoloji" → "muhendislik-teknoloji" (havuz anahtarı). */
export function kategoriSlugBul(kategori: string): string {
return turkishSlugify(kategori);
}
export interface TadimlikSatir {
programId: string;
dilim: Dilim;
gerekce: string;
riskNotu: string;
trendOzeti: string;
program: RaporSonuc["programlar"][string];
}
/**
* Kova × tür × kategori anahtarıyla tadımlık satırı seçer. Kategoriye özel
* satır yoksa "genel"e düşer; o da yoksa null — çağıran taraf bileşeni hiç
* render etmez (boş/generic tadımlık gösterilmez, PRD kuralı).
*/
export async function tadimlikSec(
sira: number,
tur: PuanTuruKey,
kategoriSlug?: string,
): Promise<TadimlikSatir | null> {
const kova = kovaBul(sira);
const sluglar = [
...new Set([kategoriSlug ?? GENEL_KATEGORI, GENEL_KATEGORI]),
];
for (const slug of sluglar) {
const satir = await appDb.query.tadimlikHavuzu.findFirst({
where: and(
eq(schema.tadimlikHavuzu.kovaSlug, kova.slug),
eq(schema.tadimlikHavuzu.tur, tur),
eq(schema.tadimlikHavuzu.kategoriSlug, slug),
),
});
if (satir) {
return {
programId: satir.programId,
dilim: satir.dilim,
gerekce: satir.gerekce,
riskNotu: satir.riskNotu,
trendOzeti: satir.trendOzeti,
program: satir.program as RaporSonuc["programlar"][string],
};
}
}
return null;
}