diff --git a/docs/specs/ocr-admin-spec.html b/docs/specs/ocr-admin-spec.html new file mode 100644 index 00000000..f448b9c5 --- /dev/null +++ b/docs/specs/ocr-admin-spec.html @@ -0,0 +1,1101 @@ + + +
+ + +Neue Admin-Sektion für das OCR-Pipeline-Monitoring. Zwei Seiten: Die Übersicht zeigt Systemstatus, globale Block-Statistiken und alle Modelle (global + sendergebunden) in einer Tabelle auf einen Blick. Die Modell-Detailseite zeigt die aktuellen Trainingsmetriken und die vollständige Trainingshistorie für ein einzelnes Modell, mit Aktions-Buttons direkt über der Tabelle. Tägliche Nutzung — Statistiken und aktive Läufe sind daher immer sofort sichtbar.
+Die Übersichtsseite öffnet sich beim Klick auf „OCR" in der EntityNav. Kein Split-Panel — OCR ist kein Entity-Editor. Der volle Bereich rechts der EntityNav gehört dem Dashboard. Vertikaler Fluss: Health-Bar → Statistiken → Modelltabelle → Globale Aktionen. Beim Klick auf eine Tabellenzeile wird die Modell-Detailseite geöffnet.
+global_kurrent-Zeile hat einen leicht bläulichen Hintergrund (bg-[#F8F8FC]) und ist unabhängig von Sortierung immer die erste Zeile. Sie wird anhand personId === null identifiziert./api/ocr/jobs/{jobId}/progress-Kanal wie in der Dokumentenansicht. RUNNING-Tabellenzeilen erhalten zusätzlich bg-amber-50. Die Buttons „Training Erkennung" und „Training Segmentierung" werden disabled + opacity-50 wenn ein Lauf aktiv ist.<th> mit title="Zeichenfehlerrate (Character Error Rate)" und aria-label für Screenreader. Kleinere Werte sind besser — ein Pfeil in der Implementierung ist optional.Auf kleinen Bildschirmen stapeln sich die drei Stat-Karten einspaltig. Die Modelltabelle zeigt nur Name und Status-Pill (keine Metriken-Spalten — zu schmal). Aktions-Buttons werden vollbreit und vertikal gestapelt.
+Die Detailseite öffnet sich beim Klick auf eine Zeile der Übersichtstabelle. Sie folgt demselben vertikalen Muster: Metric-Cards oben, darunter die vollständige Trainingshistorie nur für dieses Modell. Für Sender-Modelle gibt es einen Link zur Personen-Detailseite. Zwei Varianten: Sender-Modell (mit Personen-Link) und globales Modell (ohne).
+↑ / ↓ von X % (vorher)). Grün für Verbesserung, rot für Verschlechterung. Berechnung: letzter DONE-Lauf vs. vorletzter DONE-Lauf im nach completedAt sortierten Array. Entfällt wenn weniger als zwei DONE-Läufe vorhanden.errorMessage-Feld — leicht rosa hinterlegt, kein Modal nötig. Ist errorMessage null oder leer, entfällt die zweite Zeile.SenderModelService angelegt und gelöscht. Die Detailseite bietet bewusst keinen manuellen Lösch-Button. Beim globalen Modell fehlt der „→ Person"-Link, da personId === null.Die vier Metric-Cards arrangieren sich auf Mobile in einem 2×2-Raster. Aktions-Buttons nehmen die volle Breite ein. Die Trainingshistorie zeigt auf kleinen Bildschirmen nur Status, Datum und Genauigkeit — ZFR, Blöcke und Epochs entfallen.
+Exakte Tailwind-Klassen und Pixelwerte für beide Seiten. Neue Dateien sind mit Neu markiert.
+| Element | Tailwind-Klassen | Real px / Wert | Hinweis |
|---|---|---|---|
| Routen & Dateien | |||
| Übersicht | +src/routes/admin/ocr/+page.sveltesrc/routes/admin/ocr/+page.server.ts |
+ GET /api/ocr/training-info |
+ Neu | +
| Global-Detail | +src/routes/admin/ocr/global/+page.svelte |
+ GET /api/ocr/training-info (gefiltert: personId === null) |
+ Neu | +
| Sender-Detail | +src/routes/admin/ocr/[personId]/+page.svelte |
+ GET /api/ocr/training-info (gefiltert nach personId) |
+ Neu | +
| EntityNav-Eintrag | +Vorhandene EntityNav-Komponente um OCR-Eintrag ergänzen |
+ Neue Sektion „System" mit Separator davor | +Bestehende Datei ändern | +
| Seitenstruktur (beide Seiten) | |||
| OCR Content Area | +flex-1 flex flex-col overflow-hidden min-w-0 bg-surface |
+ Kein Tree-Panel, kein Edit-Panel — volle Breite nach EntityNav | +OCR ist kein Entity-Editor; enthält kein Split-Layout | +
| Page Header Bar | +flex items-center gap-2 px-4 py-2.5 border-b border-line bg-white shrink-0 h-10 |
+ h: 40px | +Enthält Back-Link (nur Detail), Titel, optionale Status-Pill | +
| Scrollbarer Seiteninhalt | +flex-1 overflow-y-auto p-4 flex flex-col gap-3 |
+ gap: 12px zwischen Sektionen | +— | +
| Back-Link (Detail) | +text-[11px] text-gray-400 hover:text-ink transition-colors shrink-0 |
+ — | +goto('/admin/ocr') oder <a href="/admin/ocr"> |
+
| Health Bar (Übersicht) | |||
| Health Bar Container | +flex items-center gap-3 px-4 py-2 bg-white border border-line rounded-sm |
+ h: 36px | +Neu | +
| Service-Pill (online) | +inline-flex items-center gap-1 px-2 py-0.5 rounded-sm text-[10px] font-bold uppercase tracking-wide bg-green-50 text-green-700 border border-green-200 |
+ — | +Offline: bg-red-50 text-red-700 border-red-200 |
+
| Running-Pill | +bg-amber-50 text-amber-700 border border-amber-200 (gleiche Basis) |
+ — | +Nur sichtbar wenn status === 'RUNNING'. Icon ⟳ via animate-spin |
+
| Queued-Badge | +text-[10px] text-amber-800 bg-amber-100 border border-amber-200 px-2 py-0.5 rounded-sm font-bold |
+ — | +Nur sichtbar wenn mind. 1 Lauf QUEUED. Text: „⏳ N in Warteschlange" | +
| Stat Cards (Übersicht) | |||
| Grid (Desktop) | +grid grid-cols-1 sm:grid-cols-3 gap-3 |
+ Mobile: 1 Spalte; ab 640px: 3 Spalten | +Neu | +
| Stat Card | +bg-white border border-line rounded-sm px-4 py-3 |
+ padding: 12px 16px · kein Hover (nicht klickbar) | +— | +
| Stat-Zahl | +font-serif text-3xl font-bold text-ink leading-none |
+ 30px, Merriweather · deutsche Tausend-Trennung via n.toLocaleString('de-DE') |
+ — | +
| Stat-Label | +text-[10px] font-bold uppercase tracking-widest text-gray-400 mt-1 |
+ 10px | +— | +
| Stat-Subtext | +text-[9px] text-gray-300 mt-0.5 |
+ 9px · optional | +— | +
| Modell-Tabelle (Übersicht) | |||
| Tabellen-Container | +bg-white border border-line rounded-sm overflow-hidden |
+ — | +Neu — kein <table>, sondern CSS-Grid-Rows |
+
| Tabellenkopf | +bg-ink grid px-4 py-2 grid-cols-[2fr_80px_80px_80px_80px_100px] |
+ — | +Kopfzellen: text-[9px] font-bold uppercase tracking-wide text-brand-mint |
+
| Tabellenzeile | +grid px-4 py-2.5 border-b border-line items-center cursor-pointer hover:bg-surface transition-colors grid-cols-[2fr_80px_80px_80px_80px_100px] |
+ Klick → goto('/admin/ocr/{id}') |
+ Mobile: Tabelle horizontal scrollbar (overflow-x-auto) oder vereinfachte 2-Spalten-Liste |
+
| Global-Zeile | +Zusätzlich bg-[#F8F8FC] |
+ Immer erste Zeile (personId === null) |
+ — | +
| Sender-Name | +text-sm font-medium text-gray-700 |
+ 14px · darunter modelName: text-[10px] text-gray-400 mt-0.5 |
+ Name aus personNames[run.personId] des training-info-Endpoints |
+
| Metrik-Wert (gut) | +text-green-700 font-semibold |
+ Accuracy ≥ 95 % oder niedrigste CER | +— | +
| Metrik-Wert (Fehler) | +text-red-600 font-semibold |
+ — | +Zeigt „—" bei fehlenden Werten (FAILED/RUNNING) | +
| Status Pills (alle Seiten) | |||
| Basis-Klassen | +inline-flex items-center gap-1 px-2 py-0.5 rounded-sm text-[10px] font-bold uppercase tracking-wide border |
+ — | +Icon als Textzeichen vor Label (✓ ⟳ ✕ ⏳) | +
| DONE | +bg-green-50 text-green-700 border-green-200 |
+ — | — | +
| RUNNING | +bg-amber-50 text-amber-700 border-amber-200 |
+ — | +Icon ⟳ in eigenem <span class="animate-spin"> |
+
| FAILED | +bg-red-50 text-red-700 border-red-200 |
+ — | — | +
| QUEUED | +bg-yellow-50 text-yellow-800 border-yellow-200 |
+ — | — | +
| Metric Cards (Detail) | |||
| Grid | +grid grid-cols-2 sm:grid-cols-4 gap-3 |
+ Mobile: 2×2 · Desktop: 4×1 | +Neu | +
| Metric Card | +bg-white border border-line rounded-sm px-4 py-3 text-center |
+ — | — | +
| Zahl (positiv) | +font-serif text-3xl font-bold leading-none text-green-700 |
+ Accuracy ≥ 95 % | — | +
| Zahl (neutral) | +font-serif text-3xl font-bold leading-none text-ink |
+ Standard | — | +
| Delta-Zeile | +text-[10px] mt-1 + text-green-600 / text-red-600 / text-gray-400 |
+ 10px | +Format: „↑ von 94,8 % (vorher)". Entfällt bei < 2 DONE-Läufen. | +
| Trainingshistorie (Detail) | |||
| Tabellen-Container | +bg-white border border-line rounded-sm overflow-hidden |
+ — | — | +
| Tabellenkopf | +bg-ink grid px-4 py-2 grid-cols-[100px_1fr_80px_80px_80px_80px] |
+ — | +Mobile: grid-cols-[80px_1fr_80px] (Status · Datum · Genauigkeit) |
+
| RUNNING-Zeile | +bg-amber-50 zusätzlich |
+ — | +Leere Metrik-Zellen zeigen „—" | +
| FAILED-Zeile | +bg-red-50 (sehr hell, ~#fff8f8) |
+ — | +Fehlertext folgt als zweite Sub-Zeile: text-[11px] text-red-600 px-4 pb-2 col-span-full |
+
| Buttons & Aktionen | |||
| Primär-Button | +inline-flex items-center h-9 px-4 bg-ink text-white rounded-sm text-xs font-bold uppercase tracking-wide hover:opacity-90 transition-opacity |
+ h: 36px (44px auf Mobile) | +Disabled wenn Lauf aktiv: opacity-50 cursor-not-allowed pointer-events-none |
+
| Outline-Button | +inline-flex items-center h-9 px-4 border border-ink text-ink rounded-sm text-xs font-bold uppercase tracking-wide hover:bg-surface transition-colors |
+ — | +Export: window.location.href = '/api/ocr/training-data/export' |
+
| Personen-Link | +text-sm text-ink underline underline-offset-2 hover:text-brand-mint transition-colors |
+ — | +Nur auf Sender-Detailseite. Ziel: /persons/{personId}. Entfällt bei personId === null. |
+
| API-Aufrufe | |||
| Übersicht laden | +GET /api/ocr/training-info |
+ Felder: availableBlocks · totalOcrBlocks · availableDocuments · availableSegBlocks · ocrServiceAvailable · runs · personNames |
+ Im +page.server.ts laden; SenderModel-Liste zusätzlich via eigenem Endpunkt oder aus runs aggregieren |
+
| Training starten | +POST /api/ocr/train / POST /api/ocr/segtrain |
+ Response: OcrTrainingRun |
+ Sofort in Health-Bar und Tabelle anzeigen; SSE-Kanal öffnen | +
| Live-Updates | +EventSource('/api/ocr/jobs/{jobId}/progress') |
+ Server-Sent Events | +Health-Bar + Status-Pills live aktualisieren; onDestroy schließen |
+