feat(tasarim): /sonuc program tablosu "şişko ve sade" dile göre
- Liste satırı sınıfları src/components/liste-satiri.ts'e çıkarıldı; üniversite, bölüm ve /sonuc tabloları oradan okuyor - /sonuc satırı: ad sarar, üniversite adı ayrı satırda ve düzgün harfle, telefonda risk + taban sıra adın altında, 44 px butonlar, "boş kaldı" 12 px altından çıktı - Dilim sekmeleri 375 px'te taşıyordu (418 px); telefonda üç eşit hap - Tercih kapısında büyük harfli etiketler SectionEyebrow'a, harita şeridinde 10 px yazılar 12 px'e Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -336,7 +336,13 @@ verilir (logo listesi istemciye inmez).
|
||||
Hareket: yay `bounce: 0, duration: 0.35`, giriş ve çıkış aynı yoldan; "hareketi azalt"ta yalnız
|
||||
opaklık.
|
||||
|
||||
### 4.15 Liste satırı (program/üniversite listeleri) — `universite-program-tablosu.tsx`
|
||||
### 4.15 Liste satırı (program/üniversite listeleri) — `src/components/liste-satiri.ts`
|
||||
Sınıf dizileri tek dosyada (`SATIR_AD`, `SATIR_META`, `SATIR_CIP`, `SATIR_MOBIL_SAYI`,
|
||||
`SATIR_BUTON_KABI`…); üniversite, bölüm ve `/sonuc` tabloları oradan okur. Yeni bir liste
|
||||
tablosu bu sabitleri kullanır, sınıfı elle yazmaz.
|
||||
- Risk sütunu (`/sonuc`) sm'den itibaren ayrı sütun; telefonda adın altındaki sayı satırına
|
||||
iner. Etiket §4.5: `size-2` nokta + `text-xs font-semibold text-{renk}-600` + `RISK_ETIKET`.
|
||||
- Veride BÜYÜK HARF gelen üniversite adı ekranda `trBaslikDuzeni` ile yazılır.
|
||||
- **Ad kesilmez, sarar:** `whitespace-normal` + `block text-base font-semibold leading-snug`.
|
||||
`truncate` ile "İngiliz Dili ve E…" yazılmaz.
|
||||
- Ad linkinin hover'ı **mavi**: `transition-colors hover:text-primary hover:underline`
|
||||
@@ -394,6 +400,11 @@ Haplar `h-11` (44 px), hover §2.1. Telefonda (sm altı) tek satır kalsın diye
|
||||
aktif sayfa · Sonraki görünür; diğer numaralar `hidden sm:inline-flex` ile HTML'de durur
|
||||
(tarayıcı botları tüm sayfaları görür). 375 px'te 7 sayfalık şerit iki satıra kırılıyordu.
|
||||
|
||||
### 5.2.2 Dilim sekmeleri (Hayal / Dengeli / Güvenli) — `program-tablosu.tsx`
|
||||
Tek satırda üç hap 375 px'e sığmıyordu (418 px, sayfa yana kayıyordu). Telefonda
|
||||
`grid w-full grid-cols-3`, hapın içinde ikon+ad üstte, sayı altta; sm'den tek satır. Hap en az
|
||||
44 px (`min-h-11`), hover §2.1.
|
||||
|
||||
### 5.3 Verimli ve havadar
|
||||
- İlişkili öğeler yan yana dizilir (iki sütun) — tek sütunda alt alta uzayan panel yapılmaz.
|
||||
- Yan yana dizmek "hepsini tek şeride sıkıştırmak" değildir: 23 Eyl'de denenen yatay "bilgi
|
||||
|
||||
45
src/components/liste-satiri.ts
Normal file
45
src/components/liste-satiri.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Liste satırının (DESIGN.md §4.15) sınıf dizileri — tek kaynak. Üniversite,
|
||||
* bölüm ve /sonuc program tabloları (ileride rapor listesi) aynı satırı
|
||||
* çizer; sınıflar her dosyada ayrı yazılınca ölçüler kayıyordu. "use client"
|
||||
* yok: sunucu bileşenleri de değer olarak okuyabilsin (§9.3).
|
||||
*/
|
||||
|
||||
/** Satırın adı (bölüm/üniversite sayfasına link): sarar, hover mavi (§2.1 istisnası). */
|
||||
export const SATIR_AD =
|
||||
"block text-base font-semibold leading-snug text-slate-900 transition-colors hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
||||
|
||||
/** Link olmayan ad (slug yoksa). */
|
||||
export const SATIR_AD_DUZ =
|
||||
"block text-base font-semibold leading-snug text-slate-900";
|
||||
|
||||
/** Adın altındaki ikinci satır (varyant ya da üniversite adı). */
|
||||
export const SATIR_ALT_AD = "mt-0.5 block text-sm leading-snug text-slate-600";
|
||||
|
||||
/** Satırdaki ikincil link (ör. üniversite adı) — §2.1 tek turuncu. */
|
||||
export const SATIR_IKINCIL_LINK =
|
||||
"transition-colors hover:text-orange-500 hover:underline";
|
||||
|
||||
/** Tür/il/süre satırı. */
|
||||
export const SATIR_META =
|
||||
"mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-slate-500";
|
||||
|
||||
/** Tür çipi (Devlet, Vakıf, SAY…). */
|
||||
export const SATIR_CIP =
|
||||
"shrink-0 rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700";
|
||||
|
||||
/** Telefonda sayısal sütunun adın altına indiği satır. */
|
||||
export const SATIR_MOBIL_SAYI =
|
||||
"mt-1.5 flex flex-wrap items-center gap-x-1.5 gap-y-1 text-sm text-slate-500 sm:hidden";
|
||||
|
||||
/** Tablo başlık hücresi. */
|
||||
export const SATIR_BASLIK = "h-12 text-sm font-semibold text-slate-500";
|
||||
|
||||
/** Adın bulunduğu hücre. */
|
||||
export const SATIR_AD_HUCRE = "w-full whitespace-normal py-4 pl-4 sm:pl-5";
|
||||
|
||||
/** Butonların hücresi ve kabı: telefonda alt alta, sm'den yan yana. */
|
||||
export const SATIR_BUTON_HUCRE = "py-4 pr-4 text-right sm:pr-5";
|
||||
export const SATIR_BUTON_KABI =
|
||||
"inline-flex flex-col items-center gap-2 sm:flex-row";
|
||||
export const SATIR_BUTON_BASLIK = "h-12 w-14 text-right sm:w-28";
|
||||
@@ -12,6 +12,19 @@ import {
|
||||
ProgramSiraGecmisi,
|
||||
ProgramTabanTrendi,
|
||||
} from "@/components/program-liste-verileri";
|
||||
import {
|
||||
SATIR_AD,
|
||||
SATIR_AD_DUZ,
|
||||
SATIR_AD_HUCRE,
|
||||
SATIR_ALT_AD,
|
||||
SATIR_BASLIK,
|
||||
SATIR_BUTON_BASLIK,
|
||||
SATIR_BUTON_HUCRE,
|
||||
SATIR_BUTON_KABI,
|
||||
SATIR_CIP,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
} from "@/components/liste-satiri";
|
||||
import { ProgramEkleButonu } from "@/features/liste/components/program-ekle-butonu";
|
||||
import { ProgramNetleriButonu } from "./program-netleri";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
@@ -162,10 +175,10 @@ export function BolumProgramListesi({
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="hover:bg-transparent">
|
||||
<TableHead className="h-12 pl-4 text-sm font-semibold text-slate-500 sm:pl-5">
|
||||
<TableHead className={`${SATIR_BASLIK} pl-4 sm:pl-5`}>
|
||||
Üniversite
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right text-sm font-semibold text-slate-500 lg:table-cell">
|
||||
<TableHead className={`hidden text-right lg:table-cell ${SATIR_BASLIK}`}>
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right lg:table-cell">
|
||||
@@ -192,7 +205,7 @@ export function BolumProgramListesi({
|
||||
onSirala={sirala}
|
||||
/>
|
||||
</TableHead>
|
||||
<TableHead className="h-12 w-14 text-right sm:w-28">
|
||||
<TableHead className={SATIR_BUTON_BASLIK}>
|
||||
<span className="sr-only">Netler ve listeye ekle</span>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
@@ -206,7 +219,7 @@ export function BolumProgramListesi({
|
||||
|
||||
return (
|
||||
<TableRow key={p.id} className="kt-cv-satir">
|
||||
<TableCell className="w-full whitespace-normal py-4 pl-4 sm:pl-5">
|
||||
<TableCell className={SATIR_AD_HUCRE}>
|
||||
<span className="flex items-start gap-3">
|
||||
{p.universiteSlug ? (
|
||||
<UniLogo slug={p.universiteSlug} ad={uniAd} boy="sm" />
|
||||
@@ -215,23 +228,23 @@ export function BolumProgramListesi({
|
||||
{p.universiteSlug ? (
|
||||
<Link
|
||||
href={`/universite/${p.universiteSlug}`}
|
||||
className="block text-base font-semibold leading-snug text-slate-900 transition-colors hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className={SATIR_AD}
|
||||
>
|
||||
{uniAd}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="block text-base font-semibold leading-snug text-slate-900">
|
||||
<span className={SATIR_AD_DUZ}>
|
||||
{uniAd}
|
||||
</span>
|
||||
)}
|
||||
{varyant ? (
|
||||
<span className="mt-0.5 block text-sm leading-snug text-slate-600">
|
||||
<span className={SATIR_ALT_AD}>
|
||||
{varyant}
|
||||
</span>
|
||||
) : null}
|
||||
<span className="mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-slate-500">
|
||||
<span className={SATIR_META}>
|
||||
{p.unitur ? (
|
||||
<span className="shrink-0 rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700">
|
||||
<span className={SATIR_CIP}>
|
||||
{UNITUR_ETIKET[p.unitur]?.cip ??
|
||||
trBaslikDuzeni(p.unitur)}
|
||||
</span>
|
||||
@@ -245,7 +258,7 @@ export function BolumProgramListesi({
|
||||
<span className="shrink-0">{p.sure} yıl</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className="mt-1.5 flex items-center gap-1.5 text-sm text-slate-500 sm:hidden">
|
||||
<span className={SATIR_MOBIL_SAYI}>
|
||||
Taban sıra
|
||||
<span className="font-semibold tabular-nums text-slate-900">
|
||||
{tabanMetni}
|
||||
@@ -272,8 +285,8 @@ export function BolumProgramListesi({
|
||||
{tabanMetni}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="py-4 pr-4 text-right sm:pr-5">
|
||||
<span className="inline-flex flex-col items-center gap-2 sm:flex-row">
|
||||
<TableCell className={SATIR_BUTON_HUCRE}>
|
||||
<span className={SATIR_BUTON_KABI}>
|
||||
<ProgramNetleriButonu
|
||||
programId={p.id}
|
||||
isim={p.isim.trim()}
|
||||
|
||||
@@ -8,6 +8,18 @@ import {
|
||||
ProgramTabanTrendi,
|
||||
programEtkinSira,
|
||||
} from "@/components/program-liste-verileri";
|
||||
import {
|
||||
SATIR_AD,
|
||||
SATIR_AD_DUZ,
|
||||
SATIR_AD_HUCRE,
|
||||
SATIR_BASLIK,
|
||||
SATIR_BUTON_BASLIK,
|
||||
SATIR_BUTON_HUCRE,
|
||||
SATIR_BUTON_KABI,
|
||||
SATIR_CIP,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
} from "@/components/liste-satiri";
|
||||
import { ProgramEkleButonu } from "@/features/liste/components/program-ekle-butonu";
|
||||
import { ProgramNetleriButonu } from "./program-netleri";
|
||||
import {
|
||||
@@ -50,22 +62,22 @@ export function UniversiteProgramTablosu({
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="hover:bg-transparent">
|
||||
<TableHead className="h-12 pl-4 text-sm font-semibold text-slate-500 sm:pl-5">
|
||||
<TableHead className={`${SATIR_BASLIK} pl-4 sm:pl-5`}>
|
||||
Program
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right text-sm font-semibold text-slate-500 lg:table-cell">
|
||||
<TableHead className={`hidden text-right lg:table-cell ${SATIR_BASLIK}`}>
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right text-sm font-semibold text-slate-500 lg:table-cell">
|
||||
<TableHead className={`hidden text-right lg:table-cell ${SATIR_BASLIK}`}>
|
||||
{SON_YIL} puan
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right text-sm font-semibold text-slate-500 md:table-cell">
|
||||
<TableHead className={`hidden text-right md:table-cell ${SATIR_BASLIK}`}>
|
||||
Kontenjan
|
||||
</TableHead>
|
||||
<TableHead className="hidden h-12 text-right text-sm font-semibold text-slate-500 sm:table-cell">
|
||||
<TableHead className={`hidden text-right sm:table-cell ${SATIR_BASLIK}`}>
|
||||
Taban sıra
|
||||
</TableHead>
|
||||
<TableHead className="h-12 w-14 text-right sm:w-28">
|
||||
<TableHead className={SATIR_BUTON_BASLIK}>
|
||||
<span className="sr-only">Netler ve listeye ekle</span>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
@@ -76,28 +88,28 @@ export function UniversiteProgramTablosu({
|
||||
const tabanMetni = taban != null ? `~${sayi(taban)}.` : "—";
|
||||
return (
|
||||
<TableRow key={p.id} className="kt-cv-satir">
|
||||
<TableCell className="w-full whitespace-normal py-4 pl-4 sm:pl-5">
|
||||
<TableCell className={SATIR_AD_HUCRE}>
|
||||
{p.bolumSlug ? (
|
||||
<Link
|
||||
href={`/bolum/${p.bolumSlug}`}
|
||||
className="block text-base font-semibold leading-snug text-slate-900 transition-colors hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className={SATIR_AD}
|
||||
>
|
||||
{p.isim.trim()}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="block text-base font-semibold leading-snug text-slate-900">
|
||||
<span className={SATIR_AD_DUZ}>
|
||||
{p.isim.trim()}
|
||||
</span>
|
||||
)}
|
||||
<span className="mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-slate-500">
|
||||
<span className="shrink-0 rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700">
|
||||
<span className={SATIR_META}>
|
||||
<span className={SATIR_CIP}>
|
||||
{TUR_ETIKET[p.tur] ?? p.tur}
|
||||
</span>
|
||||
{p.sure ? (
|
||||
<span className="shrink-0">{p.sure} yıl</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className="mt-1.5 flex items-center gap-1.5 text-sm text-slate-500 sm:hidden">
|
||||
<span className={SATIR_MOBIL_SAYI}>
|
||||
Taban sıra
|
||||
<span className="font-semibold tabular-nums text-slate-900">
|
||||
{tabanMetni}
|
||||
@@ -124,8 +136,8 @@ export function UniversiteProgramTablosu({
|
||||
{tabanMetni}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="py-4 pr-4 text-right sm:pr-5">
|
||||
<span className="inline-flex flex-col items-center gap-2 sm:flex-row">
|
||||
<TableCell className={SATIR_BUTON_HUCRE}>
|
||||
<span className={SATIR_BUTON_KABI}>
|
||||
<ProgramNetleriButonu
|
||||
programId={p.id}
|
||||
isim={p.isim.trim()}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
riskHesapla,
|
||||
type RiskSeviyesi,
|
||||
} from "@/lib/risk";
|
||||
import { bolumSayfaSlug, uniSayfaSlug } from "@/lib/slug";
|
||||
import { bolumSayfaSlug, trBaslikDuzeni, uniSayfaSlug } from "@/lib/slug";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-yillari";
|
||||
import {
|
||||
ProgramSiraGecmisi,
|
||||
@@ -46,6 +46,20 @@ import { ProgramEkleButonu } from "@/features/liste/components/program-ekle-buto
|
||||
import { preloadTercihHaritasi } from "@/features/liste/components/tercih-haritasi-lazy";
|
||||
import { ProgramNetleriButonu } from "@/features/katalog/components/program-netleri";
|
||||
import { UniLogo } from "@/components/uni-logo";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
SATIR_AD,
|
||||
SATIR_AD_HUCRE,
|
||||
SATIR_ALT_AD,
|
||||
SATIR_BASLIK,
|
||||
SATIR_BUTON_BASLIK,
|
||||
SATIR_BUTON_HUCRE,
|
||||
SATIR_BUTON_KABI,
|
||||
SATIR_CIP,
|
||||
SATIR_IKINCIL_LINK,
|
||||
SATIR_META,
|
||||
SATIR_MOBIL_SAYI,
|
||||
} from "@/components/liste-satiri";
|
||||
|
||||
const DILIMLER: {
|
||||
key: DilimKey;
|
||||
@@ -195,14 +209,17 @@ export function ProgramTablosu({
|
||||
navbar'daki sıralama rozetinden değiştirilir. Aktif hap, nav'daki
|
||||
"Giriş yap" butonuyla aynı dilde (koyu, rounded-full) ve layoutId
|
||||
ile sekmeler arasında kayarak taşınır. */}
|
||||
<TabsList variant="pill" className="self-center">
|
||||
<TabsList
|
||||
variant="pill"
|
||||
className="grid w-full grid-cols-3 self-center sm:inline-flex sm:w-auto"
|
||||
>
|
||||
{DILIMLER.map((d) => {
|
||||
const aktif = aktifDilim === d.key;
|
||||
return (
|
||||
<TabsTrigger
|
||||
key={d.key}
|
||||
value={d.key}
|
||||
className="relative cursor-pointer px-3 py-1.5 transition-colors duration-200 hover:bg-slate-100 sm:px-4 sm:py-2"
|
||||
className="relative min-h-11 cursor-pointer px-2 py-1.5 text-sm font-semibold transition-colors duration-200 hover:text-orange-500 data-active:hover:text-white group-data-[variant=pill]/tabs-list:h-auto sm:px-4"
|
||||
>
|
||||
{aktif ? (
|
||||
<motion.span
|
||||
@@ -216,13 +233,17 @@ export function ProgramTablosu({
|
||||
aria-hidden
|
||||
/>
|
||||
) : null}
|
||||
<span className="relative z-10 inline-flex items-center gap-1.5">
|
||||
<d.icon
|
||||
className={`size-3.5 ${aktif ? d.renkAktif : d.renk}`}
|
||||
aria-hidden
|
||||
/>
|
||||
{d.etiket}
|
||||
<span className="text-xs tabular-nums text-slate-400">
|
||||
<span className="relative z-10 inline-flex flex-col items-center gap-0.5 sm:flex-row sm:gap-1.5">
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<d.icon
|
||||
className={`size-4 ${aktif ? d.renkAktif : d.renk}`}
|
||||
aria-hidden
|
||||
/>
|
||||
{d.etiket}
|
||||
</span>
|
||||
<span
|
||||
className={`text-sm font-medium tabular-nums ${aktif ? "text-white/70" : "text-slate-400"}`}
|
||||
>
|
||||
{sonuclar.toplam[d.key].toLocaleString("tr-TR")}
|
||||
</span>
|
||||
</span>
|
||||
@@ -236,7 +257,7 @@ export function ProgramTablosu({
|
||||
const toplam = sonuclar.toplam[d.key];
|
||||
return (
|
||||
<TabsContent key={d.key} value={d.key}>
|
||||
<div className="mt-3 overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
<div className="mt-4 overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
{satirlar.length === 0 ? (
|
||||
<p className="px-5 py-8 text-center text-sm text-slate-500">
|
||||
Bu dilimde filtrene uyan program bulunamadı.
|
||||
@@ -244,17 +265,31 @@ export function ProgramTablosu({
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-8 pl-4 pr-0">Risk</TableHead>
|
||||
<TableHead>Program</TableHead>
|
||||
<TableHead className="hidden text-right md:table-cell">
|
||||
<TableRow className="hover:bg-transparent">
|
||||
<TableHead
|
||||
className={`hidden w-28 pl-4 pr-0 sm:table-cell sm:pl-5 ${SATIR_BASLIK}`}
|
||||
>
|
||||
Risk
|
||||
</TableHead>
|
||||
<TableHead className={`pl-4 ${SATIR_BASLIK}`}>
|
||||
Program
|
||||
</TableHead>
|
||||
<TableHead
|
||||
className={`hidden text-right lg:table-cell ${SATIR_BASLIK}`}
|
||||
>
|
||||
Son {VERI_YIL_SAYISI} yıl
|
||||
</TableHead>
|
||||
<TableHead className="hidden text-right sm:table-cell">
|
||||
<TableHead
|
||||
className={`hidden text-right md:table-cell ${SATIR_BASLIK}`}
|
||||
>
|
||||
Kontenjan
|
||||
</TableHead>
|
||||
<TableHead className="text-right">Taban sıra</TableHead>
|
||||
<TableHead className="w-24 text-right">
|
||||
<TableHead
|
||||
className={`hidden text-right sm:table-cell ${SATIR_BASLIK}`}
|
||||
>
|
||||
Taban sıra
|
||||
</TableHead>
|
||||
<TableHead className={SATIR_BUTON_BASLIK}>
|
||||
<span className="sr-only">Netler ve listeye ekle</span>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
@@ -273,17 +308,19 @@ export function ProgramTablosu({
|
||||
)}
|
||||
</div>
|
||||
{satirlar.length < toplam ? (
|
||||
<div className="mt-3 flex flex-col items-center gap-1">
|
||||
<button
|
||||
<div className="mt-4 flex flex-col items-center gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="lg"
|
||||
variant="outline"
|
||||
disabled={yuklenen === d.key}
|
||||
onClick={() => dahaFazlaYukle(d.key, satirlar.length)}
|
||||
className="inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition-colors duration-150 hover:border-slate-300 hover:bg-slate-50 disabled:cursor-default disabled:opacity-60"
|
||||
className="cursor-pointer px-6 transition-[background-color,border-color,color,transform] duration-150 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500 active:scale-[0.97]"
|
||||
>
|
||||
<ChevronDown className="size-4" aria-hidden />
|
||||
{yuklenen === d.key ? "Yükleniyor…" : "Daha fazla göster"}
|
||||
</button>
|
||||
<span className="text-xs tabular-nums text-slate-400">
|
||||
<ChevronDown className="size-4" aria-hidden />
|
||||
</Button>
|
||||
<span className="text-sm tabular-nums text-slate-500">
|
||||
{satirlar.length.toLocaleString("tr-TR")} /{" "}
|
||||
{toplam.toLocaleString("tr-TR")} program
|
||||
</span>
|
||||
@@ -297,6 +334,27 @@ export function ProgramTablosu({
|
||||
);
|
||||
}
|
||||
|
||||
// Risk etiketinin metin rengi (§4.5): nokta + renkli metin, renk tek başına değil.
|
||||
const RISK_METIN: Record<RiskSeviyesi, string> = {
|
||||
guvenli: "text-emerald-600",
|
||||
"az-riskli": "text-amber-600",
|
||||
riskli: "text-red-600",
|
||||
};
|
||||
|
||||
function RiskEtiketi({ risk }: { risk: RiskSeviyesi }) {
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center gap-1.5 text-xs font-semibold ${RISK_METIN[risk]}`}
|
||||
>
|
||||
<span
|
||||
className={`size-2 shrink-0 rounded-full ${RISK_NOKTA[risk]}`}
|
||||
aria-hidden
|
||||
/>
|
||||
{RISK_ETIKET[risk]}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ProgramSatiri({
|
||||
program: p,
|
||||
adaySira,
|
||||
@@ -309,65 +367,74 @@ function ProgramSatiri({
|
||||
const taban = programEtkinSira(p);
|
||||
const risk = riskHesapla(taban, adaySira);
|
||||
const devlet = p.unitur === "DEVLET";
|
||||
const tabanMetni = taban != null ? `~${taban.toLocaleString("tr-TR")}.` : "—";
|
||||
const bosKaldi =
|
||||
p.kontenjanSon != null &&
|
||||
p.yerlesenSon != null &&
|
||||
p.yerlesenSon < p.kontenjanSon;
|
||||
|
||||
// Şişko ve sade (DESIGN.md §4.15): program adı kesilmez, üniversite adı
|
||||
// altında ayrı satır; telefonda risk ve taban sıra adın altına iner,
|
||||
// butonlar alt alta 44 px.
|
||||
return (
|
||||
<TableRow className="kt-cv-satir">
|
||||
<TableCell className="pl-4 pr-0 text-xs font-medium">
|
||||
{risk ? (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<span
|
||||
className={`size-2.5 shrink-0 rounded-full ${RISK_NOKTA[risk]}`}
|
||||
aria-hidden
|
||||
/>
|
||||
{RISK_ETIKET[risk]}
|
||||
</span>
|
||||
) : null}
|
||||
<TableCell className="hidden pl-4 pr-0 sm:table-cell sm:pl-5">
|
||||
{risk ? <RiskEtiketi risk={risk} /> : null}
|
||||
</TableCell>
|
||||
<TableCell className="max-w-0 w-full">
|
||||
<span className="flex min-w-0 items-center gap-2.5">
|
||||
<TableCell className={`${SATIR_AD_HUCRE} sm:pl-4`}>
|
||||
<span className="flex items-start gap-3">
|
||||
<UniLogo ad={p.universite} boy="sm" />
|
||||
<span className="min-w-0 flex-1">
|
||||
<Link
|
||||
href={`/bolum/${bolumSayfaSlug(p.isim)}`}
|
||||
className="block truncate text-sm font-semibold hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
className={SATIR_AD}
|
||||
title={`${p.isim} bölüm sayfası`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{p.isim}
|
||||
</Link>
|
||||
<span className="mt-0.5 flex items-center gap-1.5 text-xs text-slate-500">
|
||||
<span
|
||||
className={`shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4 ${
|
||||
devlet
|
||||
? "bg-slate-100 text-slate-600"
|
||||
: "bg-violet-50 text-violet-600"
|
||||
}`}
|
||||
<span className={SATIR_ALT_AD}>
|
||||
<Link
|
||||
href={`/universite/${uniSayfaSlug(p.universite)}`}
|
||||
className={SATIR_IKINCIL_LINK}
|
||||
title={`${p.universite} taban puanları sayfası`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{devlet ? "Devlet" : "Vakıf"}
|
||||
</span>
|
||||
<span className="truncate">
|
||||
<Link
|
||||
href={`/universite/${uniSayfaSlug(p.universite)}`}
|
||||
className="hover:text-primary hover:underline"
|
||||
title={`${p.universite} taban puanları sayfası`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{p.universite}
|
||||
</Link>
|
||||
{p.il ? ` · ${p.il.toLocaleLowerCase("tr-TR")}` : ""}
|
||||
</span>
|
||||
{p.sure ? (
|
||||
<span className="shrink-0 text-slate-400">· {p.sure} yıl</span>
|
||||
{trBaslikDuzeni(p.universite)}
|
||||
</Link>
|
||||
</span>
|
||||
<span className={SATIR_META}>
|
||||
<span className={SATIR_CIP}>{devlet ? "Devlet" : "Vakıf"}</span>
|
||||
{p.il ? (
|
||||
<span className="shrink-0">{trBaslikDuzeni(p.il)}</span>
|
||||
) : null}
|
||||
{p.sure ? (
|
||||
<span className="shrink-0">{p.sure} yıl</span>
|
||||
) : null}
|
||||
{bosKaldi ? (
|
||||
<span className="shrink-0 rounded-full bg-amber-50 px-2.5 py-0.5 text-xs font-semibold text-amber-700 sm:hidden">
|
||||
Boş kaldı
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className={SATIR_MOBIL_SAYI}>
|
||||
{risk ? <RiskEtiketi risk={risk} /> : null}
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
Taban sıra
|
||||
<span className="font-semibold tabular-nums text-slate-900">
|
||||
{tabanMetni}
|
||||
</span>
|
||||
<ProgramTabanTrendi program={p} />
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="hidden text-right text-xs md:table-cell">
|
||||
<TableCell className="hidden text-right text-sm lg:table-cell">
|
||||
<ProgramSiraGecmisi program={p} />
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className="hidden text-right text-xs tabular-nums text-slate-500 sm:table-cell"
|
||||
className="hidden text-right text-sm tabular-nums text-slate-600 md:table-cell"
|
||||
title={
|
||||
p.kontenjanSon != null && p.yerlesenSon != null
|
||||
? `${p.kontenjanSon} kontenjan, ${p.yerlesenSon} yerleşen`
|
||||
@@ -375,38 +442,36 @@ function ProgramSatiri({
|
||||
}
|
||||
>
|
||||
{p.kontenjanSon != null ? (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
{p.yerlesenSon != null && p.yerlesenSon < p.kontenjanSon ? (
|
||||
<span className="rounded bg-amber-50 px-1 py-px text-[10px] font-medium leading-4 text-amber-600">
|
||||
boş kaldı
|
||||
<span className="inline-flex flex-col items-end gap-1">
|
||||
{p.kontenjanSon.toLocaleString("tr-TR")} kişi
|
||||
{bosKaldi ? (
|
||||
<span className="rounded-full bg-amber-50 px-2.5 py-0.5 text-xs font-semibold text-amber-700">
|
||||
Boş kaldı
|
||||
</span>
|
||||
) : null}
|
||||
{p.kontenjanSon.toLocaleString("tr-TR")} kişi
|
||||
</span>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right text-xs tabular-nums text-slate-500">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<TableCell className="hidden text-right text-base font-semibold tabular-nums text-slate-900 sm:table-cell">
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<ProgramTabanTrendi program={p} />
|
||||
{taban != null ? `~${taban.toLocaleString("tr-TR")}.` : "—"}
|
||||
{tabanMetni}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="pr-4 text-right">
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<TableCell className={SATIR_BUTON_HUCRE}>
|
||||
<span className={SATIR_BUTON_KABI}>
|
||||
<ProgramNetleriButonu
|
||||
programId={p.id}
|
||||
isim={p.isim}
|
||||
universite={p.universite}
|
||||
className="size-8 min-h-8 min-w-8"
|
||||
/>
|
||||
<ProgramEkleButonu
|
||||
program={p}
|
||||
kaynak="sonuc"
|
||||
adaySira={adaySira}
|
||||
adayTur={tur}
|
||||
className="size-8 min-h-8 min-w-8"
|
||||
/>
|
||||
</span>
|
||||
</TableCell>
|
||||
|
||||
@@ -513,10 +513,10 @@ export function TercihHaritasi({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onTercihSec ? () => onTercihSec(sira) : undefined}
|
||||
className="flex cursor-pointer items-center gap-2 rounded-full border border-slate-200 bg-white py-1.5 pl-1.5 pr-3 text-xs transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50"
|
||||
className="flex min-h-11 cursor-pointer items-center gap-2 rounded-full border border-slate-200 bg-white py-1.5 pl-1.5 pr-3.5 text-sm transition-[background-color,border-color,transform] duration-150 hover:border-orange-500 hover:bg-orange-50/60 active:scale-[0.97]"
|
||||
>
|
||||
<span
|
||||
className="flex size-5 items-center justify-center rounded-full font-heading text-[10px] font-bold text-white"
|
||||
className="flex size-7 shrink-0 items-center justify-center rounded-full font-heading text-xs font-bold text-white"
|
||||
style={{ backgroundColor: RISK_RENK[pin.risk] }}
|
||||
>
|
||||
{sira}
|
||||
@@ -543,7 +543,7 @@ export function TercihHaritasi({
|
||||
{bolum || kisaUniAdi(pin.universite)}
|
||||
</span>
|
||||
{bolum ? (
|
||||
<span className="max-w-48 truncate text-[10px] text-slate-500">
|
||||
<span className="max-w-48 truncate text-xs text-slate-500">
|
||||
{kisaUniAdi(pin.universite)}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useReducedMotion } from "motion/react";
|
||||
import useMeasure from "react-use-measure";
|
||||
import { toast } from "sonner";
|
||||
import { TransitionPanel } from "@/components/core/transition-panel";
|
||||
import { SectionEyebrow } from "@/components/pixel-decor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
@@ -295,11 +296,9 @@ function KapıIcerik({
|
||||
<div ref={panelRef} className="flex flex-col gap-5 px-px">
|
||||
<div>
|
||||
{duzenle ? (
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-primary">
|
||||
Tercihlerini güncelle
|
||||
</p>
|
||||
<SectionEyebrow>Tercihlerini güncelle</SectionEyebrow>
|
||||
) : null}
|
||||
<h2 className="mt-2 font-heading text-2xl font-bold text-slate-900 first:mt-0">
|
||||
<h2 className="mt-3 font-heading text-2xl font-bold text-slate-900 first:mt-0">
|
||||
{duzenle
|
||||
? "Seçimlerini değiştir"
|
||||
: "Tercihine ışık tutabilmemiz için önce ne istediğini öğrenelim"}
|
||||
@@ -348,11 +347,9 @@ function KapıIcerik({
|
||||
>
|
||||
<div>
|
||||
{duzenle ? (
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-primary">
|
||||
Sıralama ve tercihler
|
||||
</p>
|
||||
<SectionEyebrow>Sıralama ve tercihler</SectionEyebrow>
|
||||
) : null}
|
||||
<h2 className="mt-2 font-heading text-xl font-bold text-slate-900 first:mt-0">
|
||||
<h2 className="mt-3 font-heading text-xl font-bold text-slate-900 first:mt-0">
|
||||
{duzenle
|
||||
? "Yeni sıralaman nedir?"
|
||||
: "Başarı sıralaman nedir?"}
|
||||
@@ -366,7 +363,7 @@ function KapıIcerik({
|
||||
<div className="flex flex-col gap-2">
|
||||
<label
|
||||
htmlFor="kapisi-siralama"
|
||||
className="text-xs font-semibold uppercase tracking-wide text-slate-400"
|
||||
className="text-sm font-semibold text-slate-700"
|
||||
>
|
||||
Başarı sıralaması ve puan türü
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user