fix(ui): unify Briefwechsel search bar with document search card style
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m26s
CI / Backend Unit Tests (push) Failing after 2m25s
CI / Unit & Component Tests (pull_request) Failing after 1m15s
CI / Backend Unit Tests (pull_request) Failing after 2m20s

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>
This commit is contained in:
Marcel
2026-04-06 22:20:32 +02:00
parent 93be64878e
commit c4715f1637
4 changed files with 49 additions and 50 deletions

View File

@@ -99,37 +99,39 @@ function selectPerson(id: string) {
<CorrespondenzHero onSelectPerson={selectPerson} recentPersons={recentPersons} /> <CorrespondenzHero onSelectPerson={selectPerson} recentPersons={recentPersons} />
</div> </div>
{:else} {:else}
<!-- Results state: strips + content --> <!-- Results state: card + content -->
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<CorrespondenzPersonBar <div class="mb-8 rounded-sm border border-line bg-surface p-6 shadow-sm">
bind:senderId={senderId} <CorrespondenzPersonBar
bind:receiverId={receiverId} bind:senderId={senderId}
initialSenderName={data.initialValues.senderName} bind:receiverId={receiverId}
initialReceiverName={data.initialValues.receiverName} initialSenderName={data.initialValues.senderName}
onapplyFilters={applyFilters} initialReceiverName={data.initialValues.receiverName}
onswapPersons={swapPersons} onapplyFilters={applyFilters}
/> onswapPersons={swapPersons}
<CorrespondenzFilterControls
senderId={senderId}
bind:fromDate={fromDate}
bind:toDate={toDate}
bind:sortDir={sortDir}
documentCount={data.documents.length}
onapplyFilters={applyFilters}
ontoggleSort={toggleSort}
/>
{#if isSinglePerson}
<SinglePersonHintBar
senderName={senderName}
fromDate={fromDate || undefined}
toDate={toDate || undefined}
sortDir={sortDir}
/> />
{/if}
<div class="py-4"> <CorrespondenzFilterControls
senderId={senderId}
bind:fromDate={fromDate}
bind:toDate={toDate}
bind:sortDir={sortDir}
documentCount={data.documents.length}
onapplyFilters={applyFilters}
ontoggleSort={toggleSort}
/>
{#if isSinglePerson}
<SinglePersonHintBar
senderName={senderName}
fromDate={fromDate || undefined}
toDate={toDate || undefined}
sortDir={sortDir}
/>
{/if}
</div>
<div>
{#if data.documents.length === 0} {#if data.documents.length === 0}
<div <div
class="flex flex-col items-center justify-center rounded-sm border border-line bg-muted py-24 text-center shadow-sm" class="flex flex-col items-center justify-center rounded-sm border border-line bg-muted py-24 text-center shadow-sm"

View File

@@ -28,13 +28,13 @@ let isActive = $derived(!!(fromDate || toDate || sortDir !== 'DESC'));
<div <div
data-testid="conv-filter-controls" data-testid="conv-filter-controls"
class="flex items-center gap-[10px] border-b border-line bg-muted px-3 py-[5px] transition-opacity" class="mt-6 flex items-center gap-4 border-t border-line-2 pt-6 transition-opacity"
class:opacity-40={!senderId} class:opacity-40={!senderId}
class:pointer-events-none={!senderId} class:pointer-events-none={!senderId}
aria-disabled={!senderId} aria-disabled={!senderId}
> >
<!-- Period label --> <!-- Period label -->
<span class="hidden text-xs font-bold tracking-wide text-ink-3 uppercase sm:block"> <span class="text-xs font-bold tracking-widest text-ink-2 uppercase">
{m.conv_strip_period()} {m.conv_strip_period()}
</span> </span>
@@ -43,23 +43,23 @@ let isActive = $derived(!!(fromDate || toDate || sortDir !== 'DESC'));
bind:value={fromDate} bind:value={fromDate}
onchange={() => onapplyFilters()} onchange={() => onapplyFilters()}
placeholder={m.conv_strip_from_placeholder()} placeholder={m.conv_strip_from_placeholder()}
class="h-8 w-[100px] rounded border bg-surface px-2 text-xs text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {fromDate ? 'border-primary' : 'border-line'}" class="w-[120px] border-line py-2.5 text-sm text-ink shadow-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {fromDate ? 'border-primary' : 'border-line'}"
/> />
<span class="text-xs text-ink-3"></span> <span class="text-sm text-ink-3"></span>
<!-- To date --> <!-- To date -->
<DateInput <DateInput
bind:value={toDate} bind:value={toDate}
onchange={() => onapplyFilters()} onchange={() => onapplyFilters()}
placeholder={m.conv_strip_to_placeholder()} placeholder={m.conv_strip_to_placeholder()}
class="h-8 w-[100px] rounded border bg-surface px-2 text-xs text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {toDate ? 'border-primary' : 'border-line'}" class="w-[120px] border-line py-2.5 text-sm text-ink shadow-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {toDate ? 'border-primary' : 'border-line'}"
/> />
<!-- Document count --> <!-- Document count -->
<span <span
data-testid="conv-strip-count" data-testid="conv-strip-count"
class="ml-auto text-xs font-bold" class="ml-auto text-sm font-bold"
class:text-primary={hasDateFilter} class:text-primary={hasDateFilter}
class:text-ink-3={!hasDateFilter} class:text-ink-3={!hasDateFilter}
> >
@@ -73,18 +73,18 @@ let isActive = $derived(!!(fromDate || toDate || sortDir !== 'DESC'));
aria-label="Sortierung umkehren" aria-label="Sortierung umkehren"
aria-pressed={sortDir === 'ASC'} aria-pressed={sortDir === 'ASC'}
onclick={ontoggleSort} onclick={ontoggleSort}
class="flex h-8 min-h-[44px] items-center gap-1 rounded border px-3 text-xs font-bold" class="flex items-center gap-2 border px-4 py-2.5 text-sm font-bold tracking-wide uppercase transition hover:bg-muted hover:text-ink"
class:border-primary={isActive} class:border-primary={isActive}
class:text-primary={isActive} class:text-primary={isActive}
class:border-line={!isActive} class:border-line={!isActive}
class:text-ink-3={!isActive} class:text-ink-2={!isActive}
> >
{#if sortDir === 'ASC'} {#if sortDir === 'ASC'}
{m.conv_strip_sort_oldest()} {m.conv_strip_sort_oldest()}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="10" width="12"
height="10" height="12"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
@@ -99,8 +99,8 @@ let isActive = $derived(!!(fromDate || toDate || sortDir !== 'DESC'));
{m.conv_strip_sort_newest()} {m.conv_strip_sort_newest()}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="10" width="12"
height="10" height="12"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"

View File

@@ -53,18 +53,16 @@ function handleSuggestionSelect(id: string) {
} }
</script> </script>
<div <div data-testid="conv-person-bar" class="flex items-end gap-4">
data-testid="conv-person-bar"
class="flex items-end gap-[9px] border-b border-line bg-surface px-3 py-[9px] [&_input]:h-12"
>
<!-- Person A --> <!-- Person A -->
<div class="min-w-0 flex-1"> <div
class="min-w-0 flex-1 [&_input]:border-line [&_input]:py-2.5 [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-ink-2 [&_label]:uppercase"
>
<PersonTypeahead <PersonTypeahead
name="senderId" name="senderId"
label="Person" label="Person"
bind:value={senderId} bind:value={senderId}
initialName={initialSenderName} initialName={initialSenderName}
compact={true}
restrictToCorrespondentsOf={receiverId || undefined} restrictToCorrespondentsOf={receiverId || undefined}
onchange={() => onapplyFilters()} onchange={() => onapplyFilters()}
/> />
@@ -76,7 +74,7 @@ function handleSuggestionSelect(id: string) {
type="button" type="button"
aria-label="Personen tauschen" aria-label="Personen tauschen"
onclick={onswapPersons} onclick={onswapPersons}
class="flex h-9 w-9 shrink-0 items-center justify-center rounded border border-line bg-surface text-ink-3 transition-colors hover:border-primary hover:text-primary" class="mb-[3px] flex items-center justify-center rounded border border-line bg-muted px-3 py-2.5 text-ink-3 transition-colors hover:border-primary hover:text-primary"
class:opacity-0={!swapVisible} class:opacity-0={!swapVisible}
class:pointer-events-none={!swapVisible} class:pointer-events-none={!swapVisible}
tabindex={swapVisible ? 0 : -1} tabindex={swapVisible ? 0 : -1}
@@ -100,7 +98,7 @@ function handleSuggestionSelect(id: string) {
<!-- Korrespondent field --> <!-- Korrespondent field -->
<div <div
class="relative min-w-0 flex-1" class="relative min-w-0 flex-1 [&_input]:border-line [&_input]:py-2.5 [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-ink-2 [&_label]:uppercase"
class:[&_input]:border-dashed={!receiverId} class:[&_input]:border-dashed={!receiverId}
class:[&_input]:border-solid={!!receiverId} class:[&_input]:border-solid={!!receiverId}
class:[&_input]:bg-canvas={!receiverId} class:[&_input]:bg-canvas={!receiverId}
@@ -110,7 +108,6 @@ function handleSuggestionSelect(id: string) {
label={receiverId ? 'Korrespondent' : 'Korrespondent — optional'} label={receiverId ? 'Korrespondent' : 'Korrespondent — optional'}
bind:value={receiverId} bind:value={receiverId}
initialName={initialReceiverName} initialName={initialReceiverName}
compact={true}
placeholder="Alle Korrespondenten" placeholder="Alle Korrespondenten"
restrictToCorrespondentsOf={senderId || undefined} restrictToCorrespondentsOf={senderId || undefined}
onchange={() => { onchange={() => {

View File

@@ -19,7 +19,7 @@ let toYear = $derived(toDate ? toDate.substring(0, 4) : '');
</script> </script>
<div <div
class="flex items-center gap-[5px] border-b border-accent bg-accent-bg px-3 py-[6px] text-xs text-ink" class="mt-4 flex items-center gap-1.5 rounded-sm border border-accent bg-accent-bg px-4 py-2.5 text-sm text-ink"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"