Persons Section — Concept A: Enriched Directory

Complete developer-ready screen spec for the Persons section redesign. Covers the person list, detail view (2-column layout), edit form (dedicated route), new person form, and all edge cases. Every screen, state, and entity is specified. A developer should be able to implement the full feature from this spec alone.

Familienarchiv · Persons Spec · Concept A · v1.0 · 2026-03
0
Architecture
Routes, Layout Model & Breakpoints
Four routes. 2-column detail layout on desktop. Dedicated edit route with sticky save bar. Merge panel hidden in Danger Zone accordion.
Route map
RoutePurposeKey state
/personsPerson directory — 4-column grid, search, stats barsearch query param
/persons/newCreate new person — all 6 fieldsform validation
/persons/[id]Person detail — 2-col layout, activity areaperson id in path
/persons/[id]/editEdit form — sticky save bar, Danger Zone accordiondirty state, merge
Breakpoint behaviour
ViewportList layoutDetail layoutEdit layout
<640px Mobile1-col listStacked single colFull-page form
640–1023px Tablet2-col gridStacked single colFull-page form
≥1024px Desktop4-col grid2-col sidebar+mainFull-page form
2-column detail layout anatomy ≥1024px
PERSON CARD · 35%
OF
Otto Fischer
„Großvater Otto"
✦ 1882  †  1944
Notes
Patriarchal figure of the Fischer family…
Edit person
ACTIVITY AREA · 65%
Frequent Correspondents
HM
Hans Müller ×4
AS
Anna Schmidt ×2
Sent Documents
5
Brief an den König
12 Nov 1938
Uploaded
Grundbucheintrag Hof Fischer
3 Mar 1922
Archived
Show 3 more…
Received Documents
3
Scan ohne Titel
25 Nov 1938
Scan pending
📐 Key design decisions
  • Edit moved to /persons/[id]/edit — removes in-place toggle; PersonCard is view-only; edit state no longer lives in $state on the detail page.
  • Merge panel moved inside a collapsible Danger Zone accordion at the bottom of the edit page — reduces cognitive load on the detail view.
  • "Full Name" label removed from view mode — the <h1> / PC-name already shows the name; duplicate removed per audit finding.
  • Stats bar on list page: persons.length + totalDocs — requires documentCount field on PersonDTO (or computed client-side from separate query).
  • New person form gains birth year, death year, notes — brings parity with edit form.
1
List
Person Directory /persons
4-column card grid on desktop. Stats bar. Search. Each card shows initials circle, full name, alias, life dates, doc count chip. Hover: mint left-border + shadow.
1-A · Desktop full list 1440px default
/persons
Persons
4
Persons
·
14
Documents
🔍 Search persons…
+ New person
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
8 docs
HM
Hans Müller
✦ 1890 · † 1962
2 docs
AS
Anna Schmidt
„Oma Anna"
✦ 1905
8 docs
MW
Maria Weber
no dates
1 doc
Card 1 shown in hover state (mint left border + shadow). No alias shown for Hans Müller and Maria Weber — those fields are simply omitted when empty. "no dates" placeholder shown in muted italic when both fields absent.
1-B · Tablet 768px
/persons
Persons
4
Persons
·
14
Docs
+ New
🔍 Search persons…
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
8 docs
HM
Hans Müller
✦ 1890 · † 1962
2 docs
AS
Anna Schmidt
„Oma Anna"
✦ 1905
8 docs
MW
Maria Weber
no dates
1 doc
1-C · Mobile 375px
/persons
Persons
4 persons · 14 docs
+ New
🔍 Search…
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
8 docs
HM
Hans Müller
✦ 1890 · † 1962
2 docs
AS
Anna Schmidt
„Oma Anna"
✦ 1905
8 docs
MW
Maria Weber
1 doc
Mobile: cards switch to horizontal row layout (initials left, text right, chevron right). Touch-friendly min-height. No centered text.
1-D · Empty state desktop empty
/persons
Persons
0 persons · 0 documents
+ New person
👤
No persons yet
Add the first family member to begin building the archive's person directory.
Add the first family member
1-E · Search results desktop filtered
/persons?q=Fischer
Persons
🔍 Fischer
+ New person
1 result for "Fischer" Clear ×
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
8 docs
Search filters by first name, last name, or alias. Result count shown below header. "Clear ×" resets query param. Search box has focus ring when active.
2
Detail
Person Detail /persons/[id]
2-column layout on desktop. Left sidebar: Person Card (35%). Right: Activity Area — Frequent Correspondents, Sent Documents, Received Documents (65%). Stacked on tablet/mobile.
2-A · Desktop detail — Otto Fischer 1440px view
/persons/a1b2c3d4
OF
Otto Fischer
„Großvater Otto"
✦ 1882  ·  † 1944

Notes
Patriarchal figure of the Fischer family. Farmer from Baden-Württemberg. Married to Helene Fischer (née Braun).
✏ Edit person
Frequent Correspondents
(click to view conversation)
HM
Hans Müller ×4 💬
AS
Anna Schmidt ×2 💬
Sent Documents
5
Date ▾
Brief an den König
Berlin · 12 Nov 1938
Uploaded
Grundbucheintrag Hof Fischer
Freiburg · 3 Mar 1922
Archived
Pachtvertrag Südfeld
Karlsruhe · 14 Jul 1910
Archived
Show 2 more…
Received Documents
3
Date ▾
Scan ohne Titel
Hamburg · 25 Nov 1938
Scan pending
Scan ohne Titel
Hamburg · 25 Nov 1938
Scan pending
Scan ohne Titel
Hamburg · 25 Nov 1938
Scan pending
Left sidebar has 2px navy top accent bar. Avatar 80px. No "Full Name" label — name shown directly as PC-name h1. Notes section only rendered when notes field is non-empty. Edit button navigates to /persons/[id]/edit.
2-B · Tablet stacked 768px
/persons/a1b2c3d4
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
✏ Edit
Frequent Correspondents
HM
Hans Müller ×4
AS
Anna Schmidt ×2
Sent Documents
5
Brief an den König
Uploaded
Grundbucheintrag Hof Fischer
Archived
Show 3 more…
Received Documents
3
Scan ohne Titel
Scan pending
Scan ohne Titel
Scan pending
2-C · Mobile 375px
/persons/a1b2c3d4
OF
Otto Fischer
„Großvater Otto"
✦ 1882 · † 1944
✏ Edit person
Correspondents
HM
Hans M. ×4
AS
Anna S. ×2
Sent
5
Brief an den König
Grundbucheintrag…
Show 3 more…
Received
3
Scan ohne Titel ×3
2-D · Minimal data person — Hans Müller desktop no alias · no notes · no death year
/persons/b2c3d4e5
HM
Hans Müller
✦ 1890
No notes added
✏ Edit person
Sent Documents
1
Brief an den König
Berlin · 12 Nov 1938
Uploaded
Received Documents
1
Scan ohne Titel
Hamburg · 25 Nov 1938
Scan pending
Optional sections (alias, notes, Frequent Correspondents) collapse gracefully when empty. Death year omitted — only birth year shown. "No notes added" placeholder in muted italic; entire Notes card section still renders but with placeholder text. Correspondents section not shown when no co-correspondents exist.
3
Edit
Edit Person /persons/[id]/edit
Dedicated route. Full-bleed sticky save bar at bottom. 2-column field grid. Danger Zone accordion at the very bottom. Merge panel lives here, not on detail view.
3-A · Desktop edit form 1440px edit
/persons/a1b2c3d4/edit
Edit person: Otto Fischer
Personal Details
First name *
Otto
Last name *
Fischer
Nickname / Alias (optional)
Großvater Otto
Birth year (optional)
1882
Death year (optional)
1944
Notes (optional)
Patriarchal figure of the Fischer family. Farmer from Baden-Württemberg. Married to Helene Fischer (née Braun).
⚠ Danger Zone Destructive actions
Merge with another person…
Discard changes
Save changes
Back link says "← Back to Otto Fischer" — links to /persons/[id]. Page heading uses person full name. Danger Zone accordion collapsed by default; only the "Merge with another person…" affordance is visible. Sticky save bar uses full-bleed pattern from CLAUDE.md.
3-B · Validation error desktop error
/persons/a1b2c3d4/edit
Edit person: Otto Fischer
Personal Details
First name *
First name is required
Last name *
Fischer
Nickname / Alias
Großvater Otto
Birth year
1882
Death year
1944
Discard changes
Save changes
Errors shown on submit — not live. Affected field gets red border + error message below. Save button remains enabled (not disabled). Multiple fields can show errors simultaneously.
3-C · Danger Zone expanded — Merge panel desktop
/persons/a1b2c3d4/edit
Edit person: Otto Fischer
Personal Details
First name *
Otto
Last name *
Fischer
… more fields above …
⚠ Danger Zone Destructive actions
Merge with another person
Select a target person. All documents and links from Otto Fischer will be transferred to the target, and this person will be permanently deleted. This action cannot be undone.
Search for person to merge into…
Search persons…
Merge Otto Fischer →
Cancel
⚠ This action cannot be undone. All documents linked to Otto Fischer will be reassigned.
Discard changes
Save changes
Danger Zone expanded by clicking the header row (Svelte $state boolean toggle). Merge typeahead uses PersonTypeahead component. Red "Merge" button disabled until a target person is selected. Warning banner always visible inside expanded panel.
3-D · Mobile edit 375px edit
/persons/a1b2c3d4/edit
Edit: Otto Fischer
Personal Details
First name *
Otto
Last name *
Fischer
Nickname / Alias
Großvater Otto
Birth year
1882
Death year
1944
Notes
Patriarchal figure of the Fischer family…
⚠ Danger Zone
Discard
Save changes
Mobile: 2-column field grid collapses to 1-column. All fields stack vertically. Sticky save bar remains full-bleed. Danger Zone always at very bottom, collapsed by default.
4
New
New Person /persons/new
Centered max-w-2xl form. All 6 fields (fixes the 3-field inconsistency). Card-style save bar (not sticky). "Create person" green primary button.
4-A · Desktop new person 1440px new
/persons/new
New person
Person details
First name *
Last name *
Nickname / Alias (optional)
Birth year (optional)
Death year (optional)
Notes (optional)
Cancel
Create person
Save bar uses card-style (not sticky full-bleed) — form is short enough to always be visible. "Cancel" navigates back to /persons. "Create person" green primary. All empty fields shown — no pre-fill.
4-B · Mobile new person 375px new
/persons/new
New person
Person details
First name *
Last name *
Nickname / Alias
Birth year
Death year
Notes
Cancel
Create person
Mobile: all fields in single column. Save bar card-style (not sticky). Same 6 fields as desktop.
5
Edge Cases
Edge Cases & Error States
Person with no documents. Unsaved changes guard. Save API error.
5-A · Person with no documents — Maria Weber desktop no documents
/persons/d4e5f6g7
MW
Maria Weber
No alias · No dates
✏ Edit person
Sent Documents
0
📄
No sent documents yet
Received Documents
0
📄
No received documents yet
When both sent and received document counts are 0, empty-state dashed boxes are shown. Correspondent section is entirely absent (not rendered). Count badge shows 0 in grey, not navy.
5-B · Unsaved changes guard edit page dirty form
/persons/a1b2c3d4/edit
⚠ You have unsaved changes. Save or discard before leaving.
Save
Discard
Edit person: Otto Fischer
Personal Details
First name *
Ott 
Last name *
Fischer
Form is dirty — first name partially edited
Discard changes
Save changes
Banner triggers when user clicks the back link or uses browser navigation while form isDirty (track with Svelte $derived). [Save] in banner = same action as save bar. [Discard] resets form and navigates away. SvelteKit beforeNavigate() hook handles browser back/forward.
5-C · Save error — API failure edit page error
/persons/a1b2c3d4/edit
✕ Save failed — First name is required.
Edit person: Otto Fischer
Personal Details
First name *
First name is required
Last name *
Fischer
Birth year
1882
Death year
1944
Discard changes
Save changes
API error shown in red banner below the back link. Specific field also highlighted with inline error. Error message uses backend ErrorCode translation via getErrorMessage(). Page stays on edit route — user can correct and retry.
6
Implementation
Implementation Notes
Numbered rules covering every required code change. Follow these in order to implement the full Concept A redesign.
Implementation rules — Concept A: Enriched Directory
Rule 1 — Route change
Add /persons/[id]/edit as a new SvelteKit route (+page.svelte + +page.server.ts). Move the update form action from /persons/[id]/+page.server.ts to the edit route's server file. PersonCard.svelte edit-mode $state can be removed — the card is now view-only. The [Edit person] button navigates to /persons/{id}/edit.
Rule 2 — New person form fields
Add birthYear, deathYear, and notes fields to /persons/new/+page.svelte and the corresponding create server action. These fields are already on the PersonDTO and the backend PersonService; only the frontend form was missing them. Use parseInt(formData.get('birthYear')) — handle empty string as undefined.
Rule 3 — Card enrichment on list page
The list +page.server.ts already returns persons[]. No backend change needed — alias, birthYear, deathYear are already on the PersonDTO. Update the card template in +page.svelte to render these fields. Render alias only {#if person.alias}. Life dates: ✦ {birthYear} and † {deathYear} joined with · ; omit when null.
Rule 4 — Stats bar
Compute persons.length (person count) and totalDocs = persons.reduce((acc, p) => acc + (p.documentCount ?? 0), 0) (total documents). This requires backend to return documentCount: number on PersonDTO — add this field to the Java Person entity response (computed via @Formula or a JPQL query in PersonService.findAll()). Mark it @Schema(requiredMode = REQUIRED) and run npm run generate:api.
Rule 5 — 2-column detail layout
Replace the current vertical stack in /persons/[id]/+page.svelte with a CSS grid: class="grid md:grid-cols-[35fr_65fr] gap-5 items-start". Left child = PersonCard. Right child = Activity wrapper containing the three ACT sections. On mobile/tablet (<md:) the grid collapses to single column (PersonCard above, Activity below).
Rule 6 — Merge panel relocation
PersonMergePanel.svelte must be moved from /persons/[id]/+page.svelte to /persons/[id]/edit/+page.svelte. Wrap it in a collapsible accordion: let dangerOpen = $state(false); the accordion header toggles dangerOpen; the body uses a CSS transition on max-height or a simple {#if dangerOpen} block.
Rule 7 — "Full Name" duplication fix
Remove the <div class="FL">Full Name</div> label and associated value row from PersonCard.svelte view mode. The <h1> (PC-name class) already displays the full name. This was flagged in the audit as a direct duplication.
Rule 8 — Sticky save bar
The edit route uses the full-bleed sticky pattern from CLAUDE.md: class="sticky bottom-0 z-10 -mx-4 px-6 py-4 bg-white border-t border-brand-sand shadow-[0_-2px_8px_rgba(0,0,0,0.06)] flex items-center justify-between". The new person form uses the card-style save bar (not sticky): class="mt-4 flex items-center justify-between rounded-sm border border-brand-sand bg-white px-6 py-4 shadow-sm".
Rule 9 — Co-correspondents tooltip
Add title="Click to view conversation" to each correspondent chip element. Optionally add a small 💬 chat icon inside the chip (Unicode or a 12px SVG). The chip's href should navigate to /conversations?person1={currentId}&person2={corrId}. This clarifies the navigation target which was previously ambiguous.
Rule 10 — Document status labels
Map raw status enums to human-readable i18n labels using the existing Paraglide system: PLACEHOLDER → m.status_scan_pending(), UPLOADED → m.status_uploaded(), TRANSCRIBED → m.status_transcribed(), REVIEWED → m.status_reviewed(), ARCHIVED → m.status_archived(). Add keys to messages/de.json, en.json, es.json. Add DOCUMENT_STATUS_INVALID to ErrorCode.java and errors.ts for unknown values.
Rule 11 — Unsaved changes guard
In /persons/[id]/edit/+page.svelte, track a isDirty = $derived(...) boolean by comparing current field values to the loaded person. Use SvelteKit's beforeNavigate(({ cancel }) => { if (isDirty) cancel() }) to block browser navigation. Show the inline warning banner (yellow, above the form) when isDirty and user attempts to leave. The banner [Save] button submits the form; [Discard] resets fields and calls invalidate().
Rule 12 — Mobile card layout
On mobile (<640px) the person grid cards switch from centered column layout to horizontal row layout: initials circle on the left (36×36px), text block in the middle (name + alias + dates + doc count), chevron on the right. Use sm:flex-col sm:items-center vs base flex-row items-center in Tailwind. This improves scannability on narrow screens.
📋 Document status label map (Rule 10)
  • PLACEHOLDER → "Scan pending" — Scan pending (amber chip)
  • UPLOADED → "Uploaded" — Uploaded (navy chip)
  • TRANSCRIBED → "Transcribed" — Transcribed (blue chip)
  • REVIEWED → "Reviewed" — Reviewed (mint chip)
  • ARCHIVED → "Archived" — Archived (green chip)
Files to create / modify
FileActionRelated rule(s)
frontend/src/routes/persons/+page.svelteAdd alias, dates, doc count to card template; add stats bar; switch to 4-col grid; mobile row layout3, 4, 12
frontend/src/routes/persons/new/+page.svelteAdd birth year, death year, notes fields2
frontend/src/routes/persons/new/+page.server.tsHandle new fields in create action2
frontend/src/routes/persons/[id]/+page.svelte2-col grid layout; view-only PersonCard; remove edit toggle state; remove merge panel1, 5, 6, 7
frontend/src/routes/persons/[id]/+page.server.tsRemove update action (moved to edit route)1
frontend/src/routes/persons/[id]/edit/+page.svelteCREATE: edit form, sticky save bar, danger zone accordion, unsaved guard1, 6, 8, 11
frontend/src/routes/persons/[id]/edit/+page.server.tsCREATE: load person, update action1
frontend/src/lib/components/PersonCard.svelteRemove edit mode toggle; remove "Full Name" label row; make view-only1, 7
frontend/src/lib/components/PersonMergePanel.svelteMove to edit route; add tooltip to chips6, 9
backend/src/main/java/…/model/Person.javaAdd documentCount computed field or handle in service4
frontend/src/lib/errors.tsAdd DOCUMENT_STATUS_INVALID error code10
frontend/src/lib/messages/de.json + en.json + es.jsonAdd status label keys10