Fix StepBoxGridReview: match GridTable props interface
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 46s
CI / test-go-edu-search (push) Successful in 43s
CI / test-python-klausur (push) Failing after 2m50s
CI / test-python-agent-core (push) Successful in 37s
CI / test-nodejs-website (push) Successful in 38s

GridTable expects zone (singular), onSelectCell, onCellTextChange,
onToggleColumnBold, onToggleRowHeader, onNavigate — not the
incorrect prop names from the first version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-12 22:39:38 +02:00
parent 058eadb0e4
commit 12b194ad1a

View File

@@ -33,6 +33,8 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
loadGrid, loadGrid,
saveGrid, saveGrid,
updateCellText, updateCellText,
toggleColumnBold,
toggleRowHeader,
undo, undo,
redo, redo,
canUndo, canUndo,
@@ -44,6 +46,10 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
clearCellSelection, clearCellSelection,
toggleSelectedBold, toggleSelectedBold,
setCellColor, setCellColor,
deleteColumn,
addColumn,
deleteRow,
addRow,
} = useGridEditor(sessionId) } = useGridEditor(sessionId)
const [building, setBuilding] = useState(false) const [building, setBuilding] = useState(false)
@@ -77,7 +83,6 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
const data = await res.json().catch(() => ({})) const data = await res.json().catch(() => ({}))
throw new Error(data.detail || `HTTP ${res.status}`) throw new Error(data.detail || `HTTP ${res.status}`)
} }
// Reload grid to see updated box zones
await loadGrid() await loadGrid()
} catch (e) { } catch (e) {
setBuildError(e instanceof Error ? e.message : String(e)) setBuildError(e instanceof Error ? e.message : String(e))
@@ -87,18 +92,19 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
}, [sessionId, loadGrid]) }, [sessionId, loadGrid])
// Handle layout type change for a specific box zone // Handle layout type change for a specific box zone
const changeLayoutType = useCallback(async (zoneIndex: number, layoutType: string) => { const changeLayoutType = useCallback(async (boxIdx: number, layoutType: string) => {
await buildBoxGrids({ [String(zoneIndex)]: layoutType }) await buildBoxGrids({ [String(boxIdx)]: layoutType })
}, [buildBoxGrids]) }, [buildBoxGrids])
// Auto-build on first load if box zones have no cells // Auto-build on first load if box zones have no cells
useEffect(() => { useEffect(() => {
if (!grid || loading || building) return if (!grid || loading || building) return
const needsBuild = boxZones.some(z => !z.cells || z.cells.length === 0) const needsBuild = boxZones.length === 0 || boxZones.some(z => !z.cells || z.cells.length === 0)
if (needsBuild && boxZones.length > 0) { // Only auto-build if we know there are boxes (check structure_result via a quick fetch)
if (needsBuild && sessionId) {
buildBoxGrids() buildBoxGrids()
} }
}, [grid, loading]) // eslint-disable-line react-hooks/exhaustive-deps }, [grid?.zones?.length, loading]) // eslint-disable-line react-hooks/exhaustive-deps
if (loading) { if (loading) {
return ( return (
@@ -109,8 +115,8 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
) )
} }
// No boxes detected — skip step // No boxes after build attempt — skip step
if (boxZones.length === 0) { if (!building && boxZones.length === 0) {
return ( return (
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-8 text-center"> <div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-8 text-center">
<div className="text-4xl mb-3">📦</div> <div className="text-4xl mb-3">📦</div>
@@ -136,7 +142,7 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white"> <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
📦 Box-Review ({boxZones.length} {boxZones.length === 1 ? 'Box' : 'Boxen'}) Box-Review ({boxZones.length} {boxZones.length === 1 ? 'Box' : 'Boxen'})
</h3> </h3>
<p className="text-sm text-gray-500 dark:text-gray-400"> <p className="text-sm text-gray-500 dark:text-gray-400">
Eingebettete Boxen prüfen und korrigieren. Layout-Typ kann pro Box angepasst werden. Eingebettete Boxen prüfen und korrigieren. Layout-Typ kann pro Box angepasst werden.
@@ -186,7 +192,7 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
)} )}
{/* Box zones */} {/* Box zones */}
{boxZones.map((zone) => ( {boxZones.map((zone, boxIdx) => (
<div <div
key={zone.zone_index} key={zone.zone_index}
className="bg-white dark:bg-gray-800 rounded-xl border-2 border-amber-300 dark:border-amber-700 overflow-hidden" className="bg-white dark:bg-gray-800 rounded-xl border-2 border-amber-300 dark:border-amber-700 overflow-hidden"
@@ -197,11 +203,12 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
<span className="text-lg">📦</span> <span className="text-lg">📦</span>
<div> <div>
<span className="font-medium text-gray-900 dark:text-white"> <span className="font-medium text-gray-900 dark:text-white">
Box {zone.zone_index + 1} Box {boxIdx + 1}
</span> </span>
<span className="text-xs text-gray-500 dark:text-gray-400 ml-2"> <span className="text-xs text-gray-500 dark:text-gray-400 ml-2">
{zone.bbox_px.w}×{zone.bbox_px.h}px {zone.bbox_px?.w}x{zone.bbox_px?.h}px
{zone.cells?.length ? ` ${zone.cells.length} Zellen` : ''} {zone.cells?.length ? ` | ${zone.cells.length} Zellen` : ''}
{zone.box_layout_type ? ` | ${LAYOUT_LABELS[zone.box_layout_type as BoxLayoutType] || zone.box_layout_type}` : ''}
</span> </span>
</div> </div>
</div> </div>
@@ -209,7 +216,7 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
<label className="text-xs text-gray-500 dark:text-gray-400">Layout:</label> <label className="text-xs text-gray-500 dark:text-gray-400">Layout:</label>
<select <select
value={zone.box_layout_type || 'flowing'} value={zone.box_layout_type || 'flowing'}
onChange={(e) => changeLayoutType(zone.zone_index, e.target.value)} onChange={(e) => changeLayoutType(boxIdx, e.target.value)}
disabled={building} disabled={building}
className="text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200" className="text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200"
> >
@@ -220,60 +227,39 @@ export function StepBoxGridReview({ sessionId, onNext }: StepBoxGridReviewProps)
</div> </div>
</div> </div>
{/* Box content — image + grid side by side */} {/* Box grid table */}
<div className="flex gap-0"> <div className="p-3">
{/* Box image crop */} {zone.cells && zone.cells.length > 0 ? (
{sessionId && ( <GridTable
<div className="w-1/3 border-r border-gray-200 dark:border-gray-700 p-2 bg-gray-50 dark:bg-gray-900"> zone={zone}
<div className="relative overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700"> selectedCell={selectedCell}
<img selectedCells={selectedCells}
src={`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/cropped`} onSelectCell={setSelectedCell}
alt={`Box ${zone.zone_index + 1}`} onCellTextChange={updateCellText}
className="w-full h-auto" onToggleColumnBold={toggleColumnBold}
style={{ onToggleRowHeader={toggleRowHeader}
objectFit: 'none', onNavigate={(cellId, dir) => {
objectPosition: `-${zone.bbox_pct?.x ?? 0}% -${zone.bbox_pct?.y ?? 0}%`, const next = getAdjacentCell(cellId, dir)
// Use clip-path to show only the box region if (next) setSelectedCell(next)
}} }}
onError={(e) => { onDeleteColumn={deleteColumn}
// Fallback: hide image if endpoint doesn't exist onAddColumn={addColumn}
(e.target as HTMLImageElement).style.display = 'none' onDeleteRow={deleteRow}
}} onAddRow={addRow}
/> onToggleCellSelection={toggleCellSelection}
</div> onSetCellColor={setCellColor}
/>
) : (
<div className="text-center py-8 text-gray-400">
<p className="text-sm">Keine Zellen erkannt.</p>
<button
onClick={() => buildBoxGrids({ [String(boxIdx)]: 'flowing' })}
className="mt-2 text-xs text-amber-600 hover:text-amber-700"
>
Als Fließtext verarbeiten
</button>
</div> </div>
)} )}
{/* Box grid table */}
<div className="flex-1 p-2 overflow-x-auto">
{zone.cells && zone.cells.length > 0 ? (
<GridTable
zones={[zone]}
selectedCell={selectedCell}
onCellSelect={setSelectedCell}
onCellChange={updateCellText}
onGetAdjacentCell={getAdjacentCell}
imageWidth={grid?.image_width || 0}
imageHeight={grid?.image_height || 0}
commitUndoPoint={commitUndoPoint}
selectedCells={selectedCells}
onToggleCellSelection={toggleCellSelection}
onClearCellSelection={clearCellSelection}
onToggleSelectedBold={toggleSelectedBold}
onSetCellColor={setCellColor}
/>
) : (
<div className="text-center py-8 text-gray-400">
<p className="text-sm">Keine Zellen erkannt.</p>
<button
onClick={() => buildBoxGrids({ [String(zone.zone_index)]: 'flowing' })}
className="mt-2 text-xs text-amber-600 hover:text-amber-700"
>
Als Fließtext verarbeiten
</button>
</div>
)}
</div>
</div> </div>
</div> </div>
))} ))}