Some checks failed
CI / Backend Unit Tests (pull_request) Waiting to run
CI / E2E Tests (pull_request) Waiting to run
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
Replaces hardcoded German strings with Paraglide message keys (page_title_home/persons/admin/login/error) across de/en/es. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
125 lines
3.8 KiB
Svelte
125 lines
3.8 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { untrack } from 'svelte';
|
|
import { SvelteURLSearchParams } from 'svelte/reactivity';
|
|
import SearchFilterBar from './SearchFilterBar.svelte';
|
|
import DropZone from './DropZone.svelte';
|
|
import DocumentList from './DocumentList.svelte';
|
|
import { m } from '$lib/paraglide/messages.js';
|
|
|
|
let { data } = $props();
|
|
|
|
let q = $state(untrack(() => data.filters?.q || ''));
|
|
let qFocused = $state(false);
|
|
let from = $state(untrack(() => data.filters?.from || ''));
|
|
let to = $state(untrack(() => data.filters?.to || ''));
|
|
let senderId = $state(untrack(() => data.filters?.senderId || ''));
|
|
let receiverId = $state(untrack(() => data.filters?.receiverId || ''));
|
|
let tagNames = $state<string[]>(untrack(() => data.filters?.tags || []));
|
|
|
|
const hasAdvancedFilters = (filters: typeof data.filters) =>
|
|
(filters?.tags?.length ?? 0) > 0 ||
|
|
!!filters?.senderId ||
|
|
!!filters?.receiverId ||
|
|
!!filters?.from ||
|
|
!!filters?.to;
|
|
|
|
let showAdvanced = $state(untrack(() => hasAdvancedFilters(data.filters)));
|
|
|
|
let searchTimer: ReturnType<typeof setTimeout>;
|
|
|
|
function triggerSearch() {
|
|
const params = new SvelteURLSearchParams();
|
|
if (q) params.set('q', q);
|
|
if (from) params.set('from', from);
|
|
if (to) params.set('to', to);
|
|
if (senderId) params.set('senderId', senderId);
|
|
if (receiverId) params.set('receiverId', receiverId);
|
|
if (tagNames) tagNames.forEach((tag) => params.append('tag', tag));
|
|
goto(`/?${params.toString()}`, { keepFocus: true, noScroll: true });
|
|
}
|
|
|
|
function handleTextSearch() {
|
|
clearTimeout(searchTimer);
|
|
searchTimer = setTimeout(() => triggerSearch(), 500);
|
|
}
|
|
|
|
// Trigger search when tags change
|
|
let prevTagStr = untrack(() => tagNames.join(','));
|
|
$effect(() => {
|
|
const cur = tagNames.join(',');
|
|
if (cur !== prevTagStr) {
|
|
prevTagStr = cur;
|
|
triggerSearch();
|
|
}
|
|
});
|
|
|
|
// Sync local state with server data after navigation.
|
|
// Guard q: skip overwrite while the user is actively typing in the search field.
|
|
$effect(() => {
|
|
if (!qFocused) q = data.filters?.q || '';
|
|
from = data.filters?.from || '';
|
|
to = data.filters?.to || '';
|
|
senderId = data.filters?.senderId || '';
|
|
receiverId = data.filters?.receiverId || '';
|
|
tagNames = data.filters?.tags || [];
|
|
if (hasAdvancedFilters(data.filters)) showAdvanced = true;
|
|
});
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>{m.page_title_home()}</title>
|
|
</svelte:head>
|
|
|
|
<main class="mx-auto max-w-7xl px-4 py-8 font-sans sm:px-6 lg:px-8">
|
|
<SearchFilterBar
|
|
bind:q={q}
|
|
bind:from={from}
|
|
bind:to={to}
|
|
bind:senderId={senderId}
|
|
bind:receiverId={receiverId}
|
|
bind:tagNames={tagNames}
|
|
bind:showAdvanced={showAdvanced}
|
|
initialSenderName={data.initialValues?.senderName}
|
|
initialReceiverName={data.initialValues?.receiverName}
|
|
onSearch={handleTextSearch}
|
|
onfocus={() => (qFocused = true)}
|
|
onblur={() => (qFocused = false)}
|
|
/>
|
|
|
|
{#if data.canWrite}
|
|
<DropZone />
|
|
{/if}
|
|
|
|
{#if data.incompleteCount > 0}
|
|
<a
|
|
href="/enrich"
|
|
class="mb-6 flex items-center justify-between rounded-sm border border-accent/40 bg-accent-bg px-6 py-4 transition-colors hover:bg-accent/20"
|
|
>
|
|
<div class="flex items-center gap-4">
|
|
<img
|
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Info/Block/Info-Block-Border-MD.svg"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="h-6 w-6 opacity-60"
|
|
/>
|
|
<div>
|
|
<p class="font-sans text-xs font-bold tracking-widest text-ink uppercase">
|
|
{m.enrich_needs_metadata_title()}
|
|
</p>
|
|
<p class="mt-0.5 font-serif text-sm text-ink-2">
|
|
{m.enrich_needs_metadata_count({ count: data.incompleteCount })}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<span
|
|
class="font-sans text-xs font-bold tracking-widest text-ink uppercase transition-colors hover:text-ink-2"
|
|
>
|
|
{m.enrich_needs_metadata_cta()} →
|
|
</span>
|
|
</a>
|
|
{/if}
|
|
|
|
<DocumentList documents={data.documents ?? []} canWrite={data.canWrite} error={data.error} />
|
|
</main>
|