diff --git a/frontend/src/lib/shared/dashboard/ThemenWidget.svelte b/frontend/src/lib/shared/dashboard/ThemenWidget.svelte
index cef35418..1e8cbd20 100644
--- a/frontend/src/lib/shared/dashboard/ThemenWidget.svelte
+++ b/frontend/src/lib/shared/dashboard/ThemenWidget.svelte
@@ -41,8 +41,8 @@ const shownTags = $derived(visibleTags.slice(0, MAX_VISIBLE_TAGS));
{#each shownTags as tag (tag.id)}
0
+ ? ', ' + m.themen_dokumente({ count: tag.subtreeDocumentCount })
: ''}"
class="flex cursor-pointer items-stretch overflow-hidden rounded-sm border border-line bg-canvas hover:bg-surface focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:outline-none"
style="min-height: 56px"
@@ -54,9 +54,9 @@ const shownTags = $derived(visibleTags.slice(0, MAX_VISIBLE_TAGS));
>
{tag.name}
- {#if tag.documentCount > 0}
+ {#if tag.subtreeDocumentCount > 0}
- {m.themen_dokumente({ count: tag.documentCount })}
+ {m.themen_dokumente({ count: tag.subtreeDocumentCount })}
{/if}
diff --git a/frontend/src/lib/shared/dashboard/ThemenWidget.svelte.spec.ts b/frontend/src/lib/shared/dashboard/ThemenWidget.svelte.spec.ts
index 521ddeba..a8e1e552 100644
--- a/frontend/src/lib/shared/dashboard/ThemenWidget.svelte.spec.ts
+++ b/frontend/src/lib/shared/dashboard/ThemenWidget.svelte.spec.ts
@@ -12,9 +12,10 @@ type TagTreeNodeDTO = components['schemas']['TagTreeNodeDTO'];
function makeTag(
name: string,
documentCount: number,
- children: TagTreeNodeDTO[] = []
+ children: TagTreeNodeDTO[] = [],
+ subtreeDocumentCount: number = documentCount
): TagTreeNodeDTO {
- return { id: 'id-' + name, name, documentCount, children };
+ return { id: 'id-' + name, name, documentCount, subtreeDocumentCount, children };
}
describe('ThemenWidget', () => {
@@ -32,6 +33,14 @@ describe('ThemenWidget', () => {
expect(document.body.textContent).not.toContain('Leer');
});
+ it('displays the subtree rollup count for a tag with 0 direct documents', async () => {
+ // 0 direct documents, but the subtree rolls up to 8 — the widget shows the rollup.
+ const tags = [makeTag('Reisen', 0, [], 8)];
+ render(ThemenWidget, { tags });
+ expect(document.body.textContent).toContain('Reisen');
+ expect(document.body.textContent).toContain('8');
+ });
+
it('shows the empty state text when all tags are filtered out', async () => {
render(ThemenWidget, { tags: [makeTag('Leer', 0)] });
expect(document.body.textContent).toMatch(/Noch keine Themen/);
diff --git a/frontend/src/routes/themen/+page.svelte b/frontend/src/routes/themen/+page.svelte
index 40daf0cf..c0a50b6c 100644
--- a/frontend/src/routes/themen/+page.svelte
+++ b/frontend/src/routes/themen/+page.svelte
@@ -41,14 +41,14 @@ const visibleTree = $derived.by(() => data.tree.filter(hasAnyDocuments));
0
+ ? ', ' + m.themen_dokumente({ count: tag.subtreeDocumentCount })
: ''}"
class="flex min-h-[56px] items-center justify-between px-4 pt-4 pb-3 hover:bg-canvas focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:outline-none focus-visible:ring-inset"
>
{tag.name}
- {#if tag.documentCount > 0}{tag.documentCount}{/if}
+ {#if tag.subtreeDocumentCount > 0}{tag.subtreeDocumentCount}{/if}
›
@@ -63,7 +63,7 @@ const visibleTree = $derived.by(() => data.tree.filter(hasAnyDocuments));
>
{child.name}
- {#if child.documentCount > 0}{child.documentCount}{/if}
+ {#if child.subtreeDocumentCount > 0}{child.subtreeDocumentCount}{/if}
›
diff --git a/frontend/src/routes/themen/page.svelte.spec.ts b/frontend/src/routes/themen/page.svelte.spec.ts
index d9d9634a..4e0b9b55 100644
--- a/frontend/src/routes/themen/page.svelte.spec.ts
+++ b/frontend/src/routes/themen/page.svelte.spec.ts
@@ -12,9 +12,10 @@ type TagTreeNodeDTO = components['schemas']['TagTreeNodeDTO'];
function makeTag(
name: string,
documentCount: number,
- children: TagTreeNodeDTO[] = []
+ children: TagTreeNodeDTO[] = [],
+ subtreeDocumentCount: number = documentCount
): TagTreeNodeDTO {
- return { id: 'id-' + name, name, documentCount, children };
+ return { id: 'id-' + name, name, documentCount, subtreeDocumentCount, children };
}
describe('/themen +page', () => {
@@ -48,6 +49,14 @@ describe('/themen +page', () => {
expect(document.body.textContent).toContain('Kriegsbriefe');
});
+ it('shows the subtree rollup in the header for a parent with 0 direct documents', async () => {
+ // AC#5: 0 direct docs, but the subtree rolls up to 7 (child contributes 3).
+ const tree = [makeTag('Reisen', 0, [makeTag('Italien', 3)], 7)];
+ render(ThemenPage, { data: { tree } });
+ expect(document.body.textContent).toContain('Reisen');
+ expect(document.body.textContent).toContain('7');
+ });
+
it('shows "+ N weitere" when a root tag has more than 5 children', async () => {
const children = Array.from({ length: 7 }, (_, i) => makeTag(`Kind${i}`, i + 1));
const tree = [makeTag('Briefe', 10, children)];