diff --git a/frontend/src/components/ui/TrainingTab.tsx b/frontend/src/components/ui/TrainingTab.tsx index 283a6b1..2c67020 100644 --- a/frontend/src/components/ui/TrainingTab.tsx +++ b/frontend/src/components/ui/TrainingTab.tsx @@ -197,7 +197,7 @@ function TrainingNoticeCard(props: { return (
-
+
{props.notice.message}
@@ -896,7 +896,12 @@ function TrainingOverlay(props: { step: string; progress: number }) { ) } -function LoadingImageOverlay(props: { text?: string }) { +function LoadingImageOverlay(props: { + text?: string + progress?: number +}) { + const progress = clampPercent(props.progress ?? 0) + return (
@@ -905,16 +910,27 @@ function LoadingImageOverlay(props: { text?: string }) {
- Bild wird geladen… + Analyse läuft…
{props.text || 'Frame wird erstellt und analysiert. Bitte warten.'}
+ +
+
+
+ +
+ {Math.round(progress)}% +
) @@ -1868,6 +1884,8 @@ export default function TrainingTab(props: { const [sample, setSample] = useState(null) const [correction, setCorrection] = useState(() => predictionToCorrection(null)) const [loading, setLoading] = useState(false) + const [analysisProgress, setAnalysisProgress] = useState(0) + const [analysisStep, setAnalysisStep] = useState('') const [saving, setSaving] = useState(false) const [training, setTraining] = useState(false) const [trainingStatus, setTrainingStatus] = useState(null) @@ -2012,6 +2030,25 @@ export default function TrainingTab(props: { preserveNotice?: boolean }) => { setLoading(true) + setAnalysisProgress(8) + setAnalysisStep( + opts?.refreshPrediction + ? 'Aktuelles Bild wird neu analysiert…' + : opts?.forceNew + ? 'Neues Trainingsbild wird gesucht…' + : 'Trainingsbild wird geladen…' + ) + + let progressTimer: number | undefined + + progressTimer = window.setInterval(() => { + setAnalysisProgress((value) => { + if (value < 35) return value + 4 + if (value < 65) return value + 2 + if (value < 88) return value + 1 + return value + }) + }, 350) if (!opts?.preserveNotice) { setError(null) @@ -2026,13 +2063,19 @@ export default function TrainingTab(props: { const url = `/api/training/next${params.toString() ? `?${params.toString()}` : ''}` + setAnalysisProgress(25) + setAnalysisStep('Frame wird vorbereitet…') + const res = await fetch(url, { cache: 'no-store' }) const data = await res.json().catch(() => null) if (!res.ok) { throw new Error(data?.error || `HTTP ${res.status}`) } - + + setAnalysisProgress(92) + setAnalysisStep('Analyse-Ergebnis wird übernommen…') + const nextCorrection = predictionToCorrection(data) setDrawingBox(null) @@ -2063,7 +2106,18 @@ export default function TrainingTab(props: { } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { - setLoading(false) + if (progressTimer !== undefined) { + window.clearInterval(progressTimer) + } + + setAnalysisProgress(100) + setAnalysisStep('Analyse abgeschlossen.') + + window.setTimeout(() => { + setLoading(false) + setAnalysisProgress(0) + setAnalysisStep('') + }, 150) } }, []) @@ -2683,6 +2737,24 @@ export default function TrainingTab(props: { return null }, [error, message, trainingRunning, shownTrainingStep]) + useEffect(() => { + if (!message) return + if (error) return + if (trainingRunning) return + + const looksPartial = + message.toLowerCase().includes('übersprungen') || + message.toLowerCase().includes('fehlgeschlagen') + + if (looksPartial) return + + const timer = window.setTimeout(() => { + setMessage(null) + }, 2500) + + return () => window.clearTimeout(timer) + }, [message, error, trainingRunning]) + const detectorBoxesPanel = (
@@ -2824,22 +2896,6 @@ export default function TrainingTab(props: { return ( <> - {activeNotice ? ( -
- { - setError(null) - setMessage(null) - } - } - /> -
- ) : null} -
{/* Sidebar links */}
) : trainingRunning ? ( @@ -3368,7 +3427,10 @@ export default function TrainingTab(props: { progress={shownTrainingProgress} /> ) : loading ? ( - + ) : (
Kein Frame geladen
)} @@ -3411,9 +3473,25 @@ export default function TrainingTab(props: {
-
- Prüfe das Bild. Wenn die Erkennung stimmt: „Passt so“. Wenn nicht: korrigieren und speichern. -
+ {activeNotice ? ( +
+ { + setError(null) + setMessage(null) + } + } + /> +
+ ) : ( +
+ Prüfe das Bild. Wenn die Erkennung stimmt: „Passt so“. Wenn nicht: korrigieren und speichern. +
+ )} {/* Rechte Details/Korrektur */}