// Edit-case modal — lets the doctor correct patient_id / patient_name / // cancer_type / stage when the OCR got something wrong, without forcing a // re-upload. Backend treats blank strings as "leave alone" so partial edits // can't accidentally erase data. const EditCaseModal = ({ row, onClose, onSaved }) => { const t = window.useLanguage().t; const [patientId, setPatientId] = React.useState(row?.id === 'Unnamed' ? '' : (row?.id || '')); const [patientName, setPatientName] = React.useState(row?.name === 'Unnamed' ? '' : (row?.name || '')); const [cancerType, setCancerType] = React.useState(row?.dx || ''); const [stage, setStage] = React.useState(row?.stage || ''); const [stage_, setStage_] = React.useState({saving:false, err:''}); const save = async () => { if (!row?._caseId) return; setStage_({saving:true, err:''}); const patch = {}; // Only send fields that actually changed AND are non-empty. Backend // strips empty strings either way, but skipping them client-side // keeps the request body honest. if (patientId.trim() && patientId.trim() !== (row.id === 'Unnamed' ? '' : row.id)) patch.patient_id = patientId.trim(); if (patientName.trim() && patientName.trim() !== (row.name === 'Unnamed' ? '' : row.name)) patch.patient_name = patientName.trim(); if (cancerType.trim() && cancerType.trim() !== (row.dx || '')) patch.cancer_type = cancerType.trim(); if (stage.trim() && stage.trim() !== (row.stage || '')) patch.stage = stage.trim(); if (Object.keys(patch).length === 0) { onClose(); return; } try { await window.AicanAPI.editCase(row._caseId, patch); setStage_({saving:false, err:''}); onSaved && onSaved(); } catch (e) { setStage_({saving:false, err: e.message || 'Edit failed'}); } }; return (
e.stopPropagation()}>

{t('Edit case')}

{t('Correct any field the OCR misread. Blank fields are left unchanged.')}

setPatientId(e.target.value)}/> setPatientName(e.target.value)}/>
setCancerType(e.target.value)}/> setStage(e.target.value)}/>
{stage_.err && (
{stage_.err}
)}
); }; window.EditCaseModal = EditCaseModal;