From b67db745e4730f9190edff4b6c4415187ef3300a Mon Sep 17 00:00:00 2001 From: enlineawork Date: Tue, 8 Sep 2026 10:25:46 -0300 Subject: [PATCH] fix(f4): present locked and sealed act lifecycle --- .../inspections/InspectionClosurePanel.tsx | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/web-v2/src/features/inspections/InspectionClosurePanel.tsx b/web-v2/src/features/inspections/InspectionClosurePanel.tsx index 1fc3a90..9984f33 100644 --- a/web-v2/src/features/inspections/InspectionClosurePanel.tsx +++ b/web-v2/src/features/inspections/InspectionClosurePanel.tsx @@ -2,8 +2,13 @@ import { useEffect, useMemo, useState } from 'react'; import { useAuth } from '../../auth/AuthContext'; import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback'; import { Icon } from '../../components/Icon'; -import { getInspectionClosure, getInspectionSignatureBlob } from '../../lib/api'; -import type { InspectionAct, InspectionActSignature, InspectionClosure } from '../../lib/api'; +import { getInspectionSignatureBlob } from '../../lib/api'; +import type { InspectionActSignature } from '../../lib/api'; +import { + getInspectionClosureF4, + type InspectionActF4, + type InspectionClosureF4, +} from '../../lib/inspectionActF4Api'; import { formatDate } from '../../lib/format'; function signatureStatusLabel(value: InspectionActSignature['status']): string { @@ -16,17 +21,17 @@ function signerTypeLabel(value: InspectionActSignature['signerType']): string { return value === 'INSPECTOR' ? 'Inspector/a' : 'Responsable de la empresa'; } -export function InspectionClosurePanel({ act }: { act: InspectionAct }) { +export function InspectionClosurePanel({ act }: { act: InspectionActF4 }) { const { hasPermission } = useAuth(); const canRead = hasPermission('inspection_closure.read'); - const [closure, setClosure] = useState(null); + const [closure, setClosure] = useState(null); const [loading, setLoading] = useState(canRead); const [error, setError] = useState(''); useEffect(() => { if (!canRead) return; setLoading(true); - getInspectionClosure(act.id) + getInspectionClosureF4(act.id) .then(setClosure) .catch((requestError) => setError(errorMessage(requestError))) .finally(() => setLoading(false)); @@ -60,26 +65,43 @@ export function InspectionClosurePanel({ act }: { act: InspectionAct }) { if (loading || !closure) return
; const constanciasCompletas = inspectorSignatures.length > 0 && Boolean(companyOutcome); + const isSealed = act.status === 'SEALED' || act.status === 'CLOSED'; + const isLocked = act.status === 'LOCKED' || act.status === 'READY'; + const lifecycleLabel = isSealed + ? 'Acta sellada' + : isLocked + ? 'Esperando manifestación' + : act.status === 'CANCELLED' + ? 'Acta cancelada' + : 'Borrador en campo'; + const lifecycleClass = isSealed ? 'active' : isLocked ? 'observed' : act.status === 'CANCELLED' ? 'inactive' : 'pending'; return
-
CIERRE DEL ACTA · SÓLO LECTURA

Responsable, firmas y sellado

La preparación y firma del acta se realizan desde la APK. La inspección puede continuar con otras actas.

{act.status === 'CLOSED' ? 'Cierre sellado' : act.status === 'READY' ? 'Esperando firmas' : 'Pendiente en APK'}
+
CIERRE DEL ACTA · SÓLO LECTURA

Responsable, firmas y sellado

Al bloquearse, el contenido del Acta queda inmutable. La Inspección puede continuar y generar otras Actas.

{lifecycleLabel}
{error && {error}} -

Operación exclusiva en APK. El dashboard muestra las constancias sincronizadas, pero no permite identificar al responsable, firmar, reabrir ni cerrar el acta.

+

El dashboard no modifica el Acta. El bloqueo y la firma del Inspector se realizan desde la APK. La manifestación de la empresa puede completarse en campo o mediante el enlace seguro posterior.

1Responsable{closure.responsible ? 'Registrado' : 'Pendiente'}
-
2Congelado{closure.closure?.isCurrent ? 'SHA-256 listo' : 'Pendiente'}
-
3Constancias{inspectorSignatures.length} inspector · {companyOutcome ? 'empresa OK' : 'empresa pendiente'}
-
4Cierre{act.status === 'CLOSED' ? 'Sellado' : 'Pendiente'}
+
2Bloqueo{closure.closure?.isCurrent ? 'Contenido inmutable' : 'Pendiente'}
+
3Manifestaciones{inspectorSignatures.length} inspector · {companyOutcome ? 'empresa resuelta' : 'empresa pendiente'}
+
4Sellado{isSealed ? 'Definitivo' : 'Pendiente'}
+
+ +
+
Urgencia{closure.act.urgency === 'URGENT' ? 'Urgente' : 'No urgente'}
+
Plazo configurado{closure.act.deadlineDays ? `${closure.act.deadlineDays} días ${closure.act.deadlineDayType === 'BUSINESS' ? 'hábiles' : 'corridos'}` : 'Pendiente'}
+
Inicio del plazo{closure.act.deadlineBaseAt ? formatDate(closure.act.deadlineBaseAt) : 'Pendiente de evento válido'}
+
Vencimiento{closure.act.deadlineAt ? formatDate(closure.act.deadlineAt) : 'Todavía no iniciado'}
{closure.responsible &&
Situación{closure.responsible.attendanceStatus === 'PRESENT' ? 'Presente' : 'Ausente'}
Responsable{closure.responsible.fullName ?? 'No estuvo presente'}
Documento / cargo{closure.responsible.documentNumber ? `${closure.responsible.documentType} ${closure.responsible.documentNumber}` : 'No informado'}{closure.responsible.position ? ` · ${closure.responsible.position}` : ''}
Contacto{closure.responsible.email ?? closure.responsible.phone ?? 'No informado'}
} - {closure.closure?.isCurrent &&
CONTENIDO CONGELADO{closure.closure.schemaVersion}Preparado {formatDate(closure.closure.preparedAt)}
{closure.closure.preparedSha256}
} + {closure.closure?.isCurrent &&
CONTENIDO BLOQUEADO{closure.closure.schemaVersion}Bloqueado {formatDate(closure.act.lockedAt ?? closure.closure.preparedAt)}
{closure.act.lockedSha256 ?? closure.closure.preparedSha256}
} {closure.signatures.length > 0 &&
{closure.signatures.map((signature) =>
{signatureStatusLabel(signature.status)}{signature.signerName}{signerTypeLabel(signature.signerType)} · {formatDate(signature.createdAt)}
{signature.signaturePayloadSha256}{signature.status === 'SIGNED' ? <>{signature.signerType === 'COMPANY_RESPONSIBLE' &&

{signature.companyManifestation === 'DISSENT' ? 'Firma en disidencia' : 'Firma en conformidad'}{signature.companyStatement ? ` · ${signature.companyStatement}` : ''}

} :

{signature.reason}

}
)}
} - {act.status === 'CLOSED' && closure.closure?.finalSha256 &&
ACTA CERRADA{formatDate(closure.closure.serverClosedAt)}

El acta quedó sellada. La inspección y las demás actas tienen un ciclo independiente.

{closure.closure.finalSha256}
} + {isSealed && closure.closure?.finalSha256 &&
ACTA SELLADA{formatDate(closure.act.sealedAt ?? closure.closure.serverClosedAt)}

El Acta quedó sellada e inmutable. La Inspección y sus demás Actas continúan con ciclo independiente.

{closure.closure.finalSha256}
}
; }