All checks were successful
Deploy / deploy (push) Successful in 17s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
400 lines
24 KiB
Markdown
400 lines
24 KiB
Markdown
# 09 — Tasarım: `/sonuc` haritası en üste + üniversite sayfasında tür
|
||
|
||
> Temel: `is/0923-harita-ve-tur` worktree'si, `origin/main` (c5769e2). Karşılaştırma için
|
||
> `gece/0922-urap` dalı `git show` ile okundu. Tarayıcı/dev sunucusu açılmadı, kod değiştirilmedi.
|
||
> Piksel ve CLS değerleri **token/kod okumasından hesaplandı**, ölçülmedi — hesap §1.2'de açık.
|
||
> Her iki talimat da "yapılacak" kabul edildi; aşağıdakiler *nasıl* yapılacağıdır.
|
||
|
||
---
|
||
|
||
## Özet — 5 madde
|
||
|
||
1. **Harita en üste çıkabilir ve CLS bedeli sıfıra inebilir.** Tek gereken, listenin *sayısını*
|
||
sunucunun hidrasyondan önce bilmesi. Profil zaten `kolaytercih.profil` çerezi ile sunucuya
|
||
aynalanıyor (`sihirbaz-profil.ts:51-57`); aynı desenle `kolaytercih.liste-sayi` çerezi yazılır,
|
||
`/sonuc` bunu okuyup haritaya **tam ölçülü** yer ayırır. Uygulama yükü: 3 dosya, ~25 satır.
|
||
2. **"Boşken hiç gözükmesin" şartı bozulmuyor.** Çerez yoksa ya da 0 ise `ManuelHarita` bugünkü gibi
|
||
`null` döner — ilk kez gelen ziyaretçi için DOM'da hiçbir düğüm yoktur, dolayısıyla tadımlığın
|
||
`first:mt-0` kuralı (`tadimlik-satiri.tsx:134`) da bozulmaz. **İlk ziyaretçide sıra değişmiyor —
|
||
doğrulandı** (`manuel-harita.tsx:71` `return null`).
|
||
3. **Tadımlığın kaybı kabul edilebilir ve dar.** Kayıp yalnız "girişsiz + kayıtlı listesi olan"
|
||
ziyaretçide var. 375×812'de tadımlığın rozeti, H2'si ve açıklaması harita eklendikten sonra da
|
||
katlamanın üstünde kalıyor (hesap §1.4); yalnız örnek satır kartı aşağı iniyor. Bu ziyaretçi
|
||
"bu ürün ne?" aşamasını geçmiş, ekranda önce kendi listesini görmesi doğru.
|
||
4. **Üniversite sayfasında tür, ızgaraya değil kimlik satırına gider.** Izgara 5 karta çıkamaz:
|
||
`lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2` şablonunda harita 2 satır kaplıyor, kartlar için
|
||
**tam 4 hücre** var (`universite-icerik.tsx:291`, `universite-konum-haritasi.tsx:77`). Tür zaten
|
||
bir *metrik* değil *kategori*; sitenin geri kalanında (4 ayrı dosyada) başlığın altındaki meta
|
||
satırında çip olarak yaşıyor. Aynı çip H1'in altına konur, ızgara 4 sayısal kart kalır.
|
||
5. **Yan bulgu: `unitur` etiketinin 4 farklı sözlüğü var** ve ikisi bozuk metin basıyor
|
||
(`universite-icerik.tsx:318` ve `bolum-program-listesi.tsx:227` → "Kktc", "Vakıf Myo").
|
||
`UNITUR_ETIKET` (bugün `gece/0922-urap`'ta, bileşenin içinde) `src/lib/`'e taşınmalı — tıpkı
|
||
`DILIM_ETIKET`'in `src/lib/risk.ts`'te tek kaynak olması gibi (AGENTS.md).
|
||
|
||
---
|
||
|
||
# Talimat 1 — `/sonuc`'ta harita en üste
|
||
|
||
## 1.1 Bugünkü durum (doğrulandı)
|
||
|
||
| Olgu | Kanıt |
|
||
|---|---|
|
||
| Sıra: tadımlık → tablo → harita | `src/app/sonuc/page.tsx:118, 124, 131` |
|
||
| Liste boşken harita hiç çizilmiyor | `manuel-harita.tsx:71` (`if (liste.length === 0) return null`) |
|
||
| Sunucu snapshot'ı **her zaman** boş | `liste-store.ts:30, 86` (`getServerSnapshot → BOS_LISTE`) |
|
||
| Liste yalnız localStorage'ta | `liste-store.ts:27, 42-46` |
|
||
| Profil ise sunucuya **çerezle** aynalanıyor | `sihirbaz-profil.ts:51-57`, `sonuc/page.tsx:65` |
|
||
| Store her sayfada hidrasyonda okunuyor | `layout.tsx:98-102` → `ListeCekmecesiLazy`/`KaydetBannerLazy` → `useManuelListe` |
|
||
|
||
Son satır kritik: liste store'u **site genelinde**, ilk açılan sayfada yükleniyor. Yani yazılacak
|
||
çerez, kullanıcı `/sonuc`'a varmadan önce (ana sayfada) yazılmış oluyor.
|
||
|
||
## 1.2 CLS hesabı — "368 px" rakamı masaüstündü
|
||
|
||
`main` = `max-w-4xl px-4` (`sonuc/page.tsx:99`). Harita `w-full`, viewBox `1050×447`
|
||
(`harita.ts:10-13`, `tercih-haritasi.tsx:46-51`), iskelet de aynı oranı rezerve ediyor
|
||
(`tercih-haritasi-lazy.tsx:14`).
|
||
|
||
| Ekran | İçerik genişliği | Harita yüksekliği |
|
||
|---|---|---|
|
||
| 375 px (mobil, trafiğin ~%90'ı) | 343 px | 343 × 447/1050 = **146 px** |
|
||
| ≥ 928 px (masaüstü) | 864 px | 864 × 447/1050 = **368 px** |
|
||
|
||
Yani `sonuc/page.tsx:107`'deki "~368 px" **masaüstü** rakamı; mobilde 146 px (+ bugünkü `mt-16` = 64 px).
|
||
CLS = etki payı × mesafe payı:
|
||
|
||
| Senaryo | Etki payı | Mesafe payı | **CLS** |
|
||
|---|---|---|---|
|
||
| Bugün (harita altta), dönüş ziyaretçisi | ~0 (kayan içerik ~2,4 ekran aşağıda) | — | **~0** |
|
||
| Harita üstte, yer ayırmadan, 375×812 | (812−220)/812 ≈ 0,73 | 146/812 = 0,18 | **~0,13** ("iyileştirilmeli") |
|
||
| Harita üstte, yer ayırmadan, 1280×800 | ~0,75 | 368/800 = 0,46 | **~0,35** ("kötü") |
|
||
| **Harita üstte + çerezle yer ayrılmış** | 0 | 0 | **0** |
|
||
|
||
(Hesap; ölçüm değil. 220 px = bant + başlık + `py-12`, önceki raporun tahmini.)
|
||
|
||
## 1.3 İki alternatif
|
||
|
||
### Alternatif A — liste sayısı çerezi, tek yuva (ÖNERİLEN)
|
||
|
||
```
|
||
375 px · /sonuc · DÖNÜŞ ziyaretçisi (çerez: liste-sayi=7)
|
||
┌───────────────────────────────┐ ← ilk boyada sunucudan gelen HTML
|
||
│ [bant] [başlık] │
|
||
│ │
|
||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ 146 px · HaritaIskeleti
|
||
│ ░░░ harita yer tutucu ░░░ │ (aspect-[1050/447], tam ölçü)
|
||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||
│ │
|
||
│ ◇ Yapay zekâdan örnek satır │ chunk inince yer tutucunun
|
||
│ Yapay zekâ listendeki her │ İÇİ dolar; hiçbir şey kaymaz
|
||
│ satırı böyle anlatır │
|
||
└───────────────────────────────┘
|
||
|
||
375 px · /sonuc · İLK KEZ gelen ziyaretçi (çerez yok)
|
||
┌───────────────────────────────┐
|
||
│ [bant] [başlık] │
|
||
│ │
|
||
│ ◇ Yapay zekâdan örnek satır │ ← harita DOM'da hiç yok,
|
||
│ Yapay zekâ listendeki her │ tadımlık first:mt-0'ı korur:
|
||
│ satırı böyle anlatır │ BUGÜNKÜ EKRANIN AYNISI
|
||
└───────────────────────────────┘
|
||
```
|
||
|
||
- **CLS:** çerez isabetliyse **0**. Çerez yoksa ama liste varsa (aşağıdaki dar kaçak) 0,13 (mobil).
|
||
- **Çerezin kaçacağı tek durum:** dağıtım sonrası ilk sayfa açılışı (kullanıcı başına **bir kez**;
|
||
ana sayfada yazıldığı için `/sonuc`'a client-side geçişte bile kapanır) ve localStorage yazma
|
||
hatası. Safari ITP kaçağı **yok sayılabilir**: ITP script'le yazılan çerezi 7 günde siler ama aynı
|
||
koşulda localStorage'ı da siler — ikisi birlikte ölür, ayrışmaz. Aynı risk zaten `kolaytercih.profil`
|
||
çerezinde de var, yeni bir sınıf risk getirmiyor.
|
||
|
||
### Alternatif B — çift yuva (çerez varsa üstte, yoksa altta)
|
||
|
||
`ManuelHarita` iki konumda da yazılır; sunucu çereze bakarak hangisinin aktif olacağını seçer.
|
||
|
||
```
|
||
çerez > 0 → [HARİTA] → tadımlık → tablo → sihirbaz
|
||
çerez yok → tadımlık → tablo → [HARİTA] → sihirbaz (bugünkü davranış)
|
||
```
|
||
|
||
- **CLS:** her durumda ~0. Kaçak senaryoda bile sıfır, çünkü harita eski (güvenli) yerine düşer.
|
||
- **Bedeli:** aynı bileşen iki DOM konumunda yaşar — AGENTS.md'nin "bir eleman iki yerde görünüyorsa
|
||
tek kaynaktan" ilkesine biçimsel olarak uymasa da okuma yükü artar; ve kullanıcı bazı ziyaretlerde
|
||
haritayı üstte, bazılarında altta görür. **Tutarsızlık, kazanılan 0,13'lük tek seferlik CLS'ten
|
||
daha pahalı.**
|
||
|
||
### Hüküm: **A**
|
||
|
||
B'nin kapattığı tek delik, kullanıcı başına bir kez yaşanan ve "iyileştirilmeli" bandındaki (kırmızı
|
||
değil) bir kayma. Bunun için haritanın sabit bir yeri olmamasını kabul etmek doğru takas değil.
|
||
|
||
## 1.4 Tadımlık ne kaybediyor — hüküm: kabul edilebilir
|
||
|
||
| Ziyaretçi | Sıra değişiyor mu? | Tadımlık nerede? |
|
||
|---|---|---|
|
||
| İlk kez gelen (liste boş) | **Hayır** — harita hiç render edilmez | Bugünkü yerinde, ~220 px |
|
||
| Girişli (tadımlık zaten yok, `tadimlik-bolumu.tsx:19`) | Evet, harita tablonun üstüne çıkar | — |
|
||
| **Girişsiz + kayıtlı listesi olan** | Evet | ~220 → **~366 px** |
|
||
|
||
Üçüncü satır için 375×812 hesabı (koddan çıkarım): tadımlığın `SectionEyebrow`'u, H2'si
|
||
("Yapay zekâ listendeki her satırı böyle anlatır") ve üç satırlık açıklaması katlamanın üstünde
|
||
kalıyor; aşağı inen şey örnek satır kartının gövdesi ve CTA'sı. 375×667'de (iPhone SE) rozet + H2
|
||
görünür, paragraf yarılanır.
|
||
|
||
**Hüküm: kabul edilebilir.** Gerekçe kaybın büyüklüğü değil, kime denk geldiği: tadımlık "ödeme
|
||
öncesi tek gerçek önizleme" argümanı **ürünü ilk kez gören** kişi içindir ve o kişide sıra hiç
|
||
değişmiyor. Zaten liste kurmuş ziyaretçi için ekranın ilk işi, kendi kurduğu listeyi göstermektir;
|
||
tadımlığı onun üstüne koymak "sana hâlâ ürünü tanıtıyorum" demek olurdu.
|
||
|
||
## 1.5 Uygulayıcıya tarif (yazılımcı)
|
||
|
||
**Dosya 1 — `src/features/liste/liste-store.ts`**
|
||
|
||
1. `PROFIL_CEREZ_ADI` deseninin eşi olarak sabit ekle: `LISTE_SAYI_CEREZ_ADI = "kolaytercih.liste-sayi"`
|
||
(sabitin yeri: bu dosya; sunucu tarafı buradan import eder — `liste-store.ts` `"use client"`
|
||
olduğu için sabiti `liste-dogrula.ts`'e koymak daha temiz, orası zaten sunucu action'larıyla
|
||
paylaşılıyor, bkz. `:23-25`).
|
||
2. `sayiCereziniYaz(n: number)` — `profilCerezineYaz` (`sihirbaz-profil.ts:51-57`) ile **birebir aynı**
|
||
öznitelikler: `path=/; max-age=31536000; samesite=lax` + https'te `secure`. Değer düz tamsayı.
|
||
3. Üç çağrı yeri (listenin değiştiği her yer):
|
||
- `kaydet()` içinde (`:42-46`) — ekle/çıkar/sırala/temizle hepsi buradan geçiyor;
|
||
- `yukle()` içinde, `liste = dogrula(...)` satırından sonra (`:53`) — dağıtım sonrası ilk
|
||
açılışta çerezi kendiliğinden doğurur;
|
||
- `"storage"` dinleyicisinde `liste` atandıktan sonra (`:64`) — başka sekmedeki değişiklik.
|
||
|
||
**Dosya 2 — `src/features/liste/components/manuel-harita.tsx`**
|
||
|
||
4. İmza: `export function ManuelHarita({ yerAyir = false }: { yerAyir?: boolean })`.
|
||
5. `:71` → `if (liste.length === 0 && !yerAyir) return null;`
|
||
Boş+`yerAyir` dalında **ayrı bir iskelet dalı yazma**: fonksiyon akmaya devam etsin, `pinler` boş
|
||
dizi olur, `TercihHaritasiLazy` monte olur ve chunk inene kadar zaten `HaritaIskeleti`'ni gösterir
|
||
(`tercih-haritasi-lazy.tsx:10-21`). Böylece (a) yer tutucu ile gerçek harita **aynı** kutudan gelir,
|
||
yükseklik ayrışamaz; (b) 197 KB'lık chunk hidrasyonu beklemeden inmeye başlar.
|
||
6. `:78` `<div className="mt-16">` → `<div>`. Harita artık `main`'in ilk çocuğu; üst boşluğu
|
||
`main`'in `py-12`'sinden alır (bu, `mt-16`'nın eklenmesinden önceki durumdu). `:73-76`'daki yorum
|
||
tersini anlatıyor, **aynı commit'te** düzeltilmeli.
|
||
|
||
**Dosya 3 — `src/app/sonuc/page.tsx`**
|
||
|
||
7. `cookies()` zaten okunuyor (`:56`): `const listeSayisi = Number.parseInt(cerezler.get(LISTE_SAYI_CEREZ_ADI)?.value ?? "", 10);`
|
||
→ `const yerAyir = Number.isFinite(listeSayisi) && listeSayisi > 0;`
|
||
8. `<ManuelHarita yerAyir={yerAyir} />` satırını `:131`'den **`:118`'in üstüne** taşı.
|
||
Yeni sıra: **harita → tadımlık → tablo → sihirbaz → dipnot.**
|
||
9. `:100-114`'teki yorum bloğu bugünkü kararın tersini belgeliyor; yerine yeni gerekçe
|
||
(çerezle yer ayırma) yazılmalı — yoksa bir sonraki oturum haritayı yine aşağı alır.
|
||
10. `tadimlik-satiri.tsx:127-133`'teki yorum ("harita artık tablonun altında, üstte hiçbir kardeş
|
||
yok") da yalan olur. `first:mt-0` **kuralı doğru çalışmaya devam eder** (harita varken tadımlık
|
||
ikinci çocuk olur ve `mt-16` alır, harita yokken ilk çocuktur ve `mt-0` alır) — düzeltilecek olan
|
||
yalnız yorumun metni.
|
||
|
||
**Kabul kriterleri**
|
||
|
||
1. Listesi olmayan ziyaretçinin `/sonuc` HTML'inde harita ile ilgili **hiçbir düğüm yok**
|
||
(`grep -c "aspect-\[1050/447\]"` = 0) ve tadımlık `<section>`'ı `main`'in ilk çocuğu.
|
||
2. `kolaytercih.liste-sayi=7` çereziyle istenen `/sonuc` HTML'inde, tadımlık `<section>`'ından
|
||
**önce** `aspect-[1050/447]` kutusu var.
|
||
3. Listeye ilk `+`'dan sonra sayfa yenilenince harita en üstte ve **yukarı kayma yok**.
|
||
4. Listeyi tamamen boşaltıp sayfayı yenileyince harita yok (çerez 0 yazılmış olmalı).
|
||
|
||
**Büyüklük: S** (3 dosya, ~25 satır + 3 yorum bloğu).
|
||
|
||
## 1.6 Aynı işte kapanması gereken iki küçük delik
|
||
|
||
| # | Yer | Sorun | Düzeltme |
|
||
|---|---|---|---|
|
||
| H1 | `tercih-haritasi-lazy.tsx:14` | `animate-pulse` yer tutucu artık **katlamanın üstünde** ve `prefers-reduced-motion` okunmuyor (WCAG 2.2.2'nin ruhu; sürekli, durdurulamaz hareket) | `motion-reduce:animate-none` ekle (tek sınıf) |
|
||
| H2 | `manuel-harita.tsx:84-86` → `yurtdisi-haritalar-lazy.tsx:8-11` | `dynamic(..., { ssr:false })` ama `loading` **yok**; chunk inince ~150 px'lik şerit yoktan belirir. Bugün katlamanın altındaydı, yarın üstünde olacak | `loading: () => <div className="mt-3 h-40" aria-hidden />` ya da eşdeğer sabit yükseklik |
|
||
|
||
**Artık kalan (kabul edilen) kayma:** listesinde KKTC/yurt dışı program olan kullanıcıda yurt dışı
|
||
şeridi + "N tercih haritada gösterilemiyor" satırı sunucuda bilinemez; hidrasyonda belirir. H2
|
||
uygulanırsa bu ~150 px'lik tek kaymaya iner (mobil CLS ~0,11). Bunu da sıfırlamak çereze ikinci bir
|
||
alan yazmayı gerektirir — **önermiyorum**, kazanç bedeli haklı çıkarmıyor.
|
||
|
||
## 1.7 Reddedilen seçenekler
|
||
|
||
- **Her zaman sabit yükseklikli yer tutucu:** Bilal'in "liste eklenmezse gözükmesin" şartını
|
||
doğrudan çiğniyor.
|
||
- **Harita üstte ama küçük/sabit, büyümeyen biçim:** sorun haritanın *boyutu* değil, `null`→görünür
|
||
geçişi. 60 px'lik bir şerit bile aynı kaymayı üretir, üstelik harita okunmaz hale gelir.
|
||
- **Girişli kullanıcıda listeyi DB'den okumak:** `/sonuc` bugün oturum açmıyor; oturum + sorgu
|
||
eklemek sayfayı yavaşlatır ve **girişsiz** çoğunluğu (funnel'ın asıl kitlesi) hiç kapsamaz.
|
||
Çerez ikisini birden çözüyor.
|
||
|
||
---
|
||
|
||
# Talimat 2 — Üniversite sayfasında "tür"
|
||
|
||
## 2.1 Izgara gerçekte 4 kart değil, 4 **hücre**
|
||
|
||
```
|
||
universite-icerik.tsx:291
|
||
grid-cols-2 ← mobil
|
||
lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2 ← masaüstü
|
||
universite-konum-haritasi.tsx:77
|
||
col-span-2 lg:col-start-3 lg:row-start-1 lg:row-span-2
|
||
```
|
||
|
||
Masaüstünde harita 3. sütunu **iki satır boyunca** kaplıyor; kartlara kalan yer 2 sütun × 2 satır =
|
||
**tam 4 hücre**. 5. kart şablonu bozar: ya 3. bir satır açılır (harita yanında boşluk kalır) ya da
|
||
`grid-rows-2` elle 3'e çıkarılır ve haritanın oranı bozulur. Mobilde 5. kart 2 sütunlu ızgarada
|
||
**tek başına** bir satıra düşer (yanında boşluk) ve ilk ekrandan ~90 px daha gider.
|
||
|
||
→ **(a) "ızgara 5 karta çıksın mı?" cevabı: hayır.** Gerekçe estetik değil, şablonun kendisi.
|
||
|
||
## 2.2 Asıl mesele: tür bir metrik değil
|
||
|
||
Izgaradaki diğer üç kart sayısal ve karşılaştırılabilir (Program, Fakülte/Birim, En iyi sıralama,
|
||
URAP). "Devlet" bir ölçüm değil, kurumun **kimliği**. Sitenin geri kalanı bunu zaten böyle işliyor:
|
||
başlığın altındaki meta satırında küçük bir çip olarak, dört ayrı yerde **birebir aynı sınıf setiyle**:
|
||
|
||
```
|
||
shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4
|
||
DEVLET → bg-slate-100 text-slate-600 (kontrast 6,92:1 ✅)
|
||
diğer → bg-violet-50 text-violet-600 (kontrast 5,19:1 ✅ — hesap: WCAG 2.x, #7C3AED/#F5F3FF)
|
||
```
|
||
`program-tablosu.tsx:328-336` · `rapor-listesi.tsx:225-234` · `tadimlik-satiri.tsx:160-166` ·
|
||
`bolum-program-listesi.tsx:220-229`
|
||
|
||
Üniversite **detay** sayfası, bu çipi taşımayan tek yüzey. Eksik olan şey kart değil, çip.
|
||
|
||
## 2.3 İki alternatif
|
||
|
||
### Alternatif A — çip, H1'in altındaki meta satırında (ÖNERİLEN)
|
||
|
||
```
|
||
390 px · /universite/koc-universitesi · ilk ekran
|
||
|
||
Ana Sayfa › Üniversiteler › Koç Üniversitesi
|
||
|
||
┌────┐ Koç Üniversitesi
|
||
│logo│ Taban Puanları
|
||
│ 64 │ 2026
|
||
└────┘
|
||
[Vakıf] İstanbul ← EKLENEN TEK SATIR (mt-3, ~22 px)
|
||
çip sınıfı: program-tablosu.tsx:329 ile birebir
|
||
|
||
Koç Üniversitesi (İstanbul), vakıf üniversitesi; ← gece/0922-urap'taki
|
||
2026 YKS yerleştirme sonuçlarına göre … telafi cümlesi KALIR
|
||
|
||
[ Sıralaman Koç tabanlarının neresinde? → ]
|
||
|
||
┌─ harita (col-span-2, min-h-44) ──────────┐
|
||
│ │
|
||
└──────────────────────────────────────────┘
|
||
┌───────────────┐┌───────────────┐
|
||
│ PROGRAM ││ FAKÜLTE/BİRİM │
|
||
│ 78 ││ 12 │
|
||
└───────────────┘└───────────────┘
|
||
┌───────────────┐┌───────────────┐
|
||
│ URAP TÜRKİYE ││ EN İYİ SIRALAMA│ ← 4 hücre, hepsi sayısal
|
||
│ 34 / 198 ││ 412 │
|
||
│ 2025-2026 ││ │
|
||
└───────────────┘└───────────────┘
|
||
```
|
||
|
||
- **Yeni bileşen yok**, yeni sınıf yok: çip dört yerde duran sınıf setinin beşinci kopyası (ya da —
|
||
tercihen — hepsinin çıkarılacağı tek kaynak, bkz. §2.5).
|
||
- **Türün kaç kez geçtiği:** çip 1 + cümle 1 = 2, biri taranabilir, biri okunabilir. Kart yok.
|
||
- **Mobil:** H1 bloğu (logo `size-16` + `text-3xl`) 390 px'te zaten ~180 px; çip satırı 22 px ekler,
|
||
taşma riski yok (`flex items-center gap-1.5`, çip `shrink-0`, il `truncate`).
|
||
- **Erişilebilirlik:** çip metni okunur ("Vakıf"), renk tek başına anlam taşımıyor — AGENTS.md'nin
|
||
risk dili kuralının aynı mantığı.
|
||
- **Gereken tek yan değişiklik:** URAP'ı olmayan 31 sayfada 4. hücre bugün "Tür" kartı olarak
|
||
dolduruluyor (`gece/0922-urap`, `:394-417`). Çip geldiğinde o sayfalarda tür **üç kez** yazar.
|
||
4. hücre oraya **"Toplam kontenjan"** olur (`uni.stats.toplamKontenjan`, `lib/katalog.ts:380-401`).
|
||
Veri kontrolü: `data/yokatlas.db` — toplam kontenjanı 0 olan **hiçbir üniversite yok**
|
||
(`SELECT … GROUP BY universite HAVING SUM(COALESCE(kontenjan2026,0))=0` → 0 satır), yani bu hücrede
|
||
asla "—" çıkmaz. Precedent var: `minSira` de hem paragrafta hem kartta geçiyor.
|
||
|
||
### Alternatif B — "Tür" kartı geri gelir, URAP satır olur
|
||
|
||
```
|
||
Koç Üniversitesi (İstanbul), vakıf üniversitesi; 2026 YKS …
|
||
URAP Türkiye sıralaması: 34 / 198 (2025-2026) ← paragrafın altına yeni satır
|
||
|
||
┌ PROGRAM 78 ┐┌ FAKÜLTE 12 ┐
|
||
┌ TÜR Vakıf ┐┌ EN İYİ 412 ┐
|
||
```
|
||
|
||
- Artısı: diff küçük, `gece/0922-urap`'ın kart kısmı geri alınır.
|
||
- Eksileri: (a) **sayı prozaya gömülür** — URAP taranmak için var, "34/198" cümle içinde aranır hale
|
||
gelir; tasarımın dünkü gerekçesinin tersi. (b) Izgarada kategori/metrik karışımı sürer.
|
||
(c) Tür yine `text-2xl font-bold` ile "Vakıf MYO" gibi uzun değerlerde 190 px'lik karta sığmaz.
|
||
(d) Çip eksikliği kapanmaz: detay sayfası hâlâ sitenin geri kalanından farklı konuşur.
|
||
|
||
### Hüküm: **A**
|
||
|
||
Bilal'in "tür olsun" isteği A'da daha güçlü karşılanıyor: tür artık **ızgaranın dördüncü kutusunda,
|
||
ekranın ortasında** değil, **üniversitenin adının hemen altında** — hem ilk bakışta hem her sayfa
|
||
genişliğinde. Ve site genelindeki çiple aynı dili konuşuyor.
|
||
|
||
## 2.4 Uygulayıcıya tarif (yazılımcı)
|
||
|
||
Taban: `gece/0922-urap` (orada `UNITUR_ETIKET` ve telafi cümlesi hazır). Dal bayatsa önce
|
||
`origin/main` üstüne yeniden kurulmalı.
|
||
|
||
1. `UNITUR_ETIKET`i (`universite-icerik.tsx:54-66`) **`src/lib/unitur.ts`**'e taşı ve üçüncü alan
|
||
ekle: `{ kart, cumle, cip }`. `cip` = çipte görünen kısa metin (`Devlet`, `Vakıf`, `Vakıf MYO`,
|
||
`KKTC`, `Yurt dışı (kamu)`, `Yurt dışı (vakıf)`) — `kart` ile aynı değerler, ama `kart` alanı
|
||
§2.4-3 ile ölüyorsa ikisi birleştirilebilir. Gerekçe: aynı bilginin ekrandaki etiketi tek
|
||
kaynaktan gelmeli — `DILIM_ETIKET`'in `src/lib/risk.ts`'te durması gibi (AGENTS.md).
|
||
2. `universite-icerik.tsx`, H1 bloğunun (`:253-259`) **hemen altına**, `ilkSayfa` koşulundan
|
||
**bağımsız** (sayfalama sayfalarında da üniversite kimliği aynı):
|
||
```
|
||
<p className="mt-3 flex items-center gap-1.5 text-xs text-slate-500"> …çip… {il} </p>
|
||
```
|
||
Çipin sınıfı `program-tablosu.tsx:329-333`'ten **kopyalanır**, yeni varyant üretilmez.
|
||
Renk kuralı da aynı: `DEVLET` → slate, diğer her şey → violet.
|
||
`uni.unitur` NULL ise satırın tamamı render edilmez (kopuk boşluk kalmaz).
|
||
3. Izgaradaki 4. hücre (`gece/0922-urap`, `:394-417`): `uni.urap ? URAP : Tür` koşulu
|
||
→ `uni.urap ? URAP : Toplam kontenjan`. `dt`: "Toplam kontenjan", `dd`: `sayi(uni.stats.toplamKontenjan)`.
|
||
4. `bolum-program-listesi.tsx:227` `trBaslikDuzeni(p.unitur)` → `UNITUR_ETIKET[p.unitur].cip`.
|
||
Bugün o çip "Kktc" / "Vakıf Myo" / "Yurtdısı Kamu" basıyor (`slug.ts:56-64` tr-TR küçültmesi).
|
||
**Aynı commit'e girmeli**: tek kaynak taşınırken düzelmeyen tek tüketici kalmasın.
|
||
|
||
**Kabul kriterleri**
|
||
|
||
1. `bogazici-universitesi.html` → çipte "Devlet" **1 kez**, özet cümlesinde "devlet üniversitesi"
|
||
**1 kez**, ızgarada "Tür" **0 kez**.
|
||
2. `dogu-akdeniz-universitesi.html` (URAP yok) → çipte "KKTC", 4. hücrede "Toplam kontenjan" + sayı,
|
||
hiçbir hücrede "—" yok.
|
||
3. `/bolum/...` sayfalarında KKTC programının çipinde "KKTC" yazıyor, "Kktc" değil.
|
||
4. 390 px'te H1 bloğu + çip satırı yatay taşma üretmiyor (uzun üniversite adı: "İstanbul Medeniyet
|
||
Üniversitesi").
|
||
5. `pnpm build` temiz; 229 üniversite sayfasının 229'unda çip var.
|
||
|
||
**Büyüklük: S** (2 yeni/taşınan dosya + 3 dokunuş, ~50 satır).
|
||
|
||
## 2.5 Aynı işte kapanacak tutarlılık borcu
|
||
|
||
| Yer | Sorun | Kanonik kaynak |
|
||
|---|---|---|
|
||
| `program-tablosu.tsx:329` · `rapor-listesi.tsx:227` · `tadimlik-satiri.tsx:161` · `bolum-program-listesi.tsx:222` | Aynı çip sınıf seti **4 kez elle kopyalanmış**; 5.'si ekleniyor | AGENTS.md "ortak elemanlar tek kaynaktan". **Öneri:** `src/components/unitur-cipi.tsx` — küçük, tek amaçlı sunum bileşeni. Bu, "yeni bileşen tasarlama" yasağının kapsamına girmez: yeni bir *tasarım* değil, var olan beş kopyanın tek kaynağa alınmasıdır. Ayrı ve tek amaçlı commit. |
|
||
| `app/api/katalog-ara/route.ts:49-56` | `unitur` için **beşinci** sözlük: "KKTC / Yurt dışı" (arama sonucunda böyle, sayfada "KKTC") | Aynı `UNITUR_ETIKET`. Düşük öncelik; ayrı iş. |
|
||
| `program-tablosu.tsx:175` | Root `<section className="mt-16">` — `first:mt-0` yok. Girişli + listesiz kullanıcıda (tadımlık yok, harita yok) tablo `main`'in ilk çocuğu olur, tepede 64 px ölü alan kalır | `tadimlik-satiri.tsx:134` deseni (`mt-16 first:mt-0`). XS, Talimat 1 ile aynı commit'e sığar |
|
||
|
||
---
|
||
|
||
## Bilal'in gözle kontrol etmesi gerekenler (375–390 px)
|
||
|
||
1. **`/sonuc`, listeye 1-2 program ekledikten sonra sayfayı yenile.** Harita en üstte mi, açılışta
|
||
yukarı/aşağı zıplama var mı? (Talimat 1'in tek gerçek testi.)
|
||
2. **`/sonuc`, tarayıcı verisini sildikten sonra ilk kez.** Harita hiç yok mu, tadımlık en üstte mi
|
||
ve üstünde fazladan boşluk var mı? (`first:mt-0` kontrolü.)
|
||
3. **`/sonuc`, girişsiz + listeli.** Tadımlığın başlığı ("Yapay zekâ listendeki her satırı böyle
|
||
anlatır") hâlâ katlamanın üstünde mi? Hükmüm buna dayanıyor (§1.4).
|
||
4. **Listesinde KKTC/yurt dışı programı olan bir listeyle `/sonuc`.** Türkiye haritasının altındaki
|
||
yurt dışı şeridi geç belirip içeriği itiyor mu? (§1.6-H2)
|
||
5. **`/universite/koc-universitesi` ve `/universite/dogu-akdeniz-universitesi`.** Üniversite adının
|
||
altındaki tür çipi ilk bakışta görülüyor mu, yoksa çok mu küçük kalıyor? (A alternatifinin tek
|
||
öznel riski budur; "daha büyük olsun" dersen o **yeni bir çip varyantı** demektir ve site
|
||
genelindeki dört kopyayı da etkiler → `[BİLAL]` kararı.)
|
||
|
||
## Bilal'den istenen (3 madde)
|
||
|
||
1. **Talimat 1 için A'yı onayla** (liste sayısı çerezi + harita en üstte). Yeni bir işlevsel çerez
|
||
yazılacak; kişisel veri taşımıyor, `kolaytercih.profil` ile aynı sınıf. **~2 dk.**
|
||
2. **Talimat 2 için A'yı onayla**: tür = ad altındaki çip, URAP'ı olmayan 31 sayfada 4. kart
|
||
"Toplam kontenjan" olur. **~2 dk.**
|
||
3. **§2.5'teki `UniturCipi` tek-kaynak commit'i** ayrı iş olarak backlog'a girsin mi
|
||
(5 kopya → 1 bileşen)? **~1 dk.**
|