Commit Graph

15 Commits

Author SHA1 Message Date
Marcel
8995b6e922 fix(journey-editor): note UX, error codes, list semantics, a11y labels
- handleNotePatch routes failures through failureMessage() so a backend
  JOURNEY_NOTE_TOO_LONG renders its specific message in the row
- handleNoteBlur: '' vs undefined no-op guard (no spurious PATCH
  {note:null}), empty blur collapses the textarea, clearing an existing
  note collapses after the PATCH lands (spec LE-3)
- 'Notiz hinzufügen' toggle gets aria-expanded and moves focus into the
  revealed textarea
- journey_remove_item_aria interpolates the item title (de/en/es); dead
  journey_drag_aria_label key deleted from all locales
- editor item list is an <ol> (screen readers announce the ordering)
- editor title input gets maxlength=255 + border-danger error cue; intro
  textarea gets maxlength=4000
- Briefmeta line (date · von X an Y) under document titles in the editor
  row via the shared formatDocumentMetaLine (spec LE-2)
- new specs: successful save clears the unsaved warning; item add does
  not arm the guard; server-confirmed order after successful reorder;
  blur-without-typing no-op; focus hand-off into the note textarea

Review round 3: Sara (1-3), Felix, Elicit (LE-2/LE-3), Leonie.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:28:38 +02:00
Marcel
166003b33a fix(journey-editor): selectedPersons change calls markDirty via $effect
Skip-first-run $effect tracks selectedPersons array length; any add/remove
after mount marks the editor dirty so the unsaved-warning fires on nav.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 19:39:44 +02:00
Marcel
7ad2132a92 test(journey-editor): fix misleading test name for item-add + publish
Removed the incorrect claim "isDirty stays false" from the test name.
The banner tests added in the previous commit cover the isDirty signal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 19:36:48 +02:00
Marcel
c3afd57e19 fix(journey-editor): unsaved-warning banner + save throws on failure
JourneyEditor now renders UnsavedWarningBanner when showUnsavedWarning
is true. save() wraps onSubmit in try/catch so clearOnSuccess only fires
on success. edit/+page.svelte handleSubmit throws instead of returning
on non-ok responses so JourneyEditor sees the failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 19:36:18 +02:00
Marcel
f10b0cb73e fix(journey): editor review round — labels, errors, pending state, a11y, tests
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>
2026-06-10 07:55:12 +02:00
Marcel
c4606cef8b fix(journey): person chips rendered blank — share one PersonView→PersonOption projection
JourneyEditor fed GeschichteView.PersonView (no displayName) straight into
the displayName-rendering PersonMultiSelect, so every chip on a journey was
empty. The name mapping both editors need now lives once in
$lib/person/personOption.ts (with the [Unbekannt] fallback matching
GeschichteService.toView), and PersonMultiSelect/GeschichteSidebar import
the narrow PersonOption contract from there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 07:32:05 +02:00
Marcel
bd5e6e6fbe fix(test): narrow getByText selector to dropdown-only item in aria-disabled test
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m26s
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 3m49s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m6s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 19:38:22 +02:00
Marcel
abe5d62102 fix(test): update JourneyEditor spec to use getByText after listbox role downgrade
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m41s
CI / OCR Service Tests (pull_request) Successful in 24s
CI / Backend Unit Tests (pull_request) Successful in 3m56s
CI / fail2ban Regex (pull_request) Successful in 46s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m8s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 19:21:58 +02:00
Marcel
e5f276a164 test(journey-editor): add red test — PATCH body must send {"note":null} not {}
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 17:50:58 +02:00
Marcel
dfc065b727 test(journey-editor): verify liveAnnounce region clears after 500ms
A persistent non-empty aria-live region can cause stale re-announcements
on adjacent DOM mutations. This test confirms the region is empty after
the 500ms clear timeout fires following a move operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:47:04 +02:00
Marcel
65e196292e refactor(journey-editor): fix fragile ordering assertion with compareDocumentPosition
The previous check used document.body.textContent which includes ARIA labels
and hidden elements — a match in those could misfire the indexOf comparison.
compareDocumentPosition(DOCUMENT_POSITION_FOLLOWING) checks DOM tree position
directly and is not affected by non-visible text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:46:06 +02:00
Marcel
06b8c99ce7 test(journey-editor): add reorder tests, fix vacuous isDirty test, fix remove btn name
- Add move-up and move-down tests that verify PUT /items/reorder is called
  with the swapped ID order; 50ms delay accounts for two await levels before
  csrfFetch is called (click → handleMoveUp → handleReorder → csrfFetch)
- Replace vacuous 'isDirty stays false' test (was asserting a dialog that
  never renders) with a meaningful publish-button-enabled assertion after
  adding an item
- Update remove button query from 'Wirklich entfernen?' to 'Eintrag entfernen'
  to match the new journey_remove_item_aria aria-label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 14:46:08 +02:00
Marcel
573e5c43d7 fix(journey-editor): render empty-state message when items list is empty
Previously the item list area was blank when no items had been added.
The empty-state paragraph uses the existing journey_empty_state i18n key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 14:44:07 +02:00
Marcel
ddcf61cc5e fix(tests): resolve 9 CI test failures in journey-editor specs
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m29s
CI / OCR Service Tests (pull_request) Successful in 24s
CI / Backend Unit Tests (pull_request) Successful in 3m52s
CI / fail2ban Regex (pull_request) Successful in 48s
CI / Semgrep Security Scan (pull_request) Successful in 24s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s
- useBlockDragDrop: add runtime expect() alongside expectTypeOf so
  browser-mode runner counts at least one assertion
- JourneyAddBar: use exact:true on 'Hinzufügen' button — partial match
  was hitting '+ Brief hinzufügen' and '+ Zwischentext hinzufügen' too
- JourneyEditor: fix 4 issues — drop wrong not.toBeInTheDocument()
  (placeholder creates accessible name); pass title:'' in publish-disabled
  test (default was non-empty); use getByPlaceholder for interlude
  textarea to avoid 4-element strict-mode violation; exact:true for
  'Hinzufügen' button
- DocumentPickerDropdown: use .click({force:true}) on aria-disabled
  option — userEvent refuses non-enabled elements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 13:20:35 +02:00
Marcel
a17eec537f feat(journey-editor): build JourneyEditor orchestrator
Main editing surface for JOURNEY-type Geschichten. Manages sorted item list
with optimistic add/remove/reorder (rollback on failure), drag-and-drop reorder
via createBlockDragDrop, intro textarea, and sidebar via GeschichteSidebar.
Publish requires at least one item + non-empty title.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:55:39 +02:00