36 lines
8.2 KiB
React
36 lines
8.2 KiB
React
import React,{useEffect,useState} from 'react';
|
|
import {workshopSections,visibleWorkshopSections,workshopStates,filterWorkshopOrders,visibleWorkshopEquipment} from './workshop-menu.js';
|
|
import WorkshopStockWorkspace from './WorkshopStockWorkspace.jsx';
|
|
|
|
export default function WorkshopMenuWorkspace({section,context,can,api,navigate}) {
|
|
const unit=context.unit?.id??null,active=workshopSections.find(s=>s.id===section);
|
|
const [data,setData]=useState(null),[error,setError]=useState(''),[reload,setReload]=useState(0),[search,setSearch]=useState(''),[state,setState]=useState('');
|
|
useEffect(()=>{
|
|
let current=true;setData(null);setError('');
|
|
if(unit===null||!can('workshop.view',unit)||!['workshop_panel','workshop_equipment','workshop_team','workshop_reports'].includes(section))return ()=>{current=false;};
|
|
api('/api/workshop').then(d=>{if(current)setData(d);}).catch(e=>{if(current)setError(e.message);});
|
|
return ()=>{current=false;};
|
|
},[context.token,section,reload]);
|
|
if(!active)return null;
|
|
const orders=filterWorkshopOrders(data?.orders??[],search,state);
|
|
const link=(id,label,permission,scope=null)=>can(permission,scope)?<button key={id} onClick={()=>navigate(id)}>{label}</button>:null;
|
|
const orderTable=<div className="table-scroll"><table><thead><tr><th>Ordem</th><th>Equipamento</th><th>Cliente</th><th>Estado registado</th><th>Responsável</th><th>Acção</th></tr></thead><tbody>{orders.map(o=><tr key={o.id}><td>{o.reference}<small>#{o.id} · Revisão {o.revision}</small></td><td>{o.equipment?.identifier}<small>{o.equipment?.description}</small></td><td>#{o.client_id}</td><td>{workshopStates[o.state]??o.state}</td><td>{o.assigned_to?`#${o.assigned_to}`:'Por atribuir'}</td><td><button onClick={()=>navigate('workshop',o.id)}>Abrir ordem</button></td></tr>)}</tbody></table>{!orders.length&&<p>Sem ordens visíveis para os filtros seleccionados.</p>}</div>;
|
|
return <div className="workshop-menu-workspace"><div className="page-title"><p className="eyebrow">OFICINAS</p><h1>{active.label}</h1><p>{context.company.name} / {context.unit?.name??'Seleccione uma unidade'}</p></div>
|
|
<nav className="billing-section-tabs" aria-label="Submenus de oficinas">{visibleWorkshopSections(can,unit).map(s=><button key={s.id} aria-current={s.id===section?'page':undefined} onClick={()=>navigate(s.id)}>{s.label}</button>)}</nav>
|
|
{section==='workshop_stock'&&unit!==null&&can('workshop_stock.view',unit)&&<WorkshopStockWorkspace key={context.token} api={api} context={context} can={can}/>}
|
|
<p className="alert home-notice">Demonstração sintética. O estado operacional, a autorização de trabalho, a facturação e o pagamento são distintos. Abrir o menu não altera permissões nem confirma uma operação.</p>
|
|
{active.pending&&!(section==='workshop_stock'&&can('workshop_stock.view',unit))&&<section className="panel"><h2>Capacidade ainda não disponível</h2><p>{active.pending}</p>{['workshop_stock','workshop_quotes'].includes(section)&&<button onClick={()=>navigate('workshop')}>Consultar ordens, autorizações e apontamentos existentes</button>}{section==='workshop_stock'&&can('company.view')&&<button onClick={()=>navigate('stock_panel')}>Abrir o menu de Stock — em preparação</button>}{section==='workshop_purchasing'&&<div className="actions">{link('purchasing_budget','Controlo orçamental disponível','purchasing_budget.view',unit)}{link('supplier_payables','Obrigações a fornecedores disponíveis','supplier_payables.view',unit)}</div>}</section>}
|
|
{section==='workshop_clients'&&<section className="panel"><h2>Cadastro de clientes existente</h2><p>Reutilize o cadastro da empresa; não há um segundo registo de clientes para a oficina. Conta corrente e histórico de manutenção completo não são acrescentados por este menu.</p>{link('clients','Abrir clientes','clients.view')}</section>}
|
|
{section==='workshop_finance'&&<section className="panel"><h2>Documentos e percursos financeiros existentes</h2><div className="actions">{link('service_invoices','Facturas e documentos de teste','service_invoices.view',unit)}{link('billing_drafts','Rascunhos de facturação','billing_drafts.view',unit)}{link('workshop_billing','Oficina → rascunho','workshop_billing.view',unit)}{link('service_receivables','Recebimentos e afectações de teste','service_receivables.view',unit)}</div><p>Caixa, despesas, abertura e fecho de caixa ainda não estão implementados. Não se apresentam saldos fictícios, margens ou valores recebidos inferidos a partir de facturas.</p></section>}
|
|
{section==='workshop_settings'&&<section className="panel"><h2>Configurações existentes</h2><div className="actions">{link('company','Dados da empresa','company.view')}{link('access','Acessos autorizados','access.manage')}{link('services','Catálogo de serviços','services.view')}{link('preview_model','Modelo de apresentação documental','issuer_documents.view')}{link('service_fiscal','Perfis fiscais existentes','service_fiscal.view')}</div><p>Catálogo de tipos de equipamento, características técnicas, política de oficina e gestão de cópias de segurança ainda não têm interface própria. Os perfis do documento são uma proposta; não atribuem direitos automaticamente.</p></section>}
|
|
{['workshop_panel','workshop_equipment','workshop_team','workshop_reports'].includes(section)&&<>
|
|
<div className="actions"><button onClick={()=>setReload(r=>r+1)}>Actualizar consulta</button>{can('workshop.open',unit)&&<button className="primary" onClick={()=>navigate('workshop')}>Abrir percurso de ordem de serviço</button>}</div>
|
|
{error?<p role="alert" className="alert error">Dados indisponíveis: {error}. Não é possível concluir que existam zero ordens.</p>:!data?<p role="status">A carregar ordens autorizadas…</p>:data.blocked?<section className="panel"><h2>Ensaio indisponível neste ambiente</h2><p>O percurso existente continua limitado à base sintética autorizada.</p></section>:<>
|
|
<section className="panel"><h2>{section==='workshop_equipment'?'Pesquisar equipamentos nas ordens autorizadas':'Pesquisar ordens autorizadas'}</h2><div className="form-grid"><label>Referência, equipamento, série, cliente ou responsável<input type="search" value={search} onChange={e=>setSearch(e.target.value)}/></label><label>Estado registado<select value={state} onChange={e=>setState(e.target.value)}><option value="">Todos</option>{Object.entries(workshopStates).map(([s,label])=><option value={s} key={s}>{label}</option>)}</select></label></div>
|
|
{section==='workshop_equipment'?<><p>Equipamentos identificados nas ordens visíveis. O cadastro independente, tipos e características técnicas ainda não estão implementados.</p><div className="table-scroll"><table><thead><tr><th>Código</th><th>Descrição</th><th>Número de série</th><th>Ordens visíveis</th></tr></thead><tbody>{visibleWorkshopEquipment(orders).map(e=><tr key={e.id}><td>{e.identifier}</td><td>{e.description}</td><td>{e.serial||'Não registado'}</td><td>{e.orders.map(id=><button key={id} onClick={()=>navigate('workshop',id)}>Ordem #{id}</button>)}</td></tr>)}</tbody></table></div>{!orders.length&&<p>Sem equipamentos nas ordens que correspondem aos filtros.</p>}</>:orderTable}</section>
|
|
<section className="panel"><h2>{section==='workshop_team'?'Responsáveis e horas de serviço':section==='workshop_reports'?'Consulta operacional disponível':'Âmbito do painel'}</h2><p>{section==='workshop_team'?'A atribuição de responsáveis e o registo de horas mantêm-se nas ordens. O cadastro de recursos humanos, horários e disponibilidade não foram implementados.':section==='workshop_reports'?'Esta lista é uma consulta actual das ordens autorizadas. Relatório diário fechado, cortes, exportação e relatórios financeiros ainda não estão implementados.':'As ordens apresentadas respeitam a consulta e a atribuição autorizadas pelo servidor. Entradas, entregas do dia, alertas, agenda e indicadores de caixa ainda não dispõem das fontes necessárias neste percurso.'}</p><p>Os estados actuais de apontamentos não são convertidos nas fases futuras Recebida, Em reparação ou Entregue. Não se inferem margens, stock ou pendências de custódia.</p></section>
|
|
</>}
|
|
</>}
|
|
</div>;
|
|
}
|