- JourneyItemCard: 'Brief öffnen' back to a >=44px touch target with the
height regression spec restored
- GeschichteListRow: REISE badges text-[10px] -> text-xs; drop the
hardcoded aria-label and the mobile badge's aria-hidden so phone screen
readers learn a row is a Lesereise; mobile avatar initials -> color dot
- detail page: badge text-xs, metabar Edit/Delete h-9 -> h-11, avatar
color keyed by name to match the list
- JourneyReader: dead border-subtle class -> border-line-2
- DocumentPickerDropdown: aria-controls only while the listbox exists
- JourneyAddBar: aria-expanded/aria-controls on both toggles + focus
hand-off into the revealed picker input / interlude textarea
- GeschichteSidebar: section h2s hidden below sm (summary already shows
the label there)
- JourneyCreate: bg-brand-navy -> semantic bg-primary/text-primary-fg;
title maxlength=255
- JourneyItemRow interlude: neutral frame border + left accent only,
token utilities instead of arbitrary var() syntax and inline style
Review round 3: Leonie (1-8 + round-1 leftovers), Elicit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The c3afd57e fix made the edit page's handleSubmit throw on !res.ok but
only JourneyEditor caught it. Now: GeschichteEditor.save() catches and
keeps its dirty state (no unhandled rejection -> no GlitchTip noise on a
failed STORY save); StoryCreate throws on failure so a failed STORY
create no longer silently disarms the unsaved guard; both handleSubmit
implementations catch network rejections, surface a message, and rethrow.
Contract documented on both editors' Props. GeschichteEditor also gets
the title maxlength=255. Spec: rejecting onSubmit is caught and the
editor stays usable.
Review round 3: Felix §2, Tobias S1, Nora (3), Markus (concern 1).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
try/finally without catch swallowed TypeError network failures silently.
Added catch block that sets errorMessage so the UI shows role=alert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses the remaining #792 review blockers and concerns in the journey
editor cluster:
- Interlude rows show 'Zwischentext' (dedicated key), not the add-button text
- All four mutation handlers route the backend ErrorCode through
getErrorMessage (a 409 duplicate no longer says 'bitte Seite neu laden')
and console.error their failures so client-side errors leave a trace
- Remove implements the spec'd pending state: row stays dimmed with an
aria-live 'wird entfernt…' until the DELETE resolves; failure keeps the row
- Move announcements fire after the reorder resolves (no false 'verschoben')
- Touch targets ≥44px (remove ×, note links, create submit); focus moves to
the new row after add, to a sensible neighbor after remove, back to × on
confirm-cancel; drag handle is pointer-only; title/intro get aria-labels;
publish-disabled reason is a visible hint, not a title tooltip
- Amber warning styles use new --color-warning-* tokens with dark remaps
- Blocked interlude-clear restores the draft instead of showing phantom text
- useBlockDragDrop moves to $lib/shared/hooks — geschichte no longer imports
another domain's internals
- Test hardening: reorder-failure rollback (non-ok + reject), publish/
unpublish/empty-warning surface, destructive confirm path, maxlength
assertions, JourneyCreate failure path, edit-page STORY/JOURNEY branch,
fixture factory, m.* assertions, all fixed sleeps replaced with polling
67 component tests green across 6 spec files; transcription consumer of the
moved hook re-verified (30 green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Import layout.css in test-setup so Tailwind utilities (text-xs,
min-h-[44px]) apply in vitest-browser — fixes computed-style assertions
for badge font-size and touch-target height
- radioGroupNav: write aria-checked directly on radio buttons on arrow-key
navigation, not only via the optional onChangeFn callback
- DashboardNeedsMetadata spec: tighten footer-link matcher from /50/ to
/Alle 50/ — avoids strict-mode collision with row link whose relative
time text also contains "50" (uploadedAt is exactly 50 days ago today)
- geschichten/[id] page spec: add missing await on userEvent.click before
confirmService.settle() in both delete tests
- TypeSelector spec: replace storyCard.focus() (not on vitest-browser
Locator) with userEvent.click(); force-dispatch aria-disabled Weiter
click via element.click() to bypass Playwright actionability check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Screen readers now announce the hint paragraph text on focus when no type
is selected, so users hear why the button is disabled without having to
click it first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sentry's wrapLoadWithSentry reads event.request.method — the test's makeEvent
now provides a real Request object. createApiClient mock was a plain function;
wrapping with vi.fn() enables vi.mocked(...).mockReturnValue in individual tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verifies radioGroupNav action moves selection forward and wraps backward
so keyboard users can navigate the STORY/JOURNEY cards without a mouse.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GeschichtenCard.svelte: use GeschichteSummary instead of Geschichte
(list endpoint returns summaries; no items/createdAt/updatedAt needed)
- GeschichtenCard.svelte.test.ts: factory returns GeschichteSummary with
lean author shape; drop Geschichte-only fields (createdAt, groups, etc.)
- geschichten/[id]/+page.svelte: add focus:outline-none focus-visible:ring-2
focus-visible:ring-focus-ring to journey item document links (WCAG 2.4.7)
- page.svelte.test.ts ([id]): replace stale documents[] factory field with
items[]; test now checks placeholder text + note caption
- page.svelte.test.ts (new): remove removed initialDocuments from baseData;
rename test to reflect that only initialPersons is passed through
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api.ts: add GeschichteType, JourneyItem, GeschichteSummary schemas;
remove documentId param from list endpoint; change list response to
GeschichteSummary[]; add type + items to Geschichte; remove documents field
- GeschichteEditor: remove DocumentMultiSelect + documentIds from payload
(journey items are managed via the future Lesereisen editor, not here)
- GET /geschichten page: remove documentId filter from server load + URL logic
- geschichten/new: remove documentId pre-population from server load
- geschichten/[id]: replace g.documents with g.items (document-backed JourneyItems)
- geschichten/new + [id]/edit: remove documentIds from submit payload type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces `csrfFetch` (= `makeCsrfFetch(fetch)`) in cookies.ts as a
drop-in fetch replacement that auto-injects X-XSRF-TOKEN on POST/PUT/PATCH/DELETE.
Previously 8 call sites sent mutating requests without the CSRF header —
annotation resize, comment POST/PATCH/DELETE, Geschichte CRUD, Stammbaum
relationship creation, bulk-edit PATCH, and file upload — all would fail
with CSRF_TOKEN_MISSING if the backend's cookie-based protection triggered.
All 14 client-side mutating fetches now use csrfFetch; withCsrf/makeCsrfFetch
remain in the API for injectable-fetch use cases (e.g. useTranscriptionBlocks).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /geschichten — published-stories index with filter pills + "+ Neue Geschichte"
for BLOG_WRITERs; supports ?personId and ?documentId pre-filtering
- /geschichten/[id] — reader detail with sanitised {@html} body, person and
document chip sections, BLOG_WRITER edit/delete with confirm dialog
- /geschichten/new — editor with optional ?personId and ?documentId pre-fill
(silent ignore on unknown IDs to avoid leaking entity existence)
- /geschichten/[id]/edit — editor populated from existing story; BLOG_WRITE
guard redirects readers to the detail page
All routes load via createApiClient(fetch) with !response.ok error handling
following the project pattern; PATCH/DELETE go through raw fetch which the
Vite dev proxy / Caddy production proxy authenticates via cookie.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>