From 212f4dcee507949d1217ea0a1286820eea70cf53 Mon Sep 17 00:00:00 2001 From: enlineawork Date: Mon, 7 Sep 2026 23:01:56 -0300 Subject: [PATCH] F4: remove obsolete finding company-response workspace --- .../inspections/FindingOfficeWorkspace.tsx | 370 ------------------ 1 file changed, 370 deletions(-) delete mode 100644 web-v2/src/features/inspections/FindingOfficeWorkspace.tsx diff --git a/web-v2/src/features/inspections/FindingOfficeWorkspace.tsx b/web-v2/src/features/inspections/FindingOfficeWorkspace.tsx deleted file mode 100644 index 9ff3d37..0000000 --- a/web-v2/src/features/inspections/FindingOfficeWorkspace.tsx +++ /dev/null @@ -1,370 +0,0 @@ -import { SearchableSelect } from '../../components/SearchableSelect'; -import { useEffect, useRef, useState } from 'react'; -import type { FormEvent } from 'react'; -import { Link } from 'react-router'; -import { Alert, errorMessage } from '../../components/Feedback'; -import { Icon } from '../../components/Icon'; -import { - closeInspectionFinding, - createInspectionFindingCommunication, - updateInspectionFindingFollowUp, - uploadInspectionFindingEvidence, -} from '../../lib/api'; -import type { - InspectionFinding, - InspectionFindingCommunication, - InspectionFindingOfficeWorkflow, -} from '../../lib/api'; -import { formatDate, formatDateOnly } from '../../lib/format'; - -function localDateTime(value: string | number | Date): string { - const date = new Date(value); - if (Number.isNaN(date.getTime())) return ''; - const local = new Date(date.getTime() - date.getTimezoneOffset() * 60_000); - return local.toISOString().slice(0, 16); -} - -function workflowFallback(finding: InspectionFinding): InspectionFindingOfficeWorkflow { - if (finding.status === 'CLOSED') { - return { stage: 'CLOSED', label: 'Hallazgo cerrado', action: 'El seguimiento está finalizado.', dueOn: null, overdue: false }; - } - if (!finding.companyResponseReceivedOn) { - return { stage: 'WAITING_COMPANY', label: 'Esperando respuesta de empresa', action: 'Registrar la respuesta cuando sea recibida.', dueOn: finding.correctionDueOn, overdue: false }; - } - if (finding.latestVerification?.outcome === 'RESOLVED' && finding.latestVerification.visitStatus === 'CLOSED') { - return { stage: 'READY_TO_CLOSE', label: 'Listo para cierre', action: 'Revisar la evidencia y cerrar administrativamente.', dueOn: null, overdue: false }; - } - if (!finding.nextControlOn) { - return { stage: 'DEFINE_VERIFICATION', label: 'Definir fecha de verificación', action: 'Establecer cuándo debe volver a controlarse.', dueOn: null, overdue: false }; - } - if (finding.verificationVisit) { - return { stage: 'VERIFICATION_PLANNED', label: 'Verificación planificada', action: 'Consultar la visita vinculada.', dueOn: finding.nextControlOn, overdue: false }; - } - return { stage: 'PLAN_VERIFICATION', label: 'Verificación pendiente de planificación', action: 'Crear la visita de verificación.', dueOn: finding.nextControlOn, overdue: false }; -} - -function verificationEventLabel(eventType: NonNullable[number]['eventType']): string { - if (eventType === 'CONTROL_DATE_DEFINED') return 'Fecha de verificación definida'; - if (eventType === 'CONTROL_DATE_CHANGED') return 'Fecha de verificación reprogramada'; - if (eventType === 'CONTROL_DATE_CLEARED') return 'Fecha de verificación consumida o retirada'; - if (eventType === 'VISIT_PLANNED') return 'Visita de verificación planificada'; - return 'Resultado de verificación registrado'; -} - -function verificationOutcomeLabel(outcome: NonNullable['outcome']): string { - if (outcome === 'RESOLVED') return 'Solucionado'; - if (outcome === 'NOT_RESOLVED') return 'No solucionado'; - if (outcome === 'REQUIRES_NEW_DATE') return 'Requiere nueva fecha'; - return ''; -} - - -export function FindingOfficeWorkspace({ - finding, - canFollowUp, - canClose, - canCreateEvidence, - canCreateCommunications, - onFindingChanged, - onEvidenceChanged, -}: { - finding: InspectionFinding; - canFollowUp: boolean; - canClose: boolean; - canCreateEvidence: boolean; - canCreateCommunications: boolean; - onFindingChanged: (finding: InspectionFinding) => void; - onEvidenceChanged: () => void; -}) { - const responseFileRef = useRef(null); - const [busy, setBusy] = useState(false); - const [error, setError] = useState(''); - const [success, setSuccess] = useState(''); - const [responseText, setResponseText] = useState(''); - const [responseOccurredAt, setResponseOccurredAt] = useState(localDateTime(new Date())); - const [responseChannel, setResponseChannel] = useState('EMAIL'); - const [responseContactName, setResponseContactName] = useState(''); - const [responseContactEmail, setResponseContactEmail] = useState(''); - const [responseFile, setResponseFile] = useState(null); - const [responseDueBasis, setResponseDueBasis] = useState<'FINDING_DATE' | 'REPORT_NOTIFICATION' | ''>(finding.responseDueBasis ?? ''); - const [responseDueDays, setResponseDueDays] = useState(finding.responseDueDays == null ? '' : String(finding.responseDueDays)); - const [reportNotifiedOn, setReportNotifiedOn] = useState(finding.reportNotifiedOn ?? ''); - const [committedOn, setCommittedOn] = useState(finding.companyCommittedCorrectionOn ?? ''); - const [nextControlOn, setNextControlOn] = useState(finding.nextControlOn ?? ''); - const [closureNotes, setClosureNotes] = useState(''); - - useEffect(() => { - setResponseText(''); - setResponseDueBasis(finding.responseDueBasis ?? ''); - setResponseDueDays(finding.responseDueDays == null ? '' : String(finding.responseDueDays)); - setReportNotifiedOn(finding.reportNotifiedOn ?? ''); - setCommittedOn(finding.companyCommittedCorrectionOn ?? ''); - setNextControlOn(finding.nextControlOn ?? ''); - }, [finding.id, finding.responseDueBasis, finding.responseDueDays, finding.reportNotifiedOn, finding.companyCommittedCorrectionOn, finding.nextControlOn]); - - const workflow = finding.officeWorkflow ?? workflowFallback(finding); - const hasResponse = Boolean(finding.companyResponseReceivedOn); - const verificationDone = Boolean(finding.latestVerification?.outcome); - const closed = finding.status === 'CLOSED'; - const verificationReadyToClose = finding.latestVerification?.outcome === 'RESOLVED' - && finding.latestVerification.visitStatus === 'CLOSED'; - const verificationActive = Boolean(finding.verificationVisit); - const verificationHistory = finding.verificationHistory ?? []; - - const saveResponseDeadline = async (event: FormEvent) => { - event.preventDefault(); - if (!responseDueBasis || responseDueDays === '') return; - if (responseDueBasis === 'REPORT_NOTIFICATION' && !reportNotifiedOn) return; - setBusy(true); - setError(''); - setSuccess(''); - try { - const updated = await updateInspectionFindingFollowUp(finding.id, { - responseDueBasis, - responseDueDays: Number(responseDueDays), - reportNotifiedOn: responseDueBasis === 'REPORT_NOTIFICATION' ? reportNotifiedOn : null, - }); - onFindingChanged(updated); - setSuccess('Plazo administrativo calculado y guardado con su fecha base.'); - } catch (requestError) { - setError(errorMessage(requestError)); - } finally { - setBusy(false); - } - }; - - const registerResponse = async (event: FormEvent) => { - event.preventDefault(); - if (!responseText.trim() || !responseOccurredAt) return; - const receivedOn = responseOccurredAt.slice(0, 10); - const firstPresentation = !hasResponse; - setBusy(true); - setError(''); - setSuccess(''); - try { - let updated = finding; - if (firstPresentation) { - updated = await updateInspectionFindingFollowUp(finding.id, { - companyResponse: responseText.trim(), - companyResponseReceivedOn: receivedOn, - companyCommittedCorrectionOn: committedOn || null, - nextControlOn: nextControlOn || null, - }); - onFindingChanged(updated); - } else if ( - committedOn !== (finding.companyCommittedCorrectionOn ?? '') - || nextControlOn !== (finding.nextControlOn ?? '') - ) { - updated = await updateInspectionFindingFollowUp(finding.id, { - companyCommittedCorrectionOn: committedOn || null, - nextControlOn: nextControlOn || null, - }); - onFindingChanged(updated); - } - - let communicationId = ''; - let traceWarning = ''; - if (canCreateCommunications) { - try { - const communication = await createInspectionFindingCommunication(finding.id, { - type: 'COMPANY_RESPONSE', - direction: 'INBOUND', - channel: responseChannel, - occurredAt: new Date(responseOccurredAt).toISOString(), - subject: `${firstPresentation ? 'Respuesta' : 'Nueva presentación'} de empresa · ${finding.code}`, - details: responseText.trim(), - contactName: responseContactName.trim() || null, - contactEmail: responseContactEmail.trim() || null, - }); - communicationId = communication.id; - onEvidenceChanged(); - } catch (requestError) { - traceWarning = `${firstPresentation ? 'La primera respuesta' : 'La actualización de seguimiento'} quedó registrada, pero la presentación histórica no pudo incorporarse: ${errorMessage(requestError)}`; - } - } else { - traceWarning = 'No tenés permiso para incorporar la presentación a la trazabilidad histórica.'; - } - - if (responseFile && canCreateEvidence) { - if (!communicationId) { - traceWarning = traceWarning || 'El documento debe incorporarse desde Documentos y trazabilidad porque no existe una presentación histórica vinculada.'; - } else { - try { - await uploadInspectionFindingEvidence(finding.id, { - file: responseFile, - kind: 'DOCUMENT', - purpose: 'COMPANY_RESPONSE', - communicationId, - title: responseFile.name, - description: `Documento presentado por la empresa para ${finding.code}`, - capturedAt: new Date(responseOccurredAt).toISOString(), - deviceLabel: navigator.userAgent.slice(0, 200), - }); - onEvidenceChanged(); - } catch (requestError) { - traceWarning = `La presentación quedó registrada, pero el documento no pudo incorporarse: ${errorMessage(requestError)}`; - } - } - } - - setResponseText(''); - setResponseContactName(''); - setResponseContactEmail(''); - setResponseOccurredAt(localDateTime(new Date())); - setResponseFile(null); - if (responseFileRef.current) responseFileRef.current.value = ''; - setSuccess(firstPresentation - ? 'Primera respuesta registrada. Las presentaciones posteriores se agregarán sin modificar esta respuesta original.' - : 'Nueva presentación agregada al historial del hallazgo. La respuesta original permanece inmutable.'); - if (traceWarning) setError(traceWarning); - } catch (requestError) { - setError(errorMessage(requestError)); - } finally { - setBusy(false); - } - }; - - const saveOperationalFollowUp = async (event: FormEvent) => { - event.preventDefault(); - setBusy(true); - setError(''); - setSuccess(''); - try { - const updated = await updateInspectionFindingFollowUp(finding.id, { - companyCommittedCorrectionOn: committedOn || null, - nextControlOn: nextControlOn || null, - }); - onFindingChanged(updated); - setSuccess('Seguimiento operativo actualizado. El cambio de fecha quedó agregado al historial sin borrar los controles anteriores.'); - } catch (requestError) { - setError(errorMessage(requestError)); - } finally { - setBusy(false); - } - }; - - const closeFinding = async (event: FormEvent) => { - event.preventDefault(); - if (!closureNotes.trim()) return; - setBusy(true); - setError(''); - setSuccess(''); - try { - const updated = await closeInspectionFinding(finding.id, closureNotes.trim()); - onFindingChanged(updated); - setClosureNotes(''); - setSuccess('Hallazgo cerrado. La conclusión quedó incorporada al expediente histórico.'); - } catch (requestError) { - setError(errorMessage(requestError)); - } finally { - setBusy(false); - } - }; - - return
-
-
EXPEDIENTE OPERATIVO

Dar curso al hallazgo

Respuesta, documentación, vencimientos, verificación y cierre desde una sola ficha.

- {workflow.label} -
- - {error && {error}} - {success && {success}} - -
-
1HallazgoRegistrado
- -
2Empresa{hasResponse ? 'Respuesta recibida' : 'Esperando respuesta'}
- -
3Verificación{verificationDone ? 'Resultado registrado' : finding.verificationVisit ? 'Visita planificada' : finding.nextControlOn ? 'Fecha definida' : 'Pendiente'}
- -
4Cierre{closed ? 'Finalizado' : verificationReadyToClose ? 'Listo para cerrar' : 'Pendiente'}
-
- - {!closed &&
-
ACCIÓN RECOMENDADA{workflow.label}

{workflow.action}{workflow.dueOn ? ` Fecha: ${formatDateOnly(workflow.dueOn)}.` : ''}

-
- {(workflow.stage === 'WAITING_COMPANY' || workflow.stage === 'COMPANY_OVERDUE') && Registrar respuesta} - {(workflow.stage === 'DEFINE_VERIFICATION' || workflow.stage === 'RESCHEDULE_VERIFICATION') && Definir fecha} - {workflow.stage === 'PLAN_VERIFICATION' && Planificar visita } - {workflow.stage === 'VERIFICATION_PLANNED' && finding.verificationVisit && Abrir verificación } - {workflow.stage === 'READY_TO_CLOSE' && Revisar y cerrar} -
-
} - -
-
-
1 · ADMINISTRATIVO

Respuesta de la empresa

{hasResponse ? 'Recibida' : workflow.stage === 'COMPANY_OVERDUE' ? 'Vencida' : 'Pendiente'}
- {!hasResponse && canFollowUp &&
-
- - - {responseDueBasis === 'REPORT_NOTIFICATION' && } -
-

DH guarda la regla, la fecha base y el vencimiento calculado. Para casos críticos puede computarse desde el hallazgo; para los demás, desde la notificación del informe.

-
-
} -
Vencimiento empresa{formatDateOnly(finding.correctionDueOn)}{finding.responseDueBasis && {finding.responseDueBasis === 'FINDING_DATE' ? 'Desde hallazgo' : 'Desde notificación'} · {finding.responseDueDays ?? 0} día{finding.responseDueDays === 1 ? '' : 's'} · base {formatDateOnly(finding.responseDueBaseOn)}}
{hasResponse &&
Respuesta recibida{formatDateOnly(finding.companyResponseReceivedOn)}
}
- - {!closed && canFollowUp &&
-