feat(katalog): şehre göre üniversiteler — /universiteler/[il] sayfaları ve şehir çipleri
- Veri: getAllIller/getIlBySlug (üniversite en çok programının olduğu ile sayılır; ili boş olan hiçbir şehre girmez; tireli yurt dışı adlar baş harfli). - /universiteler: "Şehre göre" çip satırı (2+ üniversiteli 28 şehir) + "Tüm şehirler" A–Z üç sütunlu liste (#tum-sehirler). - /universiteler/[il]: 92 statik şehir sayfası — katalog detay düzeni, il ve üniversite sayısı hapları, kompakt sıra satırı, devlet/vakıf/KKTC-yurt dışı öbekleri, diğer şehir çipleri, CTA bandı; metadata + breadcrumb JSON-LD. - SehirCipleri: sihirbaz il çipiyle aynı kalıp, aktif şehir mavi dolu. - Sitemap'e şehir sayfaları eklendi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
80
src/features/katalog/components/sehir-cipleri.tsx
Normal file
80
src/features/katalog/components/sehir-cipleri.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import Link from "next/link";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { IlOzet } from "../katalog-queries";
|
||||
|
||||
const sayi = (n: number) => n.toLocaleString("tr-TR");
|
||||
|
||||
/**
|
||||
* Şehir çipi (DESIGN.md §4.17): sihirbazın il çipiyle aynı kalıp — 40 px,
|
||||
* tam yuvarlak, hover §2.1 tek turuncu; seçili/aktif hâl mavi dolu.
|
||||
* Etkileşimli çip olduğu için rozet (§4.2) kuralının dışındadır.
|
||||
*/
|
||||
const CIP_TEMEL =
|
||||
"inline-flex h-10 cursor-pointer items-center gap-1.5 rounded-full border px-3.5 text-sm font-semibold transition-[background-color,border-color,color,transform] duration-150 active:scale-[0.97]";
|
||||
const CIP_PASIF =
|
||||
"border-slate-200 bg-white text-slate-700 hover:border-orange-500 hover:bg-orange-50/60 hover:text-orange-500";
|
||||
const CIP_AKTIF = "border-primary bg-primary text-white";
|
||||
|
||||
/**
|
||||
* Şehre göre üniversite gezinme çipleri. `iller` verilen sırayla dizilir
|
||||
* (çağıran genelde üniversite sayısına göre azalan verir). `aktifSlug`
|
||||
* bulunulan şehir sayfasıdır: mavi dolu ve `aria-current="page"`.
|
||||
* `tumuHref` verilirse sona "Tüm şehirler" çipi eklenir (A–Z listesine).
|
||||
*/
|
||||
export function SehirCipleri({
|
||||
iller,
|
||||
aktifSlug,
|
||||
tumuHref,
|
||||
tumuEtiket,
|
||||
className,
|
||||
}: {
|
||||
iller: IlOzet[];
|
||||
aktifSlug?: string;
|
||||
tumuHref?: string;
|
||||
tumuEtiket?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<ul
|
||||
className={cn(
|
||||
"flex flex-wrap items-center justify-center gap-2 lg:justify-start",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{iller.map((il) => {
|
||||
const aktif = il.slug === aktifSlug;
|
||||
return (
|
||||
<li key={il.slug}>
|
||||
<Link
|
||||
href={`/universiteler/${il.slug}`}
|
||||
aria-current={aktif ? "page" : undefined}
|
||||
className={cn(CIP_TEMEL, aktif ? CIP_AKTIF : CIP_PASIF)}
|
||||
>
|
||||
{il.ad}
|
||||
<span
|
||||
className={cn(
|
||||
"font-normal tabular-nums",
|
||||
aktif ? "text-white/80" : "text-slate-400",
|
||||
)}
|
||||
>
|
||||
{sayi(il.uniler.length)}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{tumuHref ? (
|
||||
<li>
|
||||
<Link href={tumuHref} className={cn(CIP_TEMEL, CIP_PASIF)}>
|
||||
{tumuEtiket ?? "Tüm şehirler"}
|
||||
</Link>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
/** Çip satırında gösterilecek şehirler: en az iki üniversitesi olanlar. */
|
||||
export function oneCikanIller(iller: IlOzet[]): IlOzet[] {
|
||||
return iller.filter((il) => il.uniler.length >= 2);
|
||||
}
|
||||
Reference in New Issue
Block a user