From 1775392eb154e6a078eb095afd1b454e2f62454d Mon Sep 17 00:00:00 2001 From: enlineawork Date: Mon, 7 Sep 2026 23:00:01 -0300 Subject: [PATCH] F4: make findings worklist technical-only --- web-v2/src/pages/FindingsPage.tsx | 129 ++++++++++++++---------------- 1 file changed, 59 insertions(+), 70 deletions(-) diff --git a/web-v2/src/pages/FindingsPage.tsx b/web-v2/src/pages/FindingsPage.tsx index 858ee8b..b987118 100644 --- a/web-v2/src/pages/FindingsPage.tsx +++ b/web-v2/src/pages/FindingsPage.tsx @@ -7,69 +7,29 @@ import { Alert, EmptyState, LoadingBlock, errorMessage } from '../components/Fee import { Icon } from '../components/Icon'; import { useOperationalContext } from '../context/OperationalContext'; import { OperationalFilters } from '../features/inspections/OperationalFilters'; -import { listInspectionFindingsGlobal } from '../lib/api'; +import { + listFindingWorklistF4, +} from '../lib/findingWorklistF4Api'; import type { - InspectionFinding, - InspectionFindingWorkflow, - InspectionFindingWorkflowCounters, - PageMeta, -} from '../lib/api'; + FindingWorklistCountersF4, + FindingWorklistItemF4, + FindingWorklistWorkflowF4, +} from '../lib/findingWorklistF4Api'; import { formatDateOnly } from '../lib/format'; -const workflows: Array<{ value: InspectionFindingWorkflow; label: string }> = [ +const workflows: Array<{ value: FindingWorklistWorkflowF4; label: string }> = [ { value: 'OPEN', label: 'Abiertos' }, - { value: 'WAITING_COMPANY', label: 'Esperando empresa' }, - { value: 'COMPANY_OVERDUE', label: 'Respuesta vencida' }, - { value: 'TO_SCHEDULE_VERIFICATION', label: 'Programar verificación' }, - { value: 'TO_VERIFY', label: 'Para verificar' }, + { value: 'TO_SCHEDULE_VERIFICATION', label: 'Sin fecha de control' }, + { value: 'TO_VERIFY', label: 'Con control programado' }, { value: 'VERIFICATION_OVERDUE', label: 'Verificación vencida' }, { value: 'READY_TO_CLOSE', label: 'Listos para cerrar' }, { value: 'CLOSED', label: 'Cerrados' }, { value: 'ALL', label: 'Todos' }, ]; -function todayInput(): string { - const now = new Date(); - const local = new Date(now.getTime() - now.getTimezoneOffset() * 60_000); - return local.toISOString().slice(0, 10); -} - -function attentionLabel(finding: InspectionFinding): { label: string; className: string } { - const today = todayInput(); - if (finding.latestVerification?.outcome === 'RESOLVED' && finding.latestVerification.visitStatus === 'CLOSED') return { label: 'Listo para cerrar', className: 'active' }; - if (finding.status === 'CLOSED') return { label: 'Cerrado', className: 'active' }; - if (!finding.companyResponseReceivedOn) { - if (finding.correctionDueOn && finding.correctionDueOn < today) return { label: 'Respuesta vencida', className: 'danger' }; - return { label: 'Esperando empresa', className: 'pending' }; - } - if (!finding.nextControlOn) return { label: 'Programar verificación', className: 'observed' }; - if (finding.nextControlOn < today) return { label: 'Verificación vencida', className: 'danger' }; - return { label: 'Para verificar', className: 'blue' }; -} - -function deadlineText(finding: InspectionFinding): { title: string; value: string | null; overdue: boolean } { - const today = todayInput(); - if (finding.latestVerification?.outcome === 'RESOLVED' && finding.latestVerification.visitStatus === 'CLOSED') return { title: 'Verificado conforme', value: finding.latestVerification.verifiedAt, overdue: false }; - if (!finding.companyResponseReceivedOn) { - return { - title: 'Respuesta de empresa', - value: finding.correctionDueOn, - overdue: Boolean(finding.correctionDueOn && finding.correctionDueOn < today), - }; - } - return { - title: 'Verificación DH', - value: finding.nextControlOn, - overdue: Boolean(finding.nextControlOn && finding.nextControlOn < today), - }; -} - -const emptyCounters: InspectionFindingWorkflowCounters = { +const emptyCounters: FindingWorklistCountersF4 = { open: 0, - waitingCompany: 0, - companyOverdue: 0, - companyDueNext7Days: 0, - awaitingVerificationSchedule: 0, + withoutControlDate: 0, toVerify: 0, verificationOverdue: 0, verificationNext30Days: 0, @@ -77,17 +37,47 @@ const emptyCounters: InspectionFindingWorkflowCounters = { closed: 0, }; +function todayInput(): string { + const now = new Date(); + const local = new Date(now.getTime() - now.getTimezoneOffset() * 60_000); + return local.toISOString().slice(0, 10); +} + +function attentionLabel(finding: FindingWorklistItemF4): { label: string; className: string } { + const today = todayInput(); + if (finding.status === 'CLOSED') return { label: 'Cerrado', className: 'active' }; + if (finding.latestVerification?.outcome === 'RESOLVED' && finding.latestVerification.visitStatus === 'CLOSED') { + return { label: 'Listo para cerrar', className: 'active' }; + } + if (!finding.nextControlOn) return { label: 'Sin fecha de control', className: 'observed' }; + if (finding.verificationVisit) return { label: 'Verificación planificada', className: 'blue' }; + if (finding.nextControlOn < today) return { label: 'Verificación vencida', className: 'danger' }; + return { label: 'Control programado', className: 'pending' }; +} + +function deadlineText(finding: FindingWorklistItemF4): { title: string; value: string | null; overdue: boolean } { + const today = todayInput(); + if (finding.latestVerification?.outcome === 'RESOLVED' && finding.latestVerification.visitStatus === 'CLOSED') { + return { title: 'Verificado conforme', value: finding.latestVerification.verifiedAt, overdue: false }; + } + return { + title: finding.nextControlOn ? 'Verificación DH' : 'Sin fecha definida', + value: finding.nextControlOn, + overdue: Boolean(finding.nextControlOn && finding.nextControlOn < today), + }; +} + export function FindingsPage() { const [params, setParams] = useSearchParams(); const operationalContext = useOperationalContext(); - const [items, setItems] = useState([]); - const [meta, setMeta] = useState({ page: 1, pageSize: 25, total: 0, totalPages: 0 }); - const [counters, setCounters] = useState(emptyCounters); + const [items, setItems] = useState([]); + const [meta, setMeta] = useState({ page: 1, pageSize: 25, total: 0, totalPages: 0 }); + const [counters, setCounters] = useState(emptyCounters); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const workflowValue = params.get('workflow') ?? 'OPEN'; const workflow = workflows.some((item) => item.value === workflowValue) - ? workflowValue as InspectionFindingWorkflow + ? workflowValue as FindingWorklistWorkflowF4 : 'OPEN'; const search = params.get('search') ?? ''; const [draftSearch, setDraftSearch] = useState(search); @@ -101,7 +91,7 @@ export function FindingsPage() { useEffect(() => { setLoading(true); setError(''); - listInspectionFindingsGlobal({ page, pageSize: 25, search, workflow, companyId, areaId, inspectorId, dateFrom, dateTo }) + listFindingWorklistF4({ page, pageSize: 25, search, workflow, companyId, areaId, inspectorId, dateFrom, dateTo }) .then((response) => { setItems(response.data); setMeta(response.meta); @@ -140,38 +130,37 @@ export function FindingsPage() { return
-
SEGUIMIENTO

Hallazgos

Bandeja de trabajo para respuestas de empresas, vencimientos, verificaciones y cierres.

+
SEGUIMIENTO TÉCNICO

Hallazgos

Observaciones originales, evidencia, fechas de control, verificaciones y cierre técnico. Las presentaciones administrativas de empresa se siguen desde el INF.

Planificar verificaciones
-
1Vencimiento administrativoFecha límite para recibir la respuesta de la empresa.
+
1HallazgoObservación y Descripción registradas en el Acta.
-
2Respuesta recibidaSe carga la presentación y su documentación.
+
2Fecha de controlSe define cuándo corresponde volver a verificar.
-
3Vencimiento de verificaciónFecha para volver a inspeccionar o verificar la solución.
+
3VerificaciónEl inspector registra el resultado y la evidencia en campo.
-
4CierreEl hallazgo se cierra cuando la solución queda verificada.
+
4CierreSe cierra cuando la solución queda técnicamente verificada.
- - - - + + +
- + {error && {error}} - {loading ? : items.length === 0 ? :
+ {loading ? : items.length === 0 ? :
{meta.total} hallazgo{meta.total === 1 ? '' : 's'}Página {page} de {Math.max(meta.totalPages, 1)}
-
{items.map((finding) => { +
HallazgoEmpresa / áreaElementoSituaciónPróximo vencimientoDocumentación
{items.map((finding) => { const attention = attentionLabel(finding); const deadline = deadlineText(finding); return @@ -180,7 +169,7 @@ export function FindingsPage() { - + ; })}
HallazgoEmpresa / áreaElementoSituaciónFecha de controlDescripción
{finding.asset.name}{finding.asset.code} {attention.label}
{deadline.title}{formatDateOnly(deadline.value)}
{finding.companyResponseReceivedOn ? `Respuesta ${formatDateOnly(finding.companyResponseReceivedOn)}` : 'Sin respuesta'}{finding.description}