Commit Graph

248 Commits

Author SHA1 Message Date
Marcel
3a316bc382 fix(ui): center dialog, add backdrop, hover states, and cursor-pointer on buttons
- Add m-auto and w-full to ensure the native <dialog> is centred
- Add backdrop:bg-black/50 for dimmed overlay when modal is open
- Add hover:bg-danger/80 and hover:bg-primary/80 on confirm button
- Add cursor-pointer to both cancel and confirm buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:33:33 +02:00
Marcel
0d1401ce4f refactor(admin): replace window.confirm with ConfirmService in admin user delete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:04:09 +02:00
Marcel
d4ead08c17 refactor(transcription): replace window.confirm with ConfirmService in TranscriptionBlock
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 13:47:37 +02:00
Marcel
1942c2a5cb feat(confirm): add ConfirmService and ConfirmDialog with deferred-Promise pattern
- confirm.svelte.ts: context-based async service returning Promise<boolean>
- ConfirmDialog.svelte: native <dialog> element, reads service from context
- Concurrent calls return false immediately (guard at top of confirm())
- SSR-safe: confirm() returns Promise.resolve(false) on server
- getConfirmService() throws descriptive error outside provider tree
- 5 Vitest tests: confirm/cancel/Escape/concurrent/outside-provider all green

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 13:20:37 +02:00
Marcel
a1b21d6989 refactor(ui): use CSS custom properties for PersonTypeBadge colors
Replace hardcoded Tailwind utility colors with project CSS variables
(--c-badge-institution-*, --c-badge-group-*, --c-badge-unknown-*).
Dark mode variants defined in both @media and manual toggle blocks.
Extract shared badge classes and use $derived config object.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 18:35:05 +02:00
Marcel
6ee1ef73c3 feat(ui): add PersonTypeBadge to person list and detail pages
Show colored badge for non-PERSON types per design spec:
- INSTITUTION: blue with building icon
- GROUP: purple with people icon
- UNKNOWN: amber with question mark icon
- PERSON: no badge (unmarked default)

Badge appears on person cards in list and on detail page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:09:16 +02:00
Marcel
1aabd9826c test(frontend): update mock data for displayName and nullable firstName
Add displayName and personType to all Person mock objects in
component and page tests. Update assertions from reversed
"lastName, firstName" format to forward-order displayName.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:47:15 +02:00
Marcel
f11d8a38ed feat(frontend): replace all name concatenation with displayName
- 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>
2026-04-08 12:22:30 +02:00
Marcel
0ce803c7f1 build(frontend): regenerate API types for Person changes
Person type now includes displayName (readonly, required), title,
personType (required enum), and firstName is optional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:06:38 +02:00
Marcel
9e13208ccd chore(api): regenerate TypeScript API types with alias endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:24:03 +02:00
Marcel
f396e079a5 feat(i18n): add alias type labels and section strings for de/en/es
Adds 16 new keys per language: alias type labels (BIRTH, WIDOWED,
DIVORCED, OTHER), section heading, empty state, add form labels,
delete confirmation, and ALIAS_NOT_FOUND error code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:21:21 +02:00
Marcel
10fdaf7d00 refactor(ui): use CSS variable for turquoise in flash animations
Replaces hardcoded rgba(0,199,177,...) with color-mix using
var(--color-turquoise) for dark mode compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:58:04 +02:00
Marcel
e01ef56c48 fix(i18n): use getLocale() for date formatting in panel header
Replaces hardcoded 'de-DE' with the active Paraglide locale so
dates render in the user's language.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:56:23 +02:00
Marcel
b01a9ef406 refactor(ui): use bg-turquoise/10 token for paragraph hover
Replaces hardcoded rgba value with the project's turquoise color
token for dark mode compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:54:57 +02:00
Marcel
e31b73303e fix(ui): bump paragraph hover opacity from 6% to 10%
Improves visibility of the clickability affordance on uncalibrated
displays and for senior users.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:53:17 +02:00
Marcel
9d9d19ceb5 fix(a11y): increase segmented toggle height on mobile to 36px
Uses h-9 (36px) on mobile, h-7 (28px) on desktop for better tap
targets on small screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:51:56 +02:00
Marcel
0a5c82cd0e fix(a11y): increase panel close button touch target to 44px
Changes h-8 w-8 (32px) to h-11 w-11 (44px) to meet project's
minimum touch target standard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:50:32 +02:00
Marcel
1b063d4e4b test(ui): add tests for 0 blocks and lastEditedAt on PanelHeader
Verifies blockCount=0 shows "0 Abschnitte" and that a provided
lastEditedAt value renders a formatted date containing the year.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:46:53 +02:00
Marcel
b312878b3f test(ui): add annotation-flash class tests for AnnotationLayer
Verifies flashAnnotationId applies and removes the annotation-flash
CSS class correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:45:23 +02:00
Marcel
90120ca8e8 test(ui): add flash-highlight class tests for TranscriptionReadView
Verifies highlightBlockId applies and removes the flash-highlight
CSS class correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:44:01 +02:00
Marcel
4d5b8b4ead feat(ui): add collapsible PDF strip and abbreviated labels on mobile
Some checks failed
CI / Unit & Component Tests (push) Failing after 2s
CI / Backend Unit Tests (push) Failing after 2s
CI / Unit & Component Tests (pull_request) Failing after 3s
CI / Backend Unit Tests (pull_request) Failing after 1s
PDF viewer collapses to 70px on mobile in read mode, expandable to
50vh. Toggle button with chevron. Paragraph tap auto-expands strip.
Mode toggle abbreviates to "Bearb." on small screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:30:36 +02:00
Marcel
10cecb01f5 feat(a11y): respect prefers-reduced-motion for scroll-sync
Uses scrollIntoView behavior 'instant' instead of 'smooth', skips
CSS animations (static highlight instead), and extends timeout to
2s for reduced-motion users.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:27:01 +02:00
Marcel
81b14e5026 feat(ui): add bidirectional scroll-sync with flash animations
Paragraph click flashes the PDF annotation outline (1.5s fade).
Annotation click highlights the paragraph with a background flash.
Both directions scroll the target into view.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:25:23 +02:00
Marcel
e089192d7a feat(ui): wire panelMode state with read/edit view switching
Adds TranscriptionPanelHeader and TranscriptionReadView to the
document detail page. Default mode is 'read' when blocks exist,
'edit' otherwise. Annotations dimmed in read mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:21:15 +02:00
Marcel
306eef2e95 feat(ui): add TranscriptionReadView for flowing prose display
Renders transcription blocks as readable text with [unleserlich]/[...]
markers styled as italic muted text. Supports click-to-sync and
flash highlight for scroll-sync feedback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:14:53 +02:00
Marcel
7d98081390 feat(ui): add TranscriptionPanelHeader with mode toggle and status
Segmented Lesen/Bearbeiten control, block count, last-edited date,
and close button. Lesen disabled when no blocks exist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:10:39 +02:00
Marcel
d070ae2612 feat(annotation): add dimmed prop to AnnotationLayer
Hides block number badges and disables hover/active visual feedback
when dimmed=true. Click handlers remain active for scroll-sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:07:23 +02:00
Marcel
3279342ea7 feat(util): add splitByMarkers for [unleserlich] and [...] text splitting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:00:23 +02:00
Marcel
f38c384268 feat(types): add updatedAt to TranscriptionBlockData
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:58:34 +02:00
Marcel
2e943b7f91 fix(ui): De Gruyter long arrows on both sort buttons, rotate swap icon 90°
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 1m21s
CI / Backend Unit Tests (pull_request) Failing after 2m30s
Replace ↑↓ text with Long-Arrow-Up/Down-MD.svg on the document search
SortDropdown and the Briefwechsel sort button. Rotate the swap button
SVG 90° so arrows point left/right matching the horizontal person
field layout.

Refs: #179

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 22:43:45 +02:00
Marcel
e9acd44acb feat(ui): add CorrespondenzHero with discovery headline and large typeahead
New centred hero component for the Briefwechsel page: headline
"Wessen Briefe möchten Sie lesen?", cross-link to document search,
h-14 PersonTypeahead, and recent persons chips. Adds `large` prop
to PersonTypeahead and `conv_hero_crosslink` message key.

Refs: #179

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:37:58 +02:00
Marcel
c82bd61ad4 feat(a11y): fix SortDropdown accessibility — label, aria-label i18n, chevron
- Add sr-only <label> for the sort <select> (WCAG 1.3.1)
- Replace hardcoded German aria-label with Paraglide sort_dir_asc/desc keys
- Add custom SVG chevron overlay to restore visual dropdown indicator
  (appearance-none had removed the native browser arrow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 16:49:06 +02:00
Marcel
79250fb705 fix(ui): fix SortDropdown height alignment — appearance-none + items-stretch
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 15:57:35 +02:00
Marcel
fc3496abb6 fix(ui): align SortDropdown styling with SearchFilterBar button style
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 15:55:55 +02:00
Marcel
023b6ddb49 fix(search): tagQ alone now triggers search mode; selecting chip clears tagQ
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
- isDashboard was ignoring tagQ so typing in tag filter showed dashboard
- addTag now calls onTextInput('') to clear tagQ when a chip is selected

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 15:46:54 +02:00
Marcel
eeb78c98ec feat(search): add onTextInput callback to TagInput for live tag filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 13:42:08 +02:00
Marcel
aeed6e0dac feat(search): add SortDropdown component with direction toggle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 13:39:26 +02:00
Marcel
2c0748d60e feat(utils): add debounce utility with full test coverage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 13:23:14 +02:00
Marcel
d1ad4d834c chore: regenerate API types with search envelope and new sort/tagQ params
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 13:21:41 +02:00
Marcel
8e48e67cb8 fix(a11y): increase contrast
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
2026-04-06 11:24:57 +02:00
Marcel
e89d8a4ca9 test: increase coverage 2026-04-06 11:20:57 +02:00
Marcel
f359c19e4c fix: bump comment text to text-base + reload annotations on block delete
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Comment text:
- Body and quote bumped from text-sm (14px) to text-base (16px)
  to visually match the font-sans author name at text-sm

Annotation reload on delete:
- Add annotationReloadKey prop through DocumentViewer → PdfViewer
- Increment key after block delete in +page.svelte
- PdfViewer reloads annotations when key changes
- Annotation rectangle disappears immediately, not just after refresh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:40:23 +02:00
Marcel
ef11cbee4e feat(transcription): clicking annotation focuses corresponding block
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Pass activeAnnotationId to TranscriptionEditView. An $effect watches
it and sets activeBlockId to the block matching the annotation,
activating its turquoise focus border.

2 new tests (RED/GREEN):
- activates block matching activeAnnotationId (turquoise border)
- no block activated when activeAnnotationId is null

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:36:06 +02:00
Marcel
676d3cb6a7 fix(pdf): prevent scroll-sync effect from hijacking page navigation
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
The scroll-sync $effect was re-triggering on every dependency change
(including currentPage), forcing the PDF back to the annotation's page
when the user clicked next/prev. Fix: track prevActiveAnnotationId
and only scroll when the active annotation actually changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:27:52 +02:00
Marcel
d389dc2023 feat(annotations): dim non-active annotations when a block is focused
When activeAnnotationId is set, the active annotation stays at full
opacity with a highlight box-shadow, while all other annotations fade
to 30% opacity (300ms ease transition). When no block is focused,
all annotations show at full opacity.

Prop chain: activeAnnotationId flows from PdfViewer → AnnotationLayer.

2 new tests (RED/GREEN):
- dims non-active annotations when activeAnnotationId is set
- shows all at full opacity when no activeAnnotationId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:26:02 +02:00
Marcel
b4212f5e86 feat(transcription): mobile stacked layout + cross-page scroll-sync
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Mobile layout (< 768px):
- Split view stacks vertically: PDF top (min 40vh), blocks below
- Blocks panel gets border-top instead of border-left
- PDF remains interactive for drawing in stacked mode

Scroll-sync (block → PDF):
- Clicking a block sets activeAnnotationId
- PdfViewer effect watches activeAnnotationId, navigates to the
  annotation's page if different from current, then scrolls the
  annotation element into view (double-rAF for async render timing)
- Works across pages: block on page 3 navigates PDF to page 3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:13:27 +02:00
Marcel
c22f2e41b1 fix(transcription): replace broken HTML5 drag with pointer-based drag
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
HTML5 drag-and-drop didn't work — the grip handle couldn't initiate
drag properly. Replace with pointer event-based drag:

- Grip handle pointerdown starts drag, captures pointer
- Pointermove tracks offset, shows floaty style (shadow, scale, ring)
- Turquoise drop indicator line appears between blocks at cursor position
- Pointerup finalizes: reorders array and calls PUT /reorder endpoint

Visual feedback:
- Dragged block: shadow-xl, ring-2 ring-turquoise/40, scale 1.02, opacity 0.9
- Drop indicator: turquoise h-1 rounded bar between blocks

6 new TranscriptionEditView tests:
- renders blocks in sort order
- shows next-block CTA
- shows empty state
- move-up disabled on first block
- move-down disabled on last block
- drag handle present on each block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:07:42 +02:00
Marcel
7d2d615e0c feat(transcription): add drag-and-drop + arrow button reordering
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
TranscriptionBlock:
- Desktop: grip handle (⠿) on left side, serves as drag handle
- Mobile (<768px): ▲/▼ arrow buttons (44px tap targets) replace grip
- isFirst/isLast disable boundary arrows
- onMoveUp/onMoveDown callbacks for arrow button clicks

TranscriptionEditView:
- HTML5 drag-and-drop on block wrappers (only initiates from grip handle)
- Dragged block shows 40% opacity
- On drop: reorder array and call PUT /reorder endpoint
- Arrow handlers: swap adjacent blocks and call reorder endpoint

5 new tests:
- drag handle element present
- move-up disabled when isFirst
- move-down disabled when isLast
- onMoveUp fires on click
- onMoveDown fires on click

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:00:52 +02:00
Marcel
4a88b3ba82 feat(transcription): add dashed next-block CTA below block list
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Shows a muted dashed-outline box after the last block:
"Markiere eine weitere Passage im Scan, um Block N anzulegen"
Guides new users on how to create additional blocks.

Matches the spec's empty block CTA design (S1, bottom of block list).
i18n key transcription_next_block_cta added for de/en/es.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 22:52:15 +02:00
Marcel
6dc81ef2e3 fix(ui): match delete icon size + add cursor-pointer to interactive elements
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- Comment delete icon: h-3 w-3 → h-4 w-4 (matches block delete icon)
- Add cursor-pointer to: comment delete button, Kommentieren button,
  block delete button, own-comment click-to-edit text
- Add title tooltip on comment delete button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 22:46:41 +02:00