feat(#145): update home page server load for dashboard mode

- Add isDashboard flag (true when no search filters active)
- In dashboard mode: fetch mentions, incompleteDocs, recentDocs via
  Promise.allSettled so widget failures don't crash the page
- In search mode: skip widget fetches for performance
- Replace incomplete-count fetch with list fetch (derive count from
  list.length)
- Update enrich page to use IncompleteDocumentDTO (id + title only)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-29 00:32:52 +01:00
parent 4aa3855936
commit 0610f0ee0f
2 changed files with 63 additions and 34 deletions

View File

@@ -1,5 +1,10 @@
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
import { createApiClient } from '$lib/api.server'; import { createApiClient } from '$lib/api.server';
import type { components } from '$lib/generated/api';
type IncompleteDocumentDTO = components['schemas']['IncompleteDocumentDTO'];
type NotificationDTO = components['schemas']['NotificationDTO'];
type Document = components['schemas']['Document'];
export async function load({ url, fetch }) { export async function load({ url, fetch }) {
const q = url.searchParams.get('q') || ''; const q = url.searchParams.get('q') || '';
@@ -9,44 +14,76 @@ export async function load({ url, fetch }) {
const receiverId = url.searchParams.get('receiverId') || ''; const receiverId = url.searchParams.get('receiverId') || '';
const tags = url.searchParams.getAll('tag'); const tags = url.searchParams.getAll('tag');
const isDashboard = !q && !from && !to && !senderId && !receiverId && !tags.length;
const api = createApiClient(fetch); const api = createApiClient(fetch);
try { try {
const [docsResult, personsResult, incompleteCountResult] = await Promise.all([ const [docsResult, personsResult] = await Promise.all([
api.GET('/api/documents/search', { isDashboard
params: { ? Promise.resolve(null)
query: { : api.GET('/api/documents/search', {
q: q || undefined, params: {
from: from || undefined, query: {
to: to || undefined, q: q || undefined,
senderId: senderId || undefined, from: from || undefined,
receiverId: receiverId || undefined, to: to || undefined,
tag: tags.length ? tags : undefined senderId: senderId || undefined,
} receiverId: receiverId || undefined,
} tag: tags.length ? tags : undefined
}), }
api.GET('/api/persons'), }
api.GET('/api/documents/incomplete-count') }),
api.GET('/api/persons')
]); ]);
if (docsResult.response.status === 401 || personsResult.response.status === 401) { if (personsResult.response.status === 401) {
throw redirect(302, '/login');
}
if (docsResult && docsResult.response.status === 401) {
throw redirect(302, '/login'); throw redirect(302, '/login');
} }
const documents = docsResult.data ?? []; const documents: Document[] = docsResult?.data ?? [];
const allPersons: { id: string; firstName: string; lastName: string }[] = const allPersons: { id: string; firstName: string; lastName: string }[] =
personsResult.data ?? []; personsResult.data ?? [];
const senderObj = allPersons.find((p) => p.id === senderId); const senderObj = allPersons.find((p) => p.id === senderId);
const receiverObj = allPersons.find((p) => p.id === receiverId); const receiverObj = allPersons.find((p) => p.id === receiverId);
const incompleteCount = incompleteCountResult.response.ok // Dashboard widgets — failures are isolated and don't crash the page
? (incompleteCountResult.data?.count ?? 0) let mentions: NotificationDTO[] = [];
: 0; let incompleteDocs: IncompleteDocumentDTO[] = [];
let recentDocs: Document[] = [];
if (isDashboard) {
const [mentionsResult, incompleteResult, recentResult] = await Promise.allSettled([
api.GET('/api/notifications', {
params: { query: { type: 'MENTION', read: false, size: 5 } }
}),
api.GET('/api/documents/incomplete', { params: { query: { size: 5 } } }),
api.GET('/api/documents/search', {
params: { query: { status: 'REVIEWED' } }
})
]);
if (mentionsResult.status === 'fulfilled' && mentionsResult.value.response.ok) {
mentions = mentionsResult.value.data?.content ?? [];
}
if (incompleteResult.status === 'fulfilled' && incompleteResult.value.response.ok) {
incompleteDocs = incompleteResult.value.data ?? [];
}
if (recentResult.status === 'fulfilled' && recentResult.value.response.ok) {
recentDocs = (recentResult.value.data ?? []).slice(0, 5);
}
}
return { return {
isDashboard,
documents, documents,
incompleteCount, mentions,
incompleteDocs,
recentDocs,
initialValues: { initialValues: {
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '', senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.lastName}` : '' receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.lastName}` : ''
@@ -58,8 +95,11 @@ export async function load({ url, fetch }) {
if ((e as { status?: number }).status) throw e; if ((e as { status?: number }).status) throw e;
console.error('Error loading data:', e); console.error('Error loading data:', e);
return { return {
isDashboard,
documents: [], documents: [],
incompleteCount: 0, mentions: [],
incompleteDocs: [],
recentDocs: [],
initialValues: { senderName: '', receiverName: '' }, initialValues: { senderName: '', receiverName: '' },
filters: { q, from, to, senderId, receiverId, tags }, filters: { q, from, to, senderId, receiverId, tags },
error: 'Daten konnten nicht geladen werden.' as string | null error: 'Daten konnten nicht geladen werden.' as string | null

View File

@@ -5,14 +5,6 @@ let { data } = $props();
const documents = $derived(data.documents); const documents = $derived(data.documents);
const count = $derived(documents.length); const count = $derived(documents.length);
function formatUploadDate(createdAt: string): string {
return new Intl.DateTimeFormat('de-DE', {
day: 'numeric',
month: 'long',
year: 'numeric'
}).format(new Date(createdAt));
}
</script> </script>
<div class="mx-auto max-w-4xl px-4 py-10"> <div class="mx-auto max-w-4xl px-4 py-10">
@@ -85,10 +77,7 @@ function formatUploadDate(createdAt: string): string {
<p <p
class="font-serif text-lg font-medium text-ink decoration-accent decoration-2 underline-offset-4 group-hover:underline" class="font-serif text-lg font-medium text-ink decoration-accent decoration-2 underline-offset-4 group-hover:underline"
> >
{doc.title || doc.originalFilename} {doc.title}
</p>
<p class="mt-1 font-sans text-xs text-ink-3">
{formatUploadDate(doc.createdAt)}
</p> </p>
</div> </div>
<img <img