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,
|
error,
|
||||||
total = 0,
|
total = 0,
|
||||||
q = '',
|
q = '',
|
||||||
sort = 'DATE'
|
sort = 'DATE',
|
||||||
|
from = '',
|
||||||
|
to = ''
|
||||||
}: {
|
}: {
|
||||||
items: DocumentListItem[];
|
items: DocumentListItem[];
|
||||||
canWrite: boolean;
|
canWrite: boolean;
|
||||||
@@ -23,8 +25,15 @@ let {
|
|||||||
total?: number;
|
total?: number;
|
||||||
q?: string;
|
q?: string;
|
||||||
sort?: SortMode;
|
sort?: SortMode;
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
} = $props();
|
} = $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(() => {
|
const groups = $derived.by(() => {
|
||||||
if (sort === 'SENDER') return groupBySender(items);
|
if (sort === 'SENDER') return groupBySender(items);
|
||||||
if (sort === 'RECEIVER') return groupByReceiver(items);
|
if (sort === 'RECEIVER') return groupByReceiver(items);
|
||||||
@@ -119,7 +128,13 @@ function groupByReceiver(docItems: DocumentListItem[]) {
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="font-serif text-lg font-medium text-ink">{m.docs_empty_heading()}</h3>
|
<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">
|
<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>
|
</p>
|
||||||
<button
|
<button
|
||||||
onclick={() => goto('/documents')}
|
onclick={() => goto('/documents')}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ function makeItem(overrides: Partial<DocumentListItem> = {}): DocumentListItem {
|
|||||||
title: 'Testbrief',
|
title: 'Testbrief',
|
||||||
originalFilename: 'testbrief.pdf',
|
originalFilename: 'testbrief.pdf',
|
||||||
documentDate: '2024-03-15',
|
documentDate: '2024-03-15',
|
||||||
|
metaDatePrecision: 'DAY',
|
||||||
sender: undefined,
|
sender: undefined,
|
||||||
receivers: [],
|
receivers: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
@@ -278,3 +279,85 @@ describe('DocumentList – DocumentRow delegation', () => {
|
|||||||
await expect.element(mark).toHaveTextContent('Brief');
|
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}
|
canWrite={data.canWrite}
|
||||||
error={data.error}
|
error={data.error}
|
||||||
sort={sort}
|
sort={sort}
|
||||||
|
from={data.from}
|
||||||
|
to={data.to}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Pagination page={data.pageNumber} totalPages={data.totalPages} makeHref={buildPageHref} />
|
<Pagination page={data.pageNumber} totalPages={data.totalPages} makeHref={buildPageHref} />
|
||||||
|
|||||||
Reference in New Issue
Block a user