feat(ui): Arc bileşenleri, landing bölümleri ve bildirim yığını

Sonner/vaul yerine kendi bildirim yığını ve alt sayfa; çip grubu, kart
destesi, kaydırma eylemi, yazı dönüşümü, bölme tutamacı; landing'e program
duvarı, süzülen haplar ve veri akışı; yaylar tek dosyada (lib/hareket.ts).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-07 17:12:53 +03:00
parent bc920f6111
commit 3c995402c6
38 changed files with 5474 additions and 697 deletions

View File

@@ -5,7 +5,6 @@ import {
Armchair,
Briefcase,
Calculator,
ChartLine,
ChevronRight,
GraduationCap,
Landmark,
@@ -35,11 +34,7 @@ import { VeriKaynagi } from "./veri-kaynagi";
import { NavBaslik } from "./nav-baslik";
import { AkademikAyrinti } from "./akademik-ayrinti";
import { OzetKart, kartSayisi } from "@/components/ozet-kart";
import {
KartBaglantisi,
kisaUniAdi,
ortaTabanAciklamasi,
} from "./taban-karti";
import { KartBaglantisi, kisaUniAdi } from "./taban-karti";
import { trBaslikDuzeni } from "@/lib/slug";
import {
BOLUM_ISTIHDAM_KAYNAK_URL,
@@ -106,7 +101,7 @@ function MezunIstihdami({
const istihdamKaresi =
veri.istihdam != null ? (
<div data-kare="sol" className={`${kare} min-h-40 bg-orange-500 py-4`}>
<dt className={etiket}>Kayıtlı çalışan</dt>
<dt className={etiket}>İşe yerleşen</dt>
<dd className={`mt-3 ${devSayi}`}>%{ondalik(veri.istihdam)}</dd>
{ort.istihdam != null ? (
<dd className="mt-2 text-base font-bold text-white/90">
@@ -165,7 +160,7 @@ function MezunIstihdami({
>
Türkiye geneli, tüm üniversitelerin{" "}
{duzey === "lisans" ? "lisans" : "ön lisans"} mezunları birlikte;
üniversiteye göre değişir. Kayıtlı çalışan, sigortalı bir işte
üniversiteye göre değişir. İşe yerleşen, sigortalı bir işte
çalışanların oranıdır. Kazanç grubu bölümler arası kıyastır, maaş
değildir.
</p>
@@ -409,10 +404,11 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
...(bolum.onlisansProgramlari.length > 0 ? ["Önlisans"] : []),
];
// Özet kartları (Bilal, 6 Eki 2026: "kaç program var" sayıları ilgi
// çekmiyor): öğrencinin sorusuna cevap veren dört sayı — en seçici
// program, devlette en geniş kapı, orta tabanın yıllar içindeki seyri ve
// boş kalan koltuk. Hesap lib/taban-ozeti.ts; program/devlet/vakıf sayıları
// özet paragrafta kalır. Üniversite sayfasıyla aynı düzen (DESIGN.md §4.8).
// çekmiyor): öğrencinin sorusuna cevap veren üç sayı — en yüksek taban,
// devlette en düşük taban ve boş kalan kontenjan. Orta taban kartı
// kaldırıldı (Bilal, 6 Eki 2026). Hesap lib/taban-ozeti.ts; program/
// devlet/vakıf sayıları özet paragrafta kalır. Üniversite sayfasıyla aynı
// kartlar (DESIGN.md §4.8).
const { taban } = bolum;
const uniBaglantisi = (universite: string) => (
<KartBaglantisi
@@ -554,13 +550,14 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
<CtaSiraForm varyant="kompakt" konum="ust" baslik={ctaBaslik} />
</div>
{/* Sayfada en çok dört özet sayı (DESIGN.md §1.1, §4.8). Derin
sayfalarda tekrar edilmez — üniversite sayfasıyla aynı. */}
{/* Sayfada en çok dört özet sayı (DESIGN.md §1.1, §4.8); burada üç.
Telefonda iki sütunda tek kalan üçüncü kart satırı boylu boyunca
alır. Derin sayfalarda tekrar edilmez — üniversite sayfasıyla aynı. */}
{ilkSayfa ? (
<dl className="mt-8 grid grid-cols-2 gap-4 lg:grid-cols-4">
<dl className="mt-8 grid grid-cols-2 gap-4 lg:grid-cols-3 [&>*:last-child]:col-span-2 lg:[&>*:last-child]:col-span-1">
<OzetKart
ikon={Target}
etiket="En seçici"
etiket="En yüksek taban"
{...(taban.enSecici
? kartSayisi(taban.enSecici.sira)
: { deger: "—" })}
@@ -575,11 +572,11 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
)
}
/>
{/* Etiket kısa kalır ("Devlette en geniş" 131 px'te iki satıra
{/* Etiket kısa kalır ("Devlette en düşük" 131 px'te iki satıra
kırılıyor, 6 Eki 2026 ölçümü); küme açıklamanın başında. */}
<OzetKart
ikon={Landmark}
etiket="En geniş kapı"
etiket="En düşük taban"
{...(taban.enGenis
? kartSayisi(taban.enGenis.sira)
: { deger: "—" })}
@@ -595,18 +592,9 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
)
}
/>
<OzetKart
ikon={ChartLine}
etiket="Orta taban"
{...(() => {
const son = [...taban.ortaTaban].reverse().find((v) => v != null);
return son != null ? kartSayisi(son) : { deger: "—" };
})()}
alt={ortaTabanAciklamasi(taban)}
/>
<OzetKart
ikon={Armchair}
etiket="Boş koltuk"
etiket="Boş kontenjan"
deger={taban.doluluk ? sayi(taban.doluluk.bosKoltuk) : "—"}
alt={
taban.doluluk