632 lines
36 KiB
HTML
632 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Geschichten — Dokumentverknüpfung · Familienarchiv</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{--color-page:#FAFAF7;--color-surface:#F5F4EE;--color-subtle:#EDECEA;--color-border:#D8D7D0;--color-text-muted:#6B6A63;--color-text:#1C1C18;--navy:#012851;--mint:#A1DCD8;--sand:#F0EFE9;--turquoise:#00C7B1;--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--font-display:'Fraunces',Georgia,serif;--font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'DM Mono',monospace;--radius-sm:4px;--radius-md:6px;--radius-lg:10px;--radius-xl:16px;--shadow-card:0 1px 3px rgba(28,28,24,.06),0 1px 2px rgba(28,28,24,.04);--shadow-raised:0 4px 12px rgba(28,28,24,.08),0 2px 4px rgba(28,28,24,.04);--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);}
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||
body{font-family:var(--font-sans);background:#E8E7E2;color:var(--color-text);font-size:14px;line-height:1.6;}
|
||
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
|
||
.doc-header{display:flex;justify-content:space-between;align-items:flex-end;padding-bottom:28px;border-bottom:1px solid var(--color-border);margin-bottom:48px;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;border-radius:var(--radius-xl) var(--radius-xl) 0 0;}
|
||
.doc-header h1{font-family:var(--font-display);font-size:28px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||
.doc-header p{font-size:13px;color:var(--color-text-muted);max-width:680px;}
|
||
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
|
||
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;}
|
||
.pill-o{background:var(--orange-tint);color:var(--orange-dark);}
|
||
.section{margin-bottom:64px;}
|
||
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
|
||
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
|
||
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
|
||
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
|
||
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
|
||
.jh-o{background:var(--orange-tint);border:1px solid #F0C89A;}.jh-o .jn{color:var(--orange);}.jh-o p,.jh-o .fl{color:var(--orange-dark);}
|
||
.scr{margin-bottom:56px;}
|
||
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
|
||
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
|
||
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
|
||
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
|
||
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
|
||
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
|
||
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
|
||
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
||
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;flex-direction:column;min-height:480px;}
|
||
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.07);display:flex;flex-direction:column;border:6px solid #1C1C18;}
|
||
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:12px;background:var(--color-page);}.pst b{font-weight:600;}.pst span{font-size:10px;}
|
||
.pb{flex:1;overflow-y:auto;display:flex;flex-direction:column;}
|
||
.fa-nav{height:32px;background:var(--navy);display:flex;align-items:center;padding:0 12px;gap:8px;flex-shrink:0;}
|
||
.fa-logo{font-size:7px;font-weight:900;color:#fff;letter-spacing:.8px;border-bottom:2px solid var(--mint);padding-bottom:1px;}
|
||
.fa-link{font-size:5.5px;color:rgba(255,255,255,.4);font-weight:700;text-transform:uppercase;letter-spacing:.05em;}
|
||
.fa-link.on{color:rgba(255,255,255,.9);}
|
||
.fa-nav-r{margin-left:auto;display:flex;gap:5px;align-items:center;}
|
||
.fa-av{width:16px;height:16px;background:rgba(255,255,255,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:rgba(255,255,255,.5);}
|
||
.agent{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:24px;margin-top:20px;}
|
||
.agent h4{font-family:var(--font-mono);font-size:12px;font-weight:500;margin-bottom:8px;color:var(--navy);}
|
||
.agent pre{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);margin-bottom:12px;white-space:pre-wrap;}
|
||
.at{width:100%;border-collapse:collapse;font-size:11px;}
|
||
.at th{font-family:var(--font-mono);font-size:10px;font-weight:500;text-align:left;color:var(--color-text-muted);padding:6px 8px;border-bottom:2px solid var(--color-border);}
|
||
.at td{padding:5px 8px;border-bottom:1px solid var(--color-subtle);vertical-align:top;line-height:1.5;}
|
||
.at tr.grp td{font-family:var(--font-mono);font-size:9px;color:var(--color-text-muted);background:var(--color-subtle);font-weight:500;letter-spacing:.06em;text-transform:uppercase;padding:4px 8px;}
|
||
.at code{font-family:var(--font-mono);font-size:10px;color:var(--navy);background:rgba(1,40,81,.06);padding:1px 4px;border-radius:2px;}
|
||
|
||
/* ── Topbar inside frame ── */
|
||
.fa-topbar{background:#fff;border-bottom:1px solid #E4E2D8;display:flex;align-items:center;height:44px;flex-shrink:0;padding-right:10px;}
|
||
.fa-accent{width:3px;background:var(--navy);height:44px;flex-shrink:0;}
|
||
.fa-back{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;color:#6b7280;flex-shrink:0;margin:0 4px;}
|
||
.fa-tb-divider{width:1px;height:16px;background:#E4E2D8;flex-shrink:0;margin:0 6px;}
|
||
.fa-tb-title-block{flex:1;min-width:0;padding:0 4px;}
|
||
.fa-tb-title{font-family:Georgia,serif;font-size:10px;font-weight:700;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||
.fa-tb-date{font-size:7px;color:#6b7280;margin-top:1px;}
|
||
.fa-chip{display:inline-flex;align-items:center;gap:2px;padding:1px 5px 1px 2px;background:var(--sand);border:1px solid #E4E2D8;border-radius:8px;white-space:nowrap;font-size:7px;color:var(--color-text);flex-shrink:0;}
|
||
.fa-chip .av{width:12px;height:12px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:#fff;flex-shrink:0;}
|
||
.av-navy{background:var(--navy);}
|
||
.av-purple{background:#5A3080;}
|
||
.fa-tb-chips{display:flex;align-items:center;gap:4px;margin:0 6px;flex-shrink:0;}
|
||
.fa-tb-btn{height:22px;padding:0 8px;font-size:6.5px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;border-radius:3px;display:inline-flex;align-items:center;gap:3px;flex-shrink:0;border:1.5px solid #E4E2D8;color:#4b5563;}
|
||
.fa-tb-btn.primary{background:var(--navy);color:var(--mint);border-color:var(--navy);}
|
||
.fa-tb-btn.active{background:var(--navy);color:#fff;border-color:var(--navy);}
|
||
.fa-tb-btn-ico{width:22px;height:22px;border:1.5px solid #E4E2D8;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;color:#6b7280;flex-shrink:0;}
|
||
.fa-tb-kebab{width:22px;height:22px;border:1.5px solid #E4E2D8;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;flex-direction:column;gap:2px;flex-shrink:0;}
|
||
.fa-tb-kebab span{display:block;width:3px;height:3px;border-radius:50%;background:#6b7280;}
|
||
|
||
/* ── Drawer ── */
|
||
.fa-drawer{background:#fff;border-bottom:1px solid #E4E2D8;padding:12px 16px;flex-shrink:0;}
|
||
.fa-drawer-grid{display:grid;gap:16px;}
|
||
.fa-drawer-grid.cols-3{grid-template-columns:1fr 1fr 1fr;}
|
||
.fa-drawer-grid.cols-4{grid-template-columns:1fr 1fr 1fr 1fr;}
|
||
.fa-drawer-col-head{font-size:6.5px;font-weight:800;text-transform:uppercase;letter-spacing:.09em;color:#6b7280;margin-bottom:8px;}
|
||
.fa-drawer-label{font-size:6.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#9ca3af;margin-bottom:3px;}
|
||
.fa-drawer-value{font-family:Georgia,serif;font-size:9.5px;color:var(--navy);}
|
||
.fa-drawer-field{margin-bottom:7px;}
|
||
.fa-person-row{display:flex;align-items:center;gap:5px;padding:3px 4px;border-radius:3px;}
|
||
.fa-p-av{width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:6px;font-weight:800;color:#fff;flex-shrink:0;}
|
||
.fa-p-name{font-family:Georgia,serif;font-size:9px;color:var(--navy);}
|
||
.fa-tag{display:inline-block;padding:1px 5px;border-radius:2px;font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;background:#EDECEA;color:#6B6A63;margin:1.5px 1.5px;}
|
||
.fa-story-entry{margin-bottom:8px;}
|
||
.fa-story-title{font-family:Georgia,serif;font-size:9px;color:var(--navy);display:block;text-decoration:none;}
|
||
.fa-story-title:hover{text-decoration:underline;}
|
||
.fa-story-meta{font-size:6.5px;color:#9ca3af;margin-top:1px;}
|
||
.fa-story-link-muted{font-size:6.5px;color:#9ca3af;display:block;margin-top:8px;}
|
||
.fa-story-link-turquoise{font-size:6.5px;color:var(--turquoise);display:block;margin-top:4px;}
|
||
.fa-status-badge{display:inline-flex;align-items:center;gap:3px;padding:1px 5px;border-radius:3px;font-size:6px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;background:#D1FAE5;color:#065F46;border:1px solid #A7F3D0;}
|
||
.fa-status-dot{width:4px;height:4px;border-radius:50%;background:#10B981;}
|
||
|
||
/* ── PDF area ── */
|
||
.fa-pdf{background:#D4D0C8;flex:1;display:flex;align-items:center;justify-content:center;min-height:120px;}
|
||
.fa-paper{background:#FFFEF8;box-shadow:0 2px 8px rgba(0,0,0,.14);border-radius:1px;padding:9px 11px;display:flex;flex-direction:column;gap:2px;width:42%;}
|
||
.fa-pl{height:3px;background:#C4BDB0;border-radius:1px;opacity:.5;margin-bottom:2px;}
|
||
.fa-ps{height:2px;background:#C4BDB0;border-radius:1px;opacity:.28;margin-bottom:1.5px;}
|
||
|
||
/* ── Mobile nav slim ── */
|
||
.fa-nav-slim{height:26px;background:var(--navy);display:flex;align-items:center;padding:0 10px;gap:6px;flex-shrink:0;}
|
||
.fa-nav-slim .fa-logo{font-size:6px;}
|
||
|
||
/* ── LLM guide ── */
|
||
.llm{background:var(--color-page);border:2px solid var(--navy);border-radius:var(--radius-xl);padding:32px 40px;margin-top:64px;}
|
||
.llm h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;color:var(--navy);}
|
||
.llm h3{font-size:14px;font-weight:600;margin:20px 0 8px;color:var(--color-text);}
|
||
.llm p,.llm li{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
|
||
.llm ul,.llm ol{padding-left:20px;margin-bottom:12px;}
|
||
.llm li{margin-bottom:4px;}
|
||
.llm code{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:1px 5px;border-radius:3px;}
|
||
.llm table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px;}
|
||
.llm th,.llm td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border);}
|
||
.llm th{font-weight:500;color:var(--color-text);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
|
||
.llm td{color:var(--color-text-muted);}
|
||
|
||
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="doc">
|
||
|
||
<!-- ══ DOCUMENT HEADER ══════════════════════════════════════════════════════ -->
|
||
<div class="doc-header">
|
||
<div>
|
||
<h1>Geschichten — Dokumentverknüpfung</h1>
|
||
<p>Entdeckung verlinkter Geschichten über die Dokumentdetailseite. Die Geschichten-Spalte erscheint im Details-Drawer als vierte Spalte neben Details, Personen und Schlagwörtern — nur wenn mindestens eine veröffentlichte Geschichte mit diesem Dokument verknüpft ist.</p>
|
||
</div>
|
||
<div class="doc-meta">
|
||
<span class="pill pill-o">Final Spec</span><br>
|
||
2026-05-02<br>
|
||
@leonievoss
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══ JOURNEY HEADER ═══════════════════════════════════════════════════════ -->
|
||
<div class="jh jh-o">
|
||
<div class="jn">D</div>
|
||
<div>
|
||
<h2>Dokument-Entdeckung</h2>
|
||
<p>Lesende entdecken Geschichten, die sich auf einen Brief beziehen, direkt beim Öffnen des Details-Drawers auf der Dokumentdetailseite.</p>
|
||
<div class="fl">/documents/[id] · DocumentMetadataDrawer</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══ SECTION: KONZEPT ══════════════════════════════════════════════════════ -->
|
||
<div class="section">
|
||
<div class="section-title">Konzept — US-BLOG-005</div>
|
||
|
||
<p class="prose">
|
||
Es wird <strong>kein neuer Button in die DocumentTopBar</strong> eingefügt. Der bestehende "Details"-Toggle ist der einzige Einstiegspunkt — er öffnet den <code>DocumentMetadataDrawer</code>, der bereits Details, Personen und Schlagwörter enthält. Die Geschichten-Spalte erscheint dort als <strong>vierte Spalte</strong>, wenn mindestens eine veröffentlichte Geschichte mit diesem Dokument verknüpft ist.
|
||
</p>
|
||
<p class="prose">
|
||
Wenn keine Geschichte verknüpft ist, zeigt der Drawer das gewohnte 3-spaltige Layout — kein leerer Zustand, kein Hinweis, kein Platzhalter. Stille ist korrekt (US-BLOG-005 verlangt keinen Empty State). Die Grid-Klasse wechselt konditionell von <code>lg:grid-cols-3</code> zu <code>lg:grid-cols-4</code> über eine Prop.
|
||
</p>
|
||
<p class="prose">
|
||
Die Prop <code>geschichten: Geschichte[]</code> wird vom <code>+page.server.ts</code> der Dokumentdetailseite befüllt. Nur <strong>veröffentlichte</strong> Geschichten werden übergeben — die Filterung erfolgt auf Serviceebene im Backend. Der <code>DocumentMetadataDrawer</code> ist rein präsentational und trifft keine eigenen Datenbankabfragen.
|
||
</p>
|
||
</div>
|
||
|
||
|
||
<!-- ══ SCREEN D-1: DRAWER GESCHLOSSEN ═══════════════════════════════════════ -->
|
||
<div class="scr" id="d1">
|
||
<div class="scr-head">
|
||
<h3>D-1 — Dokument ohne geöffneten Drawer</h3>
|
||
<span class="scr-id">D-1</span>
|
||
</div>
|
||
<div class="scr-desc">Normalzustand: Der Details-Drawer ist geschlossen. Für den Lesenden ändert sich optisch nichts — die Geschichten-Spalte ist erst sichtbar, wenn der Drawer geöffnet wird.</div>
|
||
<div class="scr-var"><strong>Desktop ≥1024px</strong> — Drawer geschlossen, PDF-Viewer füllt die verbleibende Höhe.</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<div class="bp-lbl">Desktop · 1040px · Drawer geschlossen</div>
|
||
<div class="desk">
|
||
|
||
<!-- Nav -->
|
||
<div class="fa-nav">
|
||
<div class="fa-logo">FAMILIENARCHIV</div>
|
||
<div class="fa-link on">Dokumente</div>
|
||
<div class="fa-link">Personen</div>
|
||
<div class="fa-link">Geschichten</div>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<!-- TopBar — drawer closed -->
|
||
<div class="fa-topbar">
|
||
<div class="fa-accent"></div>
|
||
<div class="fa-back">←</div>
|
||
<div class="fa-tb-divider"></div>
|
||
<div class="fa-tb-title-block">
|
||
<div class="fa-tb-title">Brief an Franz Raddatz, 12. Juli 1938</div>
|
||
<div class="fa-tb-date">12. Juli 1938</div>
|
||
</div>
|
||
<div class="fa-tb-chips">
|
||
<div class="fa-chip"><div class="av av-navy">FR</div> Franz Raddatz</div>
|
||
<div class="fa-chip"><div class="av av-purple">EM</div> Emma Müller</div>
|
||
</div>
|
||
<div class="fa-tb-divider"></div>
|
||
<!-- Details toggle — closed state -->
|
||
<button class="fa-tb-btn" style="margin-right:6px;">Details ▾</button>
|
||
<div class="fa-tb-divider"></div>
|
||
<button class="fa-tb-btn primary" style="margin-left:6px;">Transkribieren</button>
|
||
<button class="fa-tb-btn" style="margin-left:4px;">Bearbeiten</button>
|
||
<div class="fa-tb-btn-ico" style="margin-left:4px;">
|
||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PDF viewer area — drawer closed, fills height -->
|
||
<div class="fa-pdf" style="flex:1;min-height:340px;">
|
||
<div class="fa-paper" style="min-height:200px;">
|
||
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:6px;opacity:.75;">Breslau, den 12. Juli 1938</div>
|
||
<div class="fa-pl" style="width:92%;"></div>
|
||
<div class="fa-ps" style="width:85%;"></div>
|
||
<div class="fa-ps" style="width:90%;"></div>
|
||
<div class="fa-pl" style="width:78%;"></div>
|
||
<div class="fa-ps" style="width:88%;"></div>
|
||
<div class="fa-ps" style="width:70%;"></div>
|
||
<div class="fa-pl" style="width:84%;"></div>
|
||
<div class="fa-ps" style="width:90%;"></div>
|
||
<div class="fa-ps" style="width:60%;"></div>
|
||
<div class="fa-pl" style="width:75%;"></div>
|
||
<div class="fa-ps" style="width:82%;"></div>
|
||
<div class="fa-ps" style="width:46%;"></div>
|
||
<div style="font-size:6px;color:#8A8070;margin-top:8px;text-align:right;opacity:.7;">Dein Vater</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /desk -->
|
||
</div><!-- /prev-col -->
|
||
</div><!-- /previews -->
|
||
|
||
<div class="agent">
|
||
<h4>Hinweis — D-1</h4>
|
||
<pre>Keine Änderung an der DocumentTopBar. Der "Details"-Button ist bereits implementiert und öffnet/schließt den Drawer. Dieser Screen zeigt den geschlossenen Zustand — identisch mit dem heutigen Verhalten.</pre>
|
||
</div>
|
||
</div><!-- /scr D-1 -->
|
||
|
||
|
||
<!-- ══ SCREEN D-2: DRAWER OFFEN — 4-SPALTEN-GRID ═══════════════════════════ -->
|
||
<div class="scr" id="d2">
|
||
<div class="scr-head">
|
||
<h3>D-2 — Drawer offen · 4 Spalten mit Geschichten</h3>
|
||
<span class="scr-id">D-2</span>
|
||
</div>
|
||
<div class="scr-desc">Der Drawer wird geöffnet. Weil dieses Dokument mit veröffentlichten Geschichten verknüpft ist, schaltet das Grid von 3 auf 4 Spalten um. Die Geschichten-Spalte erscheint ganz rechts — gleiche typografische Behandlung wie die anderen Spalten.</div>
|
||
<div class="scr-var"><strong>Desktop ≥1024px</strong> — Prop <code>geschichten.length > 0</code> ist true, Grid-Klasse ist <code>lg:grid-cols-4</code>.</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<div class="bp-lbl">Desktop · 1040px · Drawer offen · 4 Spalten</div>
|
||
<div class="desk">
|
||
|
||
<!-- Nav -->
|
||
<div class="fa-nav">
|
||
<div class="fa-logo">FAMILIENARCHIV</div>
|
||
<div class="fa-link on">Dokumente</div>
|
||
<div class="fa-link">Personen</div>
|
||
<div class="fa-link">Geschichten</div>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<!-- TopBar — drawer open (Details button active/filled) -->
|
||
<div class="fa-topbar">
|
||
<div class="fa-accent"></div>
|
||
<div class="fa-back">←</div>
|
||
<div class="fa-tb-divider"></div>
|
||
<div class="fa-tb-title-block">
|
||
<div class="fa-tb-title">Brief an Franz Raddatz, 12. Juli 1938</div>
|
||
<div class="fa-tb-date">12. Juli 1938</div>
|
||
</div>
|
||
<div class="fa-tb-chips">
|
||
<div class="fa-chip"><div class="av av-navy">FR</div> Franz Raddatz</div>
|
||
<div class="fa-chip"><div class="av av-purple">EM</div> Emma Müller</div>
|
||
</div>
|
||
<div class="fa-tb-divider"></div>
|
||
<!-- Details toggle — open/active state -->
|
||
<button class="fa-tb-btn active" style="margin-right:6px;">Details ▴</button>
|
||
<div class="fa-tb-divider"></div>
|
||
<button class="fa-tb-btn primary" style="margin-left:6px;">Transkribieren</button>
|
||
<button class="fa-tb-btn" style="margin-left:4px;">Bearbeiten</button>
|
||
<div class="fa-tb-btn-ico" style="margin-left:4px;">
|
||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Drawer — open, 4-column grid -->
|
||
<div class="fa-drawer">
|
||
<div class="fa-drawer-grid cols-4">
|
||
|
||
<!-- Col 1: Details -->
|
||
<div>
|
||
<div class="fa-drawer-col-head">Details</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Datum</div>
|
||
<div class="fa-drawer-value">12. Juli 1938</div>
|
||
</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Ort</div>
|
||
<div class="fa-drawer-value">Breslau</div>
|
||
</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Status</div>
|
||
<div class="fa-status-badge"><span class="fa-status-dot"></span>Hochgeladen</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 2: Personen -->
|
||
<div>
|
||
<div class="fa-drawer-col-head">Personen</div>
|
||
<div class="fa-drawer-label" style="margin-bottom:4px;">Absender</div>
|
||
<div class="fa-person-row">
|
||
<div class="fa-p-av av-navy">FR</div>
|
||
<div class="fa-p-name">Franz Raddatz</div>
|
||
</div>
|
||
<div class="fa-drawer-label" style="margin-top:8px;margin-bottom:4px;">Empfänger</div>
|
||
<div class="fa-person-row">
|
||
<div class="fa-p-av av-purple">EM</div>
|
||
<div class="fa-p-name">Emma Müller</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 3: Schlagwörter -->
|
||
<div>
|
||
<div class="fa-drawer-col-head">Schlagwörter</div>
|
||
<div style="display:flex;flex-wrap:wrap;margin:-1.5px;">
|
||
<span class="fa-tag">Familie</span>
|
||
<span class="fa-tag">Krieg</span>
|
||
<span class="fa-tag">Breslau</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 4: Geschichten (NEW) -->
|
||
<div>
|
||
<div class="fa-drawer-col-head">Geschichten</div>
|
||
|
||
<!-- Story 1 -->
|
||
<div class="fa-story-entry">
|
||
<a class="fa-story-title" href="#">Der Sommer in Breslau</a>
|
||
<div class="fa-story-meta">von Maria Raddatz · 14. März 2025</div>
|
||
</div>
|
||
|
||
<!-- Story 2 -->
|
||
<div class="fa-story-entry">
|
||
<a class="fa-story-title" href="#">Die Hochzeit im Krieg</a>
|
||
<div class="fa-story-meta">von Gertrud Koch · 18. Okt. 2024</div>
|
||
</div>
|
||
|
||
<!-- "Alle anzeigen" link — shown when stories.length > 3 -->
|
||
<!-- (hidden in this example because only 2 stories) -->
|
||
|
||
<!-- "+ Geschichte anhängen" — BLOG_WRITE only -->
|
||
<div class="fa-story-link-turquoise">+ Geschichte anhängen</div>
|
||
</div>
|
||
|
||
</div><!-- /grid -->
|
||
</div><!-- /drawer -->
|
||
|
||
<!-- PDF viewer below drawer -->
|
||
<div class="fa-pdf" style="flex:1;min-height:200px;">
|
||
<div class="fa-paper" style="min-height:120px;">
|
||
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:6px;opacity:.75;">Breslau, den 12. Juli 1938</div>
|
||
<div class="fa-pl" style="width:92%;"></div>
|
||
<div class="fa-ps" style="width:85%;"></div>
|
||
<div class="fa-ps" style="width:88%;"></div>
|
||
<div class="fa-pl" style="width:78%;"></div>
|
||
<div class="fa-ps" style="width:70%;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /desk -->
|
||
</div><!-- /prev-col -->
|
||
</div><!-- /previews -->
|
||
|
||
<!-- impl-ref table D-2 -->
|
||
<div class="agent">
|
||
<h4>impl-ref — D-2 · Geschichten-Spalte im Drawer</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr>
|
||
<th>Element</th>
|
||
<th>Wert / Klasse</th>
|
||
<th>Hinweis</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Grid</td></tr>
|
||
<tr>
|
||
<td>Grid mit Geschichten</td>
|
||
<td><code>grid grid-cols-1 gap-6 lg:grid-cols-4</code></td>
|
||
<td>Konditional: <code>geschichten.length > 0</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Grid ohne Geschichten</td>
|
||
<td><code>grid grid-cols-1 gap-6 lg:grid-cols-3</code></td>
|
||
<td>Default — keine Änderung</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Spalten-Heading</td></tr>
|
||
<tr>
|
||
<td>Col 4 Heading</td>
|
||
<td><code>mb-4 font-sans text-xs font-bold tracking-widest text-ink-3 uppercase</code></td>
|
||
<td>Identisch mit Col 1–3 Headings</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Story-Einträge</td></tr>
|
||
<tr>
|
||
<td>Eintrags-Wrapper</td>
|
||
<td><code>space-y-3</code></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Story-Titel-Link</td>
|
||
<td><code>block font-serif text-sm text-ink hover:text-primary hover:underline</code></td>
|
||
<td>Verlinkt auf <code>/geschichten/[id]</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Story-Meta</td>
|
||
<td><code>font-sans text-[10px] text-ink-3</code></td>
|
||
<td>"von [author] · [date]"</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Aktionslinks</td></tr>
|
||
<tr>
|
||
<td>"Alle anzeigen" Link</td>
|
||
<td><code>font-sans text-[10px] text-ink-3 hover:text-primary</code></td>
|
||
<td>Nur wenn <code>stories.length > 3</code>; verlinkt auf <code>/geschichten?documentId=xxx</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>"+ Geschichte anhängen"</td>
|
||
<td><code>font-sans text-[10px] text-turquoise hover:underline</code></td>
|
||
<td>Nur sichtbar für BLOG_WRITE — via <code>canWrite</code>-Prop</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Props</td></tr>
|
||
<tr>
|
||
<td>Neue Prop</td>
|
||
<td><code>geschichten: Geschichte[]</code></td>
|
||
<td>Wird von <code>+page.server.ts</code> an <code>DocumentMetadataDrawer</code> übergeben</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Neue Prop</td>
|
||
<td><code>canWrite: boolean</code></td>
|
||
<td>Steuert Sichtbarkeit von "+ Geschichte anhängen"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div><!-- /scr D-2 -->
|
||
|
||
|
||
<!-- ══ SCREEN D-3: MOBILE DRAWER ════════════════════════════════════════════ -->
|
||
<div class="scr" id="d3">
|
||
<div class="scr-head">
|
||
<h3>D-3 — Mobile · Drawer offen, Geschichten-Spalte gestapelt</h3>
|
||
<span class="scr-id">D-3</span>
|
||
</div>
|
||
<div class="scr-desc">Auf Mobile ist das Grid einspaltig (<code>grid-cols-1</code>). Alle vier Abschnitte stapeln sich vertikal. Die Geschichten-Spalte erscheint als letzter Block — unterhalb von Schlagwörtern. Layout und Typografie sind identisch mit Desktop, nur volle Breite.</div>
|
||
<div class="scr-var"><strong>Mobile <640px</strong> — kein Person-Chip-Row, kebab statt einzelner Action-Buttons, alle Drawer-Spalten gestapelt.</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<div class="bp-lbl">Mobile · 320px · Drawer offen</div>
|
||
<div class="phone">
|
||
<!-- Status bar -->
|
||
<div class="pst">
|
||
<b>9:41</b>
|
||
<span>●●●</span>
|
||
</div>
|
||
<div class="pb">
|
||
<!-- Nav slim -->
|
||
<div class="fa-nav-slim">
|
||
<div class="fa-logo">FAMILIENARCHIV</div>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<!-- TopBar mobile — compact -->
|
||
<div class="fa-topbar" style="height:40px;">
|
||
<div class="fa-accent" style="height:40px;"></div>
|
||
<div class="fa-back" style="font-size:8px;margin:0 3px;">←</div>
|
||
<div class="fa-tb-divider"></div>
|
||
<div class="fa-tb-title-block" style="padding:0 3px;">
|
||
<div class="fa-tb-title" style="font-size:9px;">Brief an Franz Raddatz…</div>
|
||
<div class="fa-tb-date" style="font-size:6px;">12. Juli 1938</div>
|
||
</div>
|
||
<!-- Details toggle active on mobile too -->
|
||
<button class="fa-tb-btn active" style="font-size:6px;height:19px;padding:0 6px;margin-right:4px;">Details ▴</button>
|
||
<div class="fa-tb-divider"></div>
|
||
<!-- Kebab menu on mobile replaces individual action buttons -->
|
||
<div class="fa-tb-kebab" style="margin-left:4px;">
|
||
<span></span><span></span><span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Drawer — open, single column stacked -->
|
||
<div class="fa-drawer" style="padding:10px 12px;">
|
||
<!-- Col 1: Details -->
|
||
<div style="margin-bottom:14px;">
|
||
<div class="fa-drawer-col-head">Details</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Datum</div>
|
||
<div class="fa-drawer-value" style="font-size:8.5px;">12. Juli 1938</div>
|
||
</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Ort</div>
|
||
<div class="fa-drawer-value" style="font-size:8.5px;">Breslau</div>
|
||
</div>
|
||
<div class="fa-drawer-field">
|
||
<div class="fa-drawer-label">Status</div>
|
||
<div class="fa-status-badge"><span class="fa-status-dot"></span>Hochgeladen</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 2: Personen -->
|
||
<div style="margin-bottom:14px;">
|
||
<div class="fa-drawer-col-head">Personen</div>
|
||
<div class="fa-drawer-label" style="margin-bottom:3px;">Absender</div>
|
||
<div class="fa-person-row">
|
||
<div class="fa-p-av av-navy" style="width:15px;height:15px;font-size:5.5px;">FR</div>
|
||
<div class="fa-p-name" style="font-size:8.5px;">Franz Raddatz</div>
|
||
</div>
|
||
<div class="fa-drawer-label" style="margin-top:6px;margin-bottom:3px;">Empfänger</div>
|
||
<div class="fa-person-row">
|
||
<div class="fa-p-av av-purple" style="width:15px;height:15px;font-size:5.5px;">EM</div>
|
||
<div class="fa-p-name" style="font-size:8.5px;">Emma Müller</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 3: Schlagwörter -->
|
||
<div style="margin-bottom:14px;">
|
||
<div class="fa-drawer-col-head">Schlagwörter</div>
|
||
<div style="display:flex;flex-wrap:wrap;margin:-1.5px;">
|
||
<span class="fa-tag">Familie</span>
|
||
<span class="fa-tag">Krieg</span>
|
||
<span class="fa-tag">Breslau</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Col 4: Geschichten — full width on mobile, below Schlagwörter -->
|
||
<div>
|
||
<div class="fa-drawer-col-head">Geschichten</div>
|
||
|
||
<div class="fa-story-entry">
|
||
<a class="fa-story-title" href="#" style="font-size:8.5px;">Der Sommer in Breslau</a>
|
||
<div class="fa-story-meta" style="font-size:6px;">von Maria Raddatz · 14. März 2025</div>
|
||
</div>
|
||
|
||
<div class="fa-story-entry">
|
||
<a class="fa-story-title" href="#" style="font-size:8.5px;">Die Hochzeit im Krieg</a>
|
||
<div class="fa-story-meta" style="font-size:6px;">von Gertrud Koch · 18. Okt. 2024</div>
|
||
</div>
|
||
|
||
<div class="fa-story-link-muted" style="font-size:6px;">Alle Geschichten anzeigen →</div>
|
||
<div class="fa-story-link-turquoise" style="font-size:6px;">+ Geschichte anhängen</div>
|
||
</div>
|
||
</div><!-- /drawer -->
|
||
|
||
<!-- PDF viewer below drawer on mobile -->
|
||
<div class="fa-pdf" style="flex:1;min-height:100px;">
|
||
<div class="fa-paper" style="width:60%;min-height:70px;">
|
||
<div class="fa-pl" style="width:88%;"></div>
|
||
<div class="fa-ps" style="width:82%;"></div>
|
||
<div class="fa-ps" style="width:90%;"></div>
|
||
<div class="fa-pl" style="width:74%;"></div>
|
||
<div class="fa-ps" style="width:68%;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /pb -->
|
||
</div><!-- /phone -->
|
||
</div><!-- /prev-col -->
|
||
</div><!-- /previews -->
|
||
|
||
<div class="agent">
|
||
<h4>Hinweis — D-3 Mobile</h4>
|
||
<pre>Mobile erfordert keine eigene Komponente. Das Tailwind-Grid `grid-cols-1 lg:grid-cols-4` erledigt das Stacking automatisch. Die Geschichten-Spalte erscheint zuletzt im DOM (nach Schlagwörter) — das ist die korrekte Lesereihenfolge auf Mobile.</pre>
|
||
</div>
|
||
</div><!-- /scr D-3 -->
|
||
|
||
|
||
<!-- ══ LLM IMPLEMENTATION GUIDE ══════════════════════════════════════════════ -->
|
||
<div class="llm">
|
||
<h2>LLM-Implementierungsguide</h2>
|
||
|
||
<h3>Backend — Endpunkt-Erweiterung</h3>
|
||
<p>Der <code>GET /api/documents/{id}</code> Response (oder ein separater Endpunkt) muss ein <code>geschichten</code>-Array enthalten:</p>
|
||
<ul>
|
||
<li>Felder pro Eintrag: <code>id</code>, <code>title</code>, <code>author: { displayName }</code>, <code>publishedAt</code></li>
|
||
<li>Nur Geschichten mit Status <code>PUBLISHED</code> werden zurückgegeben — Filterung auf Service-Ebene, nicht im Controller</li>
|
||
<li>Maximale Anzahl im Response: unbegrenzt; das Frontend blendet einen "Alle anzeigen"-Link ein, wenn mehr als 3 vorhanden sind</li>
|
||
</ul>
|
||
|
||
<h3>Frontend — Änderungen</h3>
|
||
<ol>
|
||
<li><code>+page.server.ts</code>: <code>geschichten</code>-Array aus dem API-Response extrahieren und als Prop an <code>DocumentMetadataDrawer</code> übergeben</li>
|
||
<li><code>DocumentMetadataDrawer</code>: neue Prop <code>geschichten: Geschichte[]</code> und <code>canWrite: boolean</code> annehmen</li>
|
||
<li>Grid-Klasse dynamisch: <code>lg:grid-cols-{geschichten.length > 0 ? 4 : 3}</code></li>
|
||
<li>4. Spalte konditional rendern: <code>{#if geschichten.length > 0}</code></li>
|
||
<li>"Alle anzeigen"-Link: nur wenn <code>geschichten.length > 3</code>, Link zu <code>/geschichten?documentId={id}</code></li>
|
||
<li>"+ Geschichte anhängen"-Link: nur wenn <code>canWrite</code>, Link zu <code>/geschichten/new?documentId={id}</code></li>
|
||
</ol>
|
||
|
||
<h3>Keine Änderungen erforderlich</h3>
|
||
<ul>
|
||
<li><strong>DocumentTopBar</strong>: kein neuer Button, kein neues Icon</li>
|
||
<li><strong>Andere Drawer-Spalten</strong>: Details, Personen, Schlagwörter bleiben unverändert</li>
|
||
<li><strong>Mobile Layout</strong>: das Grid-Stacking ist automatisch via Tailwind, keine separate Mobile-Logik</li>
|
||
</ul>
|
||
|
||
<h3>Implementierungsreferenz — Typedefinition</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Feld</th>
|
||
<th>Typ</th>
|
||
<th>Quelle</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>id</code></td><td><code>string (UUID)</code></td><td>Backend</td></tr>
|
||
<tr><td><code>title</code></td><td><code>string</code></td><td>Backend</td></tr>
|
||
<tr><td><code>author.displayName</code></td><td><code>string</code></td><td>Backend</td></tr>
|
||
<tr><td><code>publishedAt</code></td><td><code>string (ISO date)</code></td><td>Backend</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div><!-- /llm -->
|
||
|
||
</div><!-- /doc -->
|
||
</body>
|
||
</html>
|