From b56b9dfa743ce73136a25fc30952a57e76bf685a Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 27 May 2026 11:56:49 +0200 Subject: [PATCH] feat(frontend): render honest precision dates in detail, list and search Wires formatDocumentDate/DocumentDate into the read sites: the document detail top bar + metadata drawer (the drawer shows the visible "Originaltext:" raw line for UNKNOWN/SEASON/APPROX), the search/list rows (DocumentRow, mobile + desktop), and the document multi-select dropdown label. A MONTH or SEASON document now reads "Juni 1916"/"Sommer 1916" everywhere instead of a fabricated day. Adds metaDatePrecision to the DocumentRow/DocumentMultiSelect test fixtures (required on DocumentListItem since #671) and updates the multi-select label assertion to the honest long date. Refs #666 Co-Authored-By: Claude Opus 4.7 --- .../document/DocumentMetadataDrawer.svelte | 22 ++++++++++++-- .../lib/document/DocumentMultiSelect.svelte | 13 +++++--- .../DocumentMultiSelect.svelte.spec.ts | 4 ++- frontend/src/lib/document/DocumentRow.svelte | 24 +++++++++++++-- .../lib/document/DocumentRow.svelte.spec.ts | 1 + .../lib/document/DocumentRow.svelte.test.ts | 1 + .../src/lib/document/DocumentTopBar.svelte | 10 +++++++ .../lib/document/DocumentTopBarTitle.svelte | 30 +++++++++++++------ 8 files changed, 86 insertions(+), 19 deletions(-) diff --git a/frontend/src/lib/document/DocumentMetadataDrawer.svelte b/frontend/src/lib/document/DocumentMetadataDrawer.svelte index 01ecc62a..4b8081e9 100644 --- a/frontend/src/lib/document/DocumentMetadataDrawer.svelte +++ b/frontend/src/lib/document/DocumentMetadataDrawer.svelte @@ -4,6 +4,8 @@ import { formatDate } from '$lib/shared/utils/date'; import { formatDocumentStatus } from '$lib/document/documentStatusLabel'; import { getInitials, personAvatarColor } from '$lib/person/personFormat'; import RelationshipPill from '$lib/person/relationship/RelationshipPill.svelte'; +import DocumentDate from './DocumentDate.svelte'; +import type { DatePrecision } from '$lib/shared/utils/documentDate'; type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; type Tag = { id: string; name: string }; @@ -16,6 +18,9 @@ type GeschichteSummary = { type Props = { documentDate: string | null; + metaDatePrecision?: DatePrecision | null; + metaDateEnd?: string | null; + metaDateRaw?: string | null; location: string | null; status: string; sender: Person | null; @@ -29,6 +34,9 @@ type Props = { let { documentDate, + metaDatePrecision = null, + metaDateEnd = null, + metaDateRaw = null, location, status, sender, @@ -59,7 +67,6 @@ function formatGeschichteDate(g: GeschichteSummary): string { return formatDate(g.publishedAt.slice(0, 10), 'short'); } -const formattedDate = $derived(documentDate ? formatDate(documentDate) : '—'); const displayLocation = $derived(location ?? '—'); const statusLabel = $derived(formatDocumentStatus(status)); const visibleReceivers = $derived(receivers.slice(0, VISIBLE_RECEIVER_LIMIT)); @@ -105,7 +112,18 @@ function getFullName(person: Person): string {
{m.doc_details_field_date()}
-
{formattedDate}
+
+ {#if documentDate || metaDateRaw} + + {:else} + — + {/if} +
{m.form_label_location()}
diff --git a/frontend/src/lib/document/DocumentMultiSelect.svelte b/frontend/src/lib/document/DocumentMultiSelect.svelte index 0196544b..dcb4ecca 100644 --- a/frontend/src/lib/document/DocumentMultiSelect.svelte +++ b/frontend/src/lib/document/DocumentMultiSelect.svelte @@ -2,7 +2,8 @@ import type { components } from '$lib/generated/api'; import { m } from '$lib/paraglide/messages.js'; import { clickOutside } from '$lib/shared/actions/clickOutside'; -import { formatDate } from '$lib/shared/utils/date'; +import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate'; +import { getLocale } from '$lib/paraglide/runtime.js'; type Document = components['schemas']['Document']; type DocumentListItem = components['schemas']['DocumentListItem']; @@ -49,7 +50,9 @@ function handleInput() { const docs = body.items.map((it) => ({ id: it.id, title: it.title, - documentDate: it.documentDate + documentDate: it.documentDate, + metaDatePrecision: it.metaDatePrecision, + metaDateEnd: it.metaDateEnd })) as unknown as Document[]; results = docs.filter((d) => !selectedDocuments.some((s) => s.id === d.id)); } @@ -73,8 +76,10 @@ function removeDocument(id: string | undefined) { } function formatDocLabel(doc: Document): string { - if (doc.documentDate) return `${doc.title} · ${formatDate(doc.documentDate, 'short')}`; - return doc.title; + if (!doc.documentDate) return doc.title; + const precision = (doc.metaDatePrecision as DatePrecision | undefined) ?? 'DAY'; + const label = formatDocumentDate(doc.documentDate, precision, doc.metaDateEnd, null, getLocale()); + return `${doc.title} · ${label}`; } diff --git a/frontend/src/lib/document/DocumentMultiSelect.svelte.spec.ts b/frontend/src/lib/document/DocumentMultiSelect.svelte.spec.ts index 6514ab55..d348026c 100644 --- a/frontend/src/lib/document/DocumentMultiSelect.svelte.spec.ts +++ b/frontend/src/lib/document/DocumentMultiSelect.svelte.spec.ts @@ -9,6 +9,7 @@ const docFactory = (id: string, title: string, date = '1880-01-01') => ({ id, title, documentDate: date, + metaDatePrecision: 'DAY' as const, originalFilename: `${title}.pdf`, receivers: [], tags: [], @@ -55,7 +56,8 @@ describe('DocumentMultiSelect — rendering', () => { selectedDocuments: [docFactory('d1', 'Brief vom 1. Mai', '1882-05-01')] }); await expect.element(page.getByText(/Brief vom 1\. Mai/)).toBeInTheDocument(); - await expect.element(page.getByText(/01\.05\.1882/)).toBeInTheDocument(); + // DAY precision renders the honest long date (formatDocumentDate), not 01.05.1882. + await expect.element(page.getByText(/1\. Mai 1882/)).toBeInTheDocument(); }); it('emits a hidden documentIds input for each pre-selected document', async () => { diff --git a/frontend/src/lib/document/DocumentRow.svelte b/frontend/src/lib/document/DocumentRow.svelte index 903ed727..ac9fde0a 100644 --- a/frontend/src/lib/document/DocumentRow.svelte +++ b/frontend/src/lib/document/DocumentRow.svelte @@ -2,7 +2,7 @@ import { goto } from '$app/navigation'; import type { components } from '$lib/generated/api'; import { applyOffsets } from '$lib/document/search'; -import { formatDate } from '$lib/shared/utils/date'; +import DocumentDate from './DocumentDate.svelte'; import * as m from '$lib/paraglide/messages.js'; import { bulkSelectionStore } from '$lib/document/bulkSelection.svelte'; import ProgressRing from '$lib/shared/primitives/ProgressRing.svelte'; @@ -164,7 +164,16 @@ function safeTagColor(color: string | null | undefined): string {
- {doc.documentDate ? formatDate(doc.documentDate) : '—'} + {#if doc.documentDate} + + {:else} + — + {/if}
@@ -178,7 +187,16 @@ function safeTagColor(color: string | null | undefined): string {