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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user