import { useEffect, useState } from 'react'; import type { FormEvent } from 'react'; import { Link, useParams } from 'react-router'; import { useAuth } from '../auth/AuthContext'; import { Alert, LoadingBlock, errorMessage } from '../components/Feedback'; import { Icon } from '../components/Icon'; import { approveInspectionReport, getInspectionReport, getInspectionReportReview, inspectionReportRevisionUrl, inspectionReportWordUrl, signFinalInspectionReport, uploadInspectionReportRevision, } from '../lib/api'; import type { InspectionReport, InspectionReportReview } from '../lib/api'; import { formatDate } from '../lib/format'; function names(items: Array<{ name: string }>, empty: string): string { return items.length ? items.map((item) => item.name).join(' · ') : empty; } function reviewLabel(value: InspectionReport['reviewStatus']): string { if (value === 'SIGNED') return 'Firmado'; if (value === 'APPROVED') return 'Aprobado · falta firma'; return 'Pendiente de revisión'; } function reviewClass(value: InspectionReport['reviewStatus']): string { if (value === 'SIGNED') return 'active'; if (value === 'APPROVED') return 'observed'; return 'pending'; } function revisionSource(value: 'AUTO' | 'DIRECTOR_UPLOAD'): string { return value === 'AUTO' ? 'Automática' : 'Corrección cargada'; } export function ReportDetailPage() { const { id } = useParams(); const { hasPermission } = useAuth(); const [report, setReport] = useState(null); const [review, setReview] = useState(null); const [loading, setLoading] = useState(true); const [working, setWorking] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [revisionFile, setRevisionFile] = useState(null); const [changeSummary, setChangeSummary] = useState(''); const [approvalNote, setApprovalNote] = useState(''); const [signatureConfirmed, setSignatureConfirmed] = useState(false); const canRevise = hasPermission('inspection_reports.revise'); const canReview = hasPermission('inspection_reports.review'); const canSign = hasPermission('inspection_reports.sign_final'); const reload = async () => { if (!id) return; const [nextReport, nextReview] = await Promise.all([getInspectionReport(id), getInspectionReportReview(id)]); setReport(nextReport); setReview(nextReview); }; useEffect(() => { if (!id) return; setLoading(true); setError(''); reload() .catch((requestError) => setError(errorMessage(requestError))) .finally(() => setLoading(false)); }, [id]); const uploadRevision = async (event: FormEvent) => { event.preventDefault(); if (!id || !revisionFile || changeSummary.trim().length < 5) return; setWorking(true); setError(''); setSuccess(''); try { const next = await uploadInspectionReportRevision(id, revisionFile, changeSummary.trim()); setReview(next); setRevisionFile(null); setChangeSummary(''); setSuccess(`Versión ${next.currentRevisionNumber} agregada al historial.`); await reload(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setWorking(false); } }; const approve = async () => { if (!id) return; setWorking(true); setError(''); setSuccess(''); try { const next = await approveInspectionReport(id, approvalNote); setReview(next); setApprovalNote(''); setSuccess(`Versión ${next.currentRevisionNumber} aprobada por el Director.`); await reload(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setWorking(false); } }; const signFinal = async () => { if (!id || !signatureConfirmed) return; setWorking(true); setError(''); setSuccess(''); try { const next = await signFinalInspectionReport(id); setReview(next); setSignatureConfirmed(false); setSuccess('Informe final firmado electrónicamente. La versión firmada quedó bloqueada.'); await reload(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setWorking(false); } }; if (loading) return ; return
Informes/{report?.code ?? 'Informe'}
INFORME DE INSPECCIÓN

{report?.title ?? 'Informe'}

{report ? `${report.code} · emitido ${formatDate(report.generatedAt)}` : 'Consulta del informe.'}

{report &&
{report.wordStatus === 'READY' ? 'Word disponible' : report.wordStatus === 'FAILED' ? 'Word con error' : 'Word pendiente'}{reviewLabel(report.reviewStatus)}
}
{error && {error}} {success &&
{success}
} {report && <>

Informe Word automático. La versión inicial se genera desde la instantánea congelada del Acta. Las correcciones se agregan como nuevas versiones y nunca sobrescriben la anterior.

TRAZABILIDAD DOCUMENTAL

{report.code}

Versión del Acta: {report.actVersion}
Empresa{names(report.companies, 'Sin asignar')}
Área{names(report.areas, 'Sin asignar')}
Hallazgos{report.findingCount}
Emitido por{report.generatedBy.firstName} {report.generatedBy.lastName}
{report.wordStatus === 'READY' && }
Inspección{report.visit.code} Acta incorporada{report.act.code}{report.act.title} Hallazgos{report.findingCount}Ver seguimiento relacionado
REVISIÓN DIRECTIVA

Versiones y firma final

El Acta y los Hallazgos originales permanecen congelados. La revisión sólo versiona el documento Informe.

{review && {reviewLabel(review.reviewStatus)}}
{!review ? : <>
1Informe automático{review.revisions.length ? 'Versión inicial registrada' : 'Esperando Word'}
1 ? 'complete' : ''}>2Correcciones{review.currentRevisionNumber > 1 ? `${review.currentRevisionNumber - 1} versión/es agregada/s` : 'Sin correcciones'}
3Aprobación{review.approvedAt ? formatDate(review.approvedAt) : 'Pendiente del Director'}
4Firma final{review.signature ? formatDate(review.signature.signedAt) : 'Pendiente'}
{review.revisions.map((revision) =>
Versión {revision.revisionNumber}{revision.originalName}{revisionSource(revision.source)} · {revision.createdBy.firstName} {revision.createdBy.lastName} · {formatDate(revision.createdAt)}{revision.changeSummary &&

{revision.changeSummary}

}
{revision.sha256}Descargar
)}
{canRevise && review.reviewStatus === 'PENDING_REVIEW' &&
NUEVA VERSIÓN

Agregar Word corregido

Descargá la última versión, realizá la corrección en Word y cargala nuevamente. La versión anterior queda intacta.