feat(web): capture Yacimiento company and concession

This commit is contained in:
2026-09-11 01:06:18 -03:00
parent b88147d376
commit 718e08be13
+17 -6
View File
@@ -44,6 +44,8 @@ export function InventoryCreatePage() {
const [parentSearch, setParentSearch] = useState(''); const [parentSearch, setParentSearch] = useState('');
const [parentId, setParentId] = useState(''); const [parentId, setParentId] = useState('');
const [familyId, setFamilyId] = useState(''); const [familyId, setFamilyId] = useState('');
const [operatorCompanyId, setOperatorCompanyId] = useState('');
const [concessionTypeId, setConcessionTypeId] = useState('');
const [familyFindings, setFamilyFindings] = useState<InventoryFamilyFindings | null>(null); const [familyFindings, setFamilyFindings] = useState<InventoryFamilyFindings | null>(null);
const [name, setName] = useState(''); const [name, setName] = useState('');
const [code, setCode] = useState(''); const [code, setCode] = useState('');
@@ -66,6 +68,7 @@ export function InventoryCreatePage() {
const requiresParent = Boolean(parentLabelByKind[kind]); const requiresParent = Boolean(parentLabelByKind[kind]);
const requiresFamily = kind === 'INSTALACION' || kind === 'SUBINSTALACION'; const requiresFamily = kind === 'INSTALACION' || kind === 'SUBINSTALACION';
const requiresYacimientoContext = kind === 'YACIMIENTO';
const parentLabel = parentLabelByKind[kind] ?? ''; const parentLabel = parentLabelByKind[kind] ?? '';
useEffect(() => { useEffect(() => {
@@ -99,18 +102,25 @@ export function InventoryCreatePage() {
if (!requiresFamily) setFamilyId(''); if (!requiresFamily) setFamilyId('');
if (kind==='SUBINSTALACION' && familyId && !families.some((item)=>item.id===familyId)) setFamilyId(''); if (kind==='SUBINSTALACION' && familyId && !families.some((item)=>item.id===familyId)) setFamilyId('');
},[kind,requiresFamily,families,familyId]); },[kind,requiresFamily,families,familyId]);
useEffect(() => {
if (!requiresYacimientoContext) { setOperatorCompanyId(''); setConcessionTypeId(''); }
},[requiresYacimientoContext]);
const changeKind=(next:InventoryStructureKind) => { const changeKind=(next:InventoryStructureKind) => {
setKind(next); setParentId(''); setParentSearch(''); setFamilyId(''); setFamilyFindings(null); setError(''); setKind(next); setParentId(''); setParentSearch(''); setFamilyId(''); setFamilyFindings(null);
setOperatorCompanyId(''); setConcessionTypeId(''); setError('');
}; };
const save=async(event:FormEvent) => { const save=async(event:FormEvent) => {
event.preventDefault(); event.preventDefault();
if (requiresParent && !parentId) { setError(`Seleccioná ${parentLabel}.`); return; } if (requiresParent && !parentId) { setError(`Seleccioná ${parentLabel}.`); return; }
if (requiresYacimientoContext && !operatorCompanyId) { setError('Seleccioná la Empresa relacionada del Yacimiento.'); return; }
if (requiresYacimientoContext && !concessionTypeId) { setError('Seleccioná el Tipo de concesión del Yacimiento.'); return; }
if (requiresFamily && !familyId) { setError(`Seleccioná el tipo de ${kindLabel(kind).toLowerCase()}.`); return; } if (requiresFamily && !familyId) { setError(`Seleccioná el tipo de ${kindLabel(kind).toLowerCase()}.`); return; }
setSaving(true); setError(''); setSaving(true); setError('');
try { try {
const created=await createInventoryStructure({ const created=await createInventoryStructure({
kind,name:name.trim(),parentId:parentId || null,familyId:familyId || null, kind,name:name.trim(),parentId:parentId || null,familyId:familyId || null,
operatorCompanyId:operatorCompanyId || null,concessionTypeId:concessionTypeId || null,
code:code.trim() || null,commonName:commonName.trim() || null,description:description.trim() || null, code:code.trim() || null,commonName:commonName.trim() || null,description:description.trim() || null,
}); });
navigate(`/inventarios/${created.id}`,{replace:true}); navigate(`/inventarios/${created.id}`,{replace:true});
@@ -121,14 +131,15 @@ export function InventoryCreatePage() {
if (loading) return <LoadingBlock label="Preparando alta…" />; if (loading) return <LoadingBlock label="Preparando alta…" />;
return <section className="narrow-section asset-detail-page"> return <section className="narrow-section asset-detail-page">
<nav className="breadcrumb asset-detail-breadcrumb" aria-label="Ruta del Inventario"><Link to="/inventarios">Inventarios</Link><span></span><strong>Nuevo registro</strong></nav> <nav className="breadcrumb asset-detail-breadcrumb" aria-label="Ruta del Inventario"><Link to="/inventarios">Inventarios</Link><span></span><strong>Nuevo registro</strong></nav>
<div className="page-heading asset-editor-heading"><div><span className="eyebrow">CARGA MANUAL</span><h1>Nuevo registro</h1><p>Para crear sólo necesitás ubicación, clasificación cuando corresponda y nombre. DH genera el código automáticamente.</p></div></div> <div className="page-heading asset-editor-heading"><div><span className="eyebrow">CARGA MANUAL</span><h1>Nuevo registro</h1><p>Modelo fijo: Departamento Área Yacimiento Instalación Subinstalación. La Empresa y el Tipo de concesión pertenecen al Yacimiento.</p></div></div>
{error && <Alert>{error}</Alert>} {error && <Alert>{error}</Alert>}
<form className="panel form-panel" onSubmit={save}> <form className="panel form-panel" onSubmit={save}>
<div className="form-section"><div><h2>1. ¿Qué querés crear?</h2><p className="section-copy">Departamento Área Yacimiento Instalación Subinstalación. Empresa queda fuera del árbol físico.</p></div><label className="field"><span>Tipo</span><select value={kind} onChange={(event)=>changeKind(event.target.value as InventoryStructureKind)}>{KINDS.map((item)=><option key={item.kind} value={item.kind}>{item.label}</option>)}</select></label></div> <div className="form-section"><div><h2>1. ¿Qué querés crear?</h2><p className="section-copy">Área sólo depende de Departamento. Yacimiento define Área, Empresa relacionada y Tipo de concesión.</p></div><label className="field"><span>Tipo</span><select value={kind} onChange={(event)=>changeKind(event.target.value as InventoryStructureKind)}>{KINDS.map((item)=><option key={item.kind} value={item.kind}>{item.label}</option>)}</select></label></div>
{requiresParent && <div className="form-section"><div><h2>2. Ubicación</h2><p className="section-copy">Elegí el {parentLabel.toLowerCase()} concreto.</p></div><label className="field"><span>Buscar {parentLabel.toLowerCase()}</span><input value={parentSearch} onChange={(event)=>setParentSearch(event.target.value)} placeholder={`Buscar ${parentLabel.toLowerCase()}`} /></label><label className="field"><span>{parentLabel} <em>obligatorio</em></span><select value={parentId} onChange={(event)=>{setParentId(event.target.value);setFamilyId('');}} required><option value="">Seleccionar</option>{parents.map((parent)=><option key={parent.id} value={parent.id}>{parent.name} · {parent.code}</option>)}</select></label></div>} {requiresParent && <div className="form-section"><div><h2>2. Ubicación</h2><p className="section-copy">Elegí el {parentLabel.toLowerCase()} concreto.</p></div><label className="field"><span>Buscar {parentLabel.toLowerCase()}</span><input value={parentSearch} onChange={(event)=>setParentSearch(event.target.value)} placeholder={`Buscar ${parentLabel.toLowerCase()}`} /></label><label className="field"><span>{parentLabel} <em>obligatorio</em></span><select value={parentId} onChange={(event)=>{setParentId(event.target.value);setFamilyId('');}} required><option value="">Seleccionar</option>{parents.map((parent)=><option key={parent.id} value={parent.id}>{parent.name} · {parent.code}</option>)}</select></label></div>}
{requiresFamily && <div className="form-section"><div><h2>3. Clasificación técnica</h2><p className="section-copy">Define los datos técnicos y Hallazgos aplicables. En Subinstalaciones sólo aparecen clasificaciones compatibles con la Instalación elegida.</p></div>{kind==='SUBINSTALACION' && !selectedParent?.inventoryFamily ? <Alert>La Instalación elegida todavía no tiene clasificación técnica.</Alert> : <label className="field"><span>Tipo de {kindLabel(kind).toLowerCase()} <em>obligatorio</em></span><select value={familyId} onChange={(event)=>setFamilyId(event.target.value)} required><option value="">Seleccionar</option>{families.map((family)=><option key={family.id} value={family.id}>{family.name}</option>)}</select></label>}{kind==='SUBINSTALACION' && selectedParent?.inventoryFamily && families.length===0 && <Alert>No hay tipos de Subinstalación compatibles con {selectedParent.inventoryFamily.name}. Configuralos primero en Configuración de Inventarios.</Alert>}{selectedFamily && <div className="context-create-banner"><Icon name="alert" /><div><strong>{selectedFamily.name}</strong><span>{familyFindings ? `${familyFindings.count} Hallazgo${familyFindings.count===1?'':'s'} asociado${familyFindings.count===1?'':'s'}` : 'Cargando Hallazgos asociados…'}</span>{familyFindings && familyFindings.items.length>0 && <ul style={{margin:'8px 0 0',paddingLeft:18}}>{familyFindings.items.slice(0,6).map((item)=><li key={item.id}>{item.title}</li>)}{familyFindings.items.length>6 && <li>+ {familyFindings.items.length-6} más</li>}</ul>}</div></div>}</div>} {requiresYacimientoContext && <div className="form-section"><div><h2>3. Contexto del Yacimiento</h2><p className="section-copy">Estas relaciones pertenecen directamente al Yacimiento y no al Área.</p></div><div className="form-grid"><label className="field"><span>Empresa relacionada <em>obligatorio</em></span><select value={operatorCompanyId} onChange={(event)=>setOperatorCompanyId(event.target.value)} required><option value="">Seleccionar</option>{options?.companies.map((company)=><option key={company.id} value={company.id}>{company.name}</option>)}</select></label><label className="field"><span>Tipo de concesión <em>obligatorio</em></span><select value={concessionTypeId} onChange={(event)=>setConcessionTypeId(event.target.value)} required><option value="">Seleccionar</option>{options?.concessionTypes.map((type)=><option key={type.id} value={type.id}>{type.name}</option>)}</select></label></div></div>}
<div className="form-section"><div><h2>{requiresFamily ? '4' : requiresParent ? '3' : '2'}. Nombre</h2><p className="section-copy">Los datos técnicos se completan después, en la ficha del elemento.</p></div><label className="field"><span>Nombre <em>obligatorio</em></span><input autoFocus value={name} onChange={(event)=>setName(event.target.value)} required maxLength={200} placeholder={`Nombre de ${kindLabel(kind).toLowerCase()}`} /></label><details style={{marginTop:12}}><summary style={{cursor:'pointer',fontWeight:700}}>Datos opcionales</summary><div style={{marginTop:14}}><div className="form-grid"><label className="field"><span>Código DH</span><input value={code} onChange={(event)=>setCode(event.target.value.toUpperCase())} maxLength={120} pattern="[A-Z0-9][A-Z0-9._/-]*" placeholder="Dejalo vacío para generar automáticamente" /></label><label className="field"><span>Nombre habitual</span><input value={commonName} onChange={(event)=>setCommonName(event.target.value)} maxLength={200} /></label></div><label className="field"><span>Descripción</span><textarea value={description} onChange={(event)=>setDescription(event.target.value)} rows={2} maxLength={4000} /></label></div></details></div> {requiresFamily && <div className="form-section"><div><h2>3. Clasificación técnica</h2><p className="section-copy">Define los datos técnicos y Hallazgos aplicables. En Subinstalaciones sólo aparecen clasificaciones compatibles con la Instalación elegida.</p></div>{kind==='SUBINSTALACION' && !selectedParent?.inventoryFamily ? <Alert>La Instalación elegida todavía no tiene clasificación técnica.</Alert> : <label className="field"><span>Tipo de {kindLabel(kind).toLowerCase()} <em>obligatorio</em></span><select value={familyId} onChange={(event)=>setFamilyId(event.target.value)} required><option value="">Seleccionar</option>{families.map((family)=><option key={family.id} value={family.id}>{family.name}</option>)}</select></label>}{kind==='SUBINSTALACION' && selectedParent?.inventoryFamily && families.length===0 && <Alert>No hay tipos de Subinstalación compatibles con {selectedParent.inventoryFamily.name}.</Alert>}{selectedFamily && <div className="context-create-banner"><Icon name="alert" /><div><strong>{selectedFamily.name}</strong><span>{familyFindings ? `${familyFindings.count} Hallazgo${familyFindings.count===1?'':'s'} asociado${familyFindings.count===1?'':'s'}` : 'Cargando Hallazgos asociados…'}</span>{familyFindings && familyFindings.items.length>0 && <ul style={{margin:'8px 0 0',paddingLeft:18}}>{familyFindings.items.slice(0,6).map((item)=><li key={item.id}>{item.title}</li>)}{familyFindings.items.length>6 && <li>+ {familyFindings.items.length-6} más</li>}</ul>}</div></div>}</div>}
<div className="form-actions"><Link className="button secondary" to="/inventarios">Cancelar</Link><button className="button primary" disabled={saving || !name.trim() || (requiresParent && !parentId) || (requiresFamily && !familyId)}><Icon name="check" />{saving?'Creando…':`Crear ${kindLabel(kind)}`}</button></div> <div className="form-section"><div><h2>{requiresYacimientoContext ? '4' : requiresFamily ? '4' : requiresParent ? '3' : '2'}. Nombre</h2><p className="section-copy">El nombre es el dato propio principal de este nivel. Los datos técnicos se completan después, cuando corresponda.</p></div><label className="field"><span>Nombre <em>obligatorio</em></span><input autoFocus value={name} onChange={(event)=>setName(event.target.value)} required maxLength={200} placeholder={`Nombre de ${kindLabel(kind).toLowerCase()}`} /></label><details style={{marginTop:12}}><summary style={{cursor:'pointer',fontWeight:700}}>Datos opcionales</summary><div style={{marginTop:14}}><div className="form-grid"><label className="field"><span>Código DH</span><input value={code} onChange={(event)=>setCode(event.target.value.toUpperCase())} maxLength={120} pattern="[A-Z0-9][A-Z0-9._/-]*" placeholder="Dejalo vacío para generar automáticamente" /></label><label className="field"><span>Nombre habitual</span><input value={commonName} onChange={(event)=>setCommonName(event.target.value)} maxLength={200} /></label></div><label className="field"><span>Descripción</span><textarea value={description} onChange={(event)=>setDescription(event.target.value)} rows={2} maxLength={4000} /></label></div></details></div>
<div className="form-actions"><Link className="button secondary" to="/inventarios">Cancelar</Link><button className="button primary" disabled={saving || !name.trim() || (requiresParent && !parentId) || (requiresYacimientoContext && (!operatorCompanyId || !concessionTypeId)) || (requiresFamily && !familyId)}><Icon name="check" />{saving?'Creando…':`Crear ${kindLabel(kind)}`}</button></div>
</form> </form>
</section>; </section>;
} }