From 2c0be7ac5e3426b4e48617b9a9903a4ea62e4276 Mon Sep 17 00:00:00 2001 From: nala Date: Fri, 30 Oct 2026 22:16:58 +0200 Subject: [PATCH] commit10 --- docs/HOME_NAVIGATION_DELIVERY.md | 4 ++++ resources/css/app.css | 11 ++--------- resources/js/WorkspaceHome.jsx | 29 +++-------------------------- resources/js/app.jsx | 2 +- scripts/home-navigation-check.mjs | 10 +++++++--- 5 files changed, 17 insertions(+), 39 deletions(-) diff --git a/docs/HOME_NAVIGATION_DELIVERY.md b/docs/HOME_NAVIGATION_DELIVERY.md index 9cfa037..0901bde 100644 --- a/docs/HOME_NAVIGATION_DELIVERY.md +++ b/docs/HOME_NAVIGATION_DELIVERY.md @@ -1,5 +1,9 @@ # Página inicial e menus agrupados +**Limpeza expressamente pedida, 30/10/2026:** removidos da página inicial «Consultas e atalhos permitidos», os cartões expansíveis, os atalhos de preparação/validação e o cartão de contexto que repetia o cabeçalho. Ficam o título Início e o aviso de unidade quando aplicável, sem novo conteúdo para ocupar o espaço. Destinos continuam exclusivamente nos dropdowns existentes, por assunto, com Compras, Stock e Contabilidade separados; nenhum direito ou destino foi removido. Removidos os estilos e propriedades específicos dos cartões. Esta decisão substitui a organização em cartões descrita abaixo. Check de navegação/renderização e compilação aprovados; alteração local, sem publicação ou revisão visual declarada. O conteúdo futuro da página inicial será decidido posteriormente. + +**Organização da página inicial, 30/10/2026:** após a indicação de conteúdo espalhado, a lista plana de consultas foi substituída por cartões expansíveis por assunto, reutilizando navigationTopics e os grupos já filtrados. Cartões começam fechados, conservam destinos, estados de preparação e secções internas, incluindo Administração. Preparação/validação e contexto mantidos. CSS responsivo com atalhos em coluna dentro de cada cartão; details/summary nativos para teclado e expansão. Check de renderização cobre os assuntos principais, cartões fechados e ausência da lista plana. Alteração exclusivamente local, sem publicação, dados ou direitos novos; verificação técnica não é revisão visual no navegador. + **Reposição de visibilidade da Contabilidade, 29/10/2026:** a entrada não tinha sido eliminada: o agrupamento horizontal colocara o grupo accounting dentro de Financeiro. A pedido da Tempo, Contabilidade volta a assunto principal, com dropdown próprio, preservando os oito destinos e os mesmos direitos. A ordem passa a Início, Comercial, Compras, Stock, Oficinas, Financeiro, Contabilidade, Configuração. Sem alteração de dados ou concessões. O check existente passa a manter inventário independente dos onze grupos, dos oito destinos contabilísticos e dos assuntos principais; cobre company.view e os dois direitos contabilísticos isolados, além da ausência de direitos. O comando build executa esse check antes do Vite, interrompendo a compilação perante regressão; não depende de hooks npm opcionais. Novas alterações devem actualizar explicitamente o inventário apenas quando o âmbito aprovado mudar. Esta guarda detecta remoção/agrupamento indevido conhecido, não substitui revisão visual ou prova no destino publicado. Prova HTTP da instância 8002 verifica também Contabilidade separada para as cinco funções sintéticas existentes. Sem publicação ou checkpoint. **Conferência da instância em execução, 29/10/2026:** após «continua a executar», scripts/navigation-live-check.mjs verificou as cinco funções sintéticas já existentes (proprietário, preparador, validador, técnico e verificador) por entrada/sessão/saída em 8002. Compras e Stock surgem separados nos cinco conjuntos efectivos de concessões; todos os destinos visíveis têm componente registado e não há IDs duplicados. A aplicação serve o bundle actual com purchasing_home, não apenas código-fonte local não compilado. Prova sanitizada em docs/proofs/navigation-live.json. Sem novas contas, alteração de concessões ou comandos de negócio. Conferência HTTP e renderização estática; não prova interactiva em navegador. diff --git a/resources/css/app.css b/resources/css/app.css index 6082b30..4564a96 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -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. */ diff --git a/resources/js/WorkspaceHome.jsx b/resources/js/WorkspaceHome.jsx index 411974b..262cd1a 100644 --- a/resources/js/WorkspaceHome.jsx +++ b/resources/js/WorkspaceHome.jsx @@ -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
{[...new Map(items.map(i=>[i.id,i])).values()].map(i=>)}
;} - return <>

O SEU ESPAÇO DE TRABALHO

Início

Escolha o percurso no contexto seleccionado. Cada acção mantém a sua autorização própria.

+export default function WorkspaceHome({context}) { + return <> +

Início

{!context.unit&&

Seleccione uma unidade para consultar os percursos de trabalho dessa unidade.

} -
{prepare.length>0&&

A preparar e acompanhar

Abra o percurso para preparar propostas e acompanhar o estado das versões.

{actions(prepare)}
}{validate.length>0&&

A validar

Consulte as versões no respectivo percurso antes de tomar uma decisão. Não é permitida a auto-validação.

{actions(validate)}
}
-

Consultas e atalhos permitidos

{actions(available.filter(i=>!['home','company','units','access'].includes(i.id)))}
-

Esta página não agrega nem conta tarefas pendentes. Abra cada percurso para consultar os estados actuais.

-

Contexto actual

Empresa
{context.company.name}
Unidade
{context.unit?.name??'Sem unidade seleccionada'}
; } diff --git a/resources/js/app.jsx b/resources/js/app.jsx index 1017656..9a27697 100644 --- a/resources/js/app.jsx +++ b/resources/js/app.jsx @@ -79,7 +79,7 @@ function App() { {error &&
{error}
}{notice &&
{notice}
}

Apenas os contextos com acesso explícito são apresentados.

{!state.companies.length ?

Sem empresas autorizadas

A conta existe, mas ainda não tem acesso a uma empresa. Peça ao responsável uma concessão explícita.

: !context ?

Escolha onde vai trabalhar

Seleccione uma empresa. A pertença a um cliente contratante não dá acesso automático às suas empresas ou unidades.

:
- {page === 'home' && } + {page === 'home' && } {page === 'workshop_billing' && } {page === 'workshop_corrections' && } {page === 'company' &&

DADOS BÁSICOS

Empresa

perform(() => api(`/api/companies/${context.company.id}`, 'PATCH', { name }, context.token), 'Nome da empresa actualizado.')} />{!can('company.edit') &&

O seu acesso permite consulta. A edição exige uma concessão própria.

}
} diff --git a/scripts/home-navigation-check.mjs b/scripts/home-navigation-check.mjs index 2596159..7cba4ea 100644 --- a/scripts/home-navigation-check.mjs +++ b/scripts/home-navigation-check.mjs @@ -15,16 +15,16 @@ try { return {groups,html:renderToStaticMarkup(React.createElement(Home,{context,can,groups,navigate:()=>{}}))}; }; const preparer=check([['company.view',null],['industrial_budgets.view',7],['industrial_budgets.prepare',7]]); - assert(preparer.html.includes('A preparar e acompanhar'));assert(!preparer.html.includes('

A validar

')); + assert(preparer.html.includes('

Início

')); assert(!preparer.groups.some(g=>g.id==='billing'));assert(!preparer.groups.flatMap(g=>g.items).some(i=>i.id==='access')); const reviewer=check([['company.view',null],['industrial_budgets.view',7],['industrial_budgets.validate',7]]); - assert(reviewer.html.includes('

A validar

'));assert(!reviewer.html.includes('

A preparar e acompanhar

')); + assert(!reviewer.html.includes('g.id==='costs')); const noAccess=check([]);assert.deepEqual(noAccess.groups.map(g=>g.id),['start']); const reader=check([['company.view',null],['industrial_budgets.view',7]]); assert(!reader.html.includes('

A validar

'));assert(!reader.html.includes('

A preparar e acompanhar

')); - assert(reader.html.includes('não agrega nem conta tarefas pendentes')); + assert(!reader.html.includes('home-context')); const allGroups=navigationGroups(()=>true,7),topics=navigationTopics(allGroups); assert.deepEqual(topics.map(t=>t.label),['Início','Comercial','Compras','Stock','Oficinas','Financeiro','Contabilidade','Configuração']); // Independent inventory: comparing the generated menu with itself cannot detect a removed module. @@ -39,6 +39,10 @@ try { assert(!navigationTopics(noAccess.groups).some(t=>t.id==='accounting')); const before=allGroups.flatMap(g=>g.items.map(i=>i.id)).sort(),after=topics.flatMap(t=>t.sections.flatMap(g=>g.items.map(i=>i.id))).sort(); assert.deepEqual(after,before);assert.equal(new Set(after).size,after.length); + const groupedHome=renderToStaticMarkup(React.createElement(Home,{context,can:()=>true,groups:allGroups,topics,navigate:()=>{}})); + assert(groupedHome.includes('

Início

')); + for(const marker of ['g.items).some(i=>i.id===id)); const navHtml=renderToStaticMarkup(React.createElement(Navigation,{groups:allGroups,page:'billing_drafts',navigate:()=>{},user:{name:'Avaliador sintético',email:'consulta@example.test'},busy:false,logout:()=>{}})); assert(navHtml.includes('workspace-topbar'));assert(!navHtml.includes('grouped-sidebar'));assert(!navHtml.includes('navigation-drawer')); for(const topic of topics.filter(t=>t.id!=='start'))assert(navHtml.includes(`aria-controls="topic-${topic.id}"`));