From 08d8896cd1e7fd13ebb54ab3aaa35f9baa9718f0 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 14 Jun 2026 10:42:33 +0200 Subject: [PATCH] feat(timeline): show provenance token on the event pill (REQ-007) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The derived/curated pill subtitle now reads "{date} · abgeleitet" or "{date} · kuratiert", keyed off entry.derived so a reader sees both the date and whether the event was derived from Person data or curated. Only the single provenance token ships; the spec sheet's "· persönlich" / "· SEASON" annotations stay out (already covered by the ★ glyph + mint border and not production UI). Refs #833 Co-Authored-By: Claude Opus 4.8 --- frontend/src/lib/timeline/EventPill.svelte | 7 +++- .../src/lib/timeline/EventPill.svelte.spec.ts | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/timeline/EventPill.svelte b/frontend/src/lib/timeline/EventPill.svelte index 9b125cb7..b30420b2 100644 --- a/frontend/src/lib/timeline/EventPill.svelte +++ b/frontend/src/lib/timeline/EventPill.svelte @@ -16,6 +16,11 @@ let { entry }: { entry: TimelineEntryDTO } = $props(); const config = $derived(getAccentConfig(entry)); const dateLabel = $derived(timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd)); +// Provenance reads off entry.derived (not the accent): a derived life-event is +// "abgeleitet", a curated PERSONAL event is "kuratiert" (REQ-007). +const provenance = $derived( + entry.derived ? m.timeline_provenance_derived() : m.timeline_provenance_curated() +); const canEdit = $derived(!entry.derived && entry.eventId != null); @@ -42,7 +47,7 @@ const canEdit = $derived(!entry.derived && entry.eventId != null); > {/if} {#if dateLabel} - {dateLabel} + {dateLabel} · {provenance} {/if} {#if canEdit} diff --git a/frontend/src/lib/timeline/EventPill.svelte.spec.ts b/frontend/src/lib/timeline/EventPill.svelte.spec.ts index 945ea65f..b254185a 100644 --- a/frontend/src/lib/timeline/EventPill.svelte.spec.ts +++ b/frontend/src/lib/timeline/EventPill.svelte.spec.ts @@ -1,6 +1,8 @@ import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; +import * as m from '$lib/paraglide/messages.js'; import EventPill from './EventPill.svelte'; +import { timelineDateLabel } from './dateLabel'; import { makeEntry } from './test-factories'; afterEach(() => cleanup()); @@ -87,4 +89,35 @@ describe('EventPill', () => { render(EventPill, { entry: derived('MARRIAGE', 'Heirat') }); expect(document.querySelector('[data-testid="event-edit"]')).toBeNull(); }); + + it('appends the "abgeleitet" provenance token to a derived pill subtitle (REQ-007)', () => { + const entry = derived('BIRTH', 'Geburt: Hans'); + const date = timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd); + render(EventPill, { entry }); + expect(document.body.textContent).toContain(`${date} · ${m.timeline_provenance_derived()}`); + }); + + it('appends the "kuratiert" provenance token to a curated PERSONAL pill subtitle (REQ-007)', () => { + const entry = makeEntry({ + kind: 'EVENT', + derived: false, + type: 'PERSONAL', + eventId: EVENT_ID, + title: 'Auswanderung', + senderName: '', + receiverName: '', + precision: 'YEAR', + eventDate: '1924-01-01', + documentId: undefined + }); + const date = timelineDateLabel(entry.eventDate, entry.precision, entry.eventDateEnd); + render(EventPill, { entry }); + expect(document.body.textContent).toContain(`${date} · ${m.timeline_provenance_curated()}`); + }); + + it('never shows the spec-sheet-only "persönlich"/"SEASON" tokens (REQ-007)', () => { + render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); + expect(document.body.textContent).not.toContain('persönlich'); + expect(document.body.textContent).not.toContain('SEASON'); + }); });