'use client' /** * GridOverlay Component * * SVG overlay for displaying detected OCR grid structure on document images. * Shows recognized (green), problematic (orange), manual (blue), and empty (transparent) cells. * Supports click-to-edit for problematic cells. */ import { useCallback } from 'react' import { cn } from '@/lib/utils' export type CellStatus = 'empty' | 'recognized' | 'problematic' | 'manual' export interface GridCell { row: number col: number x: number // X position as percentage (0-100) y: number // Y position as percentage (0-100) width: number // Width as percentage (0-100) height: number // Height as percentage (0-100) text: string confidence: number status: CellStatus column_type?: 'english' | 'german' | 'example' | 'unknown' } export interface GridData { rows: number columns: number cells: GridCell[][] column_types: string[] column_boundaries: number[] row_boundaries: number[] deskew_angle: number stats: { recognized: number problematic: number empty: number manual?: number total: number coverage: number } } interface GridOverlayProps { grid: GridData imageUrl?: string onCellClick?: (cell: GridCell) => void selectedCell?: GridCell | null showEmpty?: boolean showLabels?: boolean showNumbers?: boolean // Show block numbers in cells highlightedBlockNumber?: number | null // Highlight specific block className?: string } // Status colors const STATUS_COLORS = { recognized: { fill: 'rgba(34, 197, 94, 0.2)', // green-500 with opacity stroke: '#22c55e', // green-500 hoverFill: 'rgba(34, 197, 94, 0.3)', }, problematic: { fill: 'rgba(249, 115, 22, 0.3)', // orange-500 with opacity stroke: '#f97316', // orange-500 hoverFill: 'rgba(249, 115, 22, 0.4)', }, manual: { fill: 'rgba(59, 130, 246, 0.2)', // blue-500 with opacity stroke: '#3b82f6', // blue-500 hoverFill: 'rgba(59, 130, 246, 0.3)', }, empty: { fill: 'transparent', stroke: 'rgba(148, 163, 184, 0.3)', // slate-400 with opacity hoverFill: 'rgba(148, 163, 184, 0.1)', }, } // Helper to calculate block number (1-indexed, row-by-row) export function getCellBlockNumber(cell: GridCell, grid: GridData): number { return cell.row * grid.columns + cell.col + 1 } export function GridOverlay({ grid, imageUrl, onCellClick, selectedCell, showEmpty = false, showLabels = true, showNumbers = false, highlightedBlockNumber, className, }: GridOverlayProps) { const handleCellClick = useCallback( (cell: GridCell) => { if (onCellClick && cell.status !== 'empty') { onCellClick(cell) } }, [onCellClick] ) const flatCells = grid.cells.flat() return (