- 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>
77 lines
2.1 KiB
Svelte
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>
|