Mounts the page, renders the orchestrator, exposes the hidden skip-form,
and renders the three submit-action buttons (skip, save, save+review).
4 tests covering the orchestration entry path of enrich/[id].
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
enrich/done: heading, body, both CTA links.
documents/bulk-edit: empty-store onMount redirect to /documents,
loading spinner during in-flight fetch, error banner on backend error
code, error banner on fetch rejection. Mocks fetch via vi.spyOn so the
async branches are exercised without a real backend.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
admin/groups/new: heading, both permission group renderings (4 standard
+ 4 administrative checkboxes), form-error banner branch, cancel link
href, submit button form-attribute wiring, name input requiredness.
Mocks $app/navigation so beforeNavigate doesn't crash the test runner.
enrich/+: heading, empty placeholder vs populated count + start CTA,
start CTA href derived from documents[0].id, per-row title rendering,
bulk-select checkbox gated on canWrite.
16 tests across two files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Felix B1 (data-loss regression on /documents/[id]/edit) — DocumentEditLayout
still passes initialDateIso, initialLocation, initialDocumentLocation, but
my cycle-1 cleanup removed those props. Result: existing values rendered
empty and a save would have overwritten them with "". Restored the props
on WhoWhenSection and DescriptionSection; initialisation now lives in
onMount so it runs exactly once and never stomps a parent-driven update on
a later prop change.
Felix B2 — `DescriptionSection.svelte:36` still had the top-level
`currentTitle = untrack(() => initialTitle)` mutation that I cleaned up in
WhoWhenSection but missed here. Same onMount-once treatment.
Leonie B5 — `enrich/+page.svelte:105` referenced `<BulkSelectionBar>` but
the import was lost in a prettier pass; svelte-check errored out and the
bar never rendered, leaving an 8 rem dead zone from the pb-32 reservation.
One-line fix: add the import.
Leonie B6 — Esc handler in `BulkSelectionBar` was unscoped and stole
Escape from NotificationBell, ConfirmDialog, HelpPopover, etc. (e.g.
selecting docs → opening notification bell → Esc would close the bell
AND silently wipe the selection). Now bails when an open dialog,
expanded menu, or popover is detected.
Elicit C1 — `BulkDocumentEditLayout` topbar now branches on `mode`:
shows "Massenbearbeitung" + "{count} werden bearbeitet" in edit mode
instead of the upload-flavoured "Mehrere Dokumente hochladen" + "werden
erstellt" copy. New i18n keys `bulk_edit_topbar_title` and
`bulk_edit_count_pill` in DE/EN/ES.
Tests added:
- DocumentControllerTest.patchBulk_stripsCarriageReturnsAndNewlinesFromErrorMessages
(Sara C2 follow-up — pin sanitizeForLog as a regression test)
- BulkSelectionBar.spec — count=1 → "1 Dokument", count=2 → "2 Dokumente"
(Sara C6 follow-up — pin the new bulk_edit_n_selected_one/_other branch)
Refs #225, PR #331
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
B1 — i18n the archive-box / archive-folder labels and add helper text.
Karton/Mappe were hardcoded German and broke EN/ES locales (WCAG 3.1.2).
B2 — drop the hardcoded German aria-label on the onboarding callout.
role="note" + the visible localised text is self-describing; the redundant
label was overriding the translated content for AT users on EN/ES.
B3 — Escape clears the bulk selection while the bar is visible. Adds an
"Esc: Auswahl aufheben" hint visible at ≥ sm (WCAG 2.1.1).
B4 — /documents and /enrich reserve pb-32 when the bulk-selection bar is
visible so it doesn't occlude the last row or pagination (WCAG 1.4.10).
Folded in three Leonie quick-concerns:
- C5: badge text-[10px] → text-[11px], raw text-gray-600 →
design-token text-ink-2 (dark-mode safe)
- C7: aria-live="polite" on bulk-selection-count
- C11: "Alles aufheben" → "Auswahl aufheben" (DE/EN/ES) — disambiguates
from "discard the operation entirely"
Refs #225, PR #331
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BulkSelectionBar component: sticky bottom bar shown only when canWrite
and selection is non-empty. Buttons meet WCAG 44px touch targets and
iOS safe-area inset is honoured.
- Bar mounted on /documents and /enrich.
- Alle X editieren button on /documents replaces the selection with
every UUID matching the active filter (via /api/documents/ids) and
jumps to /documents/bulk-edit.
Refs #225
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each row in the document search list and the enrichment queue gets a
WCAG-compliant (44px touch target) checkbox bound to bulkSelectionStore.
Checkbox click does not trigger the row's stretched-link navigation —
it sits inside the z-10 content sibling, the link is in the z-0 sibling,
so click events do not bubble between them.
Refs #225
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BackButton now accepts a `class` prop (default 'mb-4') so callers can
override spacing; resolves hardcoded margin in flex-row topbar snippets
- documents/[id]/edit and enrich/[id] pass class="" to suppress the margin
- Replace weak className unit test with class-prop behaviour tests
- Add [data-hydrated] comment in E2E spec explaining what emits the attribute
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 7 in-scope back navigation links converted to use history.back().
Admin panel mobile chevron converted inline (icon-only, different
visual pattern). Cancel buttons left as static <a> links.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract DocumentEditLayout shared component for the PDF+form split-panel
UI, replacing the old scrolling layout on /documents/[id]/edit with the
same fixed-panel structure used by /enrich/[id]. Removes TranscriptionSection
and FileSectionEdit from the edit page; file upload/replace is now handled
by the shared layout. Delete SaveBar and FileSectionEdit as dead code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- error_file_upload_failed key used in enrich upload handler
- label_optional key added (de/en/es) and used in DescriptionSection divider
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- text-[9px]/text-[10px] in required-fields bar raised to text-xs (12px),
meeting the project minimum for the 60+ audience (WCAG 1.4.4)
- Upload animation now uses motion-safe: prefix so it stops for users
with prefers-reduced-motion set (WCAG 2.1 SC 2.3.3)
- Strengthened UploadZone tests: onCancel uses [role=status] button
selector instead of first-button heuristic; added positive file
selection test (valid PDF calls onFile), file-too-large test, and
MIME rejection now also asserts the error message is visible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Required-fields progress bar (Pflichtfelder) with role="progressbar" ARIA tracks
Titel, Datum, and Absender live via bound props from child components
- Left panel shows UploadZone for PLACEHOLDER documents (no filePath); after upload
invalidates 'app:document' to transition to PDF viewer without page reload
- AbortController powers the cancel button during upload
- "Datei ersetzen" ghost button lives in a thin toolbar above the PDF viewer
- dateIso and currentTitle are now bound from WhoWhenSection/DescriptionSection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- TagRepository: add findDescendantIdsByName() recursive CTE query
- TagService: add expandTagNamesToDescendantIdSets() for document search
Frontend:
- TagInput: accept Tag[] (id, name, color, parentId) instead of string[]
- Chips show color dot via var(--c-tag-{color}) when tag has color
- Suggestions grouped hierarchically: children indented under their parents
- Update DescriptionSection, edit/new pages, SearchFilterBar, +page.svelte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move blob URL lifecycle management into a reusable createFileLoader()
hook that owns revoke-before-create and revoke-on-destroy. Replace
identical inline logic in documents/[id] and enrich/[id] with the hook.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Calling loadFile a second time previously leaked the previous object URL.
Add URL.revokeObjectURL(fileUrl) before creating a new one and in
onDestroy so all URLs are freed. Revoke behavior will be covered by the
useFileLoader hook tests in the next commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add displayName default method to PersonSummaryDTO
- Update native SQL queries to include title, person_type columns
- Add getInitials() utility to personFormat.ts
- Update abbreviateName/abbreviateCompact for nullable firstName
- Replace firstName+lastName concatenation with displayName in all
person-displaying components and server load files
- Regenerate API types with displayName on Person and PersonSummaryDTO
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move text-decoration-thickness/underline-offset into the global a:hover
base rule so every link that shows an underline on hover gets identical
treatment: 2px thick, 4px offset, accent colour.
Remove the now-redundant per-component decoration-brand-mint / decoration-
accent / decoration-2 / underline-offset-{2,4} utilities from DocumentList,
enrich, persons, PersonDocumentList, and PanelMetadata.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add isDashboard flag (true when no search filters active)
- In dashboard mode: fetch mentions, incompleteDocs, recentDocs via
Promise.allSettled so widget failures don't crash the page
- In search mode: skip widget fetches for performance
- Replace incomplete-count fetch with list fetch (derive count from
list.length)
- Update enrich page to use IncompleteDocumentDTO (id + title only)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes dark mode rendering: list stayed white and text stayed dark because
bg-white, text-brand-navy, border-brand-sand were not theme-aware.
Replace with bg-surface, text-ink/ink-2/ink-3, border-line, bg-muted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In dark mode --c-primary switches from navy (#012851) to mint (#a1dcd8).
Buttons using bg-primary+text-white showed white text on mint at 1.4:1
contrast — invisible. bg-brand-navy buttons were also invisible (navy on
near-black canvas, 1.3:1).
Replaced in 28 components app-wide:
- bg-primary ... text-white → text-primary-fg
- hover:bg-primary hover:text-white → hover:text-primary-fg
- bg-brand-navy ... text-white + hover:bg-brand-navy/90 →
bg-primary ... text-primary-fg + hover:bg-primary/90
Light mode is unchanged: primary-fg = white in light mode.
Dark mode: primary-fg = navy (#012851) on mint bg = readable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
enrich/+page.svelte back link: text-gray-500 → text-ink-2 / hover:text-ink
enrich/done/+page.svelte body text: text-gray-500 → text-ink-2
enrich/done/+page.svelte list link: text-gray-400 (2.6:1, fails AA) → text-ink-2
Root fix for section label contrast (text-ink-3 uppercase pattern used
app-wide) is in PR #107 via the ink-3 token value change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Align enrich/[id] with the document detail page pattern: position fixed
with runtime header height measurement instead of a hardcoded calc value.
The root layout is reverted to its original simple form with no per-route
detection. Also replaces the missing check icon on the done page with
Check-Double-LG from the icon library.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-applies the scroll fix from 0d3c557 which was missing from this branch:
- measure header height at mount, use it as top offset instead of hardcoded 68px
- fix done page icon to Check-Double-LG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Home page shows "Needs metadata" card when incomplete documents exist.
/enrich list shows all incomplete documents; /enrich/[id] provides a
split PDF-preview + compact form view with Skip / Save / Save & reviewed
actions that auto-advance through the queue.
New document page gets Save vs Save & reviewed split. Edit page gets
"Mark for review" secondary button to push a document back into the queue.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>