fix: restore Inertia rendering in development

This commit is contained in:
2026-08-04 15:59:51 +01:00
parent caf2537a98
commit 604180b735
10 changed files with 32 additions and 9 deletions

View File

@@ -5,6 +5,7 @@ APP_DEBUG=true
APP_URL=http://localhost:8000 APP_URL=http://localhost:8000
APP_PORT=8000 APP_PORT=8000
VITE_PORT=5173 VITE_PORT=5173
INERTIA_SSR_HOT_URL=http://node:5173
APP_LOCALE=pt_PT APP_LOCALE=pt_PT
APP_FALLBACK_LOCALE=pt_PT APP_FALLBACK_LOCALE=pt_PT

View File

@@ -20,6 +20,8 @@ Todas as alterações materiais são registadas neste ficheiro. O formato segue
- Nome, identidade visual e idioma orientados ao Portal de Reservas. - Nome, identidade visual e idioma orientados ao Portal de Reservas.
- Configuração padrão alterada para PostgreSQL, Redis, SMTP/Mailpit e Europa/Lisboa. - Configuração padrão alterada para PostgreSQL, Redis, SMTP/Mailpit e Europa/Lisboa.
- Configuração de desenvolvimento Vite passa a publicar módulos e HMR em `localhost:5173` e a aceitar explicitamente a origem do portal em `localhost:8000`, corrigindo a página em branco em Docker. - Configuração de desenvolvimento Vite passa a publicar módulos e HMR em `localhost:5173` e a aceitar explicitamente a origem do portal em `localhost:8000`, corrigindo a página em branco em Docker.
- As mensagens flash globais recebem agora as propriedades diretamente de `withApp`, evitando o uso de `usePage()` fora do contexto Inertia que interrompia o arranque do React.
- SSR de desenvolvimento ligado internamente de PHP para o serviço `node`, com o hostname Docker autorizado no Vite.
### Segurança ### Segurança
@@ -32,3 +34,4 @@ Todas as alterações materiais são registadas neste ficheiro. O formato segue
- Suite backend: Pint, PHPStan nível 7 e 44 testes Pest (157 asserções). - Suite backend: Pint, PHPStan nível 7 e 44 testes Pest (157 asserções).
- Frontend: TypeScript, ESLint, Prettier e build Vite de produção. - Frontend: TypeScript, ESLint, Prettier e build Vite de produção.
- Stack Docker operacional e endpoint de login acessível através do Nginx. - Stack Docker operacional e endpoint de login acessível através do Nginx.
- Renderização SSR real do login validada, incluindo o conteúdo “Entrar no Portal de Reservas”.

View File

@@ -15,6 +15,7 @@ x-app: &app
MAIL_MAILER: smtp MAIL_MAILER: smtp
MAIL_HOST: mailpit MAIL_HOST: mailpit
MAIL_PORT: 1025 MAIL_PORT: 1025
INERTIA_SSR_HOT_URL: http://node:5173
volumes: volumes:
- .:/var/www/html - .:/var/www/html
- vendor:/var/www/html/vendor - vendor:/var/www/html/vendor

View File

@@ -18,6 +18,7 @@ return [
'ssr' => [ 'ssr' => [
'enabled' => true, 'enabled' => true,
'url' => 'http://127.0.0.1:13714', 'url' => 'http://127.0.0.1:13714',
'hot_url' => env('INERTIA_SSR_HOT_URL'),
// 'bundle' => base_path('bootstrap/ssr/ssr.mjs'), // 'bundle' => base_path('bootstrap/ssr/ssr.mjs'),
], ],

View File

@@ -55,6 +55,7 @@ Validação concluída em 4 de agosto de 2026:
- PostgreSQL 17: `migrate:fresh --seed` concluído, incluindo extensão `btree_gist` e restrição de exclusão temporal; - PostgreSQL 17: `migrate:fresh --seed` concluído, incluindo extensão `btree_gist` e restrição de exclusão temporal;
- smoke test HTTP: `/login` respondeu com estado 200 através do Nginx; - smoke test HTTP: `/login` respondeu com estado 200 através do Nginx;
- Vite em Docker: URL pública, HMR e CORS verificados em `localhost:5173`; corrigida a página em branco causada pela publicação anterior de `0.0.0.0:5173`; - Vite em Docker: URL pública, HMR e CORS verificados em `localhost:5173`; corrigida a página em branco causada pela publicação anterior de `0.0.0.0:5173`;
- Inertia SSR: renderização do login validada através do serviço `node`; corrigido o componente de mensagens flash que usava `usePage()` fora do contexto Inertia;
- verificação visual no browser integrado: não executada devido a uma política administrativa temporariamente indisponível no browser desta sessão; não foi detetado erro da aplicação. - verificação visual no browser integrado: não executada devido a uma política administrativa temporariamente indisponível no browser desta sessão; não foi detetado erro da aplicação.
## Decisões pendentes do contexto institucional ## Decisões pendentes do contexto institucional

View File

@@ -32,6 +32,14 @@ curl -I -H 'Origin: http://localhost:8000' http://localhost:5173/resources/js/ap
Ambos devem responder com estado 200 e `Access-Control-Allow-Origin: http://localhost:8000`. Reiniciar o serviço com `docker compose restart node` se o ficheiro `public/hot` estiver desatualizado. Ambos devem responder com estado 200 e `Access-Control-Allow-Origin: http://localhost:8000`. Reiniciar o serviço com `docker compose restart node` se o ficheiro `public/hot` estiver desatualizado.
Confirmar também que o HTML inicial já contém conteúdo SSR:
```bash
curl -s http://localhost:8000/login | grep 'data-server-rendered="true"'
```
Se o endpoint Vite devolver um erro SSR, procurar primeiro componentes globais montados em `withApp` que chamem hooks Inertia como `usePage()`. Os elementos irmãos do argumento `app` não pertencem ao respetivo contexto; devem receber as propriedades através do segundo argumento de `withApp`.
## Atualização de código ## Atualização de código
```bash ```bash

View File

@@ -24,11 +24,11 @@ createInertiaApp({
} }
}, },
strictMode: true, strictMode: true,
withApp(app) { withApp(app, { page }) {
return ( return (
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
{app} {app}
<FlashMessages /> <FlashMessages flash={page.props.flash} />
<Toaster /> <Toaster />
</TooltipProvider> </TooltipProvider>
); );

View File

@@ -1,19 +1,23 @@
import { usePage } from '@inertiajs/react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
export function FlashMessages() { type FlashMessagesProps = {
const { flash } = usePage().props; flash?: {
success?: string | null;
error?: string | null;
};
};
export function FlashMessages({ flash }: FlashMessagesProps) {
useEffect(() => { useEffect(() => {
if (flash.success) { if (flash?.success) {
toast.success(flash.success); toast.success(flash.success);
} }
if (flash.error) { if (flash?.error) {
toast.error(flash.error); toast.error(flash.error);
} }
}, [flash.error, flash.success]); }, [flash?.error, flash?.success]);
return null; return null;
} }

View File

@@ -12,7 +12,10 @@ declare module '@inertiajs/core' {
sharedPageProps: { sharedPageProps: {
name: string; name: string;
auth: Auth; auth: Auth;
flash: { success?: string; error?: string }; flash: {
success?: string | null;
error?: string | null;
};
sidebarOpen: boolean; sidebarOpen: boolean;
[key: string]: unknown; [key: string]: unknown;
}; };

View File

@@ -11,6 +11,7 @@ export default defineConfig({
host: '0.0.0.0', host: '0.0.0.0',
port: 5173, port: 5173,
strictPort: true, strictPort: true,
allowedHosts: ['node'],
origin: 'http://localhost:5173', origin: 'http://localhost:5173',
cors: { cors: {
origin: [ origin: [