feat(v2): 9–10 Ekim çalışma ağacı — deneyim dili 1. dalga, PWA manifest, revizyon niyet modeli, listem/tadımlık bileşen denetimi
9–10 Ekim oturumlarının commit'lenmemiş işi tek parça alındı (raporlar: docs/ekip-raporlari/2026-10-09, 2026-10-10): - Landing deneyim dili 1. dalga: sayfa geçişi (ViewTransition), piksel morf/şerit, sorun sahneleri, adım akışı, sayan sayı, mobil menü, adım göstergesi, logoya tepki; DESIGN.md §11 ve AGENTS.md kuralları, tasarım denetimine yeni kurallar - Tema: tema ikonu, D kısayolu, tema geçişi - PWA: manifest.ts ve ikonlar - Danışman: revizyon niyet modeli (OPENROUTER_NIYET_MODEL), sohbet geçmişi, soru API'si - /listem ve /sonuc tadımlığı bileşen denetimi düzeltmeleri - eslint: .venv-tts/ yok sayılır tsc, eslint ve tasarım denetimi sıfır hata. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,8 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Kbd, KbdGroup } from "@/components/ui/kbd";
|
||||
import { SayiRulo } from "@/components/ui/sayi-rulo";
|
||||
import { ARAMA_AC_OLAYI } from "@/components/mobil-menu";
|
||||
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
|
||||
|
||||
type BolumSonucu = {
|
||||
@@ -172,6 +174,13 @@ export function KatalogArama({ rehberler }: { rehberler: RehberAramaKaydi[] }) {
|
||||
// modal her açılıp kapandığında remove/add döngüsü dönmesin
|
||||
// (client-event-listeners)
|
||||
const kisayolTetiklendi = useEffectEvent(() => modalDegisti(!acik));
|
||||
// Telefon menüsündeki "Ara" hapı (mobil-menu.tsx) pencereyi olayla açtırır.
|
||||
const aramaIstendi = useEffectEvent(() => modalDegisti(true));
|
||||
useEffect(() => {
|
||||
window.addEventListener(ARAMA_AC_OLAYI, aramaIstendi);
|
||||
return () => window.removeEventListener(ARAMA_AC_OLAYI, aramaIstendi);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function kisayolDinle(event: KeyboardEvent) {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
||||
@@ -279,9 +288,7 @@ export function KatalogArama({ rehberler }: { rehberler: RehberAramaKaydi[] }) {
|
||||
>
|
||||
<Trophy className="size-4 shrink-0 text-orange-500" aria-hidden />
|
||||
<span className="font-semibold tracking-tight text-slate-900">
|
||||
<span className="tabular-nums">
|
||||
{profil.sira.toLocaleString("tr-TR")}
|
||||
</span>
|
||||
<SayiRulo deger={profil.sira} className="tabular-nums" />
|
||||
<span className="font-medium text-slate-500">
|
||||
{" "}
|
||||
· {PUAN_TURU_ETIKET[profil.tur] ?? profil.tur}
|
||||
@@ -331,58 +338,42 @@ export function KatalogArama({ rehberler }: { rehberler: RehberAramaKaydi[] }) {
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* lg altında 2. satır: sıralama + ara (header flex-wrap + order-3) */}
|
||||
<div className="order-3 flex w-full basis-full items-center gap-1.5 lg:hidden">
|
||||
{profil ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={profilDuzenlemeyiAc}
|
||||
onPointerEnter={preloadTercihProfiliKapisi}
|
||||
onFocus={preloadTercihProfiliKapisi}
|
||||
aria-label={`Sıralamanı ve tercihlerini değiştir (${profil.sira.toLocaleString("tr-TR")} · ${PUAN_TURU_ETIKET[profil.tur] ?? profil.tur})`}
|
||||
className="flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-full border border-slate-200 bg-card py-1 pl-2.5 pr-1.5 text-xs font-semibold text-slate-900 transition-[background-color,transform] duration-150 hover:bg-slate-100 active:scale-[0.96] sm:h-12 sm:gap-2 sm:pl-3.5 sm:pr-2 sm:text-sm"
|
||||
>
|
||||
<Trophy
|
||||
className="size-3.5 shrink-0 text-orange-500 sm:size-4"
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="truncate tracking-tight">
|
||||
<span className="tabular-nums">
|
||||
{profil.sira.toLocaleString("tr-TR")}
|
||||
</span>
|
||||
<span className="font-medium text-slate-500">
|
||||
{" "}
|
||||
· {PUAN_TURU_ETIKET[profil.tur] ?? profil.tur}
|
||||
</span>
|
||||
</span>
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-slate-100 px-2 py-0.5 text-xs font-medium text-slate-500">
|
||||
<SquarePen className="size-3" aria-hidden />
|
||||
Değiştir
|
||||
</span>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={siralamaGirVeyaSonucaGit}
|
||||
onPointerEnter={preloadTercihProfiliKapisi}
|
||||
onFocus={preloadTercihProfiliKapisi}
|
||||
aria-label="Başarı sıralamanı gir"
|
||||
className="flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-full border border-slate-200 bg-card py-1 pl-2.5 pr-2.5 text-xs font-semibold text-slate-900 transition-[background-color,transform] duration-150 hover:bg-slate-100 active:scale-[0.96] sm:h-12 sm:gap-2 sm:pl-3.5 sm:pr-3 sm:text-sm"
|
||||
>
|
||||
<Trophy
|
||||
className="size-3.5 shrink-0 text-orange-500 sm:size-4"
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="truncate">Sıralama gir</span>
|
||||
</button>
|
||||
)}
|
||||
{/* lg altı: tek satırın ortasındaki sıralama hapı (DESIGN.md §4.36).
|
||||
Arama ve geri kalan her şey sağdaki baloncuk menüde. Sayfa başlığı
|
||||
çubuğa çıkınca (NavBaslik) hap yalnız kupaya iner, başlığa yer açar. */}
|
||||
<div className="order-2 flex shrink-0 items-center lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => modalDegisti(true)}
|
||||
aria-label="Bölüm, üniversite veya rehber ara"
|
||||
className="flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full border border-slate-200 bg-card text-slate-700 transition-[background-color,transform] duration-150 hover:bg-slate-100 active:scale-[0.96] sm:size-12"
|
||||
onClick={profil ? profilDuzenlemeyiAc : siralamaGirVeyaSonucaGit}
|
||||
onPointerEnter={preloadTercihProfiliKapisi}
|
||||
onFocus={preloadTercihProfiliKapisi}
|
||||
aria-label={
|
||||
profil
|
||||
? `Sıralamanı ve tercihlerini değiştir (${profil.sira.toLocaleString("tr-TR")} · ${PUAN_TURU_ETIKET[profil.tur] ?? profil.tur})`
|
||||
: "Başarı sıralamanı gir"
|
||||
}
|
||||
className="flex h-9 max-w-40 cursor-pointer items-center gap-1.5 rounded-full border border-slate-200 bg-card px-2.5 text-xs font-semibold text-slate-900 transition-[background-color,transform] duration-150 hover:bg-slate-100 active:scale-[0.96] motion-reduce:transition-none sm:h-12 sm:max-w-none sm:gap-2 sm:px-4 sm:text-sm"
|
||||
>
|
||||
<Search className="size-4" aria-hidden />
|
||||
<Trophy className="size-3.5 shrink-0 text-orange-500 sm:size-4" aria-hidden />
|
||||
<span className="truncate tracking-tight in-data-[nav-baslik]:hidden">
|
||||
{profil ? (
|
||||
<>
|
||||
<SayiRulo deger={profil.sira} className="tabular-nums" />
|
||||
<span className="hidden font-medium text-slate-500 sm:inline">
|
||||
{" "}
|
||||
· {PUAN_TURU_ETIKET[profil.tur] ?? profil.tur}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
"Sıralama gir"
|
||||
)}
|
||||
</span>
|
||||
{profil ? (
|
||||
<SquarePen
|
||||
className="size-3 shrink-0 text-slate-400 in-data-[nav-baslik]:hidden"
|
||||
aria-hidden
|
||||
/>
|
||||
) : null}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
AcilisSahnesi,
|
||||
LOGO_SLOTLARI,
|
||||
logoKareStili,
|
||||
LOGO_KAP_BOYU,
|
||||
} from "@/components/ui/acilis-sahnesi";
|
||||
import {
|
||||
Table,
|
||||
@@ -183,7 +184,15 @@ export function ProgramAyrintiButonu({
|
||||
<span className="sr-only">Kapat</span>
|
||||
</AltSayfaKapat>
|
||||
<AltSayfaGovde className="px-4 pt-3 pb-[max(1.25rem,env(safe-area-inset-bottom))] sm:px-6">
|
||||
<AltSayfaBaslik className="px-10 text-center font-heading text-2xl font-bold text-slate-900">
|
||||
{/* Başlık veri gelene dek görünmez: logo kareleri küçülüp buraya
|
||||
uçar, yazı onlardan netleşir (AcilisSahnesi `data-logo-hedef`). */}
|
||||
<AltSayfaBaslik
|
||||
data-logo-hedef
|
||||
className={cn(
|
||||
"px-10 text-center font-heading text-2xl font-bold text-slate-900",
|
||||
(durum === "bekliyor" || durum === "yukleniyor") && "opacity-0",
|
||||
)}
|
||||
>
|
||||
{isim}
|
||||
</AltSayfaBaslik>
|
||||
<AltSayfaAciklama className="mx-auto mt-1 max-w-md px-4 text-center text-sm text-slate-500">
|
||||
@@ -226,14 +235,20 @@ function AyrintiIcerik({ durum, veri }: { durum: Durum; veri: ProgramAyrintisi |
|
||||
</p>
|
||||
);
|
||||
}
|
||||
// Sıra = AcilisSahnesi'nin `data-sonra` sırası: logo söner, bloklar
|
||||
// yukarıdan aşağıya 0,05 s arayla netleşir.
|
||||
// Sıra = AcilisSahnesi'nin `data-sonra` sırası: logo küçülerek başlığa
|
||||
// dönüşür, bloklar yukarıdan aşağıya 0,05 s arayla netleşir.
|
||||
return (
|
||||
<>
|
||||
{/* Logo kareleri: sahne sırasında görünür, sönünce kap sıfır boya iner. */}
|
||||
<div data-logo className="relative mx-auto h-16 w-16" aria-hidden>
|
||||
{/* Logo kareleri: sahne sırasında görünür, küçülerek başlığa uçar; kap
|
||||
sıfır boya iner. */}
|
||||
<div data-logo className="relative mx-auto size-44" aria-hidden>
|
||||
{LOGO_SLOTLARI.map((slot) => (
|
||||
<span key={slot} data-logo-kare={slot} className="absolute" style={logoKareStili(slot)} />
|
||||
<span
|
||||
key={slot}
|
||||
data-logo-kare={slot}
|
||||
className="absolute"
|
||||
style={logoKareStili(slot, LOGO_KAP_BOYU)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Kunye veri={veri} />
|
||||
|
||||
Reference in New Issue
Block a user