feat(documents): explain that a date range excludes undated documents
DocumentList gains from/to props; when a date range is active and yields no results, the empty state shows the localized docs_range_excludes_undated note instead of the generic copy, so the reader understands undated letters aren't part of a range. Person-grouped modes keep undated letters under their sender/receiver (badge-on-row, no synthetic sub-group). Refs #668
This commit is contained in:
@@ -15,7 +15,9 @@ let {
|
||||
error,
|
||||
total = 0,
|
||||
q = '',
|
||||
sort = 'DATE'
|
||||
sort = 'DATE',
|
||||
from = '',
|
||||
to = ''
|
||||
}: {
|
||||
items: DocumentListItem[];
|
||||
canWrite: boolean;
|
||||
@@ -23,8 +25,15 @@ let {
|
||||
total?: number;
|
||||
q?: string;
|
||||
sort?: SortMode;
|
||||
from?: string;
|
||||
to?: string;
|
||||
} = $props();
|
||||
|
||||
// A from/to range excludes undated documents — when it yields nothing, the
|
||||
// empty state must say so explicitly (a localized constant, never a reflected
|
||||
// backend string). Issue #668.
|
||||
const hasDateRange = $derived(!!from || !!to);
|
||||
|
||||
const groups = $derived.by(() => {
|
||||
if (sort === 'SENDER') return groupBySender(items);
|
||||
if (sort === 'RECEIVER') return groupByReceiver(items);
|
||||
@@ -119,7 +128,13 @@ function groupByReceiver(docItems: DocumentListItem[]) {
|
||||
</div>
|
||||
<h3 class="font-serif text-lg font-medium text-ink">{m.docs_empty_heading()}</h3>
|
||||
<p class="mt-1 font-sans text-sm text-ink-2">
|
||||
{q ? m.docs_empty_for_term({ term: q }) : m.docs_empty_text()}
|
||||
{#if hasDateRange}
|
||||
{m.docs_range_excludes_undated()}
|
||||
{:else if q}
|
||||
{m.docs_empty_for_term({ term: q })}
|
||||
{:else}
|
||||
{m.docs_empty_text()}
|
||||
{/if}
|
||||
</p>
|
||||
<button
|
||||
onclick={() => goto('/documents')}
|
||||
|
||||
@@ -16,6 +16,7 @@ function makeItem(overrides: Partial<DocumentListItem> = {}): DocumentListItem {
|
||||
title: 'Testbrief',
|
||||
originalFilename: 'testbrief.pdf',
|
||||
documentDate: '2024-03-15',
|
||||
metaDatePrecision: 'DAY',
|
||||
sender: undefined,
|
||||
receivers: [],
|
||||
tags: [],
|
||||
@@ -278,3 +279,85 @@ describe('DocumentList – DocumentRow delegation', () => {
|
||||
await expect.element(mark).toHaveTextContent('Brief');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Undated badge in person-grouped modes (#668) ────────────────────────────
|
||||
|
||||
describe('DocumentList – undated badge in person grouping', () => {
|
||||
const sender = {
|
||||
id: 's1',
|
||||
lastName: 'Mustermann',
|
||||
displayName: 'Max Mustermann',
|
||||
personType: 'PERSON' as const,
|
||||
familyMember: false,
|
||||
provisional: false
|
||||
};
|
||||
const receiver = {
|
||||
id: 'r1',
|
||||
lastName: 'Brandt',
|
||||
displayName: 'Felix Brandt',
|
||||
personType: 'PERSON' as const,
|
||||
familyMember: false,
|
||||
provisional: false
|
||||
};
|
||||
|
||||
it('shows the undated badge on a row under SENDER grouping', async () => {
|
||||
const items = [
|
||||
makeItem({ id: '1', documentDate: undefined, metaDatePrecision: 'UNKNOWN', sender })
|
||||
];
|
||||
render(DocumentList, { ...baseProps, items, total: 1, sort: 'SENDER' });
|
||||
await expect.element(page.getByTestId('undated-badge').first()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the undated badge on a row under RECEIVER grouping', async () => {
|
||||
const items = [
|
||||
makeItem({
|
||||
id: '1',
|
||||
documentDate: undefined,
|
||||
metaDatePrecision: 'UNKNOWN',
|
||||
receivers: [receiver]
|
||||
})
|
||||
];
|
||||
render(DocumentList, { ...baseProps, items, total: 1, sort: 'RECEIVER' });
|
||||
await expect.element(page.getByTestId('undated-badge').first()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps an undated letter under its sender, not in a synthetic undated sub-group', async () => {
|
||||
const items = [
|
||||
makeItem({ id: '1', documentDate: '1916-06-15', metaDatePrecision: 'DAY', sender }),
|
||||
makeItem({ id: '2', documentDate: undefined, metaDatePrecision: 'UNKNOWN', sender })
|
||||
];
|
||||
render(DocumentList, { ...baseProps, items, total: 2, sort: 'SENDER' });
|
||||
// One sender card; no "Undatiert" group header inside person-grouped mode.
|
||||
await expect
|
||||
.element(page.getByTestId('group-header').filter({ hasText: 'Max Mustermann' }))
|
||||
.toBeInTheDocument();
|
||||
await expect.element(page.getByText('Undatiert')).not.toBeInTheDocument();
|
||||
const cards = page.getByTestId('group-card');
|
||||
await expect.element(cards.nth(1)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Date-range / undated empty state (#668) ─────────────────────────────────
|
||||
|
||||
describe('DocumentList – range-excludes-undated empty state', () => {
|
||||
it('explains that a date range excludes undated documents when from/to active and no results', async () => {
|
||||
render(DocumentList, {
|
||||
...baseProps,
|
||||
items: [],
|
||||
total: 0,
|
||||
from: '1920-01-01',
|
||||
to: '1930-12-31'
|
||||
});
|
||||
await expect
|
||||
.element(page.getByText(/Datumsfilter schließt undatierte Dokumente aus/))
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the generic empty state when no date range is active', async () => {
|
||||
render(DocumentList, { ...baseProps, items: [], total: 0 });
|
||||
await expect.element(page.getByText(/Keine Dokumente/)).toBeInTheDocument();
|
||||
await expect
|
||||
.element(page.getByText(/Datumsfilter schließt undatierte Dokumente aus/))
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -343,6 +343,8 @@ $effect(() => {
|
||||
canWrite={data.canWrite}
|
||||
error={data.error}
|
||||
sort={sort}
|
||||
from={data.from}
|
||||
to={data.to}
|
||||
/>
|
||||
|
||||
<Pagination page={data.pageNumber} totalPages={data.totalPages} makeHref={buildPageHref} />
|
||||
|
||||
Reference in New Issue
Block a user