feat(dashboard): Classic Split — 2-col layout, remove DashboardMentions widget
Some checks failed
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

Restructures the dashboard to a lg:grid-cols-[1fr_300px] split:
- Left column: DashboardRecentDocuments (with stats footnote)
- Right column: DropZone (canWrite) + DashboardNeedsMetadata (flex-1)

Adds showRightColumn guard (canWrite || incompleteDocs.length > 0) so
read-only users with a complete archive never see an empty 300px ghost
column. DashboardMentions is removed from the page; the file is kept.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-31 19:36:36 +02:00
parent f618364632
commit 92e7aa127c
2 changed files with 62 additions and 14 deletions

View File

@@ -6,7 +6,6 @@ import SearchFilterBar from './SearchFilterBar.svelte';
import DropZone from './DropZone.svelte'; import DropZone from './DropZone.svelte';
import DocumentList from './DocumentList.svelte'; import DocumentList from './DocumentList.svelte';
import DashboardResumeStrip from '$lib/components/DashboardResumeStrip.svelte'; import DashboardResumeStrip from '$lib/components/DashboardResumeStrip.svelte';
import DashboardMentions from '$lib/components/DashboardMentions.svelte';
import DashboardNeedsMetadata from '$lib/components/DashboardNeedsMetadata.svelte'; import DashboardNeedsMetadata from '$lib/components/DashboardNeedsMetadata.svelte';
import DashboardRecentDocuments from '$lib/components/DashboardRecentDocuments.svelte'; import DashboardRecentDocuments from '$lib/components/DashboardRecentDocuments.svelte';
import { m } from '$lib/paraglide/messages.js'; import { m } from '$lib/paraglide/messages.js';
@@ -69,6 +68,11 @@ $effect(() => {
tagNames = data.filters?.tags || []; tagNames = data.filters?.tags || [];
if (hasAdvancedFilters(data.filters)) showAdvanced = true; if (hasAdvancedFilters(data.filters)) showAdvanced = true;
}); });
// Right column is only rendered when there is something to show.
// Omitting it prevents an empty 300px ghost column for read-only users
// with a complete archive.
const showRightColumn = $derived(data.canWrite || (data.incompleteDocs?.length ?? 0) > 0);
</script> </script>
<svelte:head> <svelte:head>
@@ -94,20 +98,24 @@ $effect(() => {
{#if data.isDashboard} {#if data.isDashboard}
<DashboardResumeStrip /> <DashboardResumeStrip />
{#if data.canWrite} <!-- Classic Split: left column (recent docs) + right column (upload + metadata queue) -->
<div class="mt-4"> <!-- No items-start — CSS Grid stretch default makes both columns equal height -->
<DropZone /> <div class="mt-4 grid grid-cols-1 gap-4 {showRightColumn ? 'lg:grid-cols-[1fr_300px]' : ''}">
</div> <DashboardRecentDocuments recentDocs={data.recentDocs ?? []} stats={data.stats} />
{/if}
<div {#if showRightColumn}
class="mt-6 grid gap-4 {(data.mentions?.length ?? 0) > 0 && (data.incompleteDocs?.length ?? 0) > 0 ? 'lg:grid-cols-2' : ''}" <div data-testid="dashboard-right-column" class="flex h-full flex-col gap-4">
> {#if data.canWrite}
<DashboardMentions mentions={data.mentions ?? []} /> <DropZone />
<DashboardNeedsMetadata incompleteDocs={data.incompleteDocs ?? []} /> {/if}
</div> <!-- flex-1 + min-h-0 fills remaining height after DropZone.
<div class="mt-4"> min-h-0 overrides the default min-height:auto that prevents flex
<DashboardRecentDocuments recentDocs={data.recentDocs ?? []} /> children from shrinking below their content size. -->
<div class="flex min-h-0 flex-1 flex-col">
<DashboardNeedsMetadata incompleteDocs={data.incompleteDocs ?? []} />
</div>
</div>
{/if}
</div> </div>
{:else} {:else}
<DocumentList documents={data.documents ?? []} canWrite={data.canWrite} error={data.error} /> <DocumentList documents={data.documents ?? []} canWrite={data.canWrite} error={data.error} />

View File

@@ -21,8 +21,12 @@ const emptyData = {
user: undefined, user: undefined,
canWrite: true, canWrite: true,
canAnnotate: false, canAnnotate: false,
isDashboard: false,
filters: { q: '', from: '', to: '', senderId: '', receiverId: '', tags: [] }, filters: { q: '', from: '', to: '', senderId: '', receiverId: '', tags: [] },
documents: [], documents: [],
incompleteDocs: [],
recentDocs: [],
stats: null,
incompleteCount: 0, incompleteCount: 0,
initialValues: { senderName: '', receiverName: '' }, initialValues: { senderName: '', receiverName: '' },
error: null error: null
@@ -189,6 +193,42 @@ describe('Home page search input keystroke preservation', () => {
}); });
}); });
// ─── Dashboard mode ───────────────────────────────────────────────────────────
describe('Home page dashboard mode', () => {
const dashboardData = {
...emptyData,
isDashboard: true,
canWrite: false,
incompleteDocs: [],
recentDocs: []
};
it('hides the right column when canWrite is false and incompleteDocs is empty', async () => {
render(Page, { data: dashboardData });
const rightCol = page.getByTestId('dashboard-right-column');
await expect.element(rightCol).not.toBeInTheDocument();
});
it('shows the right column when canWrite is true', async () => {
render(Page, { data: { ...dashboardData, canWrite: true } });
const rightCol = page.getByTestId('dashboard-right-column');
await expect.element(rightCol).toBeInTheDocument();
});
it('shows the right column when incompleteDocs is non-empty', async () => {
render(Page, {
data: {
...dashboardData,
canWrite: false,
incompleteDocs: [{ id: 'd1', title: 'Taufschein' }]
}
});
const rightCol = page.getByTestId('dashboard-right-column');
await expect.element(rightCol).toBeInTheDocument();
});
});
// ─── Error state ────────────────────────────────────────────────────────────── // ─── Error state ──────────────────────────────────────────────────────────────
describe('Home page error state', () => { describe('Home page error state', () => {