@@ -0,0 +1,437 @@
import { useEffect , useMemo , useState } from 'react' ;
import type { FormEvent } from 'react' ;
import { Link , useSearchParams } from 'react-router' ;
import { useAuth } from '../auth/AuthContext' ;
import { Alert , EmptyState , LoadingBlock , errorMessage } from '../components/Feedback' ;
import { Icon } from '../components/Icon' ;
import { SearchableSelect } from '../components/SearchableSelect' ;
import { AssetHistoryPanel } from '../features/assets/AssetHistoryPanel' ;
import { AssetMediaPanel } from '../features/assets/AssetMediaPanel' ;
import { assetStatusClass , assetStatusLabel } from '../features/assets/assetPresentation' ;
import {
createAreaCompanyRelation ,
endAreaCompanyRelation ,
getAssetRegistry ,
listAreaCompanyRelations ,
listInspectionVisits ,
listOperationalAreas ,
updateAsset ,
upsertOrganizationProfile ,
} from '../lib/api' ;
import type {
AreaCompanyRelation ,
AssetDetail ,
AssetRegistry ,
OperationalAssetSummary ,
OrganizationKind ,
} from '../lib/api' ;
import {
listInspectionActsGlobalF4 ,
type InspectionActListItemF4 ,
type InspectionActStatusF4 ,
} from '../lib/inspectionActF4Api' ;
import {
listInspectionReportsF4 ,
type InspectionReportDetailF4 ,
} from '../lib/reportWorkflowApi' ;
import { formatDate } from '../lib/format' ;
import './companyInventory.css' ;
type CompanyTab = 'summary' | 'issued' | 'documents' | 'history' ;
const organizationLabels : Record < OrganizationKind , string > = {
COMPANY : 'Empresa' ,
UTE : 'UTE' ,
PUBLIC_ENTITY : 'Entidad pública' ,
OTHER : 'Otra organización' ,
} ;
function relationRoleLabel ( role : AreaCompanyRelation [ 'relationRole' ] ) {
if ( role === 'OPERATOR' ) return 'Operadora' ;
if ( role === 'TECHNICAL_OPERATOR' ) return 'Operadora técnica' ;
if ( role === 'CONCESSIONAIRE' ) return 'Concesionaria / titular' ;
if ( role === 'PERMIT_HOLDER' ) return 'Permisionaria' ;
if ( role === 'PARTICIPANT' ) return 'Participante' ;
return 'Otro vínculo' ;
}
function reportStatusLabel ( report : InspectionReportDetailF4 ) {
if ( report . status === 'OFFICIALIZED' ) return report . gedoIfIdentifier ? ` GEDO · ${ report . gedoIfIdentifier } ` : 'Oficializado en GEDO' ;
if ( report . status === 'WORKING' ) return 'INF en preparación' ;
if ( report . status === 'CANCELLED' ) return 'Cancelado' ;
return 'Informe histórico' ;
}
function reportStatusClass ( report : InspectionReportDetailF4 ) {
if ( report . status === 'OFFICIALIZED' ) return 'active' ;
if ( report . status === 'CANCELLED' ) return 'danger' ;
return 'pending' ;
}
function areaLabel ( items : Array < { name : string } > ) {
if ( ! items . length ) return 'Área sin informar' ;
const first = items [ 0 ] ;
if ( ! first ) return 'Área sin informar' ;
return items . length === 1 ? first . name : ` ${ first . name } + ${ items . length - 1 } ` ;
}
async function loadAllActs ( companyId : string ) : Promise < InspectionActListItemF4 [ ] > {
const statuses : InspectionActStatusF4 [ ] = [ 'SEALED' , 'CLOSED' , 'RECTIFIED' ] ;
const groups = await Promise . all ( statuses . map ( async ( status ) = > {
const collected : InspectionActListItemF4 [ ] = [ ] ;
let page = 1 ;
let totalPages = 1 ;
do {
const response = await listInspectionActsGlobalF4 ( { companyId , status , page , pageSize : 100 } ) ;
collected . push ( . . . response . data ) ;
totalPages = response . meta . totalPages ;
page += 1 ;
} while ( page <= totalPages ) ;
return collected ;
} ) ) ;
return groups . flat ( ) . sort ( ( a , b ) = > new Date ( b . occurredAt ) . getTime ( ) - new Date ( a . occurredAt ) . getTime ( ) ) ;
}
async function loadAllReports ( companyId : string ) : Promise < InspectionReportDetailF4 [ ] > {
const collected : InspectionReportDetailF4 [ ] = [ ] ;
let page = 1 ;
let totalPages = 1 ;
do {
const response = await listInspectionReportsF4 ( { companyId , page , pageSize : 100 } ) ;
collected . push ( . . . response . data ) ;
totalPages = response . meta . totalPages ;
page += 1 ;
} while ( page <= totalPages ) ;
return collected
. filter ( ( item ) = > item . status !== 'CANCELLED' )
. sort ( ( a , b ) = > new Date ( b . generatedAt ) . getTime ( ) - new Date ( a . generatedAt ) . getTime ( ) ) ;
}
export function CompanyInventoryPage ( { initialAsset } : { initialAsset : AssetDetail } ) {
const [ params , setParams ] = useSearchParams ( ) ;
const requestedTab = params . get ( 'tab' ) as CompanyTab | null ;
const tab : CompanyTab = requestedTab && [ 'summary' , 'issued' , 'documents' , 'history' ] . includes ( requestedTab )
? requestedTab
: 'summary' ;
const { hasPermission } = useAuth ( ) ;
const canEditAsset = hasPermission ( 'assets.update' ) ;
const canReadRegistry = hasPermission ( 'asset_registry.read' ) ;
const canManageRegistry = hasPermission ( 'asset_registry.manage' ) ;
const canReadRelations = hasPermission ( 'asset_relations.read' ) ;
const canManageRelations = hasPermission ( 'asset_relations.manage' ) ;
const canReadActs = hasPermission ( 'inspection_acts.read' ) ;
const canReadReports = hasPermission ( 'inspection_reports.read' ) ;
const canReadInspections = hasPermission ( 'inspections.read' ) ;
const canReadMedia = hasPermission ( 'assets.read_media' ) ;
const canManageMedia = hasPermission ( 'assets.manage_media' ) ;
const canReadHistory = hasPermission ( 'assets.read_history' ) ;
const [ asset , setAsset ] = useState ( initialAsset ) ;
const [ registry , setRegistry ] = useState < AssetRegistry | null > ( null ) ;
const [ relations , setRelations ] = useState < AreaCompanyRelation [ ] > ( [ ] ) ;
const [ areas , setAreas ] = useState < OperationalAssetSummary [ ] > ( [ ] ) ;
const [ loadingContext , setLoadingContext ] = useState ( true ) ;
const [ contextError , setContextError ] = useState ( '' ) ;
const [ success , setSuccess ] = useState ( '' ) ;
const [ editing , setEditing ] = useState ( false ) ;
const [ saving , setSaving ] = useState ( false ) ;
const [ companyForm , setCompanyForm ] = useState ( {
name : initialAsset.name ,
commonName : initialAsset.commonName ? ? '' ,
organizationKind : 'COMPANY' as OrganizationKind ,
legalName : initialAsset.name ,
taxId : '' ,
notificationEmail : '' ,
notes : '' ,
} ) ;
const [ relationForm , setRelationForm ] = useState ( { areaId : '' , reason : '' } ) ;
const [ metrics , setMetrics ] = useState ( { acts : 0 , reports : 0 , activeInspections : 0 } ) ;
const [ acts , setActs ] = useState < InspectionActListItemF4 [ ] > ( [ ] ) ;
const [ reports , setReports ] = useState < InspectionReportDetailF4 [ ] > ( [ ] ) ;
const [ documentsLoading , setDocumentsLoading ] = useState ( false ) ;
const [ documentsError , setDocumentsError ] = useState ( '' ) ;
const [ documentSearch , setDocumentSearch ] = useState ( '' ) ;
const activeRelations = useMemo ( ( ) = > relations . filter ( ( item ) = > item . active ) , [ relations ] ) ;
const loadContext = async ( ) = > {
setLoadingContext ( true ) ;
setContextError ( '' ) ;
try {
const [ loadedRegistry , loadedRelations , loadedAreas ] = await Promise . all ( [
canReadRegistry ? getAssetRegistry ( asset . id ) : Promise . resolve ( null ) ,
canReadRelations ? listAreaCompanyRelations ( { companyId : asset.id , includeHistory : true } ) : Promise . resolve ( [ ] ) ,
canManageRelations ? listOperationalAreas ( ) : Promise . resolve ( [ ] ) ,
] ) ;
setRegistry ( loadedRegistry ) ;
setRelations ( loadedRelations ) ;
setAreas ( loadedAreas ) ;
if ( loadedRegistry ? . organizationProfile ) {
const profile = loadedRegistry . organizationProfile ;
setCompanyForm ( {
name : asset.name ,
commonName : asset.commonName ? ? '' ,
organizationKind : profile.organizationKind ,
legalName : profile.legalName ? ? asset . name ,
taxId : profile.taxId ? ? '' ,
notificationEmail : profile.notificationEmail ? ? '' ,
notes : profile.notes ? ? '' ,
} ) ;
}
} catch ( requestError ) {
setContextError ( errorMessage ( requestError ) ) ;
} finally {
setLoadingContext ( false ) ;
}
} ;
useEffect ( ( ) = > { void loadContext ( ) ; } , [ asset . id ] ) ;
useEffect ( ( ) = > {
let cancelled = false ;
const loadMetrics = async ( ) = > {
try {
const [ sealed , closed , rectified , reportPage , planned , inProgress ] = await Promise . all ( [
canReadActs ? listInspectionActsGlobalF4 ( { companyId : asset.id , status : 'SEALED' , pageSize : 1 } ) : Promise . resolve ( null ) ,
canReadActs ? listInspectionActsGlobalF4 ( { companyId : asset.id , status : 'CLOSED' , pageSize : 1 } ) : Promise . resolve ( null ) ,
canReadActs ? listInspectionActsGlobalF4 ( { companyId : asset.id , status : 'RECTIFIED' , pageSize : 1 } ) : Promise . resolve ( null ) ,
canReadReports ? listInspectionReportsF4 ( { companyId : asset.id , pageSize : 1 } ) : Promise . resolve ( null ) ,
canReadInspections ? listInspectionVisits ( { companyId : asset.id , status : 'PLANNED' , pageSize : 1 } ) : Promise . resolve ( null ) ,
canReadInspections ? listInspectionVisits ( { companyId : asset.id , status : 'IN_PROGRESS' , pageSize : 1 } ) : Promise . resolve ( null ) ,
] ) ;
if ( cancelled ) return ;
setMetrics ( {
acts : ( sealed ? . meta . total ? ? 0 ) + ( closed ? . meta . total ? ? 0 ) + ( rectified ? . meta . total ? ? 0 ) ,
reports : reportPage?.meta.total ? ? 0 ,
activeInspections : ( planned ? . meta . total ? ? 0 ) + ( inProgress ? . meta . total ? ? 0 ) ,
} ) ;
} catch {
if ( ! cancelled ) setMetrics ( { acts : 0 , reports : 0 , activeInspections : 0 } ) ;
}
} ;
void loadMetrics ( ) ;
return ( ) = > { cancelled = true ; } ;
} , [ asset . id , canReadActs , canReadReports , canReadInspections ] ) ;
useEffect ( ( ) = > {
if ( tab !== 'issued' || ( ! canReadActs && ! canReadReports ) ) return ;
let cancelled = false ;
setDocumentsLoading ( true ) ;
setDocumentsError ( '' ) ;
Promise . all ( [
canReadActs ? loadAllActs ( asset . id ) : Promise . resolve ( [ ] ) ,
canReadReports ? loadAllReports ( asset . id ) : Promise . resolve ( [ ] ) ,
] )
. then ( ( [ loadedActs , loadedReports ] ) = > {
if ( cancelled ) return ;
setActs ( loadedActs ) ;
setReports ( loadedReports ) ;
} )
. catch ( ( requestError ) = > { if ( ! cancelled ) setDocumentsError ( errorMessage ( requestError ) ) ; } )
. finally ( ( ) = > { if ( ! cancelled ) setDocumentsLoading ( false ) ; } ) ;
return ( ) = > { cancelled = true ; } ;
} , [ tab , asset . id , canReadActs , canReadReports ] ) ;
const saveCompany = async ( event : FormEvent ) = > {
event . preventDefault ( ) ;
setSaving ( true ) ; setContextError ( '' ) ; setSuccess ( '' ) ;
try {
let savedAsset = asset ;
if ( canEditAsset ) {
savedAsset = await updateAsset ( asset . id , {
name : companyForm.name.trim ( ) ,
commonName : companyForm.commonName.trim ( ) || null ,
} ) ;
setAsset ( savedAsset ) ;
}
if ( canManageRegistry ) {
await upsertOrganizationProfile ( asset . id , {
organizationKind : companyForm.organizationKind ,
legalName : companyForm.legalName.trim ( ) || null ,
taxId : companyForm.taxId.trim ( ) || null ,
notificationEmail : companyForm.notificationEmail.trim ( ) || null ,
notes : companyForm.notes.trim ( ) || null ,
} ) ;
}
setEditing ( false ) ;
setSuccess ( 'Datos de la empresa actualizados.' ) ;
await loadContext ( ) ;
} catch ( requestError ) {
setContextError ( errorMessage ( requestError ) ) ;
} finally {
setSaving ( false ) ;
}
} ;
const addRelation = async ( event : FormEvent ) = > {
event . preventDefault ( ) ;
if ( ! relationForm . areaId || relationForm . reason . trim ( ) . length < 3 ) return ;
setSaving ( true ) ; setContextError ( '' ) ; setSuccess ( '' ) ;
try {
await createAreaCompanyRelation ( {
areaId : relationForm.areaId ,
companyId : asset.id ,
reason : relationForm.reason.trim ( ) ,
} ) ;
setRelationForm ( { areaId : '' , reason : '' } ) ;
setSuccess ( 'Área vinculada a la empresa.' ) ;
await loadContext ( ) ;
} catch ( requestError ) {
setContextError ( errorMessage ( requestError ) ) ;
} finally {
setSaving ( false ) ;
}
} ;
const endRelation = async ( relation : AreaCompanyRelation ) = > {
const reason = window . prompt ( ` Motivo para finalizar el vínculo con ${ relation . area . name } ` ) ;
if ( ! reason ? . trim ( ) || reason . trim ( ) . length < 3 ) return ;
setSaving ( true ) ; setContextError ( '' ) ; setSuccess ( '' ) ;
try {
await endAreaCompanyRelation ( relation . id , reason . trim ( ) ) ;
setSuccess ( 'Vínculo finalizado. El historial quedó conservado.' ) ;
await loadContext ( ) ;
} catch ( requestError ) {
setContextError ( errorMessage ( requestError ) ) ;
} finally {
setSaving ( false ) ;
}
} ;
const setTab = ( next : CompanyTab ) = > {
const nextParams = new URLSearchParams ( params ) ;
next === 'summary' ? nextParams . delete ( 'tab' ) : nextParams . set ( 'tab' , next ) ;
nextParams . delete ( 'advanced' ) ;
setParams ( nextParams ) ;
} ;
const reportByAct = useMemo ( ( ) = > new Map ( reports . map ( ( report ) = > [ report . actId , report ] ) ) , [ reports ] ) ;
const filteredActs = useMemo ( ( ) = > {
const needle = documentSearch . trim ( ) . toLocaleLowerCase ( 'es' ) ;
if ( ! needle ) return acts ;
return acts . filter ( ( act ) = > [
act . code ,
act . title ,
act . visit . code ,
. . . act . areas . map ( ( item ) = > item . name ) ,
reportByAct . get ( act . id ) ? . code ? ? '' ,
reportByAct . get ( act . id ) ? . gedoIfIdentifier ? ? '' ,
] . join ( ' ' ) . toLocaleLowerCase ( 'es' ) . includes ( needle ) ) ;
} , [ acts , documentSearch , reportByAct ] ) ;
const usedAreaIds = new Set ( activeRelations . map ( ( relation ) = > relation . area . id ) ) ;
const availableAreas = areas . filter ( ( item ) = > ! usedAreaIds . has ( item . id ) ) ;
const profile = registry ? . organizationProfile ? ? null ;
return < section className = "narrow-section company-page" >
< nav className = "breadcrumb company-breadcrumb" aria-label = "Ruta de empresa" >
< Link to = "/inventarios" > Inventarios < / Link > < span > › < / span >
< Link to = "/inventarios?section=companies" > Empresas < / Link > < span > › < / span > < strong > { asset . name } < / strong >
< / nav >
< header className = "company-hero" >
< div >
< span className = "eyebrow" > EMPRESA < / span >
< h1 > { asset . name } < / h1 >
< p > < strong > { asset . code } < / strong > { profile ? . taxId ? ` · CUIT ${ profile . taxId } ` : '' } < / p >
< / div >
< div className = "company-hero-actions" >
< span className = { ` status-badge ${ assetStatusClass ( asset . informationStatus ) } ` } > { assetStatusLabel ( asset . informationStatus ) } < / span >
{ ( canEditAsset || canManageRegistry ) && tab === 'summary' && < button type = "button" className = "button primary" onClick = { ( ) = > setEditing ( ( current ) = > ! current ) } > < Icon name = "edit" / > { editing ? 'Cerrar edición' : 'Editar empresa' } < / button > }
< / div >
< / header >
< nav className = "company-tabs" aria-label = "Secciones de la empresa" >
< button type = "button" className = { tab === 'summary' ? 'active' : '' } onClick = { ( ) = > setTab ( 'summary' ) } > Resumen < / button >
{ ( canReadActs || canReadReports ) && < button type = "button" className = { tab === 'issued' ? 'active' : '' } onClick = { ( ) = > setTab ( 'issued' ) } > Actas e informes < / button > }
{ ( canReadRegistry || canReadMedia ) && < button type = "button" className = { tab === 'documents' ? 'active' : '' } onClick = { ( ) = > setTab ( 'documents' ) } > Documentación < / button > }
{ canReadHistory && < button type = "button" className = { tab === 'history' ? 'active' : '' } onClick = { ( ) = > setTab ( 'history' ) } > Historial < / button > }
< / nav >
{ contextError && < Alert > { contextError } < / Alert > }
{ success && < Alert type = "success" > { success } < / Alert > }
{ tab === 'summary' && < div className = "company-stack" >
{ loadingContext ? < LoadingBlock label = "Cargando empresa…" / > : < >
< article className = "panel company-card" >
< div className = "company-card-heading" >
< div > < span className = "eyebrow" > IDENTIDAD < / span > < h2 > Datos de la empresa < / h2 > < p > La información que se usa para identificarla y enviar documentación oficial . < / p > < / div >
< / div >
{ editing ? < form className = "company-edit-form" onSubmit = { saveCompany } >
< div className = "form-grid" >
< label className = "field" > < span > Nombre < / span > < input value = { companyForm . name } disabled = { ! canEditAsset } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , name : event.target.value } ) } required / > < / label >
< label className = "field" > < span > Nombre corto / sigla < em > opcional < / em > < / span > < input value = { companyForm . commonName } disabled = { ! canEditAsset } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , commonName : event.target.value } ) } placeholder = "Ej.: EMESA" / > < / label >
< label className = "field" > < span > Tipo < / span > < SearchableSelect value = { companyForm . organizationKind } disabled = { ! canManageRegistry } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , organizationKind : event.target.value as OrganizationKind } ) } > { Object . entries ( organizationLabels ) . map ( ( [ value , label ] ) = > < option key = { value } value = { value } > { label } < / option > ) } < / SearchableSelect > < / label >
< label className = "field" > < span > Razón social < em > opcional < / em > < / span > < input value = { companyForm . legalName } disabled = { ! canManageRegistry } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , legalName : event.target.value } ) } / > < / label >
< label className = "field" > < span > CUIT < em > opcional < / em > < / span > < input value = { companyForm . taxId } disabled = { ! canManageRegistry } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , taxId : event.target.value } ) } / > < / label >
< label className = "field" > < span > Email oficial para actas < em > opcional < / em > < / span > < input type = "email" value = { companyForm . notificationEmail } disabled = { ! canManageRegistry } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , notificationEmail : event.target.value } ) } / > < / label >
< / div >
< label className = "field" > < span > Notas < em > opcional < / em > < / span > < textarea rows = { 2 } value = { companyForm . notes } disabled = { ! canManageRegistry } onChange = { ( event ) = > setCompanyForm ( { . . . companyForm , notes : event.target.value } ) } / > < / label >
< div className = "form-actions" > < button type = "button" className = "button secondary" onClick = { ( ) = > setEditing ( false ) } > Cancelar < / button > < button className = "button primary" disabled = { saving } > { saving ? 'Guardando…' : 'Guardar cambios' } < / button > < / div >
< / form > : < div className = "company-data-grid" >
< div > < small > Razón social < / small > < strong > { profile ? . legalName || asset . name } < / strong > < / div >
< div > < small > Tipo < / small > < strong > { organizationLabels [ profile ? . organizationKind ? ? 'COMPANY' ] } < / strong > < / div >
< div > < small > CUIT < / small > < strong > { profile ? . taxId || 'Sin informar' } < / strong > < / div >
< div > < small > Email oficial para actas < / small > < strong > { profile ? . notificationEmail || 'Sin informar' } < / strong > < / div >
< / div > }
< / article >
< article className = "panel company-card" >
< div className = "company-card-heading" >
< div > < span className = "eyebrow" > OPERACIÓN < / span > < h2 > Á reas donde opera < / h2 > < p > Estas relaciones indican dónde participa la empresa . No cambian la jerarquía física del Inventario . < / p > < / div >
< span className = "count-pill" > { activeRelations . length } < / span >
< / div >
{ activeRelations . length === 0 ? < EmptyState title = "Sin áreas vinculadas" text = "Todavía no hay áreas operativas asociadas a esta empresa." / > : < div className = "company-area-list" > { activeRelations . map ( ( relation ) = > < div className = "company-area-row" key = { relation . id } >
< Link to = { ` /inventarios/ ${ relation . area . id } ` } > < strong > { relation . area . name } < / strong > < small > { relation . area . code } · { relationRoleLabel ( relation . relationRole ) } < / small > < / Link >
< div > < span className = "status-badge active" > Activa < / span > { canManageRelations && < button type = "button" className = "button text compact" disabled = { saving } onClick = { ( ) = > void endRelation ( relation ) } > Finalizar < / button > } < / div >
< / div > ) } < / div > }
{ canManageRelations && < details className = "company-inline-action" > < summary > < Icon name = "plus" size = { 15 } / > Vincular otra á rea < / summary > < form onSubmit = { addRelation } > < div className = "form-grid" > < label className = "field" > < span > Á rea < / span > < SearchableSelect value = { relationForm . areaId } onChange = { ( event ) = > setRelationForm ( { . . . relationForm , areaId : event.target.value } ) } required > < option value = "" > Seleccionar á rea … < / option > { availableAreas . map ( ( item ) = > < option key = { item . id } value = { item . id } > { item . name } · { item . code } < / option > ) } < / SearchableSelect > < / label > < label className = "field" > < span > Motivo < / span > < input value = { relationForm . reason } onChange = { ( event ) = > setRelationForm ( { . . . relationForm , reason : event.target.value } ) } minLength = { 3 } required placeholder = "Ej.: operación vigente" / > < / label > < / div > < button className = "button primary" disabled = { saving || ! relationForm . areaId || relationForm . reason . trim ( ) . length < 3 } > Vincular á rea < / button > < / form > < / details > }
< / article >
< article className = "panel company-card company-activity-card" >
< div className = "company-card-heading" > < div > < span className = "eyebrow" > INSPECCIONES < / span > < h2 > Actividad de la empresa < / h2 > < p > Acceso rápido a la documentación que ya fue emitida . < / p > < / div > < / div >
< div className = "company-metrics" >
< div > < small > Actas emitidas < / small > < strong > { metrics . acts } < / strong > < / div >
< div > < small > Informes < / small > < strong > { metrics . reports } < / strong > < / div >
< div > < small > Inspecciones activas < / small > < strong > { metrics . activeInspections } < / strong > < / div >
< / div >
{ ( canReadActs || canReadReports ) && < div className = "company-card-actions" > < button type = "button" className = "button primary" onClick = { ( ) = > setTab ( 'issued' ) } > Ver actas e informes < Icon name = "chevron" / > < / button > < / div > }
< / article >
< / > }
< / div > }
{ tab === 'issued' && < div className = "company-stack" >
< article className = "panel company-card" >
< div className = "company-card-heading company-doc-heading" > < div > < span className = "eyebrow" > DOCUMENTACIÓN EMITIDA < / span > < h2 > Actas e informes < / h2 > < p > Historial documental de esta empresa . Sólo se muestran Actas ya emitidas y sus Informes asociados . < / p > < / div > < div className = "company-doc-counts" > < span > { acts . length } actas < / span > < span > { reports . length } informes < / span > < / div > < / div >
< label className = "company-document-search" > < Icon name = "search" / > < input value = { documentSearch } onChange = { ( event ) = > setDocumentSearch ( event . target . value ) } placeholder = "Buscar por acta, informe, inspección, área o GEDO" / > < / label >
{ documentsError && < Alert > { documentsError } < / Alert > }
{ documentsLoading ? < LoadingBlock label = "Cargando documentación emitida…" / > : filteredActs . length === 0 ? < EmptyState title = "Sin documentación emitida" text = "Todavía no hay Actas selladas para esta empresa." / > : < div className = "table-scroll company-doc-table" > < table > < thead > < tr > < th > Fecha < / th > < th > Á rea < / th > < th > Acta < / th > < th > Inspección < / th > < th > Informe < / th > < th > Oficialización < / th > < th / > < / tr > < / thead > < tbody > { filteredActs . map ( ( act ) = > {
const report = reportByAct . get ( act . id ) ;
return < tr key = { act . id } >
< td > { formatDate ( act . occurredAt ) } < / td >
< td > < strong className = "table-primary" > { areaLabel ( act . areas ) } < / strong > < / td >
< td > < Link className = "text-link" to = { ` /inspecciones/actas/ ${ act . id } ` } > { act . code } < / Link > < / td >
< td > < Link className = "text-link" to = { ` /inspecciones/ ${ act . visitId } ` } > { act . visit . code } < / Link > < / td >
< td > { report ? < Link className = "text-link" to = { ` /informes/ ${ report . id } ` } > { report . code } < / Link > : < span className = "status-badge pending" > Pendiente de INF < / span > } < / td >
< td > { report ? < span className = { ` status-badge ${ reportStatusClass ( report ) } ` } > { reportStatusLabel ( report ) } < / span > : < span className = "muted" > — < / span > } < / td >
< td className = "action-cell" > < Link className = "icon-button" to = { ` /inspecciones/actas/ ${ act . id } ` } aria-label = { ` Abrir ${ act . code } ` } > < Icon name = "chevron" / > < / Link > < / td >
< / tr > ;
} ) } < / tbody > < / table > < / div > }
< / article >
< / div > }
{ tab === 'documents' && < div className = "company-stack" >
{ loadingContext ? < LoadingBlock label = "Cargando documentación…" / > : < >
{ canReadRegistry && < article className = "panel company-card" >
< div className = "company-card-heading" > < div > < span className = "eyebrow" > RESPALDO < / span > < h2 > Documentos de la empresa < / h2 > < p > Documentos e identificadores que respaldan los datos institucionales . < / p > < / div > < Link className = "button secondary" to = { ` /inventarios/ ${ asset . id } ?advanced=1&tab=registry ` } > Administrar < / Link > < / div >
{ registry ? . sourceDocuments . length ? < div className = "company-document-list" > { registry . sourceDocuments . map ( ( document ) = > < div key = { document . linkId } > < div > < strong > { document . title } < / strong > < small > { document . documentNumber || document . documentType } { document . issuer ? ` · ${ document . issuer } ` : '' } < / small > < / div > < span className = "tag" > { document . relationType } < / span > < / div > ) } < / div > : < div className = "inline-empty" > Todavía no hay documentos respaldatorios vinculados . < / div > }
{ registry ? . externalIdentifiers . length ? < details className = "company-more" > < summary > Ver identificadores externos ( { registry . externalIdentifiers . length } ) < / summary > < div className = "company-identifier-list" > { registry . externalIdentifiers . map ( ( item ) = > < div key = { item . id } > < strong > { item . namespace } < / strong > < span > { item . value } < / span > < small > { item . validUntil ? 'Histórico' : 'Vigente' } < / small > < / div > ) } < / div > < / details > : null }
< / article > }
{ canReadMedia && < AssetMediaPanel assetId = { asset . id } assetName = { asset . name } canManage = { canManageMedia } / > }
< div className = "company-advanced-link" > < span > ¿ Necesitás procedencia , composición UTE u otros datos técnicos ? < / span > < Link to = { ` /inventarios/ ${ asset . id } ?advanced=1&tab=registry ` } > Abrir administración avanzada < / Link > < / div >
< / > }
< / div > }
{ tab === 'history' && canReadHistory && < AssetHistoryPanel assetId = { asset . id } / > }
< / section > ;
}