F6.1 hotfix · jerarquía de Inspecciones y scope Yacimiento (#37)
Corrige creación de Inspección, separa Departamento→Área→Yacimiento, elimina el UPDATE oculto de checklist en lectura, integra Preparación para campo como etapa de Inspección, limita operaciones Android al Yacimiento, preserva scope en edición, auto-incluye Inventarios accionables, valida Operadora a plannedStartAt y recompila Android 0.15.2/vc24 con el ícono exacto de presentación.
This commit is contained in:
@@ -21,7 +21,8 @@ import { AssetTypesPage } from '../pages/AssetTypesPage';
|
||||
import { HistoryPage } from '../pages/HistoryPage';
|
||||
import { TemporalAssetsPage } from '../pages/TemporalAssetsPage';
|
||||
import { InspectionVisitsPage } from '../pages/InspectionVisitsPage';
|
||||
import { InspectionVisitEditorF4Page } from '../pages/InspectionVisitEditorF4Page';
|
||||
import { InspectionVisitCreateF61Page } from '../pages/InspectionVisitCreateF61Page';
|
||||
import { InspectionVisitDetailF61Page } from '../pages/InspectionVisitDetailF61Page';
|
||||
import { InspectionActEditorPage } from '../pages/InspectionActEditorPage';
|
||||
import { FindingCatalogPage } from '../pages/FindingCatalogPage';
|
||||
import { FindingDetailPage } from '../pages/FindingDetailPage';
|
||||
@@ -57,10 +58,10 @@ export function App() {
|
||||
<Route path="/planificacion" element={<Navigate to="/inspecciones?status=PLANNED" replace />} />
|
||||
<Route element={<PermissionRoute permission="inspections.read" />}>
|
||||
<Route path="/inspecciones" element={<InspectionVisitsPage />} />
|
||||
<Route path="/inspecciones/:id" element={<InspectionVisitEditorF4Page />} />
|
||||
<Route path="/inspecciones/:id" element={<InspectionVisitDetailF61Page />} />
|
||||
<Route path="/preparacion-campo" element={<FieldBriefingsPage />} />
|
||||
</Route>
|
||||
<Route element={<PermissionRoute permission="inspections.manage" />}><Route path="/inspecciones/nueva" element={<InspectionVisitEditorF4Page />} /></Route>
|
||||
<Route element={<PermissionRoute permission="inspections.manage" />}><Route path="/inspecciones/nueva" element={<InspectionVisitCreateF61Page />} /></Route>
|
||||
<Route element={<PermissionRoute permission="inspection_acts.read" />}>
|
||||
<Route path="/inspecciones/actas/:actId" element={<InspectionActEditorPage />} />
|
||||
<Route path="/inspecciones/actas/nueva" element={<InspectionActEditorPage />} />
|
||||
|
||||
@@ -16,7 +16,6 @@ interface NavItem {
|
||||
const operational: NavItem[] = [
|
||||
{ to: '/', label: 'Inicio', icon: 'home', permission: 'dashboard.read' },
|
||||
{ to: '/inspecciones', label: 'Inspecciones', icon: 'clipboard', permission: 'inspections.read' },
|
||||
{ to: '/preparacion-campo', label: 'Preparación de campo', icon: 'clipboard', permission: 'inspections.read' },
|
||||
];
|
||||
|
||||
const followUp: NavItem[] = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link, useSearchParams } from 'react-router';
|
||||
import { SearchableSelect } from '../components/SearchableSelect';
|
||||
import { Alert, EmptyState, LoadingBlock } from '../components/Feedback';
|
||||
import { formatDate, formatDateOnly } from '../lib/format';
|
||||
@@ -17,6 +17,7 @@ interface VisitOption {
|
||||
code: string;
|
||||
status: string;
|
||||
plannedStartAt: string | null;
|
||||
scopeAsset?: { name: string; typeName?: string } | null;
|
||||
operationalArea: { name: string } | null;
|
||||
operatorCompany: { name: string } | null;
|
||||
}
|
||||
@@ -116,8 +117,10 @@ function findingAction(finding: BriefingFinding, act: BriefingAct, plannedOn: st
|
||||
}
|
||||
|
||||
export function FieldBriefingsPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const requestedInspectionId = searchParams.get('inspectionId') ?? '';
|
||||
const [visits, setVisits] = useState<VisitOption[]>([]);
|
||||
const [visitId, setVisitId] = useState('');
|
||||
const [visitId, setVisitId] = useState(requestedInspectionId);
|
||||
const [briefing, setBriefing] = useState<FieldBriefing | null>(null);
|
||||
const [loadingVisits, setLoadingVisits] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -132,12 +135,15 @@ export function FieldBriefingsPage() {
|
||||
const all = [...planned.data, ...drafts.data]
|
||||
.sort((a, b) => String(a.plannedStartAt ?? '').localeCompare(String(b.plannedStartAt ?? '')));
|
||||
setVisits(all);
|
||||
const onlyVisit = all[0];
|
||||
if (all.length === 1 && onlyVisit) setVisitId(onlyVisit.id);
|
||||
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) {
|
||||
@@ -158,11 +164,12 @@ export function FieldBriefingsPage() {
|
||||
);
|
||||
|
||||
return <section className="field-briefing-page">
|
||||
<div className="breadcrumb"><Link to="/inspecciones">Inspecciones</Link><span>/</span><span>Preparación para campo</span></div>
|
||||
<div className="page-heading">
|
||||
<div>
|
||||
<span className="eyebrow">ANTES DE SALIR A CAMPO</span>
|
||||
<h1>Preparación de campo</h1>
|
||||
<p>Seleccioná la inspección y revisá los pendientes de Actas anteriores del mismo Área/Yacimiento y Operadora.</p>
|
||||
<span className="eyebrow">ETAPA DE LA INSPECCIÓN</span>
|
||||
<h1>Preparación para campo</h1>
|
||||
<p>Revisá antecedentes y pendientes antes de iniciar la Inspección desde la APK.</p>
|
||||
</div>
|
||||
{briefing && <button className="button secondary no-print" type="button" onClick={() => window.print()}>
|
||||
Imprimir preparación
|
||||
@@ -170,15 +177,15 @@ export function FieldBriefingsPage() {
|
||||
</div>
|
||||
|
||||
<div className="form-card no-print">
|
||||
<label>Inspección planificada</label>
|
||||
<label>Inspección</label>
|
||||
{loadingVisits ? <LoadingBlock label="Cargando inspecciones…" /> : <SearchableSelect
|
||||
value={visitId}
|
||||
onChange={(event) => setVisitId(event.target.value)}
|
||||
searchPlaceholder="Buscar por código, Área u Operadora…"
|
||||
searchPlaceholder="Buscar por código, Yacimiento, Área u Operadora…"
|
||||
>
|
||||
<option value="">Seleccionar inspección…</option>
|
||||
{visits.map((visit) => <option key={visit.id} value={visit.id}>
|
||||
{visit.code} · {visit.operationalArea?.name ?? 'Sin Área'} · {visit.operatorCompany?.name ?? 'Sin Operadora'}
|
||||
{visit.code} · {visit.scopeAsset?.name ?? 'Sin Yacimiento'} · {visit.operationalArea?.name ?? 'Sin Área'} · {visit.operatorCompany?.name ?? 'Sin Operadora'}
|
||||
</option>)}
|
||||
</SearchableSelect>}
|
||||
{selected?.plannedStartAt && <small className="block-muted">Salida prevista: {formatDate(selected.plannedStartAt)}</small>}
|
||||
@@ -189,18 +196,18 @@ export function FieldBriefingsPage() {
|
||||
|
||||
{!loading && !briefing && !error && <EmptyState
|
||||
title="Seleccioná una inspección"
|
||||
text="El sistema reunirá automáticamente los pendientes de inspecciones anteriores del mismo contexto."
|
||||
text="El sistema reunirá automáticamente sus antecedentes y controles pendientes."
|
||||
/>}
|
||||
|
||||
{!loading && briefing && <>
|
||||
<div className="page-heading compact">
|
||||
<div>
|
||||
<span className="eyebrow">{briefing.inspection.code}</span>
|
||||
<h2>{briefing.inspection.area?.name ?? 'Área sin definir'} · {briefing.inspection.operatorCompany?.name ?? 'Operadora sin definir'}</h2>
|
||||
<p>Fecha de referencia: {formatDateOnly(briefing.plannedOn)}</p>
|
||||
<h2>{selected?.scopeAsset?.name ?? 'Yacimiento sin definir'} · {briefing.inspection.area?.name ?? 'Área sin definir'}</h2>
|
||||
<p>{briefing.inspection.operatorCompany?.name ?? 'Operadora sin definir'} · Fecha de referencia: {formatDateOnly(briefing.plannedOn)}</p>
|
||||
</div>
|
||||
<Link className="button secondary no-print" to={`/inspecciones/${briefing.inspection.id}`}>
|
||||
Abrir planificación
|
||||
Volver a la inspección
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
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<T>(url: string, init?: RequestInit): Promise<T> {
|
||||
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<Option[]>([]);
|
||||
const [areas, setAreas] = useState<Option[]>([]);
|
||||
const [yacimientos, setYacimientos] = useState<Option[]>([]);
|
||||
const [operators, setOperators] = useState<Option[]>([]);
|
||||
const [inspectors, setInspectors] = useState<Inspector[]>([]);
|
||||
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;
|
||||
requestJson<{ 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());
|
||||
requestJson<{ 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)));
|
||||
}, [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 requestJson<CreatedInspection>('/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 <LoadingBlock label="Cargando estructura territorial…" />;
|
||||
|
||||
return <section className="survey-editor inspection-editor">
|
||||
<div className="breadcrumb"><Link to="/inspecciones">Inspecciones</Link><span>/</span><span>Planificar inspección</span></div>
|
||||
<div className="page-heading survey-editor-heading">
|
||||
<div>
|
||||
<span className="eyebrow">INSPECCIÓN</span>
|
||||
<h1>Planificar inspección</h1>
|
||||
<p>Seleccioná la ubicación respetando la jerarquía Departamento → Área → Yacimiento.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <Alert>{error}</Alert>}
|
||||
|
||||
<form className="panel form-panel inspection-quick-create" onSubmit={submit}>
|
||||
<div className="panel-heading">
|
||||
<div>
|
||||
<span className="eyebrow">CREACIÓN RÁPIDA</span>
|
||||
<h2>Ubicación, contexto y fecha</h2>
|
||||
<p className="section-copy">La Operadora pertenece al contexto temporal del Área. El Yacimiento define el alcance físico de esta Inspección.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-grid">
|
||||
<label className="field">
|
||||
<span>Departamento</span>
|
||||
<SearchableSelect value={departmentId} onChange={(event) => setDepartmentId(event.target.value)} searchPlaceholder="Buscar Departamento…" required>
|
||||
<option value="">Seleccionar Departamento…</option>
|
||||
{departments.map((department) => <option key={department.id} value={department.id}>{department.name}</option>)}
|
||||
</SearchableSelect>
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>Área</span>
|
||||
<SearchableSelect value={areaId} onChange={(event) => setAreaId(event.target.value)} searchPlaceholder="Buscar Área…" required disabled={!departmentId}>
|
||||
<option value="">Seleccionar Área…</option>
|
||||
{areas.map((area) => <option key={area.id} value={area.id}>{area.name}</option>)}
|
||||
</SearchableSelect>
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>Yacimiento</span>
|
||||
<SearchableSelect value={yacimientoId} onChange={(event) => setYacimientoId(event.target.value)} searchPlaceholder="Buscar Yacimiento…" required disabled={!areaId}>
|
||||
<option value="">Seleccionar Yacimiento…</option>
|
||||
{yacimientos.map((yacimiento) => <option key={yacimiento.id} value={yacimiento.id}>{yacimiento.name}</option>)}
|
||||
</SearchableSelect>
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>Operadora del Área</span>
|
||||
<SearchableSelect value={operatorId} onChange={(event) => setOperatorId(event.target.value)} searchPlaceholder="Buscar Operadora…" required disabled={!areaId || !plannedStartAt || operators.length === 0}>
|
||||
<option value="">{operators.length === 0 && areaId && plannedStartAt ? 'Sin Operadora vigente para esa fecha' : 'Seleccionar Operadora…'}</option>
|
||||
{operators.map((operator) => <option key={operator.id} value={operator.id}>{operator.name}</option>)}
|
||||
</SearchableSelect>
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>Fecha y hora de inicio</span>
|
||||
<input type="datetime-local" value={plannedStartAt} onChange={(event) => setPlannedStartAt(event.target.value)} required />
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>Inspector responsable</span>
|
||||
<SearchableSelect value={inspectorId} onChange={(event) => setInspectorId(event.target.value)} searchPlaceholder="Buscar Inspector…" required>
|
||||
<option value="">Seleccionar Inspector…</option>
|
||||
{inspectors.map((inspector) => <option key={inspector.id} value={inspector.id}>{inspectorName(inspector)} · {inspector.username}</option>)}
|
||||
</SearchableSelect>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{areaId && yacimientos.length === 0 && <Alert>Esta Área no tiene Yacimientos cargados. No se puede planificar una Inspección hasta corregir su jerarquía.</Alert>}
|
||||
{areaId && plannedStartAt && operators.length === 0 && <Alert>Esta Área no tiene una Operadora vigente. Podés consultar el Inventario, pero no planificar una Inspección operativa hasta definir esa relación.</Alert>}
|
||||
|
||||
<div className="form-actions">
|
||||
<Link className="button secondary" to="/inspecciones">Volver</Link>
|
||||
<button className="button primary" disabled={busy || !departmentId || !areaId || !yacimientoId || !operatorId || !plannedStartAt || !inspectorId}>
|
||||
<Icon name="check" />{busy ? 'Creando…' : 'Crear inspección'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Link, useParams } from 'react-router';
|
||||
import { Icon } from '../components/Icon';
|
||||
import { InspectionVisitEditorF4Page } from './InspectionVisitEditorF4Page';
|
||||
|
||||
/**
|
||||
* F6.1 keeps Preparación para campo as a stage of the Inspection. The existing
|
||||
* detail remains unchanged underneath; this wrapper makes the relationship
|
||||
* explicit without creating a second operational module.
|
||||
*/
|
||||
export function InspectionVisitDetailF61Page() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
||||
return <>
|
||||
{id && <article className="panel survey-status-actions no-print">
|
||||
<div>
|
||||
<span className="eyebrow">ETAPA PREVIA AL CAMPO</span>
|
||||
<strong>Preparación para campo</strong>
|
||||
<p>Revisá antecedentes, hallazgos pendientes y próximos controles de esta Inspección antes de iniciarla desde la APK.</p>
|
||||
</div>
|
||||
<div className="form-actions">
|
||||
<Link className="button secondary" to={`/preparacion-campo?inspectionId=${id}`}>
|
||||
<Icon name="clipboard" /> Abrir preparación
|
||||
</Link>
|
||||
</div>
|
||||
</article>}
|
||||
<InspectionVisitEditorF4Page />
|
||||
</>;
|
||||
}
|
||||
@@ -207,6 +207,7 @@ export function InspectionVisitEditorF4Page() {
|
||||
} else if (id) {
|
||||
applyVisit(await updateInspectionVisit(id, {
|
||||
objective: form.objective.trim() || null,
|
||||
scopeAssetId: visit?.scopeAsset?.id ?? null,
|
||||
operationalAreaId: form.operationalAreaId || null,
|
||||
operatorCompanyId: form.operatorCompanyId || null,
|
||||
plannedStartAt,
|
||||
@@ -325,7 +326,8 @@ export function InspectionVisitEditorF4Page() {
|
||||
<div className="panel-heading"><div><span className="eyebrow">{isNew ? 'CREACIÓN RÁPIDA' : 'PLANIFICACIÓN'}</span><h2>Contexto y fecha de inicio</h2><p className="section-copy">No existe título independiente ni fecha de fin planificada. El cierre real se registra al terminar el trabajo de campo.</p></div>{visit && <small className="muted">Actualizado {formatDate(visit.updatedAt)}</small>}</div>
|
||||
{visit && <div className="inspection-generated-code"><small>Identificador institucional</small><strong>{visit.code}</strong></div>}
|
||||
<div className="form-grid">
|
||||
<label className="field"><span>Área / Yacimiento</span><SearchableSelect searchPlaceholder="Buscar Área…" value={form.operationalAreaId} onChange={(event) => { context.setAreaId(event.target.value); setForm((current) => ({ ...current, operationalAreaId: event.target.value, operatorCompanyId: '' })); }} required disabled={!canManage || !planningEditable}><option value="">Seleccionar Área…</option>{areas.map((area) => <option key={area.id} value={area.id}>{area.name} · {area.code}</option>)}</SearchableSelect></label>
|
||||
<label className="field"><span>Área</span><SearchableSelect searchPlaceholder="Buscar Área…" value={form.operationalAreaId} onChange={(event) => { context.setAreaId(event.target.value); setForm((current) => ({ ...current, operationalAreaId: event.target.value, operatorCompanyId: '' })); }} required disabled={!canManage || !planningEditable}><option value="">Seleccionar Área…</option>{areas.map((area) => <option key={area.id} value={area.id}>{area.name} · {area.code}</option>)}</SearchableSelect></label>
|
||||
{!isNew && <label className="field"><span>Yacimiento</span><input value={visit?.scopeAsset?.name ?? 'Sin Yacimiento'} readOnly aria-readonly="true" /></label>}
|
||||
<label className="field"><span>Operadora</span><SearchableSelect searchPlaceholder="Buscar Operadora…" value={form.operatorCompanyId} onChange={(event) => { context.setCompanyId(event.target.value); setForm((current) => ({ ...current, operatorCompanyId: event.target.value })); }} required disabled={!canManage || !planningEditable || !form.operationalAreaId}><option value="">Seleccionar Operadora…</option>{operators.map((operator) => <option key={operator.id} value={operator.id}>{operator.name} · {operator.code}</option>)}</SearchableSelect></label>
|
||||
<label className="field"><span>Fecha y hora de inicio</span><input type="datetime-local" value={form.plannedStartAt} onChange={(event) => setForm((current) => ({ ...current, plannedStartAt: event.target.value }))} required disabled={!canManage || !planningEditable} /></label>
|
||||
{isNew && <label className="field"><span>Inspector responsable</span><SearchableSelect searchPlaceholder="Buscar Inspector…" value={leadInspectorId} onChange={(event) => setLeadInspectorId(event.target.value)} required disabled={!canAssign}><option value="">Seleccionar Inspector…</option>{assignees.map((person) => <option key={person.id} value={person.id}>{personName(person)} · {person.username}</option>)}</SearchableSelect></label>}
|
||||
|
||||
Reference in New Issue
Block a user