feat(etiket): görünen "Garanti" dilim adı site genelinde "Güvenli"

Yalnız kullanıcıya görünen etiket değişir; iç anahtar, tip, DB değeri, API
parametresi, mermaid classDef ve analitik değeri `garanti` olarak kalır
(veri göçü yok).

- risk.ts: tek kaynak DILIM_ETIKET + dilimdenFarkliRiskEtiketi; rapor
  listesi, yazdır tablosu, /sonuc sekmeleri, seçimlerim paneli ve
  meraklısına şeridi buradan okur
- "Güvenli · Güvenli" / "Güvenli (Güvenli)": satır detayında risk etiketi
  yalnız dilimin varsayılanından farklıysa, yazdır ve seçimlerimde yalnız
  kelime farklıysa yazılır
- LLM prompt'ları: `garanti` teknik anahtar, adaya "güvenli" denir sözlüğü
  (rapor, soru, tadımlık); liste özeti modele görünen dilim adıyla gider
- meraklısına şeması (SEMA düğümü, aria-label), 2. bölüm anlatımı ve
  AGENTS.md şema kuralı görünen adla eşitlendi
- "yerleşme garantisi vermez" türü vaat reddi cümleleri aynen kaldı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-21 16:03:22 +03:00
parent d79758adc0
commit 5853b7d091
17 changed files with 103 additions and 51 deletions

View File

@@ -20,7 +20,7 @@ import type {
PuanTuruKey,
RankResultsListe,
} from "@/types/yokatlas";
import { riskHesapla, type RiskSeviyesi } from "@/lib/risk";
import { DILIM_ETIKET, riskHesapla, type RiskSeviyesi } from "@/lib/risk";
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
import {
ProgramSiraGecmisi,
@@ -51,21 +51,21 @@ const DILIMLER: {
}[] = [
{
key: "hayal",
etiket: "Hayal",
etiket: DILIM_ETIKET.hayal,
icon: Rocket,
renk: "text-red-500",
renkAktif: "text-red-400",
},
{
key: "dengeli",
etiket: "Dengeli",
etiket: DILIM_ETIKET.dengeli,
icon: Scale,
renk: "text-amber-500",
renkAktif: "text-amber-400",
},
{
key: "garanti",
etiket: "Garanti",
etiket: DILIM_ETIKET.garanti,
icon: ShieldCheck,
renk: "text-emerald-600",
renkAktif: "text-emerald-400",

View File

@@ -13,7 +13,7 @@ import {
X,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { RISK_ETIKET, type RiskSeviyesi } from "@/lib/risk";
import { DILIM_ETIKET, RISK_ETIKET, type RiskSeviyesi } from "@/lib/risk";
import { sonucHref } from "@/features/sihirbaz/sihirbaz-sabitler";
import {
tercihDegerlendir,
@@ -156,9 +156,12 @@ export function SecimlerimPaneli() {
{ozet ? (
<dl className="mt-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
<Istatistik etiket="Hayal" deger={ozet.hayal} />
<Istatistik etiket="Dengeli" deger={ozet.dengeli} />
<Istatistik etiket="Daha güvenli" deger={ozet.dahaGuvenli} />
<Istatistik etiket={DILIM_ETIKET.hayal} deger={ozet.hayal} />
<Istatistik etiket={DILIM_ETIKET.dengeli} deger={ozet.dengeli} />
<Istatistik
etiket={DILIM_ETIKET.garanti}
deger={ozet.dahaGuvenli}
/>
<Istatistik etiket="Dikkat" deger={ozet.dikkat} />
</dl>
) : null}
@@ -272,7 +275,9 @@ const SecimSatiri = memo(function SecimSatiri({
/>
) : null}
{degerlendirme.riskEtiketi}
{risk ? (
{/* Dilim adı ile risk etiketi aynı kelimeyse ("Güvenli")
parantez yazılmaz — "Güvenli (Güvenli)" olmasın. */}
{risk && RISK_ETIKET[risk] !== degerlendirme.riskEtiketi ? (
<span className="font-normal text-slate-500">
({RISK_ETIKET[risk]})
</span>

View File

@@ -6,6 +6,8 @@ import { ElCizimiSema } from "@/features/rehber/components/el-cizimi-sema";
// Renkler ürünün risk dilinden (kırmızı/sarı/yeşil) ve slate paletinden;
// renk tek başına anlam taşımaz, her düğüm metinle etiketlidir.
// Üçüncü dilimin görünen adı "güvenli"dir; `classDef garanti` yalnızca iç
// renk sınıfıdır (render edilmez), iç anahtarla aynı kalır.
const SEMA = `---
config:
look: handDrawn
@@ -25,7 +27,7 @@ flowchart TD
R --> L("En fazla 24 tercihlik liste")
L --> L1("üstte hayal — hedef 5, havuz elverdiğince")
L --> L2("ortada dengeli — hedef 13, havuz elverdiğince")
L --> L3("sonda garanti — hedef 6, havuz elverdiğince")
L --> L3("sonda güvenli — hedef 6, havuz elverdiğince")
classDef havuz fill:#eff6ff,stroke:#3b82f6,color:#1e3a8a
classDef uyari fill:#fffbeb,stroke:#f59e0b,color:#92400e
@@ -42,7 +44,7 @@ export function HuniSemasi() {
return (
<ElCizimiSema
tanim={SEMA}
ariaLabel="Sihirbaz akış şeması: sıralamanla açılan programlar alan, il ve devlet/vakıf adımlarıyla aday havuzuna daraltılır; alanlar bölümün adına ve bağlı olduğu fakülteye göre belirlenir, il adımında programı olan tüm iller listelenir; havuz 24'ün altına düşerse filtreler gevşetilir, yine de dar kalırsa liste eldeki programlarla kurulur ve dağılım havuza uyarlanır; yapay zekâ havuzdan üstte hayal, ortada dengeli, sonda garanti olacak şekilde en fazla 24 tercih seçip her satıra gerekçe yazar; risk notu ve trend özeti gerçek sıralama geçmişinden hesaplanır."
ariaLabel="Sihirbaz akış şeması: sıralamanla açılan programlar alan, il ve devlet/vakıf adımlarıyla aday havuzuna daraltılır; alanlar bölümün adına ve bağlı olduğu fakülteye göre belirlenir, il adımında programı olan tüm iller listelenir; havuz 24'ün altına düşerse filtreler gevşetilir, yine de dar kalırsa liste eldeki programlarla kurulur ve dağılım havuza uyarlanır; yapay zekâ havuzdan üstte hayal, ortada dengeli, sonda güvenli olacak şekilde en fazla 24 tercih seçip her satıra gerekçe yazar; risk notu ve trend özeti gerçek sıralama geçmişinden hesaplanır."
figcaption={
<>
Her adım havuzu biraz daha daraltır; yapay zekâ listeyi bu havuzun{" "}

View File

@@ -20,6 +20,7 @@ import { useTercihProfili } from "@/features/liste/hooks/use-tercih-profili";
import { PUAN_TURU_ETIKET, sonucHref } from "@/features/sihirbaz/sihirbaz-sabitler";
import type { DilimKey, SihirbazFacetleri } from "@/types/yokatlas";
import { onbellekliJsonGetir } from "@/lib/istek-cache";
import { DILIM_ETIKET } from "@/lib/risk";
export type DemoVeri = {
facetler: SihirbazFacetleri;
@@ -146,7 +147,7 @@ export function DilimSeridiCanli() {
<svg
viewBox="0 0 640 168"
role="img"
aria-label={`Sıralama ekseni: ${fmt(sira)}. sıradaki aday için Hayal diliminde ${fmt(t.hayal)}, Dengeli diliminde ${fmt(t.dengeli)}, Garanti diliminde ${fmt(t.garanti)} program var.`}
aria-label={`Sıralama ekseni: ${fmt(sira)}. sıradaki aday için ${DILIM_ETIKET.hayal} diliminde ${fmt(t.hayal)}, ${DILIM_ETIKET.dengeli} diliminde ${fmt(t.dengeli)}, ${DILIM_ETIKET.garanti} diliminde ${fmt(t.garanti)} program var.`}
className="w-full"
>
{/* eksen */}
@@ -174,7 +175,7 @@ export function DilimSeridiCanli() {
{/* hayal */}
<rect x="20" y="70" width="150" height="32" rx="4" className="fill-red-100" />
<text x="95" y="40" textAnchor="middle" className="fill-red-600 text-[12px] font-semibold">
Hayal
{DILIM_ETIKET.hayal}
</text>
<text x="95" y="56" textAnchor="middle" className="fill-slate-600 text-[11px] font-medium tabular-nums">
{fmt(t.hayal)} program
@@ -192,7 +193,7 @@ export function DilimSeridiCanli() {
{/* dengeli */}
<rect x="174" y="70" width="120" height="32" rx="4" className="fill-amber-100" />
<text x="234" y="40" textAnchor="middle" className="fill-amber-600 text-[12px] font-semibold">
Dengeli
{DILIM_ETIKET.dengeli}
</text>
<text x="234" y="56" textAnchor="middle" className="fill-slate-600 text-[11px] font-medium tabular-nums">
{fmt(t.dengeli)} program
@@ -204,7 +205,7 @@ export function DilimSeridiCanli() {
{/* garanti */}
<rect x="296" y="70" width="324" height="32" rx="4" className="fill-emerald-100" />
<text x="458" y="40" textAnchor="middle" className="fill-emerald-700 text-[12px] font-semibold">
Garanti
{DILIM_ETIKET.garanti}
</text>
<text x="458" y="56" textAnchor="middle" className="fill-slate-600 text-[11px] font-medium tabular-nums">
{fmt(t.garanti)} program

View File

@@ -68,12 +68,12 @@ export function MeraklisinaIcerik() {
(listenin bel kemiği — sıralamanın yaklaşık yüzde kırk fazlasına
kadar pay bırakılır), tabanı{" "}
<strong>belirgin şekilde altında</strong> kalan her şey ise{" "}
<span className="font-semibold text-emerald-600">Garanti</span>{" "}
<span className="font-semibold text-emerald-600">Güvenli</span>{" "}
dilimidir.
</p>
<DilimSeridiCanli />
<p className="mt-4">
Garanti diliminin bir sonu yok: 87. sıradaki bir adayın da 500
Güvenli diliminin bir sonu yok: 87. sıradaki bir adayın da 500
bininci sıradaki bir adayın da ulaşabildiği <em>bütün</em>{" "}
programlar listededir. Liste sana en yakın tabandan başlar; aşağı
indikçe ve &ldquo;Daha fazla göster&rdquo;e bastıkça senden
@@ -131,7 +131,7 @@ export function MeraklisinaIcerik() {
<p className="mt-2">
Tüm bu hesaplar <strong>geçen yılların</strong>{" "}
yerleştirme sonuçlarına dayanır. Kontenjanlar, tercih davranışları
ve puanlar her yıl değişir; geçen yıl &ldquo;garanti&rdquo;
ve puanlar her yıl değişir; geçen yıl &ldquo;güvenli&rdquo;
görünen bir bölüm bu yıl dolabilir. Bu yüzden satırlarda son beş
yılın taban seyrini de gösteririz — çizgi yukarı tırmanıyorsa
bölüm zorlaşıyor demektir. KolayTercih karar <em>destek</em>{" "}

View File

@@ -29,17 +29,14 @@ import {
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
import type { RaporSonuc } from "@/features/rapor/types/rapor";
import {
DILIM_ETIKET,
RISK_ETIKET,
dilimdenFarkliRiskEtiketi,
dilimdenRisk,
riskHesapla,
type RiskSeviyesi,
} from "@/lib/risk";
export const DILIM_ETIKET: Record<string, string> = {
hayal: "Hayal",
dengeli: "Dengeli",
garanti: "Garanti",
};
import type { Dilim } from "@/lib/rapor-havuzu";
// Risk renk dili: yeşil = güvenli, sarı = az riskli, kırmızı = riskli.
// Nokta hiçbir yerde tek başına durmaz; yanındaki etiket dar ekranda sr-only
@@ -75,12 +72,16 @@ export function RaporSatirDetayi({
trendOzeti: string;
}) {
const stil = RISK_STIL[risk];
// Risk etiketi yalnızca dilimin varsayılanından farklıysa yazılır; yoksa
// üçüncü dilimde başlık "Güvenli · Güvenli" olurdu (bkz. risk.ts).
const farkliRisk = dilimdenFarkliRiskEtiketi(dilim, risk);
return (
<>
<div className="mb-2 flex flex-wrap items-center gap-2 text-xs text-slate-500">
<stil.icon className="size-3.5" aria-hidden />
<span className="font-medium">
{DILIM_ETIKET[dilim]} · {RISK_ETIKET[risk]}
{DILIM_ETIKET[dilim as Dilim] ?? dilim}
{farkliRisk ? ` · ${farkliRisk}` : null}
</span>
{unitur ? <span>· {unitur}</span> : null}
</div>
@@ -345,7 +346,7 @@ export function RaporListesi({
<p className="mt-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-900">
Tercihlerine göre {rapor.kapsam.toplam} uygun program bulundu; listen{" "}
{rapor.kapsam.dagilim.hayal} hayal · {rapor.kapsam.dagilim.dengeli}{" "}
dengeli · {rapor.kapsam.dagilim.garanti} garanti olarak kuruldu. Daha
dengeli · {rapor.kapsam.dagilim.garanti} güvenli olarak kuruldu. Daha
fazla seçenek için alan veya il filtreni genişletebilirsin.
</p>
) : null}

View File

@@ -2,16 +2,19 @@ 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 { PUAN_TURLERI, type DilimKey } from "@/types/yokatlas";
import { DILIM_ETIKET, RISK_ETIKET, dilimdenRisk } from "@/lib/risk";
import type { RaporParams, RaporSonuc } from "../types/rapor";
import { Skeleton } from "@/components/ui/skeleton";
import { YazdirButonu } from "./yazdir-butonu";
const DILIM_LABEL: Record<string, string> = {
hayal: "Hayal (Riskli)",
dengeli: "Dengeli (Az riskli)",
garanti: "Garanti (Güvenli)",
};
// Dilim sütunu: dilim adı + varsayılan riski. Üçüncü dilimde ikisi aynı
// kelime ("Güvenli") olduğu için parantez düşer — "Güvenli (Güvenli)" yazmaz.
function dilimLabel(dilim: DilimKey): string {
const ad = DILIM_ETIKET[dilim] ?? dilim;
const risk = RISK_ETIKET[dilimdenRisk(dilim)];
return ad === risk ? ad : `${ad} (${risk})`;
}
export async function RaporYazdir() {
const session = await verifySession("/rapor/yazdir");
@@ -50,7 +53,7 @@ export async function RaporYazdir() {
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.
{rapor.kapsam.dagilim.garanti} güvenli olarak kuruldu.
</p>
) : null}
</header>
@@ -91,7 +94,7 @@ export async function RaporYazdir() {
{p?.universite} · {p?.il ?? "—"}
</span>
</td>
<td className="py-2 pr-2">{DILIM_LABEL[t.dilim]}</td>
<td className="py-2 pr-2">{dilimLabel(t.dilim)}</td>
<td className="py-2 pr-2">{t.gerekce}</td>
<td className="py-2 pr-2">{t.riskNotu}</td>
<td className="py-2">{t.trendOzeti}</td>

View File

@@ -76,7 +76,7 @@ export function RevizyonKutusu({ kalanHak }: { kalanHak: number }) {
onChange={(e) => setFeedback(e.target.value)}
rows={2}
maxLength={1000}
placeholder="ör. İlk 5 tercihte daha fazla İstanbul olsun, garanti dilimini genişlet…"
placeholder="ör. İlk 5 tercihte daha fazla İstanbul olsun, güvenli dilimi genişlet…"
disabled={pending}
className="w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
/>

View File

@@ -150,7 +150,7 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
</h2>
<p className="mt-2 text-sm leading-6 text-slate-600">
Sıralaman kayıtlı ({profil.sira.toLocaleString("tr-TR")}. sıra) —
gerçek YÖK Atlas verisiyle hayal, dengeli ve garanti dilimlerindeki
gerçek YÖK Atlas verisiyle hayal, dengeli ve güvenli dilimlerindeki
programların hazır.
</p>
<Link
@@ -174,7 +174,7 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
</h2>
<p className="mt-2 text-sm leading-6 text-slate-600">
YKS başarı sıralamanı gir; gerçek YÖK Atlas verisiyle sıralamana uygun
hayal, dengeli ve garanti dilimlerindeki programları anında listele.
hayal, dengeli ve güvenli dilimlerindeki programları anında listele.
Ücretsiz, üyelik gerekmez.
</p>
<Button
@@ -217,7 +217,7 @@ export function CtaSiraForm({ baslik }: { baslik?: string }) {
</h2>
<p className="mt-2 max-w-xl text-sm leading-6 text-slate-600">
Puan türünü seç, başarı sıralamanı yaz; hayal, dengeli ve
garanti dilimlerindeki programları bu sayfadan ayrılmadan
güvenli dilimlerindeki programları bu sayfadan ayrılmadan
gör. Ücretsiz, üyelik gerekmez.
</p>
</div>

View File

@@ -322,7 +322,7 @@ export function SihirbazBolumu({
<p className="relative mx-auto mt-3 max-w-xl text-white/90">
Tablodan elle eklemek her zaman serbest. Ama üç adımda ilgi
alanlarını ve şehirlerini söylersen, yapay zekâ hayal, dengeli
ve garanti dilimlerine dağıtılmış 24 tercihi — her satırı
ve güvenli dilimlerine dağıtılmış 24 tercihi — her satırı
yerleşme riski ve gerekçesiyle — senin yerine dizer.
</p>
<Button