feat(admin): OCR admin pages — overview & model detail #265

Merged
marcel merged 53 commits from feat/issue-264-ocr-admin-pages into main 2026-04-18 12:38:42 +02:00
4 changed files with 10 additions and 36 deletions
Showing only changes of commit cea1234400 - Show all commits

View File

@@ -1,23 +1,12 @@
<script lang="ts">
import TrainingHistory from './TrainingHistory.svelte';
import { m } from '$lib/paraglide/messages.js';
interface Run {
id: string;
status: 'RUNNING' | 'DONE' | 'FAILED';
blockCount: number;
documentCount: number;
modelName: string;
errorMessage?: string;
triggeredBy?: string;
createdAt: string;
completedAt?: string;
}
import type { TrainingRun } from '$lib/types/training.js';
interface TrainingInfo {
availableSegBlocks?: number;
ocrServiceAvailable?: boolean;
runs?: Run[];
runs?: TrainingRun[];
}
interface Props {

View File

@@ -3,6 +3,8 @@ import type { PageData } from './$types';
import OcrHealthBar from './OcrHealthBar.svelte';
import OcrStatCards from './OcrStatCards.svelte';
import OcrModelsTable from './OcrModelsTable.svelte';
import OcrTrainingCard from '$lib/components/OcrTrainingCard.svelte';
import SegmentationTrainingCard from '$lib/components/SegmentationTrainingCard.svelte';
import * as m from '$lib/paraglide/messages.js';
let { data }: { data: PageData } = $props();
@@ -27,6 +29,12 @@ const { trainingInfo } = $derived(data);
availableSegBlocks={trainingInfo.availableSegBlocks ?? 0}
/>
<!-- Training -->
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
<OcrTrainingCard trainingInfo={trainingInfo} />
<SegmentationTrainingCard trainingInfo={trainingInfo} />
</div>
<!-- Sender models -->
<div>
<div class="mb-3 flex items-center justify-between">

View File

@@ -1,13 +1,6 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { m } from '$lib/paraglide/messages.js';
import OcrTrainingCard from '$lib/components/OcrTrainingCard.svelte';
import SegmentationTrainingCard from '$lib/components/SegmentationTrainingCard.svelte';
import type { components } from '$lib/generated/api.js';
type TrainingInfo = components['schemas']['TrainingInfoResponse'];
let trainingInfo: TrainingInfo | null = $state(null);
let backfillResult: number | null = $state(null);
let backfillLoading = $state(false);
@@ -58,16 +51,8 @@ async function triggerImport() {
}
}
async function fetchTrainingInfo() {
const res = await fetch('/api/ocr/training-info');
if (res.ok) {
trainingInfo = await res.json();
}
}
$effect(() => {
fetchImportStatus();
fetchTrainingInfo();
});
onDestroy(() => stopPolling());
@@ -103,12 +88,6 @@ async function backfillFileHashes() {
<div class="flex-1 overflow-y-auto p-6">
<div class="mx-auto max-w-2xl space-y-5">
<!-- OCR Recognition Training -->
<OcrTrainingCard trainingInfo={trainingInfo} />
<!-- OCR Segmentation Training -->
<SegmentationTrainingCard trainingInfo={trainingInfo} />
<!-- Backfill versions -->
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
<h2 class="mb-1 font-sans text-sm font-bold text-ink">{m.admin_system_backfill_heading()}</h2>

View File

@@ -78,8 +78,6 @@ describe('Admin system page — mass import card', () => {
startedAt: null
})
})
// training info fetch → empty
.mockResolvedValueOnce({ ok: true, json: async () => ({}) })
// trigger POST → returns RUNNING immediately
.mockResolvedValueOnce({
ok: true,