from pathlib import Path import re path = Path('web-v2/src/pages/InspectionVisitEditorF4Page.tsx') text = path.read_text() state_before = " const [success, setSuccess] = useState('');\n" state_after = """ const [success, setSuccess] = useState(''); const [showCancelForm, setShowCancelForm] = useState(false); const [cancelReason, setCancelReason] = useState(''); """ if text.count(state_before) != 1: raise SystemExit('Could not locate success state exactly once') text = text.replace(state_before, state_after) lifecycle_pattern = re.compile( r" const lifecycleAction = async \(action: 'PLAN' \| 'UNPLAN' \| 'CANCEL'\) => \{.*?\n \};\n\n if \(loading\)", re.S, ) lifecycle_replacement = """ const lifecycleAction = async (action: 'PLAN' | 'UNPLAN') => { if (!id) return; setBusy(true); setError(''); setSuccess(''); try { let updated: InspectionVisit; if (action === 'PLAN') { updated = await planInspectionVisit(id); setSuccess('Inspección planificada. Ya puede iniciarse desde la APK.'); } else { updated = await unplanInspectionVisit(id); setSuccess('La Inspección volvió a borrador para corregir la planificación.'); } applyVisit(updated); } catch (requestError) { setError(errorMessage(requestError)); } finally { setBusy(false); } }; const cancelCurrentVisit = async (event: FormEvent) => { event.preventDefault(); if (!id) return; const reason = cancelReason.trim(); if (reason.length < 10) { setError('El motivo de cancelación debe tener al menos 10 caracteres.'); return; } if (reason.length > 500) { setError('El motivo de cancelación no puede superar los 500 caracteres.'); return; } setBusy(true); setError(''); setSuccess(''); try { const updated = await cancelInspectionVisit(id, reason); applyVisit(updated); setCancelReason(''); setShowCancelForm(false); setSuccess('Inspección cancelada. El motivo quedó registrado y auditado.'); } catch (requestError) { setError(errorMessage(requestError)); } finally { setBusy(false); } }; if (loading)""" text, count = lifecycle_pattern.subn(lifecycle_replacement, text, count=1) if count != 1: raise SystemExit(f'Lifecycle block replacements: {count}') actions_pattern = re.compile( r" \{visit &&
.*?
\}\n", re.S, ) actions_replacement = """ {visit &&
Flujo de la Inspección

La oficina planifica. El inicio y cierre operativo se realizan desde la APK. Una Inspección no puede cerrarse hasta que todas sus Actas estén selladas.

{canManage && visit.status === 'DRAFT' && }{canManage && visit.status === 'PLANNED' && }{canManage && ['DRAFT', 'PLANNED', 'IN_PROGRESS'].includes(visit.status) && }
} {visit && showCancelForm && ['DRAFT', 'PLANNED', 'IN_PROGRESS'].includes(visit.status) &&
CANCELACIÓN

Cancelar inspección

La cancelación es definitiva y queda auditada. No se permite si la Inspección ya tiene un Acta sellada.