Creates the alias table for historical name changes (marriage,
widowhood, etc.) and adds GIN trigram indexes on both the new
alias table and the existing persons table for substring search.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
Improves visibility of the clickability affordance on uncalibrated
displays and for senior users.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
Aligns the top/bottom padding of the Briefwechsel results view with
the document search page wrapper (both py-8).
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap button: stack right/left arrows vertically at h-3.5 for a
compact look. Timeline: replace → ← text with Long-Arrow icons on
each letter entry and the distribution bar summary.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove border-dashed and bg-canvas conditional styles so the
receiver input matches the sender input styling. The placeholder
"Alle Korrespondenten" already communicates the optional state.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only navigate (applyFilters) when a person is actually selected, not
when the sender input is cleared. Combined with showHero checking
data.filters.senderId, the user stays in the search bar view after
clearing — no jump back to the hero.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove custom placeholder props so DateInput falls back to its default
format hint (TT.MM.JJJJ / DD.MM.YYYY / DD.MM.AAAA) instead of
repeating the label text above.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace native <input type="date"> on the document search page with
the custom DateInput component (German dd.mm.yyyy format with auto-dot
insertion). Align both pages' date input styling: add rounded-md,
border, bg-surface, px-3, text-ink, placeholder color, and focus ring
to match all other inputs in the card.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Replace tiny ↑↓ text with Long-Arrow-Up/Down-MD.svg icons from the
brand icon set for better visibility and consistency.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chevrons indicate collapsible elements, not sort direction. Match
the document search SortDropdown pattern using ↑/↓ text arrows.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move sort button and filter toggle to the person row, matching the
document search page pattern (sort + filter + count inline). Date
range inputs are now a collapsible section behind the filter toggle,
using slide transition and the same grid layout as the document
search advanced filters. Fix date input padding (add px-3).
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap person bar + filter controls in a card matching the document
search page (rounded-sm border p-6 shadow-sm). Switch PersonTypeahead
to default mode with matching label/input overrides. Bump date inputs
and sort button to text-sm py-2.5. Filter row uses border-t separator
like the document search advanced section.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores early return when id is empty, preventing a wasteful
navigation to /briefwechsel with no senderId param.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds conv_hero_divider to de/en/es messages and uses it in the
CorrespondenzHero divider. Fixes i18n blocker from review.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add px-3 to person bar, filter controls, and hint bar so inputs
don't sit flush against the container edge.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move strips inside the max-w-7xl container so person bar, filter
controls, and hint bar are no longer full-bleed. Remove duplicate
side padding from strip components — the parent container handles it.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Align person bar, filter controls, and hint bar side padding to
px-4 sm:px-6 lg:px-8, matching the standard layout of all other
overview pages. Override person bar inputs from compact h-9 to h-12
for better touch targets in the results state.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hero state (no senderId): centred CorrespondenzHero with discovery
headline, cross-link, large typeahead, recent persons. No person bar
or filter controls shown. Results state (senderId set): full-width
strips then content area with max-w-7xl responsive padding matching
other overview pages. Removes focus delegation hack.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Update nav label, page heading, empty-state headline, and document
link text. German uses "Briefwechsel", English "Letters", Spanish
"Cartas". Empty-state headline now uses the discovery framing from the
design discussion.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update all internal links (AppNav, CoCorrespondentsList, goto) to the
new URL. No redirect needed — no production URLs exist yet.
Refs: #179
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add TODO comment explaining why SENDER/RECEIVER sort is in-memory
(JPA INNER JOIN drops null-sender docs) and note that pagination
will require a DB COUNT query in DocumentSearchResult.of().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A sender with lastName=null produced sort key "null Bob" which sorted
before names starting with lowercase letters (n < s, t, u, v...).
Now returns "" for null lastName, which the comparator places at end.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously any value other than ASC/DESC silently defaulted to
DESC with no feedback. Now returns 400 Bad Request.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SENDER and RECEIVER are handled by in-memory sort before resolveSort
is called, making those switch cases unreachable. Removed and added
a comment making the invariant explicit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>