From 93852c2602e53d461f5609ca01e6b1bbe0a9558e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 22:26:38 +0000 Subject: [PATCH] Fix four defects the screenshots and tests exposed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifying the rendered pages rather than the build turned up real bugs: - The WP-00 health page still sat at src/app/page.tsx and silently won the route over the new Aperçu screen, so the home page was a database status readout. Moved to /api/sante, where a probe belongs, and wired into the compose healthcheck. - The unassigned row showed a +14 h delta against a contract of zero, reading as an overshoot when it is simply the volume left to staff. It now shows what there is to fill. - Two sidebar entries lit at once: an anchor link matched its own page, and /equipe matched an employee record. Highlighting now resolves to the most specific match, and a test asserts exactly one entry lights per screen. - Section tabs with no built screen pointed at the home page, which reads as a broken tab. They now lead to their first entry's placeholder. Also gives truncated compliance alerts a title attribute, so a narrow cell no longer says there is a problem without saying which. Playwright can reuse a preinstalled browser through PLAYWRIGHT_CHROMIUM_PATH when its revision differs from the bundled one. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv --- docker-compose.yml | 12 +++++ playwright.config.ts | 18 ++++++- src/app/api/sante/route.ts | 24 +++++++++ src/app/page.tsx | 33 ------------ src/components/planning/CounterStrip.tsx | 17 ++++++ src/components/planning/ShiftChip.tsx | 8 ++- src/components/planning/WeekGrid.tsx | 1 + src/components/shell/AppShell.tsx | 10 ++-- src/components/shell/navigation.ts | 55 ++++++++++++++++--- tests/e2e/navigation.spec.ts | 60 +++++++++++++++++++++ tests/unit/navigation.test.ts | 69 ++++++++++++++++++++++++ 11 files changed, 260 insertions(+), 47 deletions(-) create mode 100644 src/app/api/sante/route.ts delete mode 100644 src/app/page.tsx create mode 100644 tests/e2e/navigation.spec.ts create mode 100644 tests/unit/navigation.test.ts diff --git a/docker-compose.yml b/docker-compose.yml index a521919..7786d64 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,6 +37,18 @@ services: APP_URL: ${APP_URL:-http://localhost:3000} ports: - '${APP_PORT:-3000}:3000' + healthcheck: + test: + [ + 'CMD', + 'node', + '-e', + "fetch('http://127.0.0.1:3000/api/sante').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))", + ] + interval: 15s + timeout: 5s + retries: 5 + start_period: 30s volumes: db-data: diff --git a/playwright.config.ts b/playwright.config.ts index 7276195..3758a96 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -18,7 +18,23 @@ export default defineConfig({ }, projects: [ - { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + // Certains environnements fournissent déjà un Chromium dont la révision + // ne correspond pas à celle qu'attend cette version de Playwright. + // PLAYWRIGHT_CHROMIUM_PATH permet de le réutiliser plutôt que d'en + // télécharger un second. + ...(process.env.PLAYWRIGHT_CHROMIUM_PATH + ? { + launchOptions: { + executablePath: process.env.PLAYWRIGHT_CHROMIUM_PATH, + }, + } + : {}), + }, + }, ], webServer: { diff --git a/src/app/api/sante/route.ts b/src/app/api/sante/route.ts new file mode 100644 index 0000000..c97ab7a --- /dev/null +++ b/src/app/api/sante/route.ts @@ -0,0 +1,24 @@ +import { NextResponse } from 'next/server'; + +import { checkDatabase } from '@/server/health'; + +export const dynamic = 'force-dynamic'; + +/** + * Point de santé, destiné aux sondes de conteneur. + * + * Il ne dit que « la base répond » : ni version, ni schéma, ni compte. Une + * sonde est joignable sans authentification, elle ne doit donc rien apprendre + * à qui la interroge. + */ +export async function GET() { + const database = await checkDatabase(); + + return NextResponse.json( + { status: database.ok ? 'ok' : 'degraded' }, + { + status: database.ok ? 200 : 503, + headers: { 'cache-control': 'no-store' }, + }, + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index 079c263..0000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { checkDatabase } from '@/server/health'; - -export const dynamic = 'force-dynamic'; - -export default async function HomePage() { - const database = await checkDatabase(); - - return ( -
-
-

PlanFlow

-

- Socle applicatif — lot WP-00. Les écrans métier arrivent aux lots suivants. -

-
- -
-
-
Base de données
-
- {database.ok ? 'connectée' : `indisponible — ${database.error}`} -
-
-
-
- ); -} diff --git a/src/components/planning/CounterStrip.tsx b/src/components/planning/CounterStrip.tsx index d314341..be4a338 100644 --- a/src/components/planning/CounterStrip.tsx +++ b/src/components/planning/CounterStrip.tsx @@ -12,6 +12,8 @@ export interface CounterStripProps { counters: WeekCountersView; /** Vrai si le contrat est au forfait jours : pas de comparaison horaire. */ forfaitJours?: boolean; + /** Ligne des besoins sans titulaire : il n'y a pas de contrat à comparer. */ + unassigned?: boolean; className?: string; } @@ -32,8 +34,23 @@ function Cell({ label, value }: { label: string; value: string }) { export function CounterStrip({ counters, forfaitJours = false, + unassigned = false, className, }: CounterStripProps) { + // Un besoin non couvert n'a pas de contrat : afficher un écart le compare à + // zéro et fait lire « +14 h » comme un dépassement, alors que c'est + // simplement le volume à pourvoir. + if (unassigned) { + return ( +
+ À pourvoir + + {counters.plannedLabel} + +
+ ); + } + if (forfaitJours) { return (
diff --git a/src/components/planning/ShiftChip.tsx b/src/components/planning/ShiftChip.tsx index 4c77431..770781b 100644 --- a/src/components/planning/ShiftChip.tsx +++ b/src/components/planning/ShiftChip.tsx @@ -87,7 +87,13 @@ export function ShiftChip({ {alert ? ( - + // Le motif est tronqué dans une cellule étroite : le `title` garde le + // texte complet accessible, sinon l'alerte dit qu'il y a un problème + // sans jamais dire lequel. + ▲ {alert} diff --git a/src/components/planning/WeekGrid.tsx b/src/components/planning/WeekGrid.tsx index 335c59e..1609616 100644 --- a/src/components/planning/WeekGrid.tsx +++ b/src/components/planning/WeekGrid.tsx @@ -106,6 +106,7 @@ function Row({ row, days }: { row: DemoWeekRow; days: readonly string[] }) {
diff --git a/src/components/shell/AppShell.tsx b/src/components/shell/AppShell.tsx index f573dc8..3f269c4 100644 --- a/src/components/shell/AppShell.tsx +++ b/src/components/shell/AppShell.tsx @@ -11,7 +11,9 @@ import { cx } from '@/lib/cx'; import { isActive, NAVIGATION, + placeholderHref, sectionForPath, + sectionHref, type NavItem, } from '@/components/shell/navigation'; @@ -36,12 +38,11 @@ export function AppShell({ children }: AppShellProps) {