commit10
This commit is contained in:
@@ -26,14 +26,7 @@
|
||||
.workspace>header{position:sticky;top:0;z-index:25;flex-wrap:wrap;box-shadow:0 2px 8px #123f3508}
|
||||
.workspace>header>.workspace-safety{flex-basis:100%;margin:4px 0 0}
|
||||
.workspace-safety{padding:12px 16px;background:#fff6df;border:1px solid #e6d4a2;color:#6e551c;border-radius:8px;margin-bottom:20px;font-size:.85rem;line-height:1.6}
|
||||
.home-work-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:20px}
|
||||
.home-work-grid .panel{margin-bottom:20px}
|
||||
.home-work-grid h2,.home-context h2{margin-top:0}
|
||||
.home-shortcuts{display:flex;flex-wrap:wrap;gap:10px}
|
||||
.home-shortcuts button{line-height:1.5;text-align:left}
|
||||
.home-notice{background:#fff6df;color:#6e551c}
|
||||
.home-context dl{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:24px 0}
|
||||
.home-context dt{font-size:.8rem;color:#5d716d;margin-bottom:6px}.home-context dd{margin:0;font-weight:600}
|
||||
.panel:has(>table){overflow-x:auto}.panel>table{min-width:540px}td,dd{overflow-wrap:anywhere}
|
||||
.mobile-menu-toggle,.navigation-drawer{display:none}
|
||||
@media(max-width:800px){
|
||||
@@ -44,8 +37,8 @@
|
||||
.navigation-drawer .sidebar nav{display:flex;flex-direction:column;overflow:visible;white-space:normal}.navigation-drawer .sidebar-label{display:block;margin-top:24px}
|
||||
.navigation-drawer .sidebar-bottom{display:block;padding-top:28px}.drawer-close{margin:12px 16px 0;background:#275b4b;color:white;border-color:#568475}
|
||||
.navigation-drawer .sidebar-bottom strong,.navigation-drawer .sidebar-bottom small,.navigation-drawer .brand span{display:block}
|
||||
.workspace main{padding:20px 16px}.home-context dl{grid-template-columns:1fr}.workspace header .local-badge{white-space:normal;text-align:center}
|
||||
.home-shortcuts{display:grid;grid-template-columns:1fr}.context-card{grid-template-columns:1fr}.workspace header strong{overflow-wrap:anywhere}.workspace header .local-badge{display:block}
|
||||
.workspace main{padding:20px 16px}.workspace header .local-badge{white-space:normal;text-align:center}
|
||||
.context-card{grid-template-columns:1fr}.workspace header strong{overflow-wrap:anywhere}.workspace header .local-badge{display:block}
|
||||
}
|
||||
|
||||
/* Facturação: document lists and filters, kept separate from financial engines. */
|
||||
|
||||
@@ -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>
|
||||
</>;
|
||||
}
|
||||
|
||||
@@ -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>}
|
||||
|
||||
Reference in New Issue
Block a user