'use client' import { useCallback, useEffect, useState } from 'react' import type { OrientationResult, SessionInfo, SubSession } from '@/app/(admin)/ai/ocr-pipeline/types' import { ImageCompareView } from './ImageCompareView' const KLAUSUR_API = '/klausur-api' interface PageSplitResult { multi_page: boolean page_count?: number sub_sessions?: { id: string; name: string; page_index: number }[] used_original?: boolean duration_seconds?: number } interface StepOrientationProps { sessionId?: string | null onNext: (sessionId: string) => void onSubSessionsCreated?: (subs: SubSession[]) => void } export function StepOrientation({ sessionId: existingSessionId, onNext, onSubSessionsCreated }: StepOrientationProps) { const [session, setSession] = useState(null) const [orientationResult, setOrientationResult] = useState(null) const [pageSplitResult, setPageSplitResult] = useState(null) const [uploading, setUploading] = useState(false) const [detecting, setDetecting] = useState(false) const [error, setError] = useState(null) const [dragOver, setDragOver] = useState(false) const [sessionName, setSessionName] = useState('') // Reload session data when navigating back useEffect(() => { if (!existingSessionId || session) return const loadSession = async () => { try { const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${existingSessionId}`) if (!res.ok) return const data = await res.json() const sessionInfo: SessionInfo = { session_id: data.session_id, filename: data.filename, image_width: data.image_width, image_height: data.image_height, original_image_url: `${KLAUSUR_API}${data.original_image_url}`, } setSession(sessionInfo) if (data.orientation_result) { setOrientationResult(data.orientation_result) } } catch (e) { console.error('Failed to reload session:', e) } } loadSession() }, [existingSessionId, session]) const handleUpload = useCallback(async (file: File) => { setUploading(true) setError(null) setOrientationResult(null) try { const formData = new FormData() formData.append('file', file) if (sessionName.trim()) { formData.append('name', sessionName.trim()) } const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions`, { method: 'POST', body: formData, }) if (!res.ok) { const err = await res.json().catch(() => ({ detail: res.statusText })) throw new Error(err.detail || 'Upload fehlgeschlagen') } const data: SessionInfo = await res.json() data.original_image_url = `${KLAUSUR_API}${data.original_image_url}` setSession(data) // Auto-trigger orientation detection setDetecting(true) const orientRes = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${data.session_id}/orientation`, { method: 'POST', }) if (!orientRes.ok) { throw new Error('Orientierungserkennung fehlgeschlagen') } const orientData = await orientRes.json() setOrientationResult({ orientation_degrees: orientData.orientation_degrees, corrected: orientData.corrected, duration_seconds: orientData.duration_seconds, }) // Auto-trigger page-split detection (double-page book spreads) try { const splitRes = await fetch( `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${data.session_id}/page-split`, { method: 'POST' }, ) if (splitRes.ok) { const splitData: PageSplitResult = await splitRes.json() setPageSplitResult(splitData) if (splitData.multi_page && splitData.sub_sessions && onSubSessionsCreated) { onSubSessionsCreated( splitData.sub_sessions.map((s) => ({ id: s.id, name: s.name, box_index: s.page_index, current_step: splitData.used_original ? 1 : 2, })) ) } } } catch (e) { console.error('Page-split detection failed:', e) // Not critical — continue as single page } } catch (e) { setError(e instanceof Error ? e.message : 'Unbekannter Fehler') } finally { setUploading(false) setDetecting(false) } }, [sessionName, onSubSessionsCreated]) const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault() setDragOver(false) const file = e.dataTransfer.files[0] if (file) handleUpload(file) }, [handleUpload]) const handleFileInput = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) handleUpload(file) }, [handleUpload]) // Upload area (no session yet) if (!session) { return (
{/* Session name input */}
setSessionName(e.target.value)} placeholder="z.B. Unit 3 Seite 42" className="w-full max-w-sm px-3 py-2 text-sm border rounded-lg dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 focus:outline-none focus:ring-2 focus:ring-teal-500" />
{ e.preventDefault(); setDragOver(true) }} onDragLeave={() => setDragOver(false)} onDrop={handleDrop} className={`border-2 border-dashed rounded-xl p-12 text-center transition-colors ${ dragOver ? 'border-teal-400 bg-teal-50 dark:bg-teal-900/20' : 'border-gray-300 dark:border-gray-600 hover:border-teal-400' }`} > {uploading ? (

Wird hochgeladen...

) : ( <>
📄

PDF oder Bild hierher ziehen

oder

)}
{error && (
{error}
)}
) } // Session active: show orientation result const orientedUrl = orientationResult ? `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${session.session_id}/image/oriented` : null return (
{/* Filename */}
Datei: {session.filename} {' '}({session.image_width} x {session.image_height} px)
{/* Loading indicator */} {detecting && (
Orientierung wird erkannt...
)} {/* Image comparison */} {/* Orientation result badge */} {orientationResult && (
{orientationResult.corrected ? ( 🔄 {orientationResult.orientation_degrees}° korrigiert ) : ( ✓ 0° (keine Drehung noetig) )} {orientationResult.duration_seconds}s
)} {/* Page-split result */} {pageSplitResult?.multi_page && (
Doppelseite erkannt — {pageSplitResult.page_count} Seiten

Jede Seite wird einzeln durch die Pipeline (Begradigung, Entzerrung, Zuschnitt, ...) verarbeitet. {pageSplitResult.used_original && ' (Seitentrennung auf dem Originalbild, da die Orientierung die Doppelseite gedreht hat.)'}

{pageSplitResult.sub_sessions?.map((s) => ( {s.name} ))}
)} {/* Next button */} {orientationResult && (
)} {error && (
{error}
)}
) }