diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index c043be7f..28bbfcce 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -1,3 +1,7 @@ +## 10/11/2026 — correcção dirigida das âncoras antes da promoção + +O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada. + ## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema. diff --git a/docs/investmoz/v2/auditoria-portal/backlog.md b/docs/investmoz/v2/auditoria-portal/backlog.md index bc8af9c5..077196d0 100644 --- a/docs/investmoz/v2/auditoria-portal/backlog.md +++ b/docs/investmoz/v2/auditoria-portal/backlog.md @@ -1,3 +1,7 @@ +## 10/11/2026 — correcção dirigida das âncoras antes da promoção + +O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada. + ## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema. diff --git a/docs/investmoz/v2/auditoria-portal/visao-consolidada-modulos.md b/docs/investmoz/v2/auditoria-portal/visao-consolidada-modulos.md index be7aff8e..6432814d 100644 --- a/docs/investmoz/v2/auditoria-portal/visao-consolidada-modulos.md +++ b/docs/investmoz/v2/auditoria-portal/visao-consolidada-modulos.md @@ -1,3 +1,7 @@ +## 10/11/2026 — correcção dirigida das âncoras antes da promoção + +O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada. + ## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema. diff --git a/resources/js/pages/investor-path/show.tsx b/resources/js/pages/investor-path/show.tsx index b3c44de0..4ed90a67 100644 --- a/resources/js/pages/investor-path/show.tsx +++ b/resources/js/pages/investor-path/show.tsx @@ -486,25 +486,36 @@ export default function InvestmentPath(path: Path) { window.location.hash, stageCodes.split(','), ); + if ( + path.preparation_action && + window.location.hash === '#tarefas' + ) { + const target = + root.current.ownerDocument.getElementById('tarefas'); + if ( + target?.tagName === 'DETAILS' && + root.current.contains(target) + ) { + (target as HTMLDetailsElement).open = true; + target.scrollIntoView({ block: 'start' }); + } + } } }; reveal(); window.addEventListener('hashchange', reveal); return () => window.removeEventListener('hashchange', reveal); - }, [path.case.id, path.context_opportunity, stageCodes, path.next?.code]); + }, [ + path.case.id, + path.context_opportunity, + stageCodes, + path.next?.code, + path.preparation_action?.id, + ]); const { auth, flash } = usePage<{ auth: { user: { id: number } }; flash: { success?: string }; }>().props; - useEffect(() => { - if (path.preparation_action && window.location.hash === '#tarefas') { - const target = document.getElementById('tarefas'); - if (target instanceof HTMLDetailsElement) { - target.open = true; - target.scrollIntoView({ block: 'start' }); - } - } - }, [path.preparation_action]); const facts = useForm({ version: path.case.version, context_opportunity: path.context_opportunity, diff --git a/tests/frontend/licensing-stage.test.mjs b/tests/frontend/licensing-stage.test.mjs index d12d4e53..f5e766a3 100644 --- a/tests/frontend/licensing-stage.test.mjs +++ b/tests/frontend/licensing-stage.test.mjs @@ -270,3 +270,74 @@ test("page reveals the initial hash and server next-stage updates, listens for h globalThis.window = previousWindow; } }); + +test('SWOT task anchor uses the shared hash listener inside the dossier and preserves S08', () => { + const f = targetFixture(), + effects = [], + listeners = new Map(); + const previousWindow = globalThis.window; + const task = { + tagName: 'DETAILS', + open: false, + scrollIntoView: (options) => f.calls.push(['task-scroll', options]), + }; + f.root.ownerDocument.getElementById = (id) => + id === 'tarefas' ? task : id === 'S08' ? f.target : null; + f.root.contains = (node) => node === task || node === f.target; + globalThis.window = { + location: { hash: '#tarefas' }, + addEventListener: (type, fn) => listeners.set(type, fn), + removeEventListener: (type) => listeners.delete(type), + }; + try { + const h = harness( + { + investment_context: null, + facts: {}, + fact_reasons: {}, + phases: {}, + fintech: null, + sector_contexts: [], + requests: [], + tasks: [], + results: [], + tools: {}, + content_sources: [], + services: [], + steps: [{ code: 'S08', preparation_allowed: true }], + preparation_action: { + id: 'swot-qa', + title: 'Risco QA', + kind: 'risk', + opportunity: 'opportunity:42', + }, + }, + { + ...React, + useRef: () => ({ current: f.root }), + useEffect: (effect) => effects.push(effect), + useState: (initial) => [ + typeof initial === 'function' ? initial() : initial, + () => {}, + ], + }, + ); + h.renderPage(); + assert.equal(effects.length, 1); + const cleanup = effects[0](); + assert.equal(task.open, true); + assert.equal(f.phase.open, false); + window.location.hash = '#S08'; + listeners.get('hashchange')(); + assert.equal(f.phase.open, true); + task.open = false; + window.location.hash = '#tarefas'; + listeners.get('hashchange')(); + assert.equal(task.open, true); + assert.equal(f.calls.filter((c) => c[0] === 'task-scroll').length, 2); + cleanup(); + assert.equal(listeners.size, 0); + } finally { + globalThis.window = previousWindow; + } +});