From 48286b9f7730e0abffb491329e192da893b3539e Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 30 Mar 2026 12:50:40 +0200 Subject: [PATCH] feat(frontend): new strip components, suggestions dropdown, empty state CorrespondenzPersonBar (Row 1), CorrespondenzFilterControls (Row 2 with live count + sort), CorrespondentSuggestionsDropdown (fetch-on-focus, keyboard nav), SinglePersonHintBar, CorrespondenzEmptyState (recent persons from localStorage). New i18n shim in messages-extra.ts until root-owned paraglide files can be regenerated. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/messages-extra.ts | 37 +++++ .../CorrespondentSuggestionsDropdown.svelte | 129 +++++++++++++++++ .../CorrespondenzEmptyState.svelte | 121 ++++++++++++++++ .../CorrespondenzFilterControls.svelte | 135 ++++++++++++++++++ .../CorrespondenzPersonBar.svelte | 87 +++++++++++ .../korrespondenz/SinglePersonHintBar.svelte | 38 +++++ 6 files changed, 547 insertions(+) create mode 100644 frontend/src/lib/messages-extra.ts create mode 100644 frontend/src/routes/korrespondenz/CorrespondentSuggestionsDropdown.svelte create mode 100644 frontend/src/routes/korrespondenz/CorrespondenzEmptyState.svelte create mode 100644 frontend/src/routes/korrespondenz/CorrespondenzFilterControls.svelte create mode 100644 frontend/src/routes/korrespondenz/CorrespondenzPersonBar.svelte create mode 100644 frontend/src/routes/korrespondenz/SinglePersonHintBar.svelte diff --git a/frontend/src/lib/messages-extra.ts b/frontend/src/lib/messages-extra.ts new file mode 100644 index 00000000..799999b6 --- /dev/null +++ b/frontend/src/lib/messages-extra.ts @@ -0,0 +1,37 @@ +/** + * Extra message functions for i18n keys added after the last paraglide compile. + * + * TODO: Remove this file once the root-owned paraglide files in src/lib/paraglide/ + * are regenerated (run `npm run dev` or the paraglide compile step as the owning user). + * At that point, these functions will be generated into _index.js and the components + * that import from here should switch back to importing from $lib/paraglide/messages.js. + * + * Note: these fall back to German only — locale switching is handled by the generated + * paraglide files, not this shim. + */ + +// Svelte auto-escapes interpolated values — do not use {@html} with these strings. + +export const conv_hint_single_person = (inputs: { name: string }) => + `Alle Briefe von ${inputs.name} — wähle einen Korrespondenten oben um einzugrenzen`; + +export const conv_hint_single_person_filtered = (inputs: { + name: string; + from: string; + to: string; + sortLabel: string; +}) => `Alle Briefe von ${inputs.name} · ${inputs.from}–${inputs.to} · ${inputs.sortLabel}`; + +export const conv_strip_period = () => 'Zeitraum'; +export const conv_strip_from_placeholder = () => 'Von…'; +export const conv_strip_to_placeholder = () => 'Bis…'; +export const conv_strip_all_correspondents = () => 'Alle Korrespondenten'; +export const conv_strip_sort_newest = () => 'Neueste'; +export const conv_strip_sort_oldest = () => 'Älteste'; +export const conv_suggestions_heading = () => 'Häufigste Korrespondenten'; +export const conv_suggestions_all_label = (inputs: { name: string }) => + `Alle Korrespondenten von ${inputs.name}`; +export const conv_letters_count = (inputs: { count: number }) => `${inputs.count} Briefe`; +export const conv_empty_search_placeholder = () => 'Person suchen…'; +export const conv_empty_recent_label = () => 'Zuletzt geöffnet'; +export const conv_no_party = () => '—'; diff --git a/frontend/src/routes/korrespondenz/CorrespondentSuggestionsDropdown.svelte b/frontend/src/routes/korrespondenz/CorrespondentSuggestionsDropdown.svelte new file mode 100644 index 00000000..e304cb4c --- /dev/null +++ b/frontend/src/routes/korrespondenz/CorrespondentSuggestionsDropdown.svelte @@ -0,0 +1,129 @@ + + +
handleKeydown(e, e.currentTarget as HTMLElement)} +> + +
+ {conv_suggestions_heading()} +
+ + + {#if !loading} + {#each results as person (person.id)} +
onselect(person.id)} + onkeydown={(e) => e.key === 'Enter' && onselect(person.id)} + > + + + + {person.lastName}, {person.firstName} + +
+ {/each} + {/if} + + +
+ + +
onselect('')} + onkeydown={(e) => e.key === 'Enter' && onselect('')} + > + {conv_suggestions_all_label({ name: senderName })} +
+
diff --git a/frontend/src/routes/korrespondenz/CorrespondenzEmptyState.svelte b/frontend/src/routes/korrespondenz/CorrespondenzEmptyState.svelte new file mode 100644 index 00000000..0ff49bbf --- /dev/null +++ b/frontend/src/routes/korrespondenz/CorrespondenzEmptyState.svelte @@ -0,0 +1,121 @@ + + +
+ +
+ +
+ + +

Korrespondenz durchsuchen

+ + +

+ Wähle eine Person aus dem Archiv um deren Briefe zu sehen — mit oder ohne Korrespondent. +

+ + + + + +
+
+ oder +
+
+ + + {#if recentPersons.length > 0} +
+ + {conv_empty_recent_label()} + +
+ {#each recentPersons as person (person.id)} + + + {/each} +
+
+ {/if} +
diff --git a/frontend/src/routes/korrespondenz/CorrespondenzFilterControls.svelte b/frontend/src/routes/korrespondenz/CorrespondenzFilterControls.svelte new file mode 100644 index 00000000..08c9a5ac --- /dev/null +++ b/frontend/src/routes/korrespondenz/CorrespondenzFilterControls.svelte @@ -0,0 +1,135 @@ + + +
+ + + + + onapplyFilters()} + placeholder={conv_strip_from_placeholder()} + aria-label="Von" + class="h-[22px] min-h-[44px] w-[80px] rounded-[3px] border px-1 text-xs focus:outline-none sm:min-h-0" + class:border-[#002850]={!!fromDate} + class:text-[#333]={!!fromDate} + class:border-[#D1D5DB]={!fromDate} + class:text-[#AAA]={!fromDate} + class:italic={!fromDate} + /> + + + + + onapplyFilters()} + placeholder={conv_strip_to_placeholder()} + aria-label="Bis" + class="h-[22px] min-h-[44px] w-[80px] rounded-[3px] border px-1 text-xs focus:outline-none sm:min-h-0" + class:border-[#002850]={!!toDate} + class:text-[#333]={!!toDate} + class:border-[#D1D5DB]={!toDate} + class:text-[#AAA]={!toDate} + class:italic={!toDate} + /> + + + + {conv_letters_count({ count: documentCount ?? 0 })} + + + + +
diff --git a/frontend/src/routes/korrespondenz/CorrespondenzPersonBar.svelte b/frontend/src/routes/korrespondenz/CorrespondenzPersonBar.svelte new file mode 100644 index 00000000..1a3b22ba --- /dev/null +++ b/frontend/src/routes/korrespondenz/CorrespondenzPersonBar.svelte @@ -0,0 +1,87 @@ + + +
+ +
+ onapplyFilters()} + /> +
+ + + + + +
+ onapplyFilters()} + /> + {#if !receiverId} + + — optional + + {/if} +
+
diff --git a/frontend/src/routes/korrespondenz/SinglePersonHintBar.svelte b/frontend/src/routes/korrespondenz/SinglePersonHintBar.svelte new file mode 100644 index 00000000..9bb49308 --- /dev/null +++ b/frontend/src/routes/korrespondenz/SinglePersonHintBar.svelte @@ -0,0 +1,38 @@ + + +
+ + + {#if hasDateFilter} + {conv_hint_single_person_filtered({ + name: senderName, + from: fromDate ?? '', + to: toDate ?? '', + sortLabel + })} + {:else} + {conv_hint_single_person({ name: senderName })} + {/if} +