121 lines
5.5 KiB
TypeScript
121 lines
5.5 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
|
import { pb, COUNTRY_LABELS, COUNTRY_FLAGS, type Kennzeichen } from "@/lib/pb";
|
|
import KennzeichenFilter from "./KennzeichenFilter";
|
|
|
|
interface Props { searchParams: { land?: string; q?: string; seite?: string; historisch?: string } }
|
|
export const metadata = { title: "Datenbank" };
|
|
|
|
async function getData(land?: string, q?: string, page = 1, historisch = false) {
|
|
try {
|
|
const safe = (s: string) => s.replace(/["\\]/g, "");
|
|
const filters: string[] = [`country!="global"`];
|
|
if (land) filters.push(`country="${safe(land)}"`);
|
|
if (!historisch) filters.push(`active=true`);
|
|
if (q) { const s = safe(q); filters.push(`(code~"${s}" || name~"${s}" || region~"${s}")`); }
|
|
return pb.collection("kennzeichen").getList<Kennzeichen>(page, 100, {
|
|
filter: filters.join(" && ") || undefined, sort: "code",
|
|
});
|
|
} catch { return null; }
|
|
}
|
|
|
|
async function getCountryCounts() {
|
|
try {
|
|
const all = await pb.collection("kennzeichen").getFullList<Kennzeichen>({ fields: "country", filter: 'active=true && country!="global"' });
|
|
const counts: Record<string, number> = {};
|
|
for (const k of all) counts[k.country] = (counts[k.country] ?? 0) + 1;
|
|
return counts;
|
|
} catch { return {}; }
|
|
}
|
|
|
|
function Pagination({ page, totalPages, land, q }: { page: number; totalPages: number; land?: string; q?: string }) {
|
|
if (totalPages <= 1) return null;
|
|
const makeHref = (p: number) => {
|
|
const params = new URLSearchParams();
|
|
if (land) params.set("land", land);
|
|
if (q) params.set("q", q);
|
|
params.set("seite", String(p));
|
|
return `?${params.toString()}`;
|
|
};
|
|
const pages: (number | string)[] = [];
|
|
for (let p = 1; p <= totalPages; p++) {
|
|
if (p === 1 || p === totalPages || Math.abs(p - page) <= 2) {
|
|
pages.push(p);
|
|
} else if (pages[pages.length - 1] !== "…") {
|
|
pages.push("…");
|
|
}
|
|
}
|
|
return (
|
|
<div className="flex gap-2 mt-6 justify-center flex-wrap">
|
|
{page > 1 && (
|
|
<a href={makeHref(page - 1)} className="w-9 h-9 flex items-center justify-center rounded-md text-sm border border-[var(--warm)] hover:border-[var(--ink)]">←</a>
|
|
)}
|
|
{pages.map((p, i) =>
|
|
p === "…" ? (
|
|
<span key={`e${i}`} className="w-9 h-9 flex items-center justify-center text-[var(--muted)]">…</span>
|
|
) : (
|
|
<a key={p} href={makeHref(p as number)}
|
|
className={`w-9 h-9 flex items-center justify-center rounded-md text-sm border transition-colors ${p === page ? "bg-[var(--ink)] text-[var(--paper)] border-[var(--ink)]" : "border-[var(--warm)] hover:border-[var(--ink)]"}`}>
|
|
{p}
|
|
</a>
|
|
)
|
|
)}
|
|
{page < totalPages && (
|
|
<a href={makeHref(page + 1)} className="w-9 h-9 flex items-center justify-center rounded-md text-sm border border-[var(--warm)] hover:border-[var(--ink)]">→</a>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default async function KennzeichenPage({ searchParams }: Props) {
|
|
const land = searchParams.land;
|
|
const q = searchParams.q;
|
|
const page = parseInt(searchParams.seite ?? "1");
|
|
const historisch = searchParams.historisch === "1";
|
|
const [data, counts] = await Promise.all([getData(land, q, page, historisch), getCountryCounts()]);
|
|
const items = data?.items ?? [];
|
|
|
|
return (
|
|
<div className="max-w-6xl mx-auto px-6 py-12">
|
|
<div className="mb-10">
|
|
<div className="text-xs font-mono text-[var(--muted)] tracking-widest uppercase mb-2">Datenbank</div>
|
|
<h1 style={{ fontFamily: "'Syne',sans-serif", fontWeight: 800, fontSize: "2.25rem", letterSpacing: "-0.03em" }} className="text-[var(--ink)]">Kennzeichen</h1>
|
|
{data && <p className="text-[var(--muted)] mt-1">{data.totalItems.toLocaleString("de")} Einträge</p>}
|
|
</div>
|
|
<KennzeichenFilter
|
|
countries={Object.entries(COUNTRY_LABELS).map(([key, label]) => ({ key, label, flag: COUNTRY_FLAGS[key] ?? "🌍", count: counts[key] ?? 0 }))}
|
|
activeLand={land} activeQ={q} historisch={historisch}
|
|
/>
|
|
<div className="border border-[var(--warm)] rounded-lg overflow-hidden bg-white/40 mt-6">
|
|
{items.length === 0 ? (
|
|
<div className="text-center py-20 text-[var(--muted)]">Keine Kennzeichen gefunden.</div>
|
|
) : (
|
|
<div className="overflow-x-auto">
|
|
<table className="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Kürzel</th><th>Ort / Kreis</th><th>Herleitung</th>
|
|
<th className="hidden md:table-cell">Region</th>
|
|
<th className="hidden lg:table-cell">Punkte</th>
|
|
<th className="hidden lg:table-cell">Bemerkung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{items.map((kz) => (
|
|
<tr key={kz.id}>
|
|
<td><span className="kz-badge">{kz.code}</span></td>
|
|
<td className="font-medium">{kz.name}</td>
|
|
<td className="text-[var(--muted)] text-sm">{kz.derivation}</td>
|
|
<td className="text-[var(--muted)] text-sm hidden md:table-cell">{kz.region}</td>
|
|
<td className="text-[var(--muted)] text-sm hidden lg:table-cell font-mono">{kz.points ?? "—"}</td>
|
|
<td className="text-[var(--muted)] text-sm hidden lg:table-cell max-w-xs truncate">{kz.note}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{data && <Pagination page={page} totalPages={data.totalPages} land={land} q={q} />}
|
|
</div>
|
|
);
|
|
}
|