From 82fb0e191bfcbd884ee83b2bde7bfdeae4f9f94a Mon Sep 17 00:00:00 2001 From: enlineawork Date: Thu, 10 Sep 2026 13:06:47 -0300 Subject: [PATCH] fix(F6.1): add explicit hierarchical inspection creation page --- .../pages/InspectionVisitCreateF61Page.tsx | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 web-v2/src/pages/InspectionVisitCreateF61Page.tsx diff --git a/web-v2/src/pages/InspectionVisitCreateF61Page.tsx b/web-v2/src/pages/InspectionVisitCreateF61Page.tsx new file mode 100644 index 0000000..8b58aa0 --- /dev/null +++ b/web-v2/src/pages/InspectionVisitCreateF61Page.tsx @@ -0,0 +1,227 @@ +import { useEffect, useState } from 'react'; +import type { FormEvent } from 'react'; +import { Link, useNavigate } from 'react-router'; +import { Alert, LoadingBlock } from '../components/Feedback'; +import { Icon } from '../components/Icon'; +import { SearchableSelect } from '../components/SearchableSelect'; + +interface Option { + id: string; + code: string; + name: string; +} + +interface Inspector { + id: string; + username: string; + firstName: string; + lastName: string; +} + +interface CreatedInspection { + id: string; + code: string; +} + +async function requestJson(url: string, init?: RequestInit): Promise { + const response = await fetch(`/api/v3${url}`, { + credentials: 'same-origin', + ...init, + headers: { + Accept: 'application/json', + ...(init?.body ? { 'Content-Type': 'application/json' } : {}), + ...(init?.headers ?? {}), + }, + }); + const payload = await response.json().catch(() => ({})); + if (!response.ok) { + const message = typeof payload?.message === 'string' + ? payload.message + : typeof payload?.error?.message === 'string' + ? payload.error.message + : `No se pudo completar la operación (${response.status}).`; + throw new Error(message); + } + return payload as T; +} + +function inspectorName(person: Inspector): string { + return `${person.firstName} ${person.lastName}`.trim() || person.username; +} + +export function InspectionVisitCreateF61Page() { + const navigate = useNavigate(); + const [departments, setDepartments] = useState([]); + const [areas, setAreas] = useState([]); + const [yacimientos, setYacimientos] = useState([]); + const [operators, setOperators] = useState([]); + const [inspectors, setInspectors] = useState([]); + const [departmentId, setDepartmentId] = useState(''); + const [areaId, setAreaId] = useState(''); + const [yacimientoId, setYacimientoId] = useState(''); + const [operatorId, setOperatorId] = useState(''); + const [inspectorId, setInspectorId] = useState(''); + const [plannedStartAt, setPlannedStartAt] = useState(''); + const [loading, setLoading] = useState(true); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + setLoading(true); + Promise.all([ + requestJson<{ data: Option[] }>('/inspection-visits/planning-context/departments'), + requestJson<{ data: Inspector[] }>('/inspection-visits/assignees'), + ]).then(([departmentResponse, inspectorResponse]) => { + setDepartments(departmentResponse.data); + setInspectors(inspectorResponse.data); + }).catch((cause) => { + setError(cause instanceof Error ? cause.message : String(cause)); + }).finally(() => setLoading(false)); + }, []); + + useEffect(() => { + setAreaId(''); + setYacimientoId(''); + setOperatorId(''); + setAreas([]); + setYacimientos([]); + setOperators([]); + if (!departmentId) return; + requestJson<{ data: Option[] }>(`/inspection-visits/planning-context/departments/${departmentId}/areas`) + .then((response) => setAreas(response.data)) + .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause))); + }, [departmentId]); + + useEffect(() => { + setYacimientoId(''); + setOperatorId(''); + setYacimientos([]); + setOperators([]); + if (!areaId) return; + Promise.all([ + requestJson<{ data: Option[] }>(`/inspection-visits/planning-context/areas/${areaId}/yacimientos`), + requestJson<{ data: Option[] }>(`/inspection-visits/planning-context/areas/${areaId}/operators`), + ]).then(([yacimientoResponse, operatorResponse]) => { + setYacimientos(yacimientoResponse.data); + setOperators(operatorResponse.data); + if (operatorResponse.data.length === 1 && operatorResponse.data[0]) { + setOperatorId(operatorResponse.data[0].id); + } + }).catch((cause) => setError(cause instanceof Error ? cause.message : String(cause))); + }, [areaId]); + + const submit = async (event: FormEvent) => { + event.preventDefault(); + setError(''); + if (!departmentId || !areaId || !yacimientoId || !operatorId || !plannedStartAt || !inspectorId) { + setError('Completá Departamento, Área, Yacimiento, Operadora, fecha e Inspector.'); + return; + } + const parsedStart = new Date(plannedStartAt); + if (Number.isNaN(parsedStart.getTime())) { + setError('La fecha y hora de inicio no es válida.'); + return; + } + + setBusy(true); + try { + const created = await requestJson('/inspection-visits', { + method: 'POST', + body: JSON.stringify({ + operationalAreaId: areaId, + scopeAssetId: yacimientoId, + operatorCompanyId: operatorId, + plannedStartAt: parsedStart.toISOString(), + leadInspectorUserId: inspectorId, + }), + }); + navigate(`/inspecciones/${created.id}`, { replace: true }); + } catch (cause) { + setError(cause instanceof Error ? cause.message : String(cause)); + } finally { + setBusy(false); + } + }; + + if (loading) return ; + + return
+
Inspecciones/Planificar inspección
+
+
+ INSPECCIÓN +

Planificar inspección

+

Seleccioná la ubicación respetando la jerarquía Departamento → Área → Yacimiento.

+
+
+ + {error && {error}} + +
+
+
+ CREACIÓN RÁPIDA +

Ubicación, contexto y fecha

+

La Operadora pertenece al contexto temporal del Área. El Yacimiento define el alcance físico de esta Inspección.

+
+
+ +
+ + + + + + + + + + + +
+ + {areaId && yacimientos.length === 0 && Esta Área no tiene Yacimientos cargados. No se puede planificar una Inspección hasta corregir su jerarquía.} + {areaId && operators.length === 0 && Esta Área no tiene una Operadora vigente. Podés consultar el Inventario, pero no planificar una Inspección operativa hasta definir esa relación.} + +
+ Volver + +
+
+
; +}