feat(lesereisen): Lesereise reader UI — curated document journey viewer #786

Closed
opened 2026-06-08 12:47:03 +02:00 by marcel · 0 comments
Owner

Context

Issue #750 introduced the data model: GeschichteType.JOURNEY, JourneyItem entity, and V72 migration that replaces the old geschichten_documents junction table.

This issue implements the reader-facing UI for a Lesereise — a curated, ordered sequence of documents with editorial notes.


Goal

When a Geschichte has type: JOURNEY, the reader sees a step-by-step journey through documents rather than a prose story. Each stop (JourneyItem) shows the document thumbnail, title/date metadata, and an optional editor note. Clicking a stop opens the full document.


Acceptance Criteria

Reader view (/geschichten/[id])

  • If g.type === 'JOURNEY', render a journey layout instead of the prose layout
  • Each JourneyItem that has a documentId shows:
    • Document thumbnail (if available)
    • Document title (fetched from the document record — not "Open document" placeholder) and date
    • item.note as editorial caption (if present)
    • Link to /documents/[documentId]
  • Items without a documentId (note-only stops) show the note as a standalone editorial paragraph
  • Items are displayed in position order (backend already returns them ordered)
  • Empty journey (no items) shows a placeholder message

Note: The current placeholder text "Dokument öffnen" / "Open document" shipped in #750 as a known stopgap. This issue must replace it with the actual document title before the reader experience can be considered complete.

Editor view (/geschichten/[id]/edit)

  • If g.type === 'JOURNEY', show the JourneyItem editor panel alongside the prose editor (prose body becomes optional/hidden for JOURNEY type)
  • Add item: pick a document from DocumentMultiSelect or write a note-only item
  • Reorder items: drag-and-drop or up/down arrow buttons
  • Remove item: trash icon with confirmation
  • Save persists items via the backend API (new endpoint needed — see below)

Backend (new endpoint)

  • PUT /api/geschichten/{id}/items — accepts List<JourneyItemDTO> with {position, documentId?, note?}, replaces the item list atomically
  • Validates CHECK constraint (documentId or note must be present)
  • Returns updated Geschichte (with items initialized)
  • Requires WRITE_ALL permission

Type toggle (create/edit)

  • GeschichteEditor gains a type selector: STORY (default) / JOURNEY (Lesereise)
  • Switching to JOURNEY shows the items panel; switching back to STORY hides it (items preserved server-side)

Out of scope

  • Mobile-optimised journey layout (tracked separately)
  • Journey analytics / reading progress
  • Public/shareable journey links

Depends on

Closes / follows on from #750

## Context Issue #750 introduced the data model: `GeschichteType.JOURNEY`, `JourneyItem` entity, and V72 migration that replaces the old `geschichten_documents` junction table. This issue implements the reader-facing UI for a Lesereise — a curated, ordered sequence of documents with editorial notes. --- ## Goal When a `Geschichte` has `type: JOURNEY`, the reader sees a step-by-step journey through documents rather than a prose story. Each stop (JourneyItem) shows the document thumbnail, title/date metadata, and an optional editor note. Clicking a stop opens the full document. --- ## Acceptance Criteria ### Reader view (`/geschichten/[id]`) - [ ] If `g.type === 'JOURNEY'`, render a journey layout instead of the prose layout - [ ] Each `JourneyItem` that has a `documentId` shows: - Document thumbnail (if available) - **Document title** (fetched from the document record — not "Open document" placeholder) and date - `item.note` as editorial caption (if present) - Link to `/documents/[documentId]` - [ ] Items without a `documentId` (note-only stops) show the note as a standalone editorial paragraph - [ ] Items are displayed in `position` order (backend already returns them ordered) - [ ] Empty journey (no items) shows a placeholder message > **Note:** The current placeholder text "Dokument öffnen" / "Open document" shipped in #750 as a known stopgap. This issue **must** replace it with the actual document title before the reader experience can be considered complete. ### Editor view (`/geschichten/[id]/edit`) - [ ] If `g.type === 'JOURNEY'`, show the JourneyItem editor panel alongside the prose editor (prose body becomes optional/hidden for JOURNEY type) - [ ] Add item: pick a document from `DocumentMultiSelect` or write a note-only item - [ ] Reorder items: drag-and-drop or up/down arrow buttons - [ ] Remove item: trash icon with confirmation - [ ] Save persists `items` via the backend API (new endpoint needed — see below) ### Backend (new endpoint) - [ ] `PUT /api/geschichten/{id}/items` — accepts `List<JourneyItemDTO>` with `{position, documentId?, note?}`, replaces the item list atomically - [ ] Validates CHECK constraint (documentId or note must be present) - [ ] Returns updated `Geschichte` (with items initialized) - [ ] Requires `WRITE_ALL` permission ### Type toggle (create/edit) - [ ] `GeschichteEditor` gains a `type` selector: STORY (default) / JOURNEY (Lesereise) - [ ] Switching to JOURNEY shows the items panel; switching back to STORY hides it (items preserved server-side) --- ## Out of scope - Mobile-optimised journey layout (tracked separately) - Journey analytics / reading progress - Public/shareable journey links --- ## Depends on Closes / follows on from #750
marcel added this to the Lesereisen (Reading Journeys) milestone 2026-06-08 12:47:03 +02:00
marcel added the P1-highfeature labels 2026-06-08 12:47:44 +02:00
Sign in to join this conversation.
No Label P1-high feature
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#786