'use client' import { useCallback, useEffect, useState } from 'react' import { useGridEditor } from '@/components/grid-editor/useGridEditor' import type { GridZone } from '@/components/grid-editor/types' import { GridTable } from '@/components/grid-editor/GridTable' const KLAUSUR_API = '/klausur-api' type BoxLayoutType = 'flowing' | 'columnar' | 'bullet_list' | 'header_only' const LAYOUT_LABELS: Record = { flowing: 'Fließtext', columnar: 'Tabelle/Spalten', bullet_list: 'Aufzählung', header_only: 'Überschrift', } interface StepBoxGridReviewProps { sessionId: string | null onNext: () => void } export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps) { const { grid, loading, saving, error, dirty, selectedCell, setSelectedCell, loadGrid, saveGrid, updateCellText, toggleColumnBold, toggleRowHeader, undo, redo, canUndo, canRedo, getAdjacentCell, commitUndoPoint, selectedCells, toggleCellSelection, clearCellSelection, toggleSelectedBold, setCellColor, deleteColumn, addColumn, deleteRow, addRow, } = useGridEditor(sessionId) const [building, setBuilding] = useState(false) const [buildError, setBuildError] = useState(null) // Load grid on mount useEffect(() => { if (sessionId) loadGrid() }, [sessionId]) // eslint-disable-line react-hooks/exhaustive-deps // Get box zones const boxZones: GridZone[] = (grid?.zones || []).filter( (z: GridZone) => z.zone_type === 'box' ) // Build box grids via backend const buildBoxGrids = useCallback(async (overrides?: Record) => { if (!sessionId) return setBuilding(true) setBuildError(null) try { const res = await fetch( `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/build-box-grids`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ overrides: overrides || {} }), }, ) if (!res.ok) { const data = await res.json().catch(() => ({})) throw new Error(data.detail || `HTTP ${res.status}`) } await loadGrid() } catch (e) { setBuildError(e instanceof Error ? e.message : String(e)) } finally { setBuilding(false) } }, [sessionId, loadGrid]) // Handle layout type change for a specific box zone const changeLayoutType = useCallback(async (boxIdx: number, layoutType: string) => { await buildBoxGrids({ [String(boxIdx)]: layoutType }) }, [buildBoxGrids]) // Auto-build on first load if box zones have no cells useEffect(() => { if (!grid || loading || building) return const needsBuild = boxZones.length === 0 || boxZones.some(z => !z.cells || z.cells.length === 0) // Only auto-build if we know there are boxes (check structure_result via a quick fetch) if (needsBuild && sessionId) { buildBoxGrids() } }, [grid?.zones?.length, loading]) // eslint-disable-line react-hooks/exhaustive-deps if (loading) { return (
Lade Grid...
) } // No boxes after build attempt — skip step if (!building && boxZones.length === 0) { return (
📦

Keine Boxen erkannt

Auf dieser Seite wurden keine eingebetteten Boxen (Grammatik-Tipps, Übungen etc.) erkannt.

) } return (
{/* Header */}

Box-Review ({boxZones.length} {boxZones.length === 1 ? 'Box' : 'Boxen'})

Eingebettete Boxen prüfen und korrigieren. Layout-Typ kann pro Box angepasst werden.

{dirty && ( )}
{/* Errors */} {(error || buildError) && (
{error || buildError}
)} {building && (
Box-Grids werden aufgebaut...
)} {/* Box zones */} {boxZones.map((zone, boxIdx) => (
{/* Box header */}
📦
Box {boxIdx + 1} {zone.bbox_px?.w}x{zone.bbox_px?.h}px {zone.cells?.length ? ` | ${zone.cells.length} Zellen` : ''} {zone.box_layout_type ? ` | ${LAYOUT_LABELS[zone.box_layout_type as BoxLayoutType] || zone.box_layout_type}` : ''}
{/* Box grid table */}
{zone.cells && zone.cells.length > 0 ? ( { const next = getAdjacentCell(cellId, dir) if (next) setSelectedCell(next) }} onDeleteColumn={deleteColumn} onAddColumn={addColumn} onDeleteRow={deleteRow} onAddRow={addRow} onToggleCellSelection={toggleCellSelection} onSetCellColor={setCellColor} /> ) : (

Keine Zellen erkannt.

)}
))}
) }