fix(frontend): show error on training start failure, add aria-live and dismiss to success message

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-17 20:46:06 +02:00
parent 0c2175aa07
commit 1e289100a1
4 changed files with 25 additions and 1 deletions

View File

@@ -330,6 +330,7 @@
"comment_time_days": "vor {count} Tag(en)",
"comment_panel_title": "Kommentare",
"comment_panel_close": "Schließen",
"comp_dismiss": "Schließen",
"doc_panel_tab_metadata": "Metadaten",
"doc_panel_tab_transcription": "Transkription",
"doc_panel_tab_discussion": "Diskussion",
@@ -541,6 +542,7 @@
"training_start_btn": "Training starten",
"training_in_progress": "…",
"training_success": "Training wurde gestartet und abgeschlossen.",
"training_start_failed": "Training konnte nicht gestartet werden. Bitte versuche es erneut.",
"training_too_few_blocks": "Mindestens 5 geprüfte Blöcke erforderlich (aktuell: {available}).",
"training_service_down": "OCR-Dienst ist nicht erreichbar.",
"training_history_heading": "Verlauf",

View File

@@ -330,6 +330,7 @@
"comment_time_days": "{count} day(s) ago",
"comment_panel_title": "Comments",
"comment_panel_close": "Close",
"comp_dismiss": "Dismiss",
"doc_panel_tab_metadata": "Metadata",
"doc_panel_tab_transcription": "Transcription",
"doc_panel_tab_discussion": "Discussion",
@@ -541,6 +542,7 @@
"training_start_btn": "Start training",
"training_in_progress": "…",
"training_success": "Training started and completed.",
"training_start_failed": "Failed to start training. Please try again.",
"training_too_few_blocks": "At least 5 reviewed blocks required (currently: {available}).",
"training_service_down": "OCR service is unavailable.",
"training_history_heading": "History",

View File

@@ -330,6 +330,7 @@
"comment_time_days": "hace {count} día(s)",
"comment_panel_title": "Comentarios",
"comment_panel_close": "Cerrar",
"comp_dismiss": "Cerrar",
"doc_panel_tab_metadata": "Metadatos",
"doc_panel_tab_transcription": "Transcripción",
"doc_panel_tab_discussion": "Discusión",
@@ -541,6 +542,7 @@
"training_start_btn": "Iniciar entrenamiento",
"training_in_progress": "…",
"training_success": "Entrenamiento iniciado y completado.",
"training_start_failed": "No se pudo iniciar el entrenamiento. Por favor, inténtalo de nuevo.",
"training_too_few_blocks": "Se requieren al menos 5 bloques revisados (actualmente: {available}).",
"training_service_down": "El servicio OCR no está disponible.",
"training_history_heading": "Historial",

View File

@@ -21,6 +21,7 @@ let { trainingInfo }: Props = $props();
let training = $state(false);
let successMessage = $state<string | null>(null);
let errorMessage = $state<string | null>(null);
const available = $derived(trainingInfo?.availableBlocks ?? 0);
const tooFewBlocks = $derived(available < 5);
@@ -30,6 +31,7 @@ const disabled = $derived(training || tooFewBlocks || serviceDown);
async function startTraining() {
training = true;
successMessage = null;
errorMessage = null;
try {
const res = await fetch('/api/ocr/train', { method: 'POST' });
if (res.ok) {
@@ -37,7 +39,11 @@ async function startTraining() {
setTimeout(() => {
successMessage = null;
}, 5000);
} else {
errorMessage = m.training_start_failed();
}
} catch {
errorMessage = m.training_start_failed();
} finally {
training = false;
}
@@ -72,7 +78,19 @@ async function startTraining() {
{/if}
{#if successMessage}
<p class="mt-2 text-xs text-green-700">{successMessage}</p>
<p class="mt-2 flex items-center gap-2 text-xs text-green-700" aria-live="polite">
{successMessage}
<button
type="button"
class="underline hover:no-underline"
onclick={() => (successMessage = null)}
aria-label={m.comp_dismiss()}>×</button
>
</p>
{/if}
{#if errorMessage}
<p class="mt-2 text-xs text-red-600" aria-live="assertive">{errorMessage}</p>
{/if}
<h3 class="mt-6 mb-3 text-xs font-bold tracking-widest text-ink-3 uppercase">