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'; import { apiRequest } from '../lib/api'; interface Option { id: string; code: string; name: string; } interface Inspector { id: string; username: string; firstName: string; lastName: string; } interface CreatedInspection { id: string; code: string; } function inspectorName(person: Inspector): string { return `${person.firstName} ${person.lastName}`.trim() || person.username; } function currentLocalDateTime(): string { const now = new Date(); const local = new Date(now.getTime() - now.getTimezoneOffset() * 60_000); return local.toISOString().slice(0, 16); } 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(currentLocalDateTime); const [loading, setLoading] = useState(true); const [loadingOperators, setLoadingOperators] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); useEffect(() => { setLoading(true); Promise.all([ apiRequest<{ data: Option[] }>('/inspection-visits/planning-context/departments'), apiRequest<{ 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; apiRequest<{ 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; apiRequest<{ data: Option[] }>(`/inspection-visits/planning-context/areas/${areaId}/yacimientos`) .then((response) => setYacimientos(response.data)) .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause))); }, [areaId]); useEffect(() => { setOperatorId(''); setOperators([]); if (!areaId || !plannedStartAt) return; const parsedStart = new Date(plannedStartAt); if (Number.isNaN(parsedStart.getTime())) return; const at = encodeURIComponent(parsedStart.toISOString()); setLoadingOperators(true); apiRequest<{ data: Option[] }>(`/inspection-visits/planning-context/areas/${areaId}/operators?at=${at}`) .then((response) => { setOperators(response.data); if (response.data.length === 1 && response.data[0]) setOperatorId(response.data[0].id); }) .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause))) .finally(() => setLoadingOperators(false)); }, [areaId, plannedStartAt]); 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 apiRequest('/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. Departamento setDepartmentId(event.target.value)} searchPlaceholder="Buscar Departamento…" required> Seleccionar Departamento… {departments.map((department) => {department.name})} Área setAreaId(event.target.value)} searchPlaceholder="Buscar Área…" required disabled={!departmentId}> Seleccionar Área… {areas.map((area) => {area.name})} Yacimiento setYacimientoId(event.target.value)} searchPlaceholder="Buscar Yacimiento…" required disabled={!areaId}> Seleccionar Yacimiento… {yacimientos.map((yacimiento) => {yacimiento.name})} Operadora del Área setOperatorId(event.target.value)} searchPlaceholder="Buscar Operadora…" required disabled={!areaId || !plannedStartAt || loadingOperators || operators.length === 0}> {loadingOperators ? 'Cargando Operadora…' : operators.length === 0 && areaId && plannedStartAt ? 'Sin Operadora vigente para esa fecha' : 'Seleccionar Operadora…'} {operators.map((operator) => {operator.name})} Se obtiene de la relación vigente del Área para la fecha y hora de inicio. Fecha y hora de inicio setPlannedStartAt(event.target.value)} required /> Inspector responsable setInspectorId(event.target.value)} searchPlaceholder="Buscar Inspector…" required> Seleccionar Inspector… {inspectors.map((inspector) => {inspectorName(inspector)} · {inspector.username})} {areaId && yacimientos.length === 0 && Esta Área no tiene Yacimientos cargados. No se puede planificar una Inspección hasta corregir su jerarquía.} {areaId && plannedStartAt && !loadingOperators && operators.length === 0 && Esta Área no tiene una Operadora vigente para la fecha seleccionada. Podés consultar el Inventario, pero no planificar una Inspección operativa hasta definir esa relación.} Volver {busy ? 'Creando…' : 'Crear inspección'} ; }
Seleccioná la ubicación respetando la jerarquía Departamento → Área → Yacimiento.
La Operadora pertenece al contexto temporal del Área. El Yacimiento define el alcance físico de esta Inspección.