/** * @ai-summary Review screen for extracted maintenance schedules from manual OCR * @ai-context Dialog showing extracted items with checkboxes, inline editing, batch create */ import React, { useState, useCallback } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Box, Typography, TextField, Checkbox, IconButton, Alert, CircularProgress, Chip, useTheme, useMediaQuery, } from '@mui/material'; import EditIcon from '@mui/icons-material/Edit'; import CheckIcon from '@mui/icons-material/Check'; import CloseIcon from '@mui/icons-material/Close'; import SelectAllIcon from '@mui/icons-material/SelectAll'; import DeselectIcon from '@mui/icons-material/Deselect'; import type { MaintenanceScheduleItem } from '../../documents/hooks/useManualExtraction'; import { useCreateSchedulesFromExtraction } from '../hooks/useCreateSchedulesFromExtraction'; export interface MaintenanceScheduleReviewScreenProps { open: boolean; items: MaintenanceScheduleItem[]; vehicleId: string; onClose: () => void; onCreated: (count: number) => void; } interface EditableItem extends MaintenanceScheduleItem { selected: boolean; } const ConfidenceIndicator: React.FC<{ confidence: number }> = ({ confidence }) => { const filledDots = Math.round(confidence * 4); const isLow = confidence < 0.6; return ( {[0, 1, 2, 3].map((i) => ( ))} ); }; interface InlineFieldProps { label: string; value: string | number | null; type?: 'text' | 'number'; onSave: (value: string | number | null) => void; suffix?: string; } const InlineField: React.FC = ({ label, value, type = 'text', onSave, suffix }) => { const [isEditing, setIsEditing] = useState(false); const [editValue, setEditValue] = useState(value !== null ? String(value) : ''); const displayValue = value !== null ? (suffix ? `${value} ${suffix}` : String(value)) : '-'; const handleSave = () => { let parsed: string | number | null = editValue || null; if (type === 'number' && editValue) { const num = parseFloat(editValue); parsed = isNaN(num) ? null : num; } onSave(parsed); setIsEditing(false); }; const handleCancel = () => { setEditValue(value !== null ? String(value) : ''); setIsEditing(false); }; if (isEditing) { return ( {label}: setEditValue(e.target.value)} type={type === 'number' ? 'number' : 'text'} inputProps={{ step: type === 'number' ? 1 : undefined }} autoFocus sx={{ flex: 1, '& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.875rem' } }} onKeyDown={(e) => { if (e.key === 'Enter') handleSave(); if (e.key === 'Escape') handleCancel(); }} /> ); } return ( setIsEditing(true)} role="button" tabIndex={0} aria-label={`Edit ${label}`} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setIsEditing(true); } }} > {label}: {displayValue} ); }; export const MaintenanceScheduleReviewScreen: React.FC = ({ open, items, vehicleId, onClose, onCreated, }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const createMutation = useCreateSchedulesFromExtraction(); const [editableItems, setEditableItems] = useState(() => items.map((item) => ({ ...item, selected: true })) ); const [createError, setCreateError] = useState(null); const selectedCount = editableItems.filter((i) => i.selected).length; const handleToggle = useCallback((index: number) => { setEditableItems((prev) => prev.map((item, i) => (i === index ? { ...item, selected: !item.selected } : item)) ); }, []); const handleSelectAll = useCallback(() => { setEditableItems((prev) => prev.map((item) => ({ ...item, selected: true }))); }, []); const handleDeselectAll = useCallback(() => { setEditableItems((prev) => prev.map((item) => ({ ...item, selected: false }))); }, []); const handleFieldUpdate = useCallback((index: number, field: keyof MaintenanceScheduleItem, value: string | number | null) => { setEditableItems((prev) => prev.map((item, i) => (i === index ? { ...item, [field]: value } : item)) ); }, []); const handleCreate = async () => { setCreateError(null); const selectedItems = editableItems.filter((i) => i.selected); if (selectedItems.length === 0) return; try { await createMutation.mutateAsync({ vehicleId, items: selectedItems }); onCreated(selectedItems.length); } catch (err: any) { setCreateError(err?.message || 'Failed to create maintenance schedules'); } }; const isEmpty = items.length === 0; return ( Extracted Maintenance Schedules {isEmpty ? ( No maintenance items found The manual did not contain any recognizable routine maintenance schedules. ) : ( <> {selectedCount} of {editableItems.length} items selected {editableItems.map((item, index) => ( handleToggle(index)} sx={{ mt: -0.5, mr: 1, '& .MuiSvgIcon-root': { fontSize: 24 }, minWidth: 44, minHeight: 44 }} inputProps={{ 'aria-label': `Select ${item.service}` }} /> handleFieldUpdate(index, 'service', v)} /> handleFieldUpdate(index, 'intervalMiles', v)} suffix="mi" /> handleFieldUpdate(index, 'intervalMonths', v)} suffix="mo" /> {item.details && ( {item.details} )} {item.subtypes.length > 0 && ( {item.subtypes.map((subtype) => ( ))} )} ))} Tap any field to edit before creating schedules. )} {createError && ( {createError} )} {!isEmpty && ( <> )} ); }; export default MaintenanceScheduleReviewScreen;