'use client' import { useCallback, useEffect, useState } from 'react' import type { DeskewResult, DewarpResult, DewarpGroundTruth } from '@/app/(admin)/ai/ocr-pipeline/types' import { DewarpControls } from './DewarpControls' import { ImageCompareView } from './ImageCompareView' const KLAUSUR_API = '/klausur-api' interface StepDewarpProps { sessionId: string | null onNext: () => void } export function StepDewarp({ sessionId, onNext }: StepDewarpProps) { const [dewarpResult, setDewarpResult] = useState(null) const [deskewResult, setDeskewResult] = useState(null) const [dewarping, setDewarping] = useState(false) const [applying, setApplying] = useState(false) const [showGrid, setShowGrid] = useState(true) const [error, setError] = useState(null) // Load session info to get deskew_result (for fine-tuning init values) useEffect(() => { if (!sessionId) return const loadSession = async () => { try { const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}`) if (res.ok) { const data = await res.json() if (data.deskew_result) { setDeskewResult(data.deskew_result) } } } catch (e) { console.error('Failed to load session info:', e) } } loadSession() }, [sessionId]) // Auto-trigger dewarp when component mounts with a sessionId useEffect(() => { if (!sessionId || dewarpResult) return const runDewarp = async () => { setDewarping(true) setError(null) try { const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/dewarp`, { method: 'POST', }) if (!res.ok) { const err = await res.json().catch(() => ({ detail: res.statusText })) throw new Error(err.detail || 'Entzerrung fehlgeschlagen') } const data: DewarpResult = await res.json() data.dewarped_image_url = `${KLAUSUR_API}${data.dewarped_image_url}` setDewarpResult(data) } catch (e) { setError(e instanceof Error ? e.message : 'Unbekannter Fehler') } finally { setDewarping(false) } } runDewarp() }, [sessionId, dewarpResult]) const handleManualDewarp = useCallback(async (shearDegrees: number) => { if (!sessionId) return setApplying(true) setError(null) try { const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/dewarp/manual`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ shear_degrees: shearDegrees }), }) if (!res.ok) throw new Error('Manuelle Entzerrung fehlgeschlagen') const data = await res.json() setDewarpResult((prev) => prev ? { ...prev, method_used: data.method_used, shear_degrees: data.shear_degrees, dewarped_image_url: `${KLAUSUR_API}${data.dewarped_image_url}?t=${Date.now()}`, } : null, ) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler') } finally { setApplying(false) } }, [sessionId]) const handleCombinedAdjust = useCallback(async (rotationDegrees: number, shearDegrees: number) => { if (!sessionId) return setApplying(true) setError(null) try { const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/adjust-combined`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rotation_degrees: rotationDegrees, shear_degrees: shearDegrees }), }) if (!res.ok) throw new Error('Kombinierte Anpassung fehlgeschlagen') const data = await res.json() setDewarpResult((prev) => prev ? { ...prev, method_used: data.method_used, shear_degrees: data.shear_degrees, dewarped_image_url: `${KLAUSUR_API}${data.dewarped_image_url}?t=${Date.now()}`, } : null, ) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler') } finally { setApplying(false) } }, [sessionId]) const handleGroundTruth = useCallback(async (gt: DewarpGroundTruth) => { if (!sessionId) return try { await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/ground-truth/dewarp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(gt), }) } catch (e) { console.error('Ground truth save failed:', e) } }, [sessionId]) if (!sessionId) { return (
🔧

Schritt 2: Entzerrung (Dewarp)

Bitte zuerst Schritt 1 (Begradigung) abschliessen.

) } const deskewedUrl = `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/deskewed` const dewarpedUrl = dewarpResult?.dewarped_image_url ?? null return (
{/* Loading indicator */} {dewarping && (
Entzerrung laeuft (beide Methoden)...
)} {/* Image comparison: deskewed (left) vs dewarped (right) */} {/* Controls */} setShowGrid((v) => !v)} onManualDewarp={handleManualDewarp} onCombinedAdjust={handleCombinedAdjust} onGroundTruth={handleGroundTruth} onNext={onNext} isApplying={applying} /> {error && (
{error}
)}
) }