Files
familienarchiv/frontend/src/routes/briefwechsel/+page.svelte
Marcel 53b318f7ad
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2s
CI / Backend Unit Tests (pull_request) Failing after 1s
CI / Unit & Component Tests (push) Failing after 3s
CI / Backend Unit Tests (push) Failing after 2s
fix(ui): add py-8 to results state matching document search page
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>
2026-04-07 08:52:44 +02:00

161 lines
4.6 KiB
Svelte

<script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { untrack } from 'svelte';
import { SvelteURLSearchParams } from 'svelte/reactivity';
import CorrespondenzPersonBar from './CorrespondenzPersonBar.svelte';
import CorrespondenzFilterControls from './CorrespondenzFilterControls.svelte';
import SinglePersonHintBar from './SinglePersonHintBar.svelte';
import ConversationTimeline from './ConversationTimeline.svelte';
import CorrespondenzHero from './CorrespondenzHero.svelte';
import { m } from '$lib/paraglide/messages.js';
let { data } = $props();
let senderId = $state(untrack(() => data.filters.senderId));
let receiverId = $state(untrack(() => data.filters.receiverId));
let fromDate = $state(untrack(() => data.filters.from));
let toDate = $state(untrack(() => data.filters.to));
let sortDir = $state(untrack(() => data.filters.dir));
const senderName = $derived(data.initialValues.senderName);
const receiverName = $derived(data.initialValues.receiverName);
$effect(() => {
if (data.filters.senderId && data.initialValues.senderName) {
persistRecentPerson(data.filters.senderId, data.initialValues.senderName);
}
});
const isSinglePerson = $derived(!!senderId && !receiverId);
const showHero = $derived(!senderId && !data.filters.senderId);
let showAdvanced = $state(false);
const RECENT_STORAGE_KEY = 'korrespondenz_recent_persons';
const MAX_RECENT = 5;
interface RecentPerson {
id: string;
name: string;
}
let recentPersons = $state<RecentPerson[]>([]);
onMount(() => {
try {
const raw = localStorage.getItem(RECENT_STORAGE_KEY);
if (raw) {
recentPersons = JSON.parse(raw) as RecentPerson[];
}
} catch {
recentPersons = [];
}
});
function persistRecentPerson(id: string, name: string) {
if (!id) return;
try {
const raw = localStorage.getItem(RECENT_STORAGE_KEY);
const existing: RecentPerson[] = raw ? JSON.parse(raw) : [];
const filtered = existing.filter((p) => p.id !== id);
const updated = [{ id, name }, ...filtered].slice(0, MAX_RECENT);
localStorage.setItem(RECENT_STORAGE_KEY, JSON.stringify(updated));
} catch {
// localStorage unavailable — silently ignore
}
}
function applyFilters() {
const params = new SvelteURLSearchParams();
if (senderId) params.set('senderId', senderId);
if (receiverId) params.set('receiverId', receiverId);
if (fromDate) params.set('from', fromDate);
if (toDate) params.set('to', toDate);
params.set('dir', sortDir);
goto(`/briefwechsel?${params.toString()}`, { keepFocus: true });
}
function toggleSort() {
sortDir = sortDir === 'DESC' ? 'ASC' : 'DESC';
applyFilters();
}
function swapPersons() {
const tmp = senderId;
senderId = receiverId;
receiverId = tmp;
applyFilters();
}
function selectPerson(id: string) {
if (!id) return;
senderId = id;
receiverId = '';
applyFilters();
}
</script>
{#if showHero}
<!-- Hero state: only on fresh page load with no context -->
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<CorrespondenzHero onSelectPerson={selectPerson} recentPersons={recentPersons} />
</div>
{:else}
<!-- Results state: card + content -->
<div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<div class="mb-8 rounded-sm border border-line bg-surface p-6 shadow-sm">
<CorrespondenzPersonBar
bind:senderId={senderId}
bind:receiverId={receiverId}
initialSenderName={data.initialValues.senderName}
initialReceiverName={data.initialValues.receiverName}
sortDir={sortDir}
showAdvanced={showAdvanced}
documentCount={data.documents.length}
onapplyFilters={applyFilters}
onswapPersons={swapPersons}
ontoggleSort={toggleSort}
ontoggleAdvanced={() => (showAdvanced = !showAdvanced)}
/>
{#if showAdvanced}
<CorrespondenzFilterControls
bind:fromDate={fromDate}
bind:toDate={toDate}
onapplyFilters={applyFilters}
/>
{/if}
{#if isSinglePerson}
<SinglePersonHintBar
senderName={senderName}
fromDate={fromDate || undefined}
toDate={toDate || undefined}
sortDir={sortDir}
/>
{/if}
</div>
<div>
{#if data.documents.length === 0}
<div
class="flex flex-col items-center justify-center rounded-sm border border-line bg-muted py-24 text-center shadow-sm"
>
<p class="font-serif text-ink">{m.conv_no_results_heading()}</p>
<p class="mt-2 text-sm text-ink-3">{m.conv_no_results_text()}</p>
</div>
{:else}
<ConversationTimeline
documents={data.documents}
senderId={senderId}
receiverId={receiverId}
canWrite={data.canWrite}
senderName={senderName}
receiverName={receiverName}
/>
{/if}
</div>
</div>
{/if}