diff --git a/frontend/src/lib/components/DocumentMetadataDrawer.svelte b/frontend/src/lib/components/DocumentMetadataDrawer.svelte
index ff8e177c..e30f87c8 100644
--- a/frontend/src/lib/components/DocumentMetadataDrawer.svelte
+++ b/frontend/src/lib/components/DocumentMetadataDrawer.svelte
@@ -7,6 +7,12 @@ import RelationshipPill from '$lib/components/RelationshipPill.svelte';
type Person = { id: string; firstName?: string | null; lastName: string; displayName: string };
type Tag = { id: string; name: string };
+type GeschichteSummary = {
+ id: string;
+ title: string;
+ publishedAt?: string;
+ author?: { firstName?: string; lastName?: string; email: string };
+};
type Props = {
documentDate: string | null;
@@ -16,6 +22,9 @@ type Props = {
receivers: Person[];
tags: Tag[];
inferredRelationship?: { labelFromA: string; labelFromB: string } | null;
+ geschichten?: GeschichteSummary[];
+ documentId?: string;
+ canBlogWrite?: boolean;
};
let {
@@ -25,10 +34,30 @@ let {
sender,
receivers,
tags,
- inferredRelationship = null
+ inferredRelationship = null,
+ geschichten = [],
+ documentId,
+ canBlogWrite = false
}: Props = $props();
const VISIBLE_RECEIVER_LIMIT = 5;
+const VISIBLE_GESCHICHTEN_LIMIT = 3;
+const showGeschichtenColumn = $derived(geschichten.length > 0 || canBlogWrite);
+const visibleGeschichten = $derived(geschichten.slice(0, VISIBLE_GESCHICHTEN_LIMIT));
+const hasGeschichtenOverflow = $derived(geschichten.length >= VISIBLE_GESCHICHTEN_LIMIT);
+const gridClass = $derived(showGeschichtenColumn ? 'lg:grid-cols-4' : 'lg:grid-cols-3');
+
+function formatGeschichteAuthor(g: GeschichteSummary): string {
+ const a = g.author;
+ if (!a) return '';
+ const full = [a.firstName, a.lastName].filter(Boolean).join(' ').trim();
+ return full || a.email || '';
+}
+
+function formatGeschichteDate(g: GeschichteSummary): string {
+ if (!g.publishedAt) return '';
+ return formatDate(g.publishedAt.slice(0, 10), 'short');
+}
const formattedDate = $derived(documentDate ? formatDate(documentDate) : '—');
const displayLocation = $derived(location ?? '—');
@@ -67,7 +96,7 @@ function getFullName(person: Person): string {
{/snippet}
-
+
@@ -159,5 +188,51 @@ function getFullName(person: Person): string {
{m.doc_details_no_tags()}
{/if}
+
+
+ {#if showGeschichtenColumn}
+
+
+
+ {#if geschichten.length === 0}
+
—
+ {:else}
+
+ {#each visibleGeschichten as g (g.id)}
+ -
+
+ {g.title}
+
+
+ {formatGeschichteAuthor(g)}
+ {#if formatGeschichteDate(g)}· {formatGeschichteDate(g)}{/if}
+
+
+ {/each}
+
+
+ {#if hasGeschichtenOverflow && documentId}
+
+ {m.geschichten_card_show_all()} →
+
+ {/if}
+ {/if}
+
+ {/if}
diff --git a/frontend/src/lib/components/DocumentTopBar.svelte b/frontend/src/lib/components/DocumentTopBar.svelte
index 15cadd6b..5e480a55 100644
--- a/frontend/src/lib/components/DocumentTopBar.svelte
+++ b/frontend/src/lib/components/DocumentTopBar.svelte
@@ -25,12 +25,21 @@ type Doc = {
tags?: Tag[] | null;
};
+type GeschichteSummary = {
+ id: string;
+ title: string;
+ publishedAt?: string;
+ author?: { firstName?: string; lastName?: string; email: string };
+};
+
type Props = {
doc: Doc;
canWrite: boolean;
fileUrl: string;
transcribeMode: boolean;
inferredRelationship?: { labelFromA: string; labelFromB: string } | null;
+ geschichten?: GeschichteSummary[];
+ canBlogWrite?: boolean;
};
let {
@@ -38,7 +47,9 @@ let {
canWrite,
fileUrl,
transcribeMode = $bindable(),
- inferredRelationship = null
+ inferredRelationship = null,
+ geschichten = [],
+ canBlogWrite = false
}: Props = $props();
let detailsOpen = $state(false);
@@ -283,6 +294,9 @@ let mobileMenuOpen = $state(false);
receivers={doc.receivers ? [...doc.receivers] : []}
tags={doc.tags ? [...doc.tags] : []}
inferredRelationship={inferredRelationship}
+ geschichten={geschichten}
+ documentId={doc.id}
+ canBlogWrite={canBlogWrite}
/>
{/if}
diff --git a/frontend/src/routes/documents/[id]/+page.server.ts b/frontend/src/routes/documents/[id]/+page.server.ts
index e601dd44..6f3d297b 100644
--- a/frontend/src/routes/documents/[id]/+page.server.ts
+++ b/frontend/src/routes/documents/[id]/+page.server.ts
@@ -7,7 +7,12 @@ export async function load({ params, fetch }) {
const { id } = params;
const api = createApiClient(fetch);
- const docResult = await api.GET('/api/documents/{id}', { params: { path: { id } } });
+ const [docResult, geschichtenResult] = await Promise.all([
+ api.GET('/api/documents/{id}', { params: { path: { id } } }),
+ api.GET('/api/geschichten', {
+ params: { query: { status: 'PUBLISHED', documentId: id } }
+ })
+ ]);
if (docResult.response.status === 401) throw redirect(302, '/login');
@@ -18,8 +23,9 @@ export async function load({ params, fetch }) {
const document = docResult.data!;
const inferredRelationship = await loadInferredRelationship(api, document);
+ const geschichten = geschichtenResult.data ?? [];
- return { document, inferredRelationship };
+ return { document, inferredRelationship, geschichten };
}
async function loadInferredRelationship(
diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte
index 69f24b50..49810b90 100644
--- a/frontend/src/routes/documents/[id]/+page.svelte
+++ b/frontend/src/routes/documents/[id]/+page.svelte
@@ -424,6 +424,8 @@ onMount(() => {
fileUrl={fileLoader.fileUrl}
bind:transcribeMode={transcribeMode}
inferredRelationship={data.inferredRelationship}
+ geschichten={data.geschichten ?? []}
+ canBlogWrite={data.canBlogWrite ?? false}
/>
diff --git a/frontend/src/routes/persons/[id]/+page.server.ts b/frontend/src/routes/persons/[id]/+page.server.ts
index 33b3ae74..1fa9db55 100644
--- a/frontend/src/routes/persons/[id]/+page.server.ts
+++ b/frontend/src/routes/persons/[id]/+page.server.ts
@@ -17,14 +17,18 @@ export async function load({ params, fetch, locals }) {
receivedDocsResult,
aliasesResult,
relsResult,
- inferredResult
+ inferredResult,
+ geschichtenResult
] = await Promise.all([
api.GET('/api/persons/{id}', { params: { path: { id } } }),
api.GET('/api/persons/{id}/documents', { params: { path: { id } } }),
api.GET('/api/persons/{id}/received-documents', { params: { path: { id } } }),
api.GET('/api/persons/{id}/aliases', { params: { path: { id } } }),
api.GET('/api/persons/{id}/relationships', { params: { path: { id } } }),
- api.GET('/api/persons/{id}/inferred-relationships', { params: { path: { id } } })
+ api.GET('/api/persons/{id}/inferred-relationships', { params: { path: { id } } }),
+ api.GET('/api/geschichten', {
+ params: { query: { status: 'PUBLISHED', personId: id } }
+ })
]);
if (!personResult.response.ok) {
@@ -39,6 +43,7 @@ export async function load({ params, fetch, locals }) {
aliases: aliasesResult.data ?? [],
relationships: relsResult.data ?? [],
inferredRelationships: inferredResult.data ?? [],
+ geschichten: geschichtenResult.data ?? [],
canWrite
};
}
diff --git a/frontend/src/routes/persons/[id]/+page.svelte b/frontend/src/routes/persons/[id]/+page.svelte
index 58c398b4..3a627892 100644
--- a/frontend/src/routes/persons/[id]/+page.svelte
+++ b/frontend/src/routes/persons/[id]/+page.svelte
@@ -7,6 +7,7 @@ import NameHistoryCard from './NameHistoryCard.svelte';
import CoCorrespondentsList from './CoCorrespondentsList.svelte';
import PersonDocumentList from './PersonDocumentList.svelte';
import PersonRelationshipsCard from './PersonRelationshipsCard.svelte';
+import GeschichtenCard from '$lib/components/GeschichtenCard.svelte';
let { data } = $props();
@@ -92,6 +93,17 @@ const coCorrespondents = $derived.by(() => {
emptyMessage={m.person_no_received_docs()}
/>
+
+ {#if data.geschichten && data.geschichten.length > 0}
+
+
+
+ {/if}