Files
familienarchiv/frontend/src/lib/components/TrainingHistory.svelte
Marcel 4e08d31e01 feat(admin): add OCR training card to admin/system page
- TrainingHistory.svelte: responsive table with status badges
  (green/red/animated pulse), keyed iteration, empty-state row
- OcrTrainingCard.svelte: shows available blocks/docs, disabled states
  (< 5 blocks, service down), in-flight "…" state, 5s success message,
  embeds TrainingHistory
- Wired into admin/system/+page.svelte via fetchTrainingInfo() in $effect
- Regenerated api.ts with OcrTrainingRun + TrainingInfoResponse types
- TRAINING_ALREADY_RUNNING error code in errors.ts + de/en/es translations
- 7 OcrTrainingCard Vitest tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:58:13 +02:00

77 lines
2.1 KiB
Svelte

<script lang="ts">
interface Run {
id: string;
status: 'RUNNING' | 'DONE' | 'FAILED';
blockCount: number;
documentCount: number;
modelName: string;
errorMessage?: string;
triggeredBy?: string;
createdAt: string;
completedAt?: string;
}
interface Props {
runs: Run[];
}
let { runs }: Props = $props();
const dateFormatter = new Intl.DateTimeFormat('de-DE', {
day: 'numeric',
month: 'short',
year: 'numeric'
});
function formatDate(iso: string): string {
return dateFormatter.format(new Date(iso));
}
</script>
<table class="w-full text-sm">
<thead>
<tr class="border-b border-line text-xs font-bold tracking-widest text-ink-3 uppercase">
<th class="pb-2 text-left">Datum</th>
<th class="pb-2 text-left">Status</th>
<th class="pb-2 text-right">Blöcke</th>
<th class="hidden pb-2 text-right md:table-cell">Dokumente</th>
</tr>
</thead>
<tbody>
{#if runs.length === 0}
<tr>
<td colspan="4" class="py-4 text-center text-sm text-ink-2">
Noch keine Trainings-Läufe.
</td>
</tr>
{:else}
{#each runs as run (run.id)}
<tr class="border-b border-line/50 last:border-0">
<td class="py-2 text-ink-2">{formatDate(run.createdAt)}</td>
<td class="py-2">
{#if run.status === 'DONE'}
<span
class="inline-flex items-center gap-1 rounded-sm bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-700"
>✓ Fertig</span
>
{:else if run.status === 'FAILED'}
<span
class="inline-flex items-center gap-1 rounded-sm bg-red-100 px-1.5 py-0.5 text-xs font-medium text-red-700"
title={run.errorMessage}> Fehler</span
>
{:else}
<span
class="inline-flex items-center gap-1 rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-700"
><span class="h-1.5 w-1.5 animate-pulse rounded-full bg-yellow-500"
></span>Läuft…</span
>
{/if}
</td>
<td class="py-2 text-right text-ink-2">{run.blockCount}</td>
<td class="hidden py-2 text-right text-ink-2 md:table-cell">{run.documentCount}</td>
</tr>
{/each}
{/if}
</tbody>
</table>