107 lines
5.4 KiB
TypeScript
107 lines
5.4 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
|
import { pb, COUNTRY_LABELS, COUNTRY_FLAGS } from "@/lib/pb";
|
|
|
|
export const metadata = { title: "Meine Sammlung" };
|
|
|
|
async function getStats() {
|
|
try {
|
|
const [gesehen, total] = await Promise.all([
|
|
pb.collection("gesehen").getFullList({ fields: "land,datum" }),
|
|
pb.collection("kennzeichen").getList(1, 1, { filter: "active=true" }),
|
|
]);
|
|
const byCountry: Record<string,number> = {};
|
|
const byMonth: Record<string,number> = {};
|
|
for (const g of gesehen as any[]) {
|
|
byCountry[g.land] = (byCountry[g.land] ?? 0) + 1;
|
|
const month = g.datum?.slice(0,7);
|
|
if (month) byMonth[month] = (byMonth[month] ?? 0) + 1;
|
|
}
|
|
return { total: gesehen.length, totalKz: total.totalItems, byCountry, byMonth };
|
|
} catch { return { total: 0, totalKz: 0, byCountry: {}, byMonth: {} }; }
|
|
}
|
|
|
|
async function getLatest() {
|
|
try {
|
|
return pb.collection("gesehen").getList(1, 20, { sort: "-datum" });
|
|
} catch { return null; }
|
|
}
|
|
|
|
export default async function SammlungPage() {
|
|
const [stats, latest] = await Promise.all([getStats(), getLatest()]);
|
|
const percent = stats.totalKz > 0 ? Math.round((stats.total / stats.totalKz) * 100) : 0;
|
|
const topCountries = Object.entries(stats.byCountry).sort(([,a],[,b]) => b-a).slice(0,8);
|
|
const recentMonths = Object.entries(stats.byMonth).sort(([a],[b]) => b.localeCompare(a)).slice(0,6);
|
|
|
|
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">Persönlich</div>
|
|
<h1 style={{ fontFamily:"'Syne',sans-serif", fontWeight:800, fontSize:"2.25rem", letterSpacing:"-0.03em" }} className="text-[var(--ink)]">Meine Sammlung</h1>
|
|
</div>
|
|
<div className="border border-[var(--warm)] rounded-lg p-8 bg-white/40 mb-8">
|
|
<div className="flex items-end justify-between mb-4">
|
|
<div>
|
|
<div style={{ fontFamily:"'Syne',sans-serif", fontWeight:800, fontSize:"3rem", lineHeight:1 }} className="text-[var(--ink)]">{stats.total.toLocaleString("de")}</div>
|
|
<div className="text-[var(--muted)] text-sm mt-1">von {stats.totalKz.toLocaleString("de")} Kennzeichen gesehen</div>
|
|
</div>
|
|
<div style={{ fontFamily:"'Syne',sans-serif", fontWeight:700, fontSize:"2rem" }} className="text-[var(--accent)]">{percent}%</div>
|
|
</div>
|
|
<div className="h-3 bg-[var(--warm)] rounded-full overflow-hidden">
|
|
<div className="h-full bg-[var(--accent)] rounded-full" style={{ width:`${percent}%` }} />
|
|
</div>
|
|
</div>
|
|
<div className="grid md:grid-cols-2 gap-6 mb-8">
|
|
<div className="border border-[var(--warm)] rounded-lg p-6 bg-white/40">
|
|
<h2 style={{ fontFamily:"'Syne',sans-serif", fontWeight:700 }} className="mb-4">Nach Land</h2>
|
|
<div className="space-y-2">
|
|
{topCountries.map(([country, count]) => (
|
|
<div key={country} className="flex items-center gap-3 text-sm">
|
|
<span className="text-lg w-7">{COUNTRY_FLAGS[country] ?? "🌍"}</span>
|
|
<span className="flex-1 text-[var(--muted)]">{COUNTRY_LABELS[country] ?? country}</span>
|
|
<span className="font-mono font-medium">{count}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="border border-[var(--warm)] rounded-lg p-6 bg-white/40">
|
|
<h2 style={{ fontFamily:"'Syne',sans-serif", fontWeight:700 }} className="mb-4">Aktivität</h2>
|
|
<div className="space-y-3">
|
|
{recentMonths.map(([month, count]) => {
|
|
const max = Math.max(...recentMonths.map(([,c]) => c));
|
|
return (
|
|
<div key={month} className="flex items-center gap-3 text-sm">
|
|
<span className="font-mono text-[var(--muted)] w-16">{new Date(month+"-01").toLocaleDateString("de-DE",{month:"short",year:"2-digit"})}</span>
|
|
<div className="flex-1 h-2 bg-[var(--warm)] rounded-full overflow-hidden">
|
|
<div className="h-full bg-[var(--ink)] rounded-full" style={{ width:`${Math.round((count/max)*100)}%` }} />
|
|
</div>
|
|
<span className="font-mono font-medium w-6 text-right">{count}</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{latest && latest.items.length > 0 && (
|
|
<div className="border border-[var(--warm)] rounded-lg overflow-hidden bg-white/40">
|
|
<div className="px-6 py-4 border-b border-[var(--warm)]">
|
|
<h2 style={{ fontFamily:"'Syne',sans-serif", fontWeight:700 }}>Zuletzt gesehen</h2>
|
|
</div>
|
|
<div className="divide-y divide-[var(--warm)]">
|
|
{(latest.items as any[]).map((item) => (
|
|
<div key={item.id} className="px-6 py-3 flex items-center gap-4">
|
|
<span className="kz-badge seen">{item.kennzeichen_code}</span>
|
|
<div className="flex-1">
|
|
<span className="text-sm font-medium">{item.kennzeichen_name}</span>
|
|
<span className="text-xs text-[var(--muted)] ml-2">{COUNTRY_FLAGS[item.land] ?? ""}</span>
|
|
</div>
|
|
<span className="text-xs font-mono text-[var(--muted)]">
|
|
{new Date(item.datum).toLocaleDateString("de-DE")}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|