Adds a comment on the @AfterEach deletion sequence explaining that journey_items
must be removed before their referenced documents and geschichten.
Addresses @sara review on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the JourneyItemDocumentDeleteListener component and the
DocumentDeletingEvent edge (documentSvc -> listener) to the supporting-domains
l3 diagram. This is the first event-driven edge in the backend; the diagram
must make that in-process coupling visible per the doc-currency rule.
Addresses @markus review (blocker) on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds flushAutomatically = true to @Modifying on deleteNoteLessByDocumentId so
the flush-before-bulk-delete contract is explicit instead of relying on
Hibernate AUTO flush-mode behaviour.
Addresses @markus review on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The call site passes a null payload and carries the id in the documentId
column (matching FILE_UPLOADED), so the javadoc claiming Payload:
{"documentId": "uuid"} misdescribed the audit schema. Audit javadocs are the
contract forensic queries are written against.
Addresses @felix, @nora and @elicit review on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the fully-qualified org.springframework.context.ApplicationEventPublisher
field declaration with an import + simple type name, consistent with every
other field in DocumentService.
Addresses @felix review on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
deleteDocument now threads the acting user through to the audit log so
DOCUMENT_DELETED records who deleted the document, matching every other
audited write path (storeDocument, updateDocument, applyBulkEditToDocument,
attachFile). Tightens the AC-7 assertion from any() to the concrete actor.
Also adds the missing ApplicationEventPublisher @Mock to DocumentServiceTest,
which the publishEvent call (added with the cascade fix) left null.
Addresses @nora (CWE-778, blocker), @felix and @sara review on PR #806.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Publishes DocumentDeletingEvent from DocumentService.deleteDocument before
deleteById; JourneyItemDocumentDeleteListener handles it synchronously so
note-less items are gone before ON DELETE SET NULL fires on note-carrying rows.
Plain @EventListener chosen over AFTER_COMMIT (fires too late) and @Async
(breaks rollback atomicity) — see ADR-038. Adds DOCUMENT_DELETED to AuditKind.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GeschichteSidebar gains optional geschichteId/items props and renders the
panel only when geschichteId is set. GeschichteEditor derives both from
its existing GeschichteView prop — null on /geschichten/new, so the panel
stays hidden there (create-then-edit, same as journeys). JourneyEditor's
sidebar call site is untouched, so journeys never show the panel.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sidebar-section-styled panel (p-4 card, mobile <details> accordion, no
inner scroll clamp) that lists a story's journey items in position order.
Add is pessimistic via POST /items; remove is optimistic with snapshot
rollback via DELETE /items/{id}; both through csrfFetch. Already-linked
documents are unselectable in the reused DocumentPickerDropdown (visible
label wired via inputId). Document-less items (ON DELETE SET NULL)
render as removable placeholder rows. 409 capacity/duplicate map to
story-worded messages, everything else through getErrorMessage(). Add/
remove are announced in a polite live region and focus moves to the
previous row's remove button (picker input when the list empties).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Panel header, hint, empty state, picker label + placeholder, deleted-
document placeholder, remove-button label, live-region announcements,
and the story-worded capacity/duplicate errors (the generic
JOURNEY_AT_CAPACITY / JOURNEY_DOCUMENT_ALREADY_ADDED messages say
"Lesereise" — the wrong frame inside a STORY panel).
Side effect: the JSON round-trip collapsed three pre-existing duplicate
keys per locale (identical values, last-wins either way) —
error_geschichte_title_too_long, error_geschichte_intro_too_long,
person_unknown.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The input always carries an id (generated doc-picker-input-{uid} default,
mirroring the listbox id scheme). When a caller passes inputId it wires a
visible <label for> — the aria-label fallback is dropped then so the
visible label wins. JourneyAddBar stays untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Delete the JOURNEY-only type guard in JourneyItemService.append() so the
existing item endpoints serve both Geschichte types. GeschichteType has
exactly two constants, so an allowlist replacement would be unreachable
dead code. Fix the not-found messages that claimed "Journey", and remove
the now-orphaned GESCHICHTE_TYPE_MISMATCH error code end to end
(ErrorCode, errors.ts union + mapping, i18n keys in de/en/es).
Tests: three STORY append unit tests written red against the guard, plus
end-to-end STORY coverage (append+retrieve, V72-style position-gap rows
incl. removal, dangling document-deleted item). The two STORY-rejection
tests die with the guard — no third enum value exists to feed it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>