This commit is contained in:
2026-10-30 22:16:58 +02:00
parent b45c1a816e
commit 2c0be7ac5e
5 changed files with 17 additions and 39 deletions
+3 -26
View File
@@ -1,31 +1,8 @@
import React from 'react';
export default function WorkspaceHome({context,can,groups,navigate}) {
const unit=context.unit?.id??null;
const available=groups.flatMap(g=>g.items);
const links=(rules)=>rules.filter(([,permission,scope])=>can(permission,scope)).map(([id,,,label])=>({id,label:label??available.find(i=>i.id===id)?.label})).filter(i=>available.some(a=>a.id===i.id));
const prepare=links([
['purchasing_budget','purchasing_budget.create',unit],['purchasing_budget','purchasing_budget.record',unit],
['workshop_corrections','workshop_corrections.request',unit],
['workshop_billing','workshop_billing.prepare',unit],
['clients','clients.create',null],['services','services.create',null],['service_drafts','service_drafts.create',unit],['billing_drafts','billing_drafts.create',unit],
['documents','issuer_documents.edit',null],['documents','client_documents.edit',null],['documents','draft_documents.edit',unit],
['catalogue_accounting','catalogue_accounting.propose',null],['service_fiscal','service_fiscal.propose',null],
['workshop','workshop.open',unit],['direct_costs','direct_costs.prepare',unit],['industrial_costs','industrial_costs.prepare',unit],['industrial_budgets','industrial_budgets.prepare',unit],
]);
const validate=links([
['purchasing_budget','purchasing_budget.approve',unit],
['workshop_corrections','workshop_corrections.review',unit],
['workshop_billing','workshop_billing.review',unit],
['catalogue_accounting','catalogue_accounting.approve',null],['service_fiscal','service_fiscal.approve',null],['service_invoices','invoice_series.approve',null],
['service_receivables','service_receivables.confirm',unit],['service_receivables','service_receivables.correction_approve',unit],['workshop','workshop.verify',unit],['direct_costs','direct_costs.validate',unit],['industrial_costs','industrial_costs.validate',unit],['industrial_budgets','industrial_budgets.validate',unit],
]);
function actions(items){return <div className="home-shortcuts">{[...new Map(items.map(i=>[i.id,i])).values()].map(i=><button key={i.id} onClick={()=>navigate(i.id)}>{i.label}{i.informational&&<small className="shortcut-status">Em preparação</small>}<span aria-hidden="true"> →</span></button>)}</div>;}
return <><div className="page-title"><p className="eyebrow">O SEU ESPAÇO DE TRABALHO</p><h1>Início</h1><p className="muted">Escolha o percurso no contexto seleccionado. Cada acção mantém a sua autorização própria.</p></div>
export default function WorkspaceHome({context}) {
return <>
<div className="page-title"><h1>Início</h1></div>
{!context.unit&&<p className="alert home-notice" role="status">Seleccione uma unidade para consultar os percursos de trabalho dessa unidade.</p>}
<div className="home-work-grid">{prepare.length>0&&<section className="panel"><h2>A preparar e acompanhar</h2><p>Abra o percurso para preparar propostas e acompanhar o estado das versões.</p>{actions(prepare)}</section>}{validate.length>0&&<section className="panel"><h2>A validar</h2><p>Consulte as versões no respectivo percurso antes de tomar uma decisão. Não é permitida a auto-validação.</p>{actions(validate)}</section>}</div>
<section className="panel"><h2>Consultas e atalhos permitidos</h2>{actions(available.filter(i=>!['home','company','units','access'].includes(i.id)))}</section>
<p className="help">Esta página não agrega nem conta tarefas pendentes. Abra cada percurso para consultar os estados actuais.</p>
<section className="panel home-context"><h2>Contexto actual</h2><dl><div><dt>Empresa</dt><dd>{context.company.name}</dd></div><div><dt>Unidade</dt><dd>{context.unit?.name??'Sem unidade seleccionada'}</dd></div></dl><div className="actions"><button onClick={()=>navigate('company')}>Consultar empresa</button><button onClick={()=>navigate('units')}>Ver unidades autorizadas</button></div></section>
</>;
}
+1 -1
View File
@@ -79,7 +79,7 @@ function App() {
{error && <div className="alert error" role="alert">{error}</div>}{notice && <div className="alert success" role="status">{notice}</div>}
<section className="context-card"><label>Empresa<select aria-label="Empresa activa" disabled={busy} value={context?.company.id || ''} onChange={e => select(e.target.value)}><option value="">Escolher empresa autorizada</option>{state.companies.map(c => <option value={c.id} key={c.id}>{c.name}</option>)}</select></label><label>Unidade<select aria-label="Unidade activa" disabled={busy || !context} value={context?.unit?.id || ''} onChange={e => select(context.company.id, e.target.value ? Number(e.target.value) : null)}><option value="">Sem unidade seleccionada</option>{context?.units.map(u => <option value={u.id} key={u.id}>{u.name}</option>)}</select></label><p>Apenas os contextos com acesso explícito são apresentados.</p></section>
{!state.companies.length ? <section className="panel"><h1>Sem empresas autorizadas</h1><p>A conta existe, mas ainda não tem acesso a uma empresa. Peça ao responsável uma concessão explícita.</p></section> : !context ? <section className="panel"><h1>Escolha onde vai trabalhar</h1><p>Seleccione uma empresa. A pertença a um cliente contratante não dá acesso automático às suas empresas ou unidades.</p></section> : <div key={context.token}>
{page === 'home' && <WorkspaceHome context={context} can={can} groups={groups} navigate={navigate} />}
{page === 'home' && <WorkspaceHome context={context} />}
{page === 'workshop_billing' && <WorkshopBillingWorkspace context={context} can={can} api={api} busy={busy} />}
{page === 'workshop_corrections' && <WorkshopCorrectionsWorkspace context={context} can={can} api={api} busy={busy} />}
{page === 'company' && <section className="panel"><p className="eyebrow">DADOS BÁSICOS</p><h1>Empresa</h1><NameForm key={context.company.id} label="Nome da empresa" initial={context.company.name} editable={can('company.edit')} busy={busy} onSave={name => perform(() => api(`/api/companies/${context.company.id}`, 'PATCH', { name }, context.token), 'Nome da empresa actualizado.')} />{!can('company.edit') && <p className="help">O seu acesso permite consulta. A edição exige uma concessão própria.</p>}</section>}