From f11d8a38edf9424df28d87d170ae3ff282cd7a9b Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 8 Apr 2026 12:22:30 +0200 Subject: [PATCH] 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 --- .../familienarchiv/dto/PersonSummaryDTO.java | 10 ++++++++++ .../repository/PersonRepository.java | 8 +++++--- .../controller/PersonControllerTest.java | 2 ++ .../components/DashboardRecentDocuments.svelte | 2 +- .../components/DocumentMetadataDrawer.svelte | 8 ++++---- .../src/lib/components/DocumentTopBar.svelte | 2 +- .../lib/components/OverflowPillButton.svelte | 5 ++--- frontend/src/lib/components/PersonChip.svelte | 14 +++++--------- .../src/lib/components/PersonChipRow.svelte | 2 +- .../lib/components/PersonMultiSelect.svelte | 5 ++--- .../src/lib/components/PersonTypeahead.svelte | 4 ++-- frontend/src/lib/generated/api.ts | 7 +++++-- frontend/src/lib/utils/personFormat.ts | 9 ++++++++- frontend/src/routes/+page.server.ts | 4 ++-- frontend/src/routes/DocumentList.svelte | 8 ++++---- .../src/routes/briefwechsel/+page.server.ts | 8 ++++---- .../briefwechsel/ConversationTimeline.svelte | 13 +++++++++---- .../CorrespondentSuggestionsDropdown.svelte | 9 ++++++--- .../src/routes/conversations/+page.server.ts | 8 ++++---- .../conversations/ConversationTimeline.svelte | 9 +++++++-- .../routes/documents/[id]/edit/+page.svelte | 2 +- .../src/routes/documents/new/+page.server.ts | 18 +++++++++++++++--- frontend/src/routes/documents/new/+page.svelte | 5 ++--- frontend/src/routes/enrich/[id]/+page.svelte | 2 +- frontend/src/routes/persons/+page.svelte | 5 ++--- frontend/src/routes/persons/[id]/+page.svelte | 4 ++-- .../routes/persons/[id]/NameHistoryCard.svelte | 2 +- .../src/routes/persons/[id]/PersonCard.svelte | 8 ++++---- .../persons/[id]/PersonMergePanel.svelte | 4 ++-- .../src/routes/persons/[id]/edit/+page.svelte | 3 +-- .../persons/[id]/edit/PersonDangerZone.svelte | 2 +- .../persons/[id]/edit/PersonEditForm.svelte | 2 +- 32 files changed, 117 insertions(+), 77 deletions(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonSummaryDTO.java b/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonSummaryDTO.java index d31539ac..edb448f8 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonSummaryDTO.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/dto/PersonSummaryDTO.java @@ -9,11 +9,21 @@ import java.util.UUID; */ public interface PersonSummaryDTO { UUID getId(); + String getTitle(); String getFirstName(); String getLastName(); + String getPersonType(); String getAlias(); Integer getBirthYear(); Integer getDeathYear(); String getNotes(); long getDocumentCount(); + + default String getDisplayName() { + StringBuilder sb = new StringBuilder(); + if (getTitle() != null) sb.append(getTitle()).append(" "); + if (getFirstName() != null) sb.append(getFirstName()).append(" "); + sb.append(getLastName()); + return sb.toString().trim(); + } } diff --git a/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonRepository.java b/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonRepository.java index e41be561..782dde24 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonRepository.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/repository/PersonRepository.java @@ -35,7 +35,8 @@ public interface PersonRepository extends JpaRepository { // --- PersonSummaryDTO with document count --- @Query(value = """ - SELECT p.id, p.first_name AS firstName, p.last_name AS lastName, + SELECT p.id, p.title, p.first_name AS firstName, p.last_name AS lastName, + p.person_type AS personType, p.alias, p.birth_year AS birthYear, p.death_year AS deathYear, p.notes, (SELECT COUNT(*) FROM documents d WHERE d.sender_id = p.id) + (SELECT COUNT(*) FROM document_receivers dr WHERE dr.person_id = p.id) AS documentCount @@ -46,7 +47,8 @@ public interface PersonRepository extends JpaRepository { List findAllWithDocumentCount(); @Query(value = """ - SELECT p.id, p.first_name AS firstName, p.last_name AS lastName, + SELECT p.id, p.title, p.first_name AS firstName, p.last_name AS lastName, + p.person_type AS personType, p.alias, p.birth_year AS birthYear, p.death_year AS deathYear, p.notes, (SELECT COUNT(*) FROM documents d WHERE d.sender_id = p.id) + (SELECT COUNT(*) FROM document_receivers dr WHERE dr.person_id = p.id) AS documentCount @@ -56,7 +58,7 @@ public interface PersonRepository extends JpaRepository { OR LOWER(CONCAT(p.last_name,' ',COALESCE(p.first_name,''))) LIKE LOWER(CONCAT('%',:query,'%')) OR LOWER(p.alias) LIKE LOWER(CONCAT('%',:query,'%')) OR LOWER(a.last_name) LIKE LOWER(CONCAT('%',:query,'%')) - GROUP BY p.id, p.first_name, p.last_name, p.alias, p.birth_year, p.death_year, p.notes + GROUP BY p.id, p.title, p.first_name, p.last_name, p.person_type, p.alias, p.birth_year, p.death_year, p.notes ORDER BY p.last_name ASC, p.first_name ASC """, nativeQuery = true) diff --git a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java index bd41be36..02973927 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java @@ -73,8 +73,10 @@ class PersonControllerTest { private PersonSummaryDTO mockPersonSummary(String firstName, String lastName) { return new PersonSummaryDTO() { public java.util.UUID getId() { return UUID.randomUUID(); } + public String getTitle() { return null; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } + public String getPersonType() { return "PERSON"; } public String getAlias() { return null; } public Integer getBirthYear() { return null; } public Integer getDeathYear() { return null; } diff --git a/frontend/src/lib/components/DashboardRecentDocuments.svelte b/frontend/src/lib/components/DashboardRecentDocuments.svelte index bfe10e0f..25b93c8b 100644 --- a/frontend/src/lib/components/DashboardRecentDocuments.svelte +++ b/frontend/src/lib/components/DashboardRecentDocuments.svelte @@ -7,7 +7,7 @@ type Document = { id: string; title: string; updatedAt?: string; - sender?: { id: string; firstName: string; lastName: string }; + sender?: { id: string; firstName?: string | null; lastName: string; displayName: string }; }; type StatsDTO = components['schemas']['StatsDTO']; diff --git a/frontend/src/lib/components/DocumentMetadataDrawer.svelte b/frontend/src/lib/components/DocumentMetadataDrawer.svelte index 22e8b13a..d057a993 100644 --- a/frontend/src/lib/components/DocumentMetadataDrawer.svelte +++ b/frontend/src/lib/components/DocumentMetadataDrawer.svelte @@ -2,9 +2,9 @@ import { m } from '$lib/paraglide/messages.js'; import { formatDate } from '$lib/utils/date'; import { formatDocumentStatus } from '$lib/utils/documentStatusLabel'; -import { personAvatarColor } from '$lib/utils/personFormat'; +import { getInitials as calcInitials, personAvatarColor } from '$lib/utils/personFormat'; -type Person = { id: string; firstName: string; lastName: string }; +type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; type Tag = { id: string; name: string }; type Props = { @@ -33,11 +33,11 @@ let showAllReceivers = $state(false); const displayedReceivers = $derived(showAllReceivers ? receivers : visibleReceivers); function getInitials(person: Person): string { - return `${person.firstName.charAt(0)}${person.lastName.charAt(0)}`.toUpperCase(); + return calcInitials(person); } function getFullName(person: Person): string { - return `${person.firstName} ${person.lastName}`; + return person.displayName; } diff --git a/frontend/src/lib/components/DocumentTopBar.svelte b/frontend/src/lib/components/DocumentTopBar.svelte index 7d610706..c7604e7c 100644 --- a/frontend/src/lib/components/DocumentTopBar.svelte +++ b/frontend/src/lib/components/DocumentTopBar.svelte @@ -7,7 +7,7 @@ import PersonChipRow from './PersonChipRow.svelte'; import OverflowPillButton from './OverflowPillButton.svelte'; import DocumentMetadataDrawer from './DocumentMetadataDrawer.svelte'; -type Person = { id: string; firstName: string; lastName: string }; +type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; type Tag = { id: string; name: string }; type Doc = { diff --git a/frontend/src/lib/components/OverflowPillButton.svelte b/frontend/src/lib/components/OverflowPillButton.svelte index 5541edc7..3661b5c4 100644 --- a/frontend/src/lib/components/OverflowPillButton.svelte +++ b/frontend/src/lib/components/OverflowPillButton.svelte @@ -3,7 +3,7 @@ import { tick } from 'svelte'; import { m } from '$lib/paraglide/messages.js'; import { clickOutside } from '$lib/actions/clickOutside'; -type Person = { id: string; firstName: string; lastName: string }; +type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; type Props = { extraCount: number; @@ -67,8 +67,7 @@ function handleKeydown(e: KeyboardEvent) { href="/persons/{person.id}" class="block py-0.5 text-[18px] text-ink hover:text-primary focus-visible:ring-2 focus-visible:ring-primary" > - {person.firstName} - {person.lastName} + {person.displayName} {/each} diff --git a/frontend/src/lib/components/PersonChip.svelte b/frontend/src/lib/components/PersonChip.svelte index d17e7365..97796c00 100644 --- a/frontend/src/lib/components/PersonChip.svelte +++ b/frontend/src/lib/components/PersonChip.svelte @@ -1,7 +1,7 @@ {initials} - {displayName} + {name} diff --git a/frontend/src/lib/components/PersonChipRow.svelte b/frontend/src/lib/components/PersonChipRow.svelte index e50ed3e3..d292e22d 100644 --- a/frontend/src/lib/components/PersonChipRow.svelte +++ b/frontend/src/lib/components/PersonChipRow.svelte @@ -2,7 +2,7 @@ import PersonChip from './PersonChip.svelte'; import OverflowPillDisplay from './OverflowPillDisplay.svelte'; -type Person = { id: string; firstName: string; lastName: string }; +type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; type Props = { sender: Person | null | undefined; diff --git a/frontend/src/lib/components/PersonMultiSelect.svelte b/frontend/src/lib/components/PersonMultiSelect.svelte index 536ef97b..7847787f 100644 --- a/frontend/src/lib/components/PersonMultiSelect.svelte +++ b/frontend/src/lib/components/PersonMultiSelect.svelte @@ -73,8 +73,7 @@ function removePerson(id: string | undefined) { - {person.firstName} - {person.lastName} + {person.displayName}