import { useCallback, useEffect, useState } from 'react'; /** * Die letzten Drucke. * * Gespeichert ist **nur die Anleitung** — Bogen, Maße, Stückzahlen und die * Verweise auf die Bilder. Kein PDF: Ein A3-Bogen ist schnell 20–80 MB, und * das Modul rechnet ohne KI, also ergibt derselbe Auftrag wieder exakt * dasselbe Blatt. Neu erzeugen ist billiger als aufheben. * * „Nochmal drucken" geht deshalb nur, wenn **alle** Bilder aus der Bibliothek * kamen und noch da sind. Bei hochgeladenen Dateien ist die Quelle nach dem * Aufräumen weg — das steht dann auch so am Eintrag. */ interface Run { id: string; origin: string; summary: string | null; paper: string | null; pages: number; pieces: number; bytes: number | null; created_at: string; bilder: { id: string; filename: string | null }[]; fehlend: number; hochgeladen: number; wiederholbar: boolean; config: any; } const wann = (s: string) => { const d = new Date(s); const minuten = Math.round((Date.now() - d.getTime()) / 60000); if (minuten < 1) return 'gerade eben'; if (minuten < 60) return `vor ${minuten} min`; if (minuten < 24 * 60) return `vor ${Math.round(minuten / 60)} h`; return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); }; // Ein Testbogen sind 20 KB, ein A3-Poster 80 MB. `toFixed(1)` in MB machte aus // dem kleinen Fall „0.0 MB groß" — also unterhalb eines Megabytes in KB. const groesse = (b: number | null) => { if (!b) return ''; if (b < 1024 * 1024) return `${Math.max(1, Math.round(b / 1024))} KB`; return `${(b / 1048576).toFixed(1)} MB`.replace('.', ','); }; const zahl = (n: number, ein: string, viele: string) => `${n} ${n === 1 ? ein : viele}`; export default function PrintRuns({ onLaden }: { onLaden?: (config: any) => void }) { const [runs, setRuns] = useState([]); const [laden, setLaden] = useState(true); const [fehler, setFehler] = useState(false); const [busy, setBusy] = useState(null); const [msg, setMsg] = useState<{ t: string; ok: boolean } | null>(null); const holen = useCallback(async () => { try { const r = await fetch('/api/print/runs'); if (!r.ok) throw new Error(String(r.status)); const d = await r.json(); setRuns(d.runs || []); setFehler(false); } catch { setFehler(true); } finally { setLaden(false); } }, []); useEffect(() => { holen(); }, [holen]); const sagen = (t: string, ok = true) => { setMsg({ t, ok }); setTimeout(() => setMsg(null), 5000); }; /** Denselben Bogen noch einmal erzeugen — dieselbe Anfrage, dasselbe PDF. */ const nochmal = async (r: Run) => { setBusy(r.id); try { const res = await fetch('/api/print/sheet', { method: 'POST', headers: { 'Content-Type': 'application/json' }, // `origin` markiert den Lauf als Wiederholung, damit die Liste nicht // aussieht, als hätte jemand denselben Bogen zweimal von Hand gebaut. body: JSON.stringify({ ...r.config, origin: 'wiederholung' }), }); if (!res.ok) { const j = await res.json().catch(() => ({})); sagen(j.error || 'Das hat nicht geklappt — vielleicht fehlt ein Bild.', false); return; } const blob = await res.blob(); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `klarbild-druck-${new Date().toISOString().slice(0, 10)}.pdf`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(a.href), 4000); sagen('PDF neu erzeugt. Beim Drucken „Tatsächliche Größe / 100 %" wählen.'); holen(); } catch { sagen('Netzwerkfehler.', false); } finally { setBusy(null); } }; const loeschen = async (r: Run) => { if (!confirm('Diesen Eintrag aus der Liste nehmen?\n\nDie Bilder bleiben in der Bibliothek — es verschwindet nur der Verlaufseintrag.')) return; const res = await fetch(`/api/print/runs?id=${r.id}`, { method: 'DELETE' }).then((x) => x.json()).catch(() => ({})); if (res.ok) { setRuns((p) => p.filter((x) => x.id !== r.id)); } else sagen(res.error || 'Hat nicht geklappt.', false); }; if (laden) return

Wird geladen …

; if (fehler) return (

Der Verlauf ließ sich nicht laden.{' '}

); if (!runs.length) return (

Noch nichts gedruckt

Sobald du im Druckbereich ein PDF erzeugst, steht es hier — mit einem Knopf, um genau denselben Bogen noch einmal zu bekommen.

); return (
{msg &&

{msg.t}

}
{runs.map((r) => (

{r.summary || 'Druckbogen'}

{wann(r.created_at)}
{r.origin === 'wiederholung' && wiederholt} {r.origin === 'telegram' && Telegram}
{r.paper} · {zahl(r.pages, 'Bogen', 'Bogen')} · {zahl(r.pieces, 'Teil', 'Teile')} {r.bytes ? <> · das PDF war {groesse(r.bytes)} groß : null}
{r.bilder.length > 0 && (
{r.bilder.slice(0, 8).map((b) => ( {b.filename { (e.currentTarget as HTMLImageElement).style.visibility = 'hidden'; }} /> ))} {r.bilder.length > 8 && +{r.bilder.length - 8}}
)}
{r.wiederholbar ? ( ) : ( {r.fehlend > 0 ? `${zahl(r.fehlend, 'Bild ist', 'Bilder sind')} inzwischen gelöscht — nicht wiederholbar.` : 'Damals hochgeladen statt aus der Bibliothek — die Quelldatei ist weg.'} )} {onLaden && ( )}
))}

Hier liegen nur die Einstellungen, nicht die PDFs — ein A3-Bogen wären schnell 20 bis 80 MB. Weil das Druckmodul ohne KI rechnet, ergibt derselbe Auftrag wieder exakt dasselbe Blatt. Aufgehoben werden die letzten {100} Drucke je Person.

); } function PrintRunsStyles() { return ; }