feat(persons): enrich /persons list with stats bar, life dates, doc count chip

Load /api/stats in parallel; PersonsStatsBar shows totals; person cards
show alias, life date range, and document count badge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-29 19:52:37 +02:00
parent 3abdf9bb68
commit f4c99cabd5
5 changed files with 119 additions and 25 deletions

View File

@@ -11,11 +11,24 @@ const makePerson = (overrides = {}) => ({
id: '1',
firstName: 'Max',
lastName: 'Mustermann',
documentCount: 0,
...overrides
});
const emptyData = { user: undefined, canWrite: true, canAnnotate: false, q: '', persons: [] };
const dataWithPersons = { ...emptyData, persons: [makePerson()] };
const defaultStats = { totalPersons: 0, totalDocuments: 0 };
const emptyData = {
user: undefined,
canWrite: true,
canAnnotate: false,
q: '',
persons: [],
stats: defaultStats
};
const dataWithPersons = {
...emptyData,
persons: [makePerson()],
stats: { totalPersons: 1, totalDocuments: 3 }
};
afterEach(cleanup);
@@ -48,6 +61,22 @@ describe('Persons page rendering', () => {
.element(page.getByRole('link', { name: /Max Mustermann/ }))
.toHaveAttribute('href', '/persons/1');
});
it('shows alias in italic when provided', async () => {
render(Page, { data: { ...emptyData, persons: [makePerson({ alias: 'Maxi' })] } });
await expect.element(page.getByText('"Maxi"')).toBeInTheDocument();
});
it('shows life date range when birthYear is provided', async () => {
render(Page, { data: { ...emptyData, persons: [makePerson({ birthYear: 1900 })] } });
await expect.element(page.getByText('* 1900')).toBeInTheDocument();
});
it('shows stats bar with person and document counts', async () => {
render(Page, { data: dataWithPersons });
await expect.element(page.getByText(/1 Person/)).toBeInTheDocument();
await expect.element(page.getByText(/3 Dokumente/)).toBeInTheDocument();
});
});
// ─── Keystroke preservation (issue #34) ──────────────────────────────────────