'use client' /** * OCR Import Panel * * Loads OCR export data from the klausur-service API (shared between admin-v2 and studio-v2) * and imports recognized words as editable text objects onto the Fabric.js canvas. */ import { useState, useEffect, useCallback } from 'react' import { useTheme } from '@/lib/ThemeContext' import { useWorksheet } from '@/lib/worksheet-editor/WorksheetContext' import type { OCRExportData, OCRWord } from '@/lib/worksheet-editor/ocr-integration' import { createTextProps, getColumnColor } from '@/lib/worksheet-editor/ocr-integration' interface OCRImportPanelProps { isOpen: boolean onClose: () => void } export function OCRImportPanel({ isOpen, onClose }: OCRImportPanelProps) { const { isDark } = useTheme() const { canvas, saveToHistory } = useWorksheet() const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [ocrData, setOcrData] = useState(null) const [selectedWords, setSelectedWords] = useState>(new Set()) const [importing, setImporting] = useState(false) const [importSuccess, setImportSuccess] = useState(false) // Load OCR data when panel opens useEffect(() => { if (!isOpen) return loadOCRData() }, [isOpen]) const loadOCRData = useCallback(async () => { setLoading(true) setError(null) setOcrData(null) setSelectedWords(new Set()) setImportSuccess(false) try { const res = await fetch('/klausur-api/api/v1/vocab/ocr-export/latest') if (!res.ok) { throw new Error('not_found') } const data: OCRExportData = await res.json() setOcrData(data) // Select all words by default setSelectedWords(new Set(data.words.map((_, i) => i))) } catch { setError( 'Keine OCR-Daten gefunden. Bitte zuerst im OCR-Compare Tool "Zum Editor exportieren" klicken.' ) } finally { setLoading(false) } }, []) const toggleWord = useCallback((index: number) => { setSelectedWords(prev => { const next = new Set(prev) if (next.has(index)) { next.delete(index) } else { next.add(index) } return next }) }, []) const selectAll = useCallback(() => { if (!ocrData) return setSelectedWords(new Set(ocrData.words.map((_, i) => i))) }, [ocrData]) const deselectAll = useCallback(() => { setSelectedWords(new Set()) }, []) const handleImport = useCallback(async () => { if (!ocrData || !canvas || selectedWords.size === 0) return setImporting(true) try { // Dynamic import of fabric to avoid SSR issues const { IText } = await import('fabric') // Save current state for undo if (saveToHistory) { saveToHistory('OCR Import') } const wordsToImport = ocrData.words.filter((_, i) => selectedWords.has(i)) for (const word of wordsToImport) { const props = createTextProps(word) const textObj = new IText(props.text, { left: props.left, top: props.top, fontSize: props.fontSize, fontFamily: props.fontFamily, fill: props.fill, editable: true, }) canvas.add(textObj) } canvas.renderAll() setImportSuccess(true) // Close after brief delay setTimeout(() => { onClose() }, 1500) } catch (e) { console.error('Import failed:', e) setError('Import fehlgeschlagen. Bitte erneut versuchen.') } finally { setImporting(false) } }, [ocrData, canvas, selectedWords, saveToHistory, onClose]) if (!isOpen) return null // Count column types const columnSummary = ocrData ? ocrData.words.reduce>((acc, w) => { acc[w.column_type] = (acc[w.column_type] || 0) + 1 return acc }, {}) : {} return (
{/* Backdrop */}
{/* Panel */}
{/* Header */}

OCR Daten importieren

Erkannte Texte aus der Grid-Analyse einfuegen

{/* Content */}
{/* Loading */} {loading && (

Lade OCR-Daten...

)} {/* Error */} {error && (

{error}

)} {/* Success State */} {importSuccess && (

{selectedWords.size} Texte erfolgreich importiert!

)} {/* OCR Data Display */} {ocrData && !importSuccess && ( <> {/* Summary */}
{ocrData.words.length} Woerter {Object.entries(columnSummary).map(([type, count]) => ( {type === 'english' ? 'Englisch' : type === 'german' ? 'Deutsch' : type === 'example' ? 'Beispiel' : 'Unbekannt'} : {count} ))}

Session: {ocrData.session_id.slice(0, 8)}... | Seite{' '} {ocrData.page_number} | Exportiert:{' '} {new Date(ocrData.exported_at).toLocaleString('de-DE')}

{/* Selection Controls */}
{selectedWords.size} von {ocrData.words.length} ausgewaehlt
{/* Word List */}
{ocrData.words.map((word, idx) => ( ))}
)}
{/* Footer */} {ocrData && !importSuccess && (
)}
) }