import { useEffect, useMemo, useState } from 'react'; import { Link, useSearchParams } from 'react-router'; import { SearchableSelect } from '../components/SearchableSelect'; import { Alert, EmptyState, LoadingBlock } from '../components/Feedback'; import { formatDate, formatDateOnly } from '../lib/format'; type AdminState = | 'ACT_RESPONSE_OVERDUE' | 'ACT_RESPONSE_DUE_SOON' | 'WAITING_RESPONSE' | 'COMPANY_COMMITMENT_OVERDUE' | 'VERIFICATION_PENDING' | 'RESPONSE_RECEIVED'; interface VisitOption { id: string; code: string; status: string; plannedStartAt: string | null; scopeAsset?: { name: string; typeName?: string } | null; operationalArea: { name: string } | null; operatorCompany: { name: string } | null; } interface BriefingFinding { id: string; code: string; title: string; status: string; severity: number | null; nextControlOn: string | null; asset: { id: string; code: string; name: string; typeName: string; }; } interface BriefingAct { actId: string; actCode: string; occurredAt: string; adminState: AdminState; responseDueOn: string | null; responseReceivedOn: string | null; committedCorrectionOn: string | null; latestResponseId: string | null; findings: BriefingFinding[]; } interface FieldBriefing { inspection: { id: string; code: string; status: string; plannedStartAt: string | null; area: { id: string; code: string | null; name: string | null } | null; operatorCompany: { id: string; code: string | null; name: string | null } | null; }; plannedOn: string; summary: { acts: number; findings: number; inventoryItems: number; responseOverdue: number; commitmentOverdue: number; verificationPending: number; }; acts: BriefingAct[]; } async function getJson(url: string): Promise { const response = await fetch(url, { credentials: 'same-origin', headers: { Accept: 'application/json' }, }); const payload = await response.json().catch(() => ({})); if (!response.ok) { const message = typeof payload?.message === 'string' ? payload.message : 'No se pudo preparar el paquete de campo.'; throw new Error(message); } return payload as T; } function adminStateLabel(state: AdminState): string { const labels: Record = { ACT_RESPONSE_OVERDUE: 'Respuesta del Acta vencida', ACT_RESPONSE_DUE_SOON: 'Respuesta próxima a vencer', WAITING_RESPONSE: 'Esperando respuesta', COMPANY_COMMITMENT_OVERDUE: 'Compromiso de empresa vencido', VERIFICATION_PENDING: 'Verificación pendiente', RESPONSE_RECEIVED: 'Respuesta recibida', }; return labels[state]; } function findingAction(finding: BriefingFinding, act: BriefingAct, plannedOn: string) { if (finding.nextControlOn) { if (finding.nextControlOn <= plannedOn) { return { label: 'Revisar en campo', detail: 'Control programado o vencido', tone: 'danger' }; } return { label: 'Próximo control', detail: `Previsto ${formatDateOnly(finding.nextControlOn)}`, tone: 'pending' }; } if (act.adminState === 'VERIFICATION_PENDING') { return { label: 'Revisar en campo', detail: 'Respuesta recibida sin control programado', tone: 'danger' }; } if (act.adminState === 'COMPANY_COMMITMENT_OVERDUE') { return { label: 'Revisar en campo', detail: 'Regularización comprometida vencida', tone: 'danger' }; } if (act.adminState === 'RESPONSE_RECEIVED') { return { label: 'Antecedente', detail: 'Respuesta de empresa recibida', tone: 'pending' }; } return { label: 'Antecedente', detail: adminStateLabel(act.adminState), tone: 'pending' }; } export function FieldBriefingsPage() { const [searchParams] = useSearchParams(); const requestedInspectionId = searchParams.get('inspectionId') ?? ''; const [visits, setVisits] = useState([]); const [visitId, setVisitId] = useState(requestedInspectionId); const [briefing, setBriefing] = useState(null); const [loadingVisits, setLoadingVisits] = useState(true); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); useEffect(() => { setLoadingVisits(true); Promise.all([ getJson<{ data: VisitOption[] }>('/api/v3/inspection-visits?status=PLANNED&page=1&pageSize=100'), getJson<{ data: VisitOption[] }>('/api/v3/inspection-visits?status=DRAFT&page=1&pageSize=100'), ]).then(([planned, drafts]) => { const all = [...planned.data, ...drafts.data] .sort((a, b) => String(a.plannedStartAt ?? '').localeCompare(String(b.plannedStartAt ?? ''))); setVisits(all); if (requestedInspectionId && all.some((visit) => visit.id === requestedInspectionId)) { setVisitId(requestedInspectionId); } else if (all.length === 1 && all[0]) { setVisitId(all[0].id); } }).catch((cause) => { setError(cause instanceof Error ? cause.message : String(cause)); }).finally(() => setLoadingVisits(false)); }, [requestedInspectionId]); useEffect(() => { if (!visitId) { setBriefing(null); return; } setLoading(true); setError(''); getJson(`/api/v3/inspection-visits/${visitId}/field-briefing`) .then(setBriefing) .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause))) .finally(() => setLoading(false)); }, [visitId]); const selected = useMemo( () => visits.find((visit) => visit.id === visitId) ?? null, [visits, visitId], ); return
Inspecciones/Preparación para campo
ETAPA DE LA INSPECCIÓN

Preparación para campo

Revisá antecedentes y pendientes antes de iniciar la Inspección desde la APK.

{briefing && }
{loadingVisits ? : setVisitId(event.target.value)} searchPlaceholder="Buscar por código, Yacimiento, Área u Operadora…" > {visits.map((visit) => )} } {selected?.plannedStartAt && Salida prevista: {formatDate(selected.plannedStartAt)}}
{error && {error}} {loading && } {!loading && !briefing && !error && } {!loading && briefing && <>
{briefing.inspection.code}

{selected?.scopeAsset?.name ?? 'Yacimiento sin definir'} · {briefing.inspection.area?.name ?? 'Área sin definir'}

{briefing.inspection.operatorCompany?.name ?? 'Operadora sin definir'} · Fecha de referencia: {formatDateOnly(briefing.plannedOn)}

Volver a la inspección
Actas {briefing.summary.acts} Hallazgos {briefing.summary.findings} Inventario {briefing.summary.inventoryItems} Respuestas vencidas {briefing.summary.responseOverdue} Compromisos vencidos {briefing.summary.commitmentOverdue} Verificaciones pendientes {briefing.summary.verificationPending}
{briefing.acts.length === 0 ? : briefing.acts.map((act) =>
{act.actCode} {formatDate(act.occurredAt)} · {adminStateLabel(act.adminState)}
Plazo del Acta {act.responseDueOn ? formatDateOnly(act.responseDueOn) : 'Sin definir'}
Respuesta empresa {act.responseReceivedOn ? formatDateOnly(act.responseReceivedOn) : 'Pendiente'}
Compromiso empresa {act.committedCorrectionOn ? formatDateOnly(act.committedCorrectionOn) : 'Sin fecha'}
{act.findings.map((finding) => { const action = findingAction(finding, act, briefing.plannedOn); return ; })}
Qué hacerHallazgoInventarioControl
{action.label} {action.detail} {finding.code} {finding.title} {finding.asset.code} {finding.asset.name} · {finding.asset.typeName} {finding.nextControlOn ? formatDateOnly(finding.nextControlOn) : 'Sin fecha'}
)} }
; }