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) {