Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.9 KiB
geschichte (frontend)
UI for family stories (Geschichte) and reading journeys (Lesereise): the rich-text editor, story/journey readers, type badge, and list rows.
What this domain owns
Components: GeschichteEditor.svelte, GeschichtenCard.svelte, GeschichteListRow.svelte, StoryReader.svelte, JourneyReader.svelte, JourneyItemCard.svelte, JourneyInterlude.svelte.
Utilities: utils.ts.
What this domain does NOT own
- Comment/discussion UI — shared via
shared/discussion/(same component used for document comments) - Person display —
person/PersonChip.svelteis used inside story content (cross-domain import) - Document display — document references in stories use components from
document/
Key components
| Component | Used in | Notes |
|---|---|---|
GeschichteEditor.svelte |
/geschichten/new, /geschichten/[id]/edit |
Rich-text editor with person/document @-mentions and inline embeds |
GeschichtenCard.svelte |
Person-detail sidebar | Sidebar card showing the 3 most recent stories linked to a person — NOT the list page |
GeschichteListRow.svelte |
/geschichten (list) |
Row component for the list; shows JOURNEY type badge (text-xs orange pill) |
StoryReader.svelte |
/geschichten/[id] (STORY branch) |
Renders sanitised rich-text body, persons section, documents section, and author actions |
JourneyReader.svelte |
/geschichten/[id] (JOURNEY branch) |
Renders intro paragraph, ordered items list, empty-state; delegates to ItemCard/Interlude |
JourneyItemCard.svelte |
JourneyReader.svelte |
Whole-card <a> for a document item; dated/undated aria-label, ✎ annotation glyph |
JourneyInterlude.svelte |
JourneyReader.svelte |
Typographic aside between letters; ❦ glyph, aria-label="Kuratorennotiz" |
utils.ts
formatAuthorName(author) — joins firstName + lastName, falls back to email (for list/summary shapes).
formatAuthorDisplayName(author) — returns displayName (for detail AuthorView shape).
formatPublishedAt(publishedAt, style) — wraps formatDate with null check; style is 'short' (list) or 'long' (detail).
Public list is PUBLISHED-only
GET /api/geschichten constrains status=PUBLISHED, so DRAFT journeys never appear in the list.
The REISE badge is only ever seen for published journeys.
Empty-state and draft-preview paths are reachable ONLY via the detail route (/geschichten/[id]), not the list.
Wire empty-state E2E tests through the detail route, not by expecting a draft journey in the list.
TypeSelector route component
TypeSelector.svelte lives in src/routes/geschichten/new/ (single-use route UI).
It is NOT in $lib/geschichte/ — route-specific, not reused elsewhere.
StoryCreate.svelte (also in new/) wraps GeschichteEditor so tree-shaking excludes TipTap from the JOURNEY placeholder path.
Audience note
The /geschichten route primarily serves readers (younger family members on mobile). Cards must have ≥ 44 px touch targets. Status must not rely on color alone. JourneyReader mobile layout is Critical; TypeSelector is Minor.
Cross-domain imports
person/PersonChip.svelte— inline person references in story contentdocument/DocumentThumbnail.svelte— inline document referencesshared/discussion/— comment thread below published stories
Backend counterpart
backend/src/main/java/org/raddatz/familienarchiv/geschichte/README.md