This commit is contained in:
@@ -1,3 +1,13 @@
|
|||||||
|
## 14/11/2026 — correcção do erro 500 na abertura HTML do dashboard
|
||||||
|
|
||||||
|
Erro confirmado no registo de 14/11 às 10:00 UTC da revisão `73e1d7b9`: `Unable to locate file in Vite manifest: resources/js/pages/management/editorial-workflow.tsx`. O componente de página era também importado estaticamente por outras páginas para reutilizar os estados editoriais. O compilador gerou um chunk partilhado sem a chave de página exigida pelo `@vite` da resposta HTML. O login/302 e os dados JSON podiam funcionar; essas provas não demonstravam a abertura HTML autenticada.
|
||||||
|
|
||||||
|
Correcção delimitada: os mesmos estados passam para `resources/js/lib/editorial-states.ts`; as cinco páginas reutilizam esse módulo, sem importar estaticamente o dashboard. Sem mudança de rotas, permissões, configurações ou dados. Não se alterou o template global nem a segurança para contornar o erro.
|
||||||
|
|
||||||
|
Regressão reproduzida com o manifesto anterior. Com o build corrigido, o teste `EditorialDashboardBuildTest` usa o manifesto real e abre as duas páginas HTML autenticadas (visão geral e legislação), sem `withoutVite`: **1/8**. Os testes frontend afectados e o novo controlo de manifesto passaram (**12 casos**), com tipos/build aprovados. O controlo `editorial-dashboard-manifest.test.mjs` corre depois da compilação na bateria frontend obrigatória e impede promover uma candidata sem a entrada de página. Provas em `outputs/editorial-dashboard-500-20261114`.
|
||||||
|
|
||||||
|
Estado desta passagem: correcção em QA para reparar a entrega autorizada; instalação e conferência do build servido em curso pelo executor Git existente, com novo backup/restauro. Não declarar o 500 resolvido antes de conferir o manifesto e a geração HTML da revisão instalada. Conferência visual continua separada e pendente pelo conector indisponível.
|
||||||
|
|
||||||
## 14/11/2026 — menu Administração/Dashboard e apresentação instalados em 73e1d7b9
|
## 14/11/2026 — menu Administração/Dashboard e apresentação instalados em 73e1d7b9
|
||||||
|
|
||||||
**Entrega autorizada concluída:** `73e1d7b96419873c6924e04e94f2a7374c18eb87`, confirmada em `https://teste-investidor.tempo.co.mz/build/revision.json`. Administração fica no menu principal para contas com destinos administrativos autorizados; Dashboard abre o submenu das actividades. A página `/management/editorial` apresenta os três grupos de cartões descritos abaixo, com fila e notificações preservadas. O painel operacional anterior, quando autorizado, fica também dentro de Dashboard. Visitantes mantêm os acessos públicos; não houve concessão de permissões.
|
**Entrega autorizada concluída:** `73e1d7b96419873c6924e04e94f2a7374c18eb87`, confirmada em `https://teste-investidor.tempo.co.mz/build/revision.json`. Administração fica no menu principal para contas com destinos administrativos autorizados; Dashboard abre o submenu das actividades. A página `/management/editorial` apresenta os três grupos de cartões descritos abaixo, com fila e notificações preservadas. O painel operacional anterior, quando autorizado, fica também dentro de Dashboard. Visitantes mantêm os acessos públicos; não houve concessão de permissões.
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export const states: Record<string, string> = {
|
||||||
|
draft: 'Rascunho',
|
||||||
|
submitted: 'Submetido',
|
||||||
|
in_review: 'Em revisão',
|
||||||
|
approved: 'Aprovado',
|
||||||
|
published: 'Publicado',
|
||||||
|
returned: 'Devolvido',
|
||||||
|
rejected: 'Rejeitado',
|
||||||
|
archived: 'Arquivado',
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Link, useForm } from '@inertiajs/react';
|
import { Link, useForm } from '@inertiajs/react';
|
||||||
import { Management, Field, button, control } from './shared';
|
import { Management, Field, button, control } from './shared';
|
||||||
import { states } from './editorial-workflow';
|
import { states } from '@/lib/editorial-states';
|
||||||
import { labels as portfolioLabels } from '../portfolio/shared';
|
import { labels as portfolioLabels } from '../portfolio/shared';
|
||||||
import SubjectForm from './editorial-subject-form';
|
import SubjectForm from './editorial-subject-form';
|
||||||
import type { SubjectField } from './editorial-subject-form';
|
import type { SubjectField } from './editorial-subject-form';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Link } from '@inertiajs/react';
|
import { Link } from '@inertiajs/react';
|
||||||
import { Management } from './shared';
|
import { Management } from './shared';
|
||||||
import { states } from './editorial-workflow';
|
import { states } from '@/lib/editorial-states';
|
||||||
|
|
||||||
const titles: Record<string, string> = {
|
const titles: Record<string, string> = {
|
||||||
projects: 'Projectos',
|
projects: 'Projectos',
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
import { Link, router } from '@inertiajs/react';
|
import { Link, router } from '@inertiajs/react';
|
||||||
import { Field, Management, button, control } from './shared';
|
import { Field, Management, button, control } from './shared';
|
||||||
|
|
||||||
export const states: Record<string, string> = {
|
import { states } from '@/lib/editorial-states';
|
||||||
draft: 'Rascunho',
|
|
||||||
submitted: 'Submetido',
|
|
||||||
in_review: 'Em revisão',
|
|
||||||
approved: 'Aprovado',
|
|
||||||
published: 'Publicado',
|
|
||||||
returned: 'Devolvido',
|
|
||||||
rejected: 'Rejeitado',
|
|
||||||
archived: 'Arquivado',
|
|
||||||
};
|
|
||||||
type Item = {
|
type Item = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type {
|
|||||||
Force,
|
Force,
|
||||||
} from '@/lib/cluster-competitiveness';
|
} from '@/lib/cluster-competitiveness';
|
||||||
import { button, control } from '../management/shared';
|
import { button, control } from '../management/shared';
|
||||||
import { states as editorialStates } from '../management/editorial-workflow';
|
import { states as editorialStates } from '@/lib/editorial-states';
|
||||||
|
|
||||||
const labels = {
|
const labels = {
|
||||||
evidence: 'Observações e evidências locais',
|
evidence: 'Observações e evidências locais',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
import type { Paginated } from '../management/shared';
|
import type { Paginated } from '../management/shared';
|
||||||
import { titles, statuses } from './types';
|
import { titles, statuses } from './types';
|
||||||
import type { Structure, StructureRecord } from './types';
|
import type { Structure, StructureRecord } from './types';
|
||||||
import { states as editorialStates } from '../management/editorial-workflow';
|
import { states as editorialStates } from '@/lib/editorial-states';
|
||||||
|
|
||||||
export default function Index({
|
export default function Index({
|
||||||
structure,
|
structure,
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Support\IsolatedQaDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Foundation\Vite;
|
||||||
|
use Tests\Support\AuthenticatesQaActors;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
final class EditorialDashboardBuildTest extends TestCase
|
||||||
|
{
|
||||||
|
use AuthenticatesQaActors, DatabaseTransactions;
|
||||||
|
|
||||||
|
public function test_authenticated_full_html_opens_with_the_real_compiled_dashboard_entry(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(IsolatedQaDatabase::allowsSchemaChanges());
|
||||||
|
$directory = getenv('EDITORIAL_QA_BUILD_DIRECTORY') ?: 'build';
|
||||||
|
$manifest = json_decode(file_get_contents(public_path($directory.'/manifest.json')), true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
$key = 'resources/js/pages/management/editorial-workflow.tsx';
|
||||||
|
$this->assertArrayHasKey($key, $manifest, 'The authenticated dashboard must be a compiled page entry, not only a shared chunk.');
|
||||||
|
$this->app->make(Vite::class)->useBuildDirectory($directory)->useHotFile(storage_path('phpunit-no-vite-hot'));
|
||||||
|
config(['portal.qa_build_directory' => $directory, 'editorial_workflow.enabled' => true, 'portal.profile_access_enabled' => false]);
|
||||||
|
$actor = User::factory()->create();
|
||||||
|
config(['portal.managers' => [$actor->email]]);
|
||||||
|
$this->actingAsWithMfa($actor);
|
||||||
|
foreach (['/management/editorial', '/management/editorial?kind=legislation'] as $url) {
|
||||||
|
$this->get($url, ['Accept' => 'text/html'])->assertOk()->assertSee('data-page=', false)->assertSee($manifest[$key]['file'], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ const Page = load('../../resources/js/pages/structures/competitiveness.tsx', {
|
|||||||
PortalApiError: Error,
|
PortalApiError: Error,
|
||||||
},
|
},
|
||||||
'../management/shared': { button: '', control: '' },
|
'../management/shared': { button: '', control: '' },
|
||||||
'../management/editorial-workflow': {
|
'@/lib/editorial-states': {
|
||||||
states: {
|
states: {
|
||||||
draft: 'Rascunho',
|
draft: 'Rascunho',
|
||||||
approved: 'Aprovado',
|
approved: 'Aprovado',
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import {fileURLToPath} from 'node:url';
|
||||||
|
test('compiled dashboard remains a resolvable page entry for authenticated full HTML', () => {
|
||||||
|
const manifestPath=process.env.EDITORIAL_BUILD_MANIFEST || fileURLToPath(new URL('../../public/build/manifest.json',import.meta.url));
|
||||||
|
const manifest=JSON.parse(fs.readFileSync(manifestPath,'utf8'));
|
||||||
|
const key='resources/js/pages/management/editorial-workflow.tsx';
|
||||||
|
assert.ok(manifest[key], 'Dashboard page entry missing; a shared chunk cannot satisfy the server-rendered Vite directive');
|
||||||
|
assert.equal(manifest[key].isDynamicEntry || manifest[key].isEntry, true);
|
||||||
|
assert.ok(fs.existsSync(path.join(path.dirname(manifestPath),manifest[key].file)));
|
||||||
|
});
|
||||||
@@ -7,7 +7,7 @@ import {renderToStaticMarkup} from 'react-dom/server';
|
|||||||
import ts from 'typescript';
|
import ts from 'typescript';
|
||||||
const require=createRequire(import.meta.url), module={exports:{}};
|
const require=createRequire(import.meta.url), module={exports:{}};
|
||||||
const code=ts.transpileModule(fs.readFileSync(new URL('../../resources/js/pages/management/editorial-workflow.tsx',import.meta.url),'utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,jsx:ts.JsxEmit.ReactJSX}}).outputText;
|
const code=ts.transpileModule(fs.readFileSync(new URL('../../resources/js/pages/management/editorial-workflow.tsx',import.meta.url),'utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,jsx:ts.JsxEmit.ReactJSX}}).outputText;
|
||||||
new Function('require','module','exports',code)(name=>name==='@inertiajs/react'?{Link:({href,children,...props})=>React.createElement('a',{href,...props},children),router:{}}:name==='./shared'?{Management:({title,children})=>React.createElement('main',null,React.createElement('h1',null,title),children),Field:({label,children})=>React.createElement('label',null,label,children),button:'',control:''}:require(name),module,module.exports);
|
new Function('require','module','exports',code)(name=>name==='@/lib/editorial-states'?{states:{}}:name==='@inertiajs/react'?{Link:({href,children,...props})=>React.createElement('a',{href,...props},children),router:{}}:name==='./shared'?{Management:({title,children})=>React.createElement('main',null,React.createElement('h1',null,title),children),Field:({label,children})=>React.createElement('label',null,label,children),button:'',control:''}:require(name),module,module.exports);
|
||||||
const base={items:{data:[],current_page:1,last_page:1},notices:[],resourceAccess:true,opportunitiesUrl:'/opportunities-qa',opportunityPending:2,clustersUrl:'/clusters-qa',nativePending:{clusters:3,territories:0,zones:0},subjectsUrl:'/subjects-qa',territorialUrl:'/territories-qa',demographyUrl:'/demography-qa'};
|
const base={items:{data:[],current_page:1,last_page:1},notices:[],resourceAccess:true,opportunitiesUrl:'/opportunities-qa',opportunityPending:2,clustersUrl:'/clusters-qa',nativePending:{clusters:3,territories:0,zones:0},subjectsUrl:'/subjects-qa',territorialUrl:'/territories-qa',demographyUrl:'/demography-qa'};
|
||||||
test('dashboard groups authorised activities once with contextual counts and an empty notification state',()=>{
|
test('dashboard groups authorised activities once with contextual counts and an empty notification state',()=>{
|
||||||
const html=renderToStaticMarkup(React.createElement(module.exports.default,base));
|
const html=renderToStaticMarkup(React.createElement(module.exports.default,base));
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ new Function('require', 'module', 'exports', code)(
|
|||||||
control: '',
|
control: '',
|
||||||
button: '',
|
button: '',
|
||||||
};
|
};
|
||||||
if (name === './editorial-workflow')
|
if (name === '@/lib/editorial-states')
|
||||||
return {
|
return {
|
||||||
states: {
|
states: {
|
||||||
draft: 'Rascunho',
|
draft: 'Rascunho',
|
||||||
|
|||||||
Reference in New Issue
Block a user