'use client' import { useState, useEffect, useRef } from 'react' const KLAUSUR_API = '/klausur-api' interface PageSplitResult { multi_page: boolean page_count?: number page_splits?: { x: number; y: number; width: number; height: number; page_index: number }[] sub_sessions?: { id: string; name: string; page_index: number }[] used_original?: boolean duration_seconds?: number } interface StepPageSplitProps { sessionId: string | null sessionName: string onNext: () => void onSplitComplete: (firstChildId: string, firstChildName: string) => void } export function StepPageSplit({ sessionId, sessionName, onNext, onSplitComplete }: StepPageSplitProps) { const [detecting, setDetecting] = useState(false) const [splitResult, setSplitResult] = useState(null) const [error, setError] = useState('') const didDetect = useRef(false) // Auto-detect page split when step opens useEffect(() => { if (!sessionId || didDetect.current) return didDetect.current = true detectPageSplit() // eslint-disable-next-line react-hooks/exhaustive-deps }, [sessionId]) const detectPageSplit = async () => { if (!sessionId) return setDetecting(true) setError('') try { // First check if this session was already split (status='split') const sessionRes = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}`) if (sessionRes.ok) { const sessionData = await sessionRes.json() if (sessionData.status === 'split' && sessionData.crop_result?.multi_page) { // Already split — find the child sessions in the session list const listRes = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions`) if (listRes.ok) { const listData = await listRes.json() // Child sessions have names like "ParentName — Seite N" const baseName = sessionName || sessionData.name || '' const children = (listData.sessions || []) .filter((s: { name?: string }) => s.name?.startsWith(baseName + ' — ')) .sort((a: { name: string }, b: { name: string }) => a.name.localeCompare(b.name)) if (children.length > 0) { setSplitResult({ multi_page: true, page_count: children.length, sub_sessions: children.map((s: { id: string; name: string }, i: number) => ({ id: s.id, name: s.name, page_index: i, })), }) onSplitComplete(children[0].id, children[0].name) setDetecting(false) return } } } } // Run page-split detection const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/page-split`, { method: 'POST', }) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.detail || 'Seitentrennung fehlgeschlagen') } const data: PageSplitResult = await res.json() setSplitResult(data) if (data.multi_page && data.sub_sessions?.length) { // Rename sub-sessions to "Title — S. 1", "Title — S. 2" const baseName = sessionName || 'Dokument' for (let i = 0; i < data.sub_sessions.length; i++) { const sub = data.sub_sessions[i] const newName = `${baseName} — S. ${i + 1}` await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sub.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newName }), }).catch(() => {}) sub.name = newName } // Signal parent to switch to the first child session onSplitComplete(data.sub_sessions[0].id, data.sub_sessions[0].name) } } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { setDetecting(false) } } if (!sessionId) return null const imageUrl = `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/oriented` return (
{/* Image */}
{/* eslint-disable-next-line @next/next/no-img-element */} Orientiertes Bild { // Fallback to non-oriented image (e.target as HTMLImageElement).src = `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image` }} />
{/* Detection status */} {detecting && (
Doppelseiten-Erkennung laeuft...
)} {/* Detection result */} {splitResult && !detecting && ( splitResult.multi_page ? (
Doppelseite erkannt — {splitResult.page_count} Seiten getrennt

Jede Seite wird als eigene Session weiterverarbeitet (eigene Begradigung, Entzerrung, etc.). {splitResult.used_original && ' Trennung auf Originalbild, da Orientierung die Doppelseite gedreht hat.'}

{splitResult.sub_sessions?.map(s => ( {s.name} ))}
{splitResult.duration_seconds != null && (
{splitResult.duration_seconds.toFixed(1)}s
)}
) : (
Einzelseite — keine Trennung noetig
{splitResult.duration_seconds != null && (
{splitResult.duration_seconds.toFixed(1)}s
)}
) )} {/* Error */} {error && (
{error}
)} {/* Next button — only show when detection is done */} {(splitResult || error) && !detecting && (
)}
) }