Der bestehende Dashboard-Aufbau (Neueste Aktivität links, DropZone + Metadaten-Widget rechts) bleibt unverändert. Unterhalb des Zwei-Spalten-Gitters erscheint ein neuer vollbreiter Mission-Control-Streifen mit drei gleichwertigen Spalten: Rahmen einzeichnen (Segmentierung, kein Vorwissen nötig), Text eintippen (Transkription, Kurrent hilfreich), Lesefertig ✓ (Belohnungsbereich).
Die „Transkription fehlt"-Spalte aus Issue #240 wird in Segmentierung + Transkription aufgeteilt, um eine klare Beitragspyramide zu schaffen: Jeder kann Rahmen einzeichnen — nicht jeder kann Kurrent lesen. Ein wöchentlich rotierender Sort mit Experten-gesucht-Escape-Hatch verhindert, dass schwer lesbare Dokumente dauerhaft die Spalte blockieren.
Dokumente ohne Annotationsrahmen. Kein Kurrent nötig — Textblöcke markieren reicht.
Bedingung: annotation_count = 0
Sort: Wöchentliche Rotation (seeded shuffle, s. u.)
Fortschritt: Wochenpuls „↑ +5 diese Woche", kein globaler Balken
Annotationen vorhanden, aber Text fehlt oder reviewed < 90 %. Kurrent-Kenntnisse hilfreich.
Bedingung: annotation_count > 0 AND reviewed_pct < 0.90
Sort: Teilfortschritt zuerst, dann wöchentliche Rotation; needsExpert-Flagge schiebt nach hinten
Fortschritt: Per-Dokument-Balken „3 / 8 Blöcke"
Reviewed ≥ 90 %. Keine Aufgabe — Einladung zum Lesen.
Bedingung: reviewed_pct >= 0.90
Sort: Neueste zuerst
Fortschritt: „94 % geprüft" als Text — kein Balken, die mint-Spalte ist das Signal
Leerstand: Cross-Column-Redirect zu Spalte 1
updated_at
können dieselben 3 besonders schwer lesbaren Dokumente dauerhaft die Spalte blockieren.
Jeder öffnet sie, gibt auf, und die Spalte wird zur Sackgasse.Document.needsExpert = true (1 Boolean, keine Migration wenn Flyway-Migration V{n} hinzugefügt wird).
In der Transkriptions-Spalte zeigen flagged Dokumente einen lila Badge und werden hinter unflagged Dokumenten einsortiert.
Kein Leaderboard, keine Scham — nur ein ehrliches Signal an die Community.
| Element | SQL / Tailwind | Wert | Hinweis |
|---|---|---|---|
| Sort Transkription | ORDER BY textedBlocks DESC, HASHTEXT(id::text || EXTRACT(WEEK FROM NOW())::int::text) | — | Kein neues Feld nötig; ändert sich automatisch jede Woche |
needsExpert-Flag | ALTER TABLE documents ADD COLUMN needs_expert BOOLEAN NOT NULL DEFAULT FALSE | Flyway V{n}__add_needs_expert.sql | Flagged Docs ans Ende: ORDER BY needs_expert ASC, ... |
| Experten-Badge | inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-purple-50 border border-purple-200 text-purple-700 | Kontrast 6,8:1 ✓ | Nur wenn doc.needsExpert === true |
| „Zu schwer"-Button (Enrich) | text-xs text-gray-400 hover:text-gray-600 underline underline-offset-2 | — | Unscheinbar — kein roter Knopf, keine Scham |
| Endpoint (Flagge setzen) | PATCH /api/documents/{id}/needs-expert | @RequirePermission(READ_ALL) | Jeder angemeldete Nutzer darf flaggen |
Die rechte Spalte (DropZone + Metadaten) erscheint auf Mobil zuerst im DOM (lg:order-last schiebt sie auf Desktop nach rechts).
Der Streifen stapelt seine drei Spalten vertikal. Jede Spalte hat volle Breite — keine Overflow-Probleme.
min-h-[44px] (WCAG 2.2).
Dokument-Zeilen in den Spalten: min-h-[44px] py-2.
Der „Zu schwer"-Button auf der Enrich-Seite: min-h-[44px] als Icon-Button mit aria-label.
Unter jedem Spaltentitel. „Ohne Vorkenntnisse" (grün) vs. „Kurrent hilfreich" (navy-neutral). Senkt die Hemmschwelle — Neueinsteiger sehen sofort, was ohne Kurrent-Kenntnisse möglich ist.
bg-green-50 border-green-200 text-green-800 / bg-surface border-line text-ink
„↑ +5 diese Woche · 1 480 offen" statt globalem Fortschrittsbalken. Zeigt Schwung, nicht den Berg. Psychologisch: 0,8 %-Balken ist demotivierender als kein Balken.
SELECT COUNT(*) WHERE created_at > NOW() - INTERVAL '7 days'
Nur in Spalte 2, nur wenn annotation_count > 0. Richtiger Maßstab:
8 Blöcke sind in einer Sitzung abschließbar. Zeigt auch, welche Dokumente „fast fertig" sind.
width: {textedBlocks / totalBlocks * 100}%; Guard: totalBlocks === 0 → width: 0
Max. 3 Initialen-Bubbles der letzten Beitragenden pro Spalte. Kein Leaderboard (Wettbewerb) — soziale Sichtbarkeit (Zugehörigkeit). Farbe deterministisch aus User-ID-Hash.
DTO: lastContributors: [{initials, colorIndex}] — nur Initialen, keine Namen (Nora)
Ein einziger opinionated Button je Aufgaben-Spalte, der direkt zum nächsten Dokument springt. Entscheidungslähmung ist der Hauptgrund für Non-Participation bei Familienprojekten.
/enrich?filter=NEEDS_SEGMENTATION&next=1 (Segmentierung)/enrich?filter=NEEDS_TRANSCRIPTION&next=1 (Transkription)
Wenn Spalte 3 leer ist (frühe Phase), erscheint kein toter Endpunkt sondern: „Erscheint hier, sobald die Transkription abgeschlossen ist — jetzt mithelfen →". Der Link springt zu Spalte 1.
{#if readyToRead.length === 0} → DashboardReadyToReadEmpty.svelte
| Element | Tailwind-Klassen | Pixel / Wert | Hinweis |
|---|---|---|---|
| Streifen-Wrapper | mt-4 bg-white border border-line rounded-sm p-6 | padding 24 px | Direkt nach bestehendem div.mt-4.grid |
| Streifen-Titel | text-xs font-bold uppercase tracking-widest text-gray-400 mb-4 | 12 px / 700 | Standard-Section-Title-Muster |
| 3-Spalten-Grid | grid grid-cols-1 gap-4 sm:grid-cols-3 | gap 16 px | sm = 640 px; darunter stapeln |
| Segmentierung-Spalte | bg-surface rounded-sm border border-line p-4 flex flex-col gap-3 | — | Neutral |
| Transkription-Spalte | bg-surface rounded-sm border border-line p-4 flex flex-col gap-3 | — | Neutral — es ist eine Aufgabe |
| Lesefertig-Spalte (gefüllt) | bg-mint/10 rounded-sm border border-mint p-4 flex flex-col gap-3 | — | Mint-Ton = Erfolg |
| Lesefertig-Spalte (leer) | flex flex-col items-center justify-center text-center bg-mint/5 border border-dashed border-mint rounded-sm p-6 min-h-[120px] | min-h 120 px | Kein toter Endpunkt |
| Skill-Pill easy | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-green-50 border border-green-200 text-green-800 | Kontrast 9,7:1 ✓ AAA | — |
| Skill-Pill kurrent | inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold bg-surface border border-line text-ink | Kontrast 14,5:1 ✓ AAA | Neutral — kein Abschreck-Signal |
| Wochenpuls-Zahl | text-xs font-semibold text-green-700 (Seg.) / text-ink (Trans.) | 12 px | Kein globaler Balken |
| Per-Dokument-Track | flex-1 h-1 bg-navy/20 rounded-full overflow-hidden | h 4 px | Nur wenn annotation_count > 0 |
| Per-Dokument-Fill | h-full bg-ink rounded-full transition-all + style="width:{pct}%" | — | Guard: totalBlocks === 0 → 0% |
| Lesefertig-Prozent | text-xs font-semibold text-green-800 | 12 px | Kein Balken — mint-Spalte ist das Signal |
| Contributor-Avatar | w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white shrink-0 | 24 × 24 px | Farbe: 6 Werte, Index = userIdHash % 6 |
| CTA-Button (primär) | block w-full text-center text-xs font-semibold text-white bg-ink rounded-sm py-2 mt-2 hover:bg-ink-2 transition-colors focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-1 | min-h 36 px | aria-label mit Dokumenttitel falls nötig |
| CTA-Button (ghost, Leerstand) | inline-flex items-center text-xs font-semibold text-ink border border-ink rounded-sm px-3 py-2 hover:bg-ink hover:text-white transition-colors | min-h 36 px | — |
| Experten-gesucht-Badge | inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-purple-50 border border-purple-200 text-purple-700 | Kontrast 6,8:1 ✓ AA | Nur wenn doc.needsExpert === true |
| Sichtbarkeit Streifen | {#if needsSegmentation.length > 0 || needsTranscription.length > 0 || readyToRead.length > 0} | — | Streifen verschwindet wenn alle drei Buckets leer |
| Dokument-Zeile Mindesthöhe | min-h-[44px] flex items-start py-2 | 44 px ✓ WCAG 2.2 | Gilt für alle klickbaren Zeilen |
| Endpoint / Query | Bedingung | Sort | Auth |
|---|---|---|---|
GET /api/documents/needs-segmentation?size=3 | NOT EXISTS (SELECT 1 FROM document_annotations WHERE document_id = d.id) | HASHTEXT(id::text || week::text) | READ_ALL |
GET /api/documents/needs-transcription?size=3 | EXISTS annotation AND (no blocks OR reviewed_pct < 0.90) | textedBlocks DESC, needs_expert ASC, HASHTEXT(...) | READ_ALL |
GET /api/documents/ready-to-read?size=3 | reviewed_pct >= 0.90 | updated_at DESC | READ_ALL |
PATCH /api/documents/{id}/needs-expert | Setzt needs_expert = true | — | READ_ALL (jeder Nutzer darf flaggen) |
GET /api/stats/strip-activity | Wochenpuls: COUNT(*) WHERE created_at > NOW() - INTERVAL '7 days' pro Bucket | — | READ_ALL |
| Flyway-Migration | ALTER TABLE documents ADD COLUMN needs_expert BOOLEAN NOT NULL DEFAULT FALSE | — | V{n}__add_needs_expert_flag.sql |
| Index prüfen (Tobias) | document_annotations(document_id), transcription_blocks(document_id, reviewed) | — | EXPLAIN ANALYZE vor Merge |
| Division durch 0 (Sara) | Alle reviewed_pct-Queries: CASE WHEN COUNT(*) = 0 THEN 0 ELSE SUM(...)::float / COUNT(*) END | — | — |
DashboardMissionControl.svelteWrapper für den vollbreiten Streifen. Props: needsSegmentation, needsTranscription,
readyToRead, weeklyActivity. Rendert die drei Spalten und ist komplett unsichtbar wenn alle Arrays leer sind.
DashboardSegmentationCol.svelteSpalte 1: Skill-Pill, Wochenpuls, Avatare, Dokumentliste, CTA. Keine Balken — keine Dokument-Metadaten vorhanden.
DashboardTranscriptionCol.svelteSpalte 2: Skill-Pill, Wochenpuls, Avatare, per-Dokument-Balken, Experten-Badge bei needsExpert, CTA.
DashboardReadyToReadCol.svelteSpalte 3: Zeigt gefüllten Zustand (Liste mit %-Text) oder leeren Zustand (Cross-Column-Redirect zu Segmentierung).
DashboardNeedsMetadata.svelte ist unverändert —
sie lebt weiterhin in der rechten Spalte. Der Mission-Control-Streifen ist vollständig additiv und ändert nichts am bestehenden Layout.