Compare commits
1 commit
27fb01f619
...
8544648cc4
| Author | SHA1 | Date | |
|---|---|---|---|
| 8544648cc4 |
6 changed files with 19 additions and 68 deletions
|
|
@ -4,8 +4,8 @@ services:
|
|||
restart: unless-stopped
|
||||
volumes:
|
||||
- pb_data:/pb/pb_data
|
||||
ports:
|
||||
- "4444:8090"
|
||||
# ports:
|
||||
#- "4444:8090"
|
||||
nextjs:
|
||||
build: ./frontend
|
||||
restart: unless-stopped
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||
<Nav />
|
||||
<main className="min-h-screen">{children}</main>
|
||||
<footer className="border-t border-[var(--warm)] mt-24 py-10 px-6">
|
||||
<div className="max-w-6xl mx-auto flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 text-sm text-[var(--muted)]">
|
||||
<div className="max-w-6xl mx-auto flex items-center justify-between text-sm text-[var(--muted)]">
|
||||
<span style={{ fontFamily: "'Syne', sans-serif", fontWeight: 600 }}>
|
||||
Kennzeichensammler
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
export const dynamic = 'force-dynamic';
|
||||
import { unstable_noStore as noStore } from 'next/cache';
|
||||
import Link from "next/link";
|
||||
import { pb, COUNTRY_LABELS, COUNTRY_FLAGS } from "@/lib/pb";
|
||||
|
||||
async function getStats() {
|
||||
noStore();
|
||||
try {
|
||||
const [kz, diplo, gesehen] = await Promise.all([
|
||||
pb.collection("kennzeichen").getList(1, 1, { filter: "active=true" }),
|
||||
|
|
@ -22,7 +20,6 @@ async function getStats() {
|
|||
}
|
||||
|
||||
async function getLasteSeen() {
|
||||
noStore();
|
||||
try {
|
||||
return pb.collection("gesehen").getList(1, 5, { sort: "-datum" });
|
||||
} catch {
|
||||
|
|
@ -67,7 +64,7 @@ export default async function Home() {
|
|||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-3 gap-3 sm:gap-4 mb-20">
|
||||
<div className="grid grid-cols-3 gap-4 mb-20">
|
||||
{[
|
||||
{ label: "Kennzeichen", value: stats.kennzeichen.toLocaleString("de") },
|
||||
{ label: "Diplomatenkz.", value: stats.diplomaten.toLocaleString("de") },
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default async function SammlungPage() {
|
|||
<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 flex-wrap items-end justify-between gap-2 mb-4">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,30 @@
|
|||
"use client";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
const links = [
|
||||
{ href: "/", label: "Start" },
|
||||
{ href: "/kennzeichen", label: "Datenbank" },
|
||||
{ href: "/diplomatenkennzeichen", label: "Diplomaten" },
|
||||
{ href: "/sammlung", label: "Sammlung" },
|
||||
{ href: "/blog", label: "Blog" },
|
||||
{ href: "/", label: "Start" },
|
||||
{ href: "/kennzeichen", label: "Datenbank" },
|
||||
{ href: "/diplomatenkennzeichen",label: "Diplomaten" },
|
||||
{ href: "/sammlung", label: "Sammlung" },
|
||||
{ href: "/blog", label: "Blog" },
|
||||
];
|
||||
|
||||
export default function Nav() {
|
||||
const path = usePathname();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-[var(--warm)] bg-[var(--paper)]/90 backdrop-blur-sm">
|
||||
<div className="max-w-6xl mx-auto px-6 h-14 flex items-center justify-between">
|
||||
<Link
|
||||
href="/"
|
||||
style={{ fontFamily: "'Syne', sans-serif", fontWeight: 700, fontSize: 15 }}
|
||||
style={{ fontFamily: "'Syne', sans-serif", fontWeight: 700, fontSize: 15, letterSpacing: "0em" }}
|
||||
className="text-[var(--ink)] hover:text-[var(--accent)] transition-colors"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Kennzeichensammler
|
||||
</Link>
|
||||
|
||||
{/* Desktop nav */}
|
||||
<nav className="hidden sm:flex items-center gap-1">
|
||||
<nav className="flex items-center gap-1">
|
||||
{links.map((l) => (
|
||||
<Link
|
||||
key={l.href}
|
||||
|
|
@ -46,40 +41,7 @@ export default function Nav() {
|
|||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Mobile hamburger */}
|
||||
<button
|
||||
className="sm:hidden flex flex-col gap-1.5 p-2 -mr-2"
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
aria-label="Menü"
|
||||
>
|
||||
<span className={clsx("block w-5 h-0.5 bg-[var(--ink)] transition-all origin-center", open && "rotate-45 translate-y-2")} />
|
||||
<span className={clsx("block w-5 h-0.5 bg-[var(--ink)] transition-all", open && "opacity-0")} />
|
||||
<span className={clsx("block w-5 h-0.5 bg-[var(--ink)] transition-all origin-center", open && "-rotate-45 -translate-y-2")} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu */}
|
||||
{open && (
|
||||
<div className="sm:hidden border-t border-[var(--warm)] bg-[var(--paper)] px-4 py-3 flex flex-col gap-1">
|
||||
{links.map((l) => (
|
||||
<Link
|
||||
key={l.href}
|
||||
href={l.href}
|
||||
onClick={() => setOpen(false)}
|
||||
className={clsx(
|
||||
"px-3 py-2.5 rounded-md text-sm transition-colors",
|
||||
path === l.href
|
||||
? "bg-[var(--ink)] text-[var(--paper)]"
|
||||
: "text-[var(--muted)] hover:text-[var(--ink)] hover:bg-[var(--warm)]"
|
||||
)}
|
||||
style={{ fontFamily: "'Syne', sans-serif", fontWeight: 500 }}
|
||||
>
|
||||
{l.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,6 +385,13 @@ def import_backup(pb, backup_path, db_path):
|
|||
if land in ("global", "global2", "laender"):
|
||||
kz_code = normalize_diplo_code(kz_code)
|
||||
|
||||
# Duplikat prüfen
|
||||
existing = pb.find_record("gesehen",
|
||||
f'kennzeichen_code="{kz_code}" && land="{folder}"')
|
||||
if existing:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
# Kennzeichen-Namen aus Referenz holen
|
||||
try:
|
||||
rcur.execute(f"SELECT ort FROM {land} WHERE id=?", (kennid,))
|
||||
|
|
@ -393,21 +400,6 @@ def import_backup(pb, backup_path, db_path):
|
|||
except:
|
||||
kz_name = ""
|
||||
|
||||
existing = pb.find_record("gesehen",
|
||||
f'kennzeichen_code="{kz_code}" && land="{folder}"')
|
||||
if existing:
|
||||
# Datum aktualisieren wenn neue Sichtung neuer ist
|
||||
if datum_iso > (existing.get("datum") or ""):
|
||||
requests.patch(
|
||||
f"{pb.url}/api/collections/gesehen/records/{existing['id']}",
|
||||
headers=pb.h(),
|
||||
json={"datum": datum_iso},
|
||||
)
|
||||
imported += 1
|
||||
else:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
pb.create("gesehen", {
|
||||
"kennzeichen_code": kz_code,
|
||||
"kennzeichen_name": kz_name,
|
||||
|
|
|
|||
Loading…
Reference in a new issue