feat(dashboard): replace notifications fetch with stats in server load

Removes /api/notifications from the dashboard widget fetches and replaces
it with /api/stats so the page no longer needs to own notification data.
Returns stats: StatsDTO | null (null on failure) instead of mentions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-31 19:23:31 +02:00
parent 6d61297182
commit 20923d04b6
2 changed files with 40 additions and 17 deletions

View File

@@ -22,14 +22,14 @@ function makeUrl(params: Record<string, string | string[]> = {}) {
// ─── dashboard mode (no search filters) ──────────────────────────────────────
describe('home page load — dashboard mode', () => {
it('sets isDashboard true and fetches all three widget APIs', async () => {
it('sets isDashboard true and fetches stats, incomplete, and recent APIs', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // persons
.mockResolvedValueOnce({
response: { ok: true },
data: { content: [{ id: 'n1' }] }
}) // notifications
data: { totalDocuments: 42, totalPersons: 7 }
}) // stats
.mockResolvedValueOnce({ response: { ok: true }, data: [{ id: 'd1' }] }) // incomplete
.mockResolvedValueOnce({ response: { ok: true }, data: [{ id: 'd2' }] }); // recent
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
@@ -39,17 +39,20 @@ describe('home page load — dashboard mode', () => {
const result = await load({ url: makeUrl(), fetch: vi.fn() as unknown as typeof fetch });
expect(result.isDashboard).toBe(true);
expect(result.mentions).toHaveLength(1);
expect(result.stats).toEqual({ totalDocuments: 42, totalPersons: 7 });
expect(result.incompleteDocs).toHaveLength(1);
expect(result.recentDocs).toHaveLength(1);
expect(result.documents).toEqual([]);
});
it('defaults mentions to [] when notifications API rejects', async () => {
it('returns stats with totalDocuments from /api/stats', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // persons
.mockRejectedValueOnce(new Error('network')) // notifications
.mockResolvedValueOnce({
response: { ok: true },
data: { totalDocuments: 248, totalPersons: 34 }
}) // stats
.mockResolvedValueOnce({ response: { ok: true }, data: [] }) // incomplete
.mockResolvedValueOnce({ response: { ok: true }, data: [] }); // recent
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
@@ -58,7 +61,24 @@ describe('home page load — dashboard mode', () => {
const result = await load({ url: makeUrl(), fetch: vi.fn() as unknown as typeof fetch });
expect(result.mentions).toEqual([]);
expect(result.stats?.totalDocuments).toBe(248);
expect(result.stats?.totalPersons).toBe(34);
});
it('returns stats: null when /api/stats rejects', async () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // persons
.mockRejectedValueOnce(new Error('network')) // stats
.mockResolvedValueOnce({ response: { ok: true }, data: [] }) // incomplete
.mockResolvedValueOnce({ response: { ok: true }, data: [] }); // recent
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
>);
const result = await load({ url: makeUrl(), fetch: vi.fn() as unknown as typeof fetch });
expect(result.stats).toBeNull();
});
it('defaults incompleteDocs to [] when incomplete API rejects', async () => {
@@ -81,7 +101,10 @@ describe('home page load — dashboard mode', () => {
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // persons
.mockResolvedValueOnce({ response: { ok: true }, data: { content: [] } }) // notifications
.mockResolvedValueOnce({
response: { ok: true },
data: { totalDocuments: 0, totalPersons: 0 }
}) // stats
.mockResolvedValueOnce({ response: { ok: true }, data: [] }) // incomplete
.mockRejectedValueOnce(new Error('network')); // recent
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
@@ -113,7 +136,7 @@ describe('home page load — search mode', () => {
expect(result.isDashboard).toBe(false);
expect(result.documents).toHaveLength(1);
expect(result.mentions).toEqual([]);
expect(result.stats).toBeNull();
expect(result.incompleteDocs).toEqual([]);
expect(result.recentDocs).toEqual([]);
// Only two API calls — no widget calls