feat(ux): liste çekmecesinde denge ve tamamlanma

- Listenin risk dağılımı metinle ("Az riskli 20 · Güvenli 4")
- 24/24'te "Listen tamam" satırı; durum satırı ekran okuyucuya duyurulur
- Riskleri bilinen 6+ tercihli listede güvenli yoksa tek cümlelik ipucu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-24 01:25:16 +03:00
parent f4df52000b
commit e29a0ceb9a

View File

@@ -28,6 +28,7 @@ import {
} from "@/components/ui/drawer";
import {
ArrowRight,
Check,
GripVertical,
ListChecks,
Plus,
@@ -89,6 +90,13 @@ export function ListeCekmecesi() {
const [oturdu, setOturdu] = useState(false);
const doluluk = Math.round((liste.length / MANUEL_LISTE_MAX) * 100);
// Risk dağılımı: riskliden güvenliye, sıfır olanlar yazılmaz
const riskSayilari = (["riskli", "az-riskli", "guvenli"] as const)
.map((r) => [r, liste.filter((t) => t.risk === r).length] as const)
.filter(([, adet]) => adet > 0);
const riskBilinen = liste.filter((t) => t.risk != null).length;
const guvenliYok =
riskBilinen >= 6 && !liste.some((t) => t.risk === "guvenli");
// Profilsiz + ile eklenenlerin riski henüz hesaplanmamıştır; alt kart
// olmayan bir analizi "hazır" diye duyurmasın. Profil varken null risk,
// programın taban sırası olmadığındandır — sıra istemek yanlış olur.
@@ -139,8 +147,18 @@ export function ListeCekmecesi() {
<DrawerTitle className="px-10 text-center font-heading text-2xl font-bold text-slate-900">
Kendi Listem
</DrawerTitle>
<p className="mt-1 px-10 text-center text-sm text-slate-500">
{liste.length} / {MANUEL_LISTE_MAX} tercih
{/* Durum ekran okuyucuya da duyurulur (WCAG 4.1.3) */}
<p role="status" className="mt-1 px-10 text-center text-sm text-slate-500">
{liste.length === MANUEL_LISTE_MAX ? (
<span className="inline-flex items-center gap-1.5 font-semibold text-primary">
<Check className="size-4" aria-hidden />
Listen tamam · {liste.length} / {MANUEL_LISTE_MAX}
</span>
) : (
<>
{liste.length} / {MANUEL_LISTE_MAX} tercih
</>
)}
</p>
<div
className="mx-auto mt-3 h-2 w-full max-w-xs overflow-hidden rounded-full bg-slate-100"
@@ -156,6 +174,28 @@ export function ListeCekmecesi() {
transition={{ type: "spring", duration: 0.5, bounce: 0 }}
/>
</div>
{/* Listenin dengesi metinle (renk tek başına değil, §2.2) */}
{riskSayilari.length > 0 ? (
<p className="mt-2.5 flex flex-wrap items-center justify-center gap-x-3 gap-y-1">
{riskSayilari.map(([seviye, adet]) => (
<span key={seviye} className="inline-flex items-center gap-1.5">
<RiskEtiketi risk={seviye} />
<span className="text-sm font-semibold text-slate-900">
{adet}
</span>
</span>
))}
</p>
) : null}
{/* Denge ipucu: 24'lük iskeletin mantığı (alt sıralarda güvenli
programlar). Yalnız riskleri bilinen ve en az 6 tercihli
listede; vaat değil, gerekçe. */}
{guvenliYok ? (
<p className="mx-auto mt-2 max-w-sm text-center text-sm leading-relaxed text-slate-600">
Güvenli program yok; alta birkaç tane eklemek açıkta kalma riskini
azaltır.
</p>
) : null}
</DrawerHeader>
<div