'use client' /** * CellCorrectionDialog Component * * Modal dialog for manually correcting OCR text in problematic or recognized cells. * Shows cropped image of the cell for reference and allows text input. */ import { useState, useEffect } from 'react' import type { GridCell } from './GridOverlay' interface CellCorrectionDialogProps { cell: GridCell columnType: 'english' | 'german' | 'example' | 'unknown' sessionId: string pageNumber: number onSave: (text: string) => void onRetryOCR?: () => void onClose: () => void } export function CellCorrectionDialog({ cell, columnType, sessionId, pageNumber, onSave, onRetryOCR, onClose, }: CellCorrectionDialogProps) { const [text, setText] = useState(cell.text || '') const [loading, setLoading] = useState(false) const [retrying, setRetrying] = useState(false) const [cropUrl, setCropUrl] = useState(null) const KLAUSUR_API = '/klausur-api' // Load cell crop image useEffect(() => { const loadCrop = async () => { try { const res = await fetch( `${KLAUSUR_API}/api/v1/vocab/sessions/${sessionId}/cell-crop/${pageNumber}/${cell.row}/${cell.col}` ) if (res.ok) { const blob = await res.blob() setCropUrl(URL.createObjectURL(blob)) } } catch (e) { console.error('Failed to load cell crop:', e) } } loadCrop() return () => { if (cropUrl) { URL.revokeObjectURL(cropUrl) } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [sessionId, pageNumber, cell.row, cell.col]) const handleSave = async () => { if (!text.trim()) return setLoading(true) try { const formData = new FormData() formData.append('text', text) const res = await fetch( `${KLAUSUR_API}/api/v1/vocab/sessions/${sessionId}/cell/${cell.row}/${cell.col}`, { method: 'PUT', body: formData, } ) if (res.ok) { onSave(text) onClose() } else { console.error('Failed to save cell:', await res.text()) } } catch (e) { console.error('Save failed:', e) } finally { setLoading(false) } } const handleRetryOCR = async () => { if (!onRetryOCR) return setRetrying(true) try { await onRetryOCR() } finally { setRetrying(false) } } const getColumnLabel = () => { switch (columnType) { case 'english': return 'Englisch' case 'german': return 'Deutsch' case 'example': return 'Beispielsatz' default: return 'Text' } } const getPlaceholder = () => { switch (columnType) { case 'english': return 'Englisches Wort eingeben...' case 'german': return 'Deutsche Ubersetzung eingeben...' case 'example': return 'Beispielsatz eingeben...' default: return 'Text eingeben...' } } return (
{/* Backdrop */}
{/* Dialog */}
{/* Header */}

{cell.status === 'problematic' ? 'Nicht erkannter Bereich' : 'Text bearbeiten'}

Zeile {cell.row + 1}, Spalte {cell.col + 1} ({getColumnLabel()})

{/* Content */}
{/* Cell image preview */}

Originalbild:

{cropUrl ? ( Zellinhalt ) : (
Lade Vorschau...
)}
{/* Status indicator */} {cell.status === 'problematic' && (
Diese Zelle konnte nicht automatisch erkannt werden.
)} {/* Current recognized text */} {cell.status === 'recognized' && cell.text && (

Erkannter Text:

{cell.text}

{cell.confidence < 1 && (

Konfidenz: {Math.round(cell.confidence * 100)}%

)}
)} {/* Text input */}
{columnType === 'example' ? (