Person detail (/persons/[id]):
- Server load fetches GET /api/geschichten?status=PUBLISHED&personId={id}
in parallel with the existing person/document queries.
- Renders <GeschichtenCard> below the received-documents list when the
person has at least one published story.
Document detail (/documents/[id]):
- Server load adds the same parallel call with documentId={id}.
- DocumentTopBar gains geschichten + canBlogWrite props that flow through
to DocumentMetadataDrawer.
- DocumentMetadataDrawer's grid expands to lg:grid-cols-4 when the
Geschichten column should appear (stories exist OR user can author),
and shows "+ Geschichte anhängen" / "Alle anzeigen" links following the
>= 3-story threshold from issue comment #5758.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
110 lines
2.9 KiB
Svelte
110 lines
2.9 KiB
Svelte
<script lang="ts">
|
|
import { m } from '$lib/paraglide/messages.js';
|
|
import { SvelteMap } from 'svelte/reactivity';
|
|
import BackButton from '$lib/components/BackButton.svelte';
|
|
import PersonCard from './PersonCard.svelte';
|
|
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();
|
|
|
|
const person = $derived(data.person);
|
|
const sentDocuments = $derived(data.sentDocuments);
|
|
const receivedDocuments = $derived(data.receivedDocuments);
|
|
|
|
const coCorrespondents = $derived.by(() => {
|
|
const freq = new SvelteMap<string, { id: string; name: string; count: number }>();
|
|
|
|
for (const doc of sentDocuments) {
|
|
for (const receiver of doc.receivers ?? []) {
|
|
const key = receiver.id;
|
|
const existing = freq.get(key);
|
|
if (existing) existing.count++;
|
|
else
|
|
freq.set(key, {
|
|
id: receiver.id,
|
|
name: receiver.displayName,
|
|
count: 1
|
|
});
|
|
}
|
|
}
|
|
|
|
for (const doc of receivedDocuments) {
|
|
if (doc.sender && doc.sender.id !== person.id) {
|
|
const key = doc.sender.id;
|
|
const existing = freq.get(key);
|
|
if (existing) existing.count++;
|
|
else
|
|
freq.set(key, {
|
|
id: doc.sender.id,
|
|
name: doc.sender.displayName,
|
|
count: 1
|
|
});
|
|
}
|
|
}
|
|
|
|
return [...freq.values()].sort((a, b) => b.count - a.count).slice(0, 5);
|
|
});
|
|
</script>
|
|
|
|
<div class="mx-auto max-w-6xl px-4 py-10">
|
|
<!-- Back Link -->
|
|
<div class="mb-6">
|
|
<BackButton />
|
|
</div>
|
|
|
|
<!-- 2-column layout on large screens -->
|
|
<div class="lg:grid lg:grid-cols-[35%_65%] lg:gap-8">
|
|
<!-- Left column: Person card + name history -->
|
|
<div>
|
|
<PersonCard person={person} canWrite={data.canWrite} />
|
|
<div class="mt-6">
|
|
<NameHistoryCard aliases={data.aliases} personFirstName={person.firstName} />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right column: correspondents + relationships + documents -->
|
|
<div>
|
|
<CoCorrespondentsList coCorrespondents={coCorrespondents} personId={person.id} />
|
|
|
|
<div class="mt-6">
|
|
<PersonRelationshipsCard
|
|
personId={person.id}
|
|
relationships={data.relationships}
|
|
inferredRelationships={data.inferredRelationships}
|
|
/>
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<PersonDocumentList
|
|
documents={sentDocuments}
|
|
heading={m.person_docs_heading()}
|
|
emptyMessage={m.person_no_docs()}
|
|
/>
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<PersonDocumentList
|
|
documents={receivedDocuments}
|
|
heading={m.person_received_docs_heading()}
|
|
emptyMessage={m.person_no_received_docs()}
|
|
/>
|
|
</div>
|
|
|
|
{#if data.geschichten && data.geschichten.length > 0}
|
|
<div class="mt-6">
|
|
<GeschichtenCard
|
|
geschichten={data.geschichten}
|
|
personId={person.id}
|
|
personName={person.displayName}
|
|
canWrite={data.canBlogWrite ?? false}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|