feat(dashboard): wire EnrichmentBlock between Resume strip and Mission Control
Dashboard loader fetches /api/documents/incomplete?size=5 plus the existing /incomplete-count and surfaces both via data; +page.svelte renders EnrichmentBlock with the top 5 docs, the total count, and the bannerCount state bound to DropZone's onUploadComplete callback (issue #296). The block returns null when there is nothing to show, so dashboards without pending uploads stay uncluttered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ type TranscriptionWeeklyStatsDTO = components['schemas']['TranscriptionWeeklySta
|
|||||||
type DashboardResumeDTO = components['schemas']['DashboardResumeDTO'];
|
type DashboardResumeDTO = components['schemas']['DashboardResumeDTO'];
|
||||||
type DashboardPulseDTO = components['schemas']['DashboardPulseDTO'];
|
type DashboardPulseDTO = components['schemas']['DashboardPulseDTO'];
|
||||||
type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO'];
|
type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO'];
|
||||||
|
type IncompleteDocumentDTO = components['schemas']['IncompleteDocumentDTO'];
|
||||||
|
|
||||||
export async function load({ fetch }) {
|
export async function load({ fetch }) {
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
@@ -27,7 +28,9 @@ export async function load({ fetch }) {
|
|||||||
segmentationResult,
|
segmentationResult,
|
||||||
transcriptionResult,
|
transcriptionResult,
|
||||||
readyResult,
|
readyResult,
|
||||||
weeklyStatsResult
|
weeklyStatsResult,
|
||||||
|
incompleteResult,
|
||||||
|
incompleteCountResult
|
||||||
] = await Promise.allSettled([
|
] = await Promise.allSettled([
|
||||||
api.GET('/api/stats'),
|
api.GET('/api/stats'),
|
||||||
api.GET('/api/dashboard/resume'),
|
api.GET('/api/dashboard/resume'),
|
||||||
@@ -36,7 +39,9 @@ export async function load({ fetch }) {
|
|||||||
api.GET('/api/transcription/segmentation-queue'),
|
api.GET('/api/transcription/segmentation-queue'),
|
||||||
api.GET('/api/transcription/transcription-queue'),
|
api.GET('/api/transcription/transcription-queue'),
|
||||||
api.GET('/api/transcription/ready-to-read'),
|
api.GET('/api/transcription/ready-to-read'),
|
||||||
api.GET('/api/transcription/weekly-stats')
|
api.GET('/api/transcription/weekly-stats'),
|
||||||
|
api.GET('/api/documents/incomplete', { params: { query: { size: 5 } } }),
|
||||||
|
api.GET('/api/documents/incomplete-count')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let stats: StatsDTO | null = null;
|
let stats: StatsDTO | null = null;
|
||||||
@@ -47,6 +52,8 @@ export async function load({ fetch }) {
|
|||||||
let transcriptionDocs: TranscriptionQueueItemDTO[] = [];
|
let transcriptionDocs: TranscriptionQueueItemDTO[] = [];
|
||||||
let readyDocs: TranscriptionQueueItemDTO[] = [];
|
let readyDocs: TranscriptionQueueItemDTO[] = [];
|
||||||
let weeklyStats: TranscriptionWeeklyStatsDTO | null = null;
|
let weeklyStats: TranscriptionWeeklyStatsDTO | null = null;
|
||||||
|
let incompleteDocs: IncompleteDocumentDTO[] = [];
|
||||||
|
let incompleteTotal = 0;
|
||||||
|
|
||||||
if (statsResult.status === 'fulfilled' && statsResult.value.response.ok) {
|
if (statsResult.status === 'fulfilled' && statsResult.value.response.ok) {
|
||||||
stats = statsResult.value.data ?? null;
|
stats = statsResult.value.data ?? null;
|
||||||
@@ -72,6 +79,12 @@ export async function load({ fetch }) {
|
|||||||
if (weeklyStatsResult.status === 'fulfilled' && weeklyStatsResult.value.response.ok) {
|
if (weeklyStatsResult.status === 'fulfilled' && weeklyStatsResult.value.response.ok) {
|
||||||
weeklyStats = weeklyStatsResult.value.data ?? null;
|
weeklyStats = weeklyStatsResult.value.data ?? null;
|
||||||
}
|
}
|
||||||
|
if (incompleteResult.status === 'fulfilled' && incompleteResult.value.response.ok) {
|
||||||
|
incompleteDocs = (incompleteResult.value.data ?? []) as IncompleteDocumentDTO[];
|
||||||
|
}
|
||||||
|
if (incompleteCountResult.status === 'fulfilled' && incompleteCountResult.value.response.ok) {
|
||||||
|
incompleteTotal = (incompleteCountResult.value.data?.count as number | undefined) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stats,
|
stats,
|
||||||
@@ -82,6 +95,8 @@ export async function load({ fetch }) {
|
|||||||
transcriptionDocs,
|
transcriptionDocs,
|
||||||
readyDocs,
|
readyDocs,
|
||||||
weeklyStats,
|
weeklyStats,
|
||||||
|
incompleteDocs,
|
||||||
|
incompleteTotal,
|
||||||
error: null as string | null
|
error: null as string | null
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -96,6 +111,8 @@ export async function load({ fetch }) {
|
|||||||
transcriptionDocs: [],
|
transcriptionDocs: [],
|
||||||
readyDocs: [],
|
readyDocs: [],
|
||||||
weeklyStats: null,
|
weeklyStats: null,
|
||||||
|
incompleteDocs: [] as IncompleteDocumentDTO[],
|
||||||
|
incompleteTotal: 0,
|
||||||
error: 'Daten konnten nicht geladen werden.' as string | null
|
error: 'Daten konnten nicht geladen werden.' as string | null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import DashboardResumeStrip from '$lib/components/DashboardResumeStrip.svelte';
|
|||||||
import MissionControlStrip from '$lib/components/MissionControlStrip.svelte';
|
import MissionControlStrip from '$lib/components/MissionControlStrip.svelte';
|
||||||
import DashboardFamilyPulse from '$lib/components/DashboardFamilyPulse.svelte';
|
import DashboardFamilyPulse from '$lib/components/DashboardFamilyPulse.svelte';
|
||||||
import DashboardActivityFeed from '$lib/components/DashboardActivityFeed.svelte';
|
import DashboardActivityFeed from '$lib/components/DashboardActivityFeed.svelte';
|
||||||
|
import EnrichmentBlock from '$lib/components/EnrichmentBlock.svelte';
|
||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
let bannerCount = $state(0);
|
||||||
|
|
||||||
const greetingText = $derived.by(() => {
|
const greetingText = $derived.by(() => {
|
||||||
const name = data?.user?.firstName ?? '';
|
const name = data?.user?.firstName ?? '';
|
||||||
const h = new Date().getHours();
|
const h = new Date().getHours();
|
||||||
@@ -32,6 +35,13 @@ const greetingText = $derived.by(() => {
|
|||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
<DashboardResumeStrip resumeDoc={data.resumeDoc ?? null} />
|
<DashboardResumeStrip resumeDoc={data.resumeDoc ?? null} />
|
||||||
|
|
||||||
|
<EnrichmentBlock
|
||||||
|
topDocs={data.incompleteDocs ?? []}
|
||||||
|
totalCount={data.incompleteTotal ?? 0}
|
||||||
|
bannerCount={bannerCount}
|
||||||
|
onBannerClose={() => (bannerCount = 0)}
|
||||||
|
/>
|
||||||
|
|
||||||
<section aria-label={m.dashboard_mission_caption()}>
|
<section aria-label={m.dashboard_mission_caption()}>
|
||||||
<h2 class="mb-3 font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
|
<h2 class="mb-3 font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
|
||||||
{m.dashboard_mission_caption()}
|
{m.dashboard_mission_caption()}
|
||||||
@@ -49,7 +59,7 @@ const greetingText = $derived.by(() => {
|
|||||||
<DashboardFamilyPulse pulse={data.pulse ?? null} />
|
<DashboardFamilyPulse pulse={data.pulse ?? null} />
|
||||||
<DashboardActivityFeed feed={data.activityFeed ?? []} />
|
<DashboardActivityFeed feed={data.activityFeed ?? []} />
|
||||||
{#if data.canWrite}
|
{#if data.canWrite}
|
||||||
<DropZone />
|
<DropZone onUploadComplete={(count) => (bannerCount = count)} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user