feat(admin): OCR admin pages — overview & model detail #265
@@ -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 {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user