"use client"; import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import type { TischLabels, TischObject } from "./types"; interface ObjectFaceProps { obj: TischObject; labels: TischLabels; flipped?: boolean; } const pad = (n: number) => String(n).padStart(2, "0"); // Rand und Rundung, die alle Objekte teilen const edge = "h-full w-full overflow-hidden rounded-2xl border-2 border-ink"; /** Zeichnet ein Objekt. Größe und Position bestimmt der Container (Tisch oder Stapel). */ export default function ObjectFace({ obj, labels, flipped = false }: ObjectFaceProps) { switch (obj.kind) { case "project": return (
({pad(obj.index + 1)}) {obj.year}

{obj.title}

{obj.tags.slice(0, 3).join(" · ")}
{labels.flip} ↻
{obj.title} {obj.year}

{obj.description}

e.stopPropagation()} > {labels.details}
); case "ticket": return (
{labels.ticketTitle}
{obj.daysLeft}
{labels.ticketDays}
{labels.ticketJob}
{labels.ticketStub}
); case "server": return (

{labels.serverTitle}

{labels.servers.map((s) => (
))}

{labels.serverNote}

); case "book": return ; case "photo": return (
Dennis Konkol
{labels.photoCaption}
); case "sticker": return (
{labels.sticker}
); } } interface CurrentBook { title: string; authors: string[]; image: string | null; progress: number; } // Tisch und Stapel zeigen das Buch beide; so wird es nur einmal geladen let bookRequest: Promise | null = null; function loadCurrentBook(): Promise { if (!bookRequest) { bookRequest = fetch("/api/n8n/hardcover/currently-reading") .then((res) => (res.ok ? res.json() : null)) .then((data) => { const list = data?.currentlyReading; const first = Array.isArray(list) ? list[0] : list; return first ?? null; }) .catch(() => null); } return bookRequest; } function BookFace({ href, labels }: { href: string; labels: TischLabels }) { const [book, setBook] = useState(undefined); useEffect(() => { let alive = true; loadCurrentBook().then((b) => { if (alive) setBook(b); }); return () => { alive = false; }; }, []); const progress = book && Number.isFinite(book.progress) ? Math.max(0, Math.min(100, book.progress)) : 0; return (
{book?.image && ( )}
{labels.bookLabel} {book === undefined ? (