diff --git a/frontend/messages/de.json b/frontend/messages/de.json
index 1ac6f5f8..4bfa4b62 100644
--- a/frontend/messages/de.json
+++ b/frontend/messages/de.json
@@ -460,6 +460,7 @@
"dashboard_badge_new": "Neu",
"dashboard_badge_updated": "Aktualisiert",
"dashboard_reader_all_stories": "Alle Geschichten →",
+ "dashboard_reader_doc_count_suffix": "Dok.",
"dashboard_resume_label": "Zuletzt geöffnet:",
"dashboard_resume_fallback": "Unbekanntes Dokument",
"doc_status_placeholder": "Platzhalter",
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 37fa2e75..b7bdff0b 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -460,6 +460,7 @@
"dashboard_badge_new": "New",
"dashboard_badge_updated": "Updated",
"dashboard_reader_all_stories": "All Stories →",
+ "dashboard_reader_doc_count_suffix": "docs.",
"dashboard_resume_label": "Last opened:",
"dashboard_resume_fallback": "Unknown document",
"doc_status_placeholder": "Placeholder",
diff --git a/frontend/messages/es.json b/frontend/messages/es.json
index cfbeff87..5300ef4d 100644
--- a/frontend/messages/es.json
+++ b/frontend/messages/es.json
@@ -460,6 +460,7 @@
"dashboard_badge_new": "Nuevo",
"dashboard_badge_updated": "Actualizado",
"dashboard_reader_all_stories": "Todas las historias →",
+ "dashboard_reader_doc_count_suffix": "docs.",
"dashboard_resume_label": "Último abierto:",
"dashboard_resume_fallback": "Documento desconocido",
"doc_status_placeholder": "Marcador",
diff --git a/frontend/src/lib/shared/dashboard/ReaderPersonChips.svelte b/frontend/src/lib/shared/dashboard/ReaderPersonChips.svelte
index 8aa8d8c3..57a602df 100644
--- a/frontend/src/lib/shared/dashboard/ReaderPersonChips.svelte
+++ b/frontend/src/lib/shared/dashboard/ReaderPersonChips.svelte
@@ -45,7 +45,9 @@ const { persons }: Props = $props();
{p.displayName ?? p.lastName}
- {p.documentCount ?? 0} Dok.
+ {p.documentCount ?? 0} {m.dashboard_reader_doc_count_suffix()}
{/each}
diff --git a/frontend/src/routes/page.server.spec.ts b/frontend/src/routes/page.server.spec.ts
index 1aae2752..b51540c3 100644
--- a/frontend/src/routes/page.server.spec.ts
+++ b/frontend/src/routes/page.server.spec.ts
@@ -375,4 +375,39 @@ describe('home page load — reader branch (isReader = !canWrite && !canAnnotate
expect(result.isReader).toBe(false);
});
+
+ it('returns topPersons=[] when topPersons fetch fails, rest of data still loads', async () => {
+ const okStats = {
+ response: { ok: true, status: 200 },
+ data: { totalDocuments: 5, totalPersons: 2, totalStories: 1 }
+ };
+ const failPersons = Promise.reject(new Error('timeout'));
+ const okSearch = { response: { ok: true, status: 200 }, data: { items: [] } };
+ const okStories = { response: { ok: true, status: 200 }, data: [] };
+
+ const mockGet = vi
+ .fn()
+ .mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // initial persons check
+ .mockResolvedValueOnce(okStats)
+ .mockReturnValueOnce(failPersons)
+ .mockResolvedValueOnce(okSearch)
+ .mockResolvedValueOnce(okStories);
+ vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
+ typeof createApiClient
+ >);
+
+ const result = await load({
+ url: makeUrl(),
+ fetch: vi.fn() as unknown as typeof fetch,
+ parent: vi
+ .fn()
+ .mockResolvedValue({ canWrite: false, canAnnotate: false, canBlogWrite: false })
+ } as Parameters[0]);
+
+ expect(result.isReader).toBe(true);
+ if (result.isReader) {
+ expect(result.topPersons).toEqual([]);
+ expect(result.readerStats?.totalDocuments).toBe(5);
+ }
+ });
});