- Fix overflow horizontal sur /parametres/preferences : <fieldset> a un
min-width: min-content par défaut du navigateur, ce qui empêchait la
grille de l'IngredientPicker de wrapper (page entière poussée à
~3100px de large). Reset min-width: 0 sur .disliked-ingredients-field.
- Recatégorise les laits/crèmes végétaux (coco, amande, avoine) de
PRODUITS_LAITIERS_OEUFS vers LIQUIDES_BOISSONS — ce ne sont pas des
produits laitiers.
- Nouveau modèle IngredientDiet (many-to-many ingrédient <-> régime) :
quels régimes (Végétarien, Végan, Pescétarien) chaque ingrédient
respecte. Omnivore volontairement absent (trivial) et Sans gluten
aussi (déjà dérivable de l'allergène Gluten existant).
- reference-seed-data.ts : chaque groupe de catégorie porte un
defaultDiets, avec dietNames en override pour les exceptions
(fromages, viandes, poissons, sauces à base d'œuf/poisson...).
- IngredientView.diets exposé par /reference/ingredients et /recipes,
affiché via DietBadges dans IngredientPicker et IngredientRow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Prisma: enum IngredientCategory (18 valeurs) + Ingredient.category,
migration appliquée
- reference-seed-data.ts restructuré en 18 groupes de catégories
- packages/shared: INGREDIENT_CATEGORIES + IngredientView.category
- API: category exposé par /reference/ingredients et /recipes
- Web: nouveau IngredientPicker (chips catégories + recherche + grille
de cartes) remplaçant IngredientAutocomplete, branché dans le
formulaire de recette et le champ aliments-pas-aimés
- i18n: clés recipes.form.category.* et libellés du picker
- Fix test reference.test.ts pour la nouvelle forme d'IngredientView
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found on http://batch.dev.kyuno.fr/: GET /reference/diets and
/reference/allergies both returned [] — onboarding's "régime
alimentaire" step and the profile's food-preferences tab had nothing to
show. Cause: the Docker image's CMD only ran `prisma migrate deploy`
(schema), never the seed that populates Diet/Category/Allergy.
Adds src/scripts/seed-runtime.ts — a runtime-only seed entry point
(distinct from prisma/seed.ts, the dev-time one wired to `prisma db
seed`/`prisma migrate reset` via tsx importing from ../src, which isn't
shipped in the runtime image). This one lives under src/ so tsc compiles
it into dist/ alongside everything else, and runs via plain `node`,
reusing the same idempotent seedReferenceData() (upserts by unique name)
already used by prisma/seed.ts and test-support/reset-db.ts.
Dockerfile CMD now runs it between migrate deploy and starting the
server — safe on every container start/restart, confirmed idempotent
locally (no duplicates, no error on a second run against an
already-seeded database).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found on http://batch.dev.kyuno.fr/: login/signup succeeded (200/201,
profile in the body) but every subsequent request 401'd. Cause: the
session cookie is `secure: NODE_ENV === "production"`, and
docker-compose.yml sets NODE_ENV=production regardless of whether the
deployment actually has TLS in front of it. A Secure cookie is silently
never sent back by the browser over plain HTTP — no error, just a cookie
that never round-trips.
Adds COOKIE_SECURE, independent from NODE_ENV, to override the flag per
deployment. Unset (default) keeps prior behavior — secure in production.
Set COOKIE_SECURE=false only for a deployment reachable over plain HTTP
(no TLS yet), like this dev instance.
Verified locally: docker compose up with COOKIE_SECURE=false persists
and round-trips the cookie (signup -> /auth/me 200); without it, the
cookie still gets Secure as before. Full pnpm --filter api test / test:bdd
suites still pass (66 + 25 scenarios).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found live on http://batch.dev.kyuno.fr/: every API call went to
http://localhost:3000 in the visitor's own browser instead of the
deployed origin, because API_BASE_URL fell back to a hardcoded
"http://localhost:3000" whenever VITE_API_URL wasn't set at build time
— which is exactly the case for the production Docker image (apps/web/.env
is excluded by .dockerignore, so Vite never sees VITE_API_URL there).
Now defaults to "" (same origin as the page), correct for the merged
single-container deployment where the API serves this very frontend
build. Native dev (pnpm dev:web) is unaffected — apps/web/.env still sets
VITE_API_URL=http://localhost:3000 explicitly there.
Verified: built the Docker image, confirmed "localhost:3000" no longer
appears in the bundled JS, and docker compose up shows /auth/me called
against the container's own origin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
push (all branches) already fires for a PR branch on this repo; keeping
pull_request too just ran every job twice on the same commit. GitHub
still attaches the push-triggered run to the PR by SHA regardless of
which event fired it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- express-tools: ExpressServer.serveStaticFrontend() sert le build du
frontend (assets + fallback SPA), monté après les routes API et avant
le 404 JSON. Opt-in via FRONTEND_DIST_DIR (uniquement défini dans
l'image Docker) — le dev natif (dev:api/dev:web) est inchangé.
- apps/api/Dockerfile: build aussi apps/web, embarque son dist dans le
runtime ; corrige au passage l'oubli de packages/date-tools. Supprime
apps/web/Dockerfile et nginx.conf (plus de conteneur nginx séparé).
- docker-compose.yml: un seul service "app" (postgres + app), un seul
port APP_PORT, plus de WEB_PORT/CORS_ORIGIN à coordonner entre deux
origines. Garde `build:` (pas de registre — Portainer build depuis le
repo Git).
- ci.yml: éclate le job unique lint-and-test+e2e en 4 jobs indépendants
(lint/test/build/e2e), sans chaînage, déclenchés sur chaque push
(toute branche) + PR vers main.
- release.yml (nouveau): sur tag vX.Y.Z, sanity-build de l'image Docker,
GitHub Release avec changelog auto-généré, puis notification best-effort
du webhook Portainer (secret PORTAINER_WEBHOOK_URL).
- README: documente le conteneur unique et le pipeline de release.
- apps/api: les tests Planning (mocha et cucumber) lisaient l'horloge
systeme (new Date()/DateTime.utc()) pour construire leurs fixtures et
interroger /planning, ce qui les rendait non deterministes. Ajoute
test-support/reference-date.ts (TEST_REFERENCE_DATE, une date UTC
fixe) et l'utilise dans planning.test.ts / planning.steps.ts a la
place du systeme.
- apps/web: nouveau style global pour tous les radio/checkbox de
l'app (theme-select, allergy-select, onboarding) - "carte
selectionnable" : le controle natif reste reel/accessible mais
visuellement cache, toute la ligne devient la surface interactive
(bordure + fond teinte + coche au survol/selection). Corrige au
passage le bug de fond qui causait le desalignement des radios sur
/parametres/preferences-utilisateur (la regle generique
input, select { width: 100% } de profile-forms.scss s'appliquait
aussi aux checkbox/radio) et une regression de font-weight ou les
lignes non selectionnees du theme apparaissaient en gras comme si
elles l'etaient.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Repli/déploiement via le bouton dédié, labels masqués (pas retirés du
DOM — restent pour le title="") mais liens toujours visibles/cliquables
icône seule, persistance via localStorage vérifiée après cy.reload().
Design validé via une maquette HTML itérée avec l'utilisateur (voir
historique de conversation) avant implémentation.
- nav-icons.tsx: jeu d'icônes trait fin (24x24, inline SVG) pour les 3
items de nav principaux, les 4 pages de Paramètres et le chevron de
repli — pas de librairie d'icônes pour une poignée de glyphes
- AppLayout: bouton replier/déplier à côté du logo — la sidebar passe
de 15rem à un rail 4.25rem icônes-seules ; un seul toggle de classe
CSS sur le <aside> pilote tout (aucun sous-composant n'a besoin de
savoir que la sidebar est repliée), état persisté en localStorage
- Logo : monogramme "bC" en repli plutôt qu'une icône arbitraire à la
place du nom complet
- Avatar rond (initiale) dans le menu compte, popover recalée en
largeur fixe quand la sidebar est repliée (sinon écrasée à 4.25rem)
- title="" sur chaque item pour l'infobulle native une fois replié
- user-preferences.cy.ts: défaut SYSTEM, thème sauvegardé restitué et
appliqué au document, autosave sans bouton "Enregistrer"
- sidebar.cy.ts: 4 pages de paramètres (libellé "Préférences
alimentaires" renommé + nouvelle entrée "Préférences utilisateur")
- auth.cy.ts/onboarding.cy.ts: corrige un intercept **/planning/current
périmé (route supprimée dans la PR précédente) — masqué jusqu'ici car
aucune assertion n'en dépendait directement, mais provoquait un appel
réseau non mocké
- ThemeContext (features/theme/) : charge/applique le thème du profil
connecté via l'attribut data-theme (SYSTEM = pas d'attribut, laisse
la media query prefers-color-scheme décider) ; échec réseau non
bloquant (worst case reste au thème courant, pas d'unhandled rejection)
- UserPreferencesPage, routée /parametres/preferences-utilisateur,
hot-save (3 boutons radio Clair/Sombre/Système)
- layout.settings.nav.preferences renommé "Préférences alimentaires"
(évite la confusion avec ce nouveau concept plus large), nouvelle
entrée "Préférences utilisateur"
- _theme.scss: commentaires mis à jour (l'attribut data-theme est
désormais réellement posé, plus une simple anticipation)
- getPreferences: SYSTEM par défaut si aucune ligne (même logique que
dietId/allergies : absent = valeur par défaut, pas une omission)
- updatePreferences: upsert (crée la ligne au premier PATCH)
- Tests Mocha + Cucumber : 401, valeur invalide, défaut, création à la
volée, cloisonnement entre profils
- home-planning.cy.ts → planning-page.cy.ts : nav sidebar reprise
telle quelle, nouveaux cas pour la grille (case vide, recettes
placées dans les bonnes cases, colonne du jour courant, navigation
semaine précédente/suivante, popover calendrier)
- cy.clock fige "aujourd'hui" (2026-08-17, un lundi) pour des
assertions de date déterministes ; cy.viewport élargi (desktop-only,
cf. décision produit) pour que les 7 colonnes tiennent sans scroll
horizontal lors des assertions de visibilité
- Remplace HomePage (table jour unique) par PlanningPage : grille
7 jours × 5 repas, groupés Matin/Midi/Après-midi/Soir (séparateurs
pleins, plus épais entre groupes), recettes en pastilles pleine
largeur, bouton "+" pleine largeur sans bordure (pas encore branché
— pas de catalogue de recettes côté API, tâche future)
- WeekNavigator + CalendarPopover (sur date-tools) : flèches semaine
précédente/suivante, popover calendrier (mois navigable, clic sur
un jour → sa semaine), fermeture au clic extérieur
- apiClient.getPlanningForWeek(date) remplace getCurrentPlanning()
- i18n: namespace home → planning (+ nouvelles clés jours/repas/
calendrier), common.loadError factorisé (repris par les pages
Foyer/Préférences qui réutilisaient l'ancien home.error)
- getPlanningForDate(houseId, date: DateTime) — paramétré au lieu de
toujours "aujourd'hui", même logique de recherche sinon
- GET /planning?date=YYYY-MM-DD, validation de forme (zod) puis de
validité calendaire (parseDateOnly, 400 VALIDATION_ERROR sinon) —
un seul endpoint générique au lieu de deux qui se recouvrent
- Tests Mocha + Cucumber adaptés, + cas date manquante/malformée/
impossible et "semaine différente d'aujourd'hui"
- packages/date-tools: parseDateOnly/formatDateOnly/toDateOnly,
getWeekStart/addWeeks/buildCalendarMonth, sur Luxon DateTime (UTC)
- packages/shared: WEEK_DAYS/WeekDay, MEALS/Meal (contrat de valeurs
documenté pour PlanningItemView.weekDay/.meal, pas encore enforcé
en base), getPlanningByDateSchema (validation de forme de ?date=)
- household.cy.ts scindé en preferences.cy.ts (régime/allergies) et
household-settings.cy.ts (nom, créer/rejoindre/membres/suppression/
quitter, avec et sans droits admin)
- onboarding.cy.ts réordonné (régime → foyer → allergènes), avec les
cas skip / créer / rejoindre à l'étape foyer
- account.cy.ts: identité, suppression de compte (erreur de mot de
passe, succès, annulation)
- sidebar.cy.ts: menu Paramètres (3 liens) et menu compte (Mon compte,
déconnexion), redirection /foyer → /parametres/foyer
- auth.cy.ts/home-planning.cy.ts: adaptés au nouveau menu compte et au
premier écran d'onboarding (régime, plus foyer)
- Ordre: régime (1) → foyer (2) → allergènes (3), au lieu de
foyer → régime → allergènes
- L'étape foyer est désormais optionnelle: créer, rejoindre par code
d'invitation, ou passer (aucun foyer n'est créé au signup)
- pages/settings/AccountSettingsPage: identité + suppression de compte
(confirmation en deux temps, mot de passe requis)
- pages/settings/PreferencesPage: régime + allergies/intolérances,
sorti de HouseholdPage (attribut du profil, pas du foyer)
- pages/settings/HouseholdSettingsPage: sans foyer → créer/rejoindre ;
avec foyer → renommer (hot-save), code d'invitation, membres,
retirer un membre / supprimer le foyer (admin) ou le quitter
- HouseholdPage.tsx/.scss supprimés (contenu réparti ci-dessus)
- apiClient: createHouse/joinHouse/leaveHouse/deleteHouse/
removeHouseMember/deleteAccount
- AuthContext: deleteAccount()
- i18n: namespaces account/preferences réorganisés, household réduit
au foyer, common.saving/saved factorisées
- AppLayout: retire "Foyer & profil" de la nav principale
- SettingsMenu: bloc repliable (Compte/Préférences/Foyer), ouvert par
défaut si la route courante est sous /parametres
- AccountMenu: remplace le footer statique (salutation + déconnexion)
par un petit menu déroulant (Mon compte, Se déconnecter)
- house.test.ts réécrit (le foyer n'est plus auto-créé) + POST /house,
POST /house/join, POST /house/leave, DELETE /house/current,
DELETE /house/members/:id
- auth.test.ts: signup renvoie houseId=null, DELETE /auth/me (mauvais
mot de passe, suppression, transfert d'admin)
- planning.test.ts/steps.ts: création explicite du foyer (POST /house)
- household.feature: scénarios créer/rejoindre/quitter/supprimer/
retirer un membre, via un second agent (CustomWorld.secondAgent)
- auth.feature: scénarios de suppression de compte
- signup() ne crée plus de House — houseId démarre à null, le foyer
devient une étape optionnelle de l'onboarding (créer/rejoindre/passer)
- deleteAccount(): revérifie le mot de passe, transfère l'adminship ou
supprime le foyer si nécessaire (leaveCurrentHouse), puis supprime
le profil (cascade sur les allergies)
- DELETE /auth/me — nouvelle route, gated par mot de passe
- clearCookie n'envoie plus maxAge (corrige un warning de dépréciation
Express, déjà latent sur /auth/logout)
Retour de CI (job e2e, PR #10) : "creates a profile and lands on the
home page" échouait avec une exception non gérée ("Failed to fetch")
depuis OnboardingHouseholdPage's GET /house/current — jamais mocké
dans ce test.
Cause : ce test n'avait jamais été mis à jour quand le signup a commencé
à rediriger vers /onboarding/foyer au lieu de "/" directement (fait dans
le commit du wizard d'inscription, plus tôt sur cette PR) — seul le
texte "Bonjour Alice Martin" -> "Bonjour Alice" avait été corrigé à
l'époque, pas la destination réelle de la redirection. Invisible
localement : je n'ai pas pu faire tourner Cypress dans ce sandbox
(crash GPU headless, confirmé pré-existant), donc ce test n'avait
jamais réellement été exécuté après ce changement — seulement
typechecké.
Fix : le test vérifie maintenant qu'il atterrit sur /onboarding/foyer
(pas la home) avec le nom du foyer préaffiché, et mocke GET
/house/current comme le fait déjà onboarding.cy.ts pour ce même écran.
Vérifié via tsc --noEmit (Cypress toujours injouable localement ici) —
CI à re-checker après push.
Retour fonctionnel : allergies et intolérances doivent être distinguées
dans l'UI, et /foyer doit sauvegarder à la volée plutôt que via des
boutons "Enregistrer".
- AllergySelect prend un `legend` en prop au lieu d'un libellé interne
fixe — le même composant est rendu deux fois par chaque page
consommatrice (HouseholdPage, OnboardingAllergensPage), une fois par
`kind` (ALLERGY / INTOLERANCE), la sélection restant une seule liste
d'IDs partagée.
- HouseholdPage : suppression des boutons "Enregistrer", autosave
déclenché depuis le handler onChange de chaque champ (jamais un
useEffect générique sur la valeur — se déclencherait aussi au
chargement initial, sans distinction propre "chargé" vs "modifié").
Nom du foyer et allergènes/intolérances debouncés (600ms/500ms),
régime sauvegardé immédiatement (sélection discrète). Validation
client (nom vide) empêche l'autosave plutôt que de déclencher un
aller-retour API voué à l'échec.
- i18n : household.form.allergiesLabel devient "Allergies" (au lieu de
"Allergies & intolérances"), nouvelle clé intolerancesLabel, save/
saved remplacés par saving/saved (plus de bouton à libeller).
- Cypress (household.cy.ts réécrit, onboarding.cy.ts mis à jour) +
specs/frontend-architecture.md + README.md.
Vérifié dans le navigateur : wizard d'inscription affiche bien les
deux groupes (12 allergies / 2 intolérances) ; /foyer sans aucun
bouton, chaque section sauvegarde automatiquement (vérifié en base
après édition du nom du foyer et du régime) ; compte de test nettoyé.
Clôt le retour fonctionnel sur la feature profil/foyer/régime/
allergènes (8 commits au total sur cette PR).
Retour fonctionnel : les allergies et intolérances doivent être
distinguées, pas listées ensemble.
- schema.prisma: enum AllergenKind (ALLERGY|INTOLERANCE) + Category.kind
(@default(ALLERGY), migration écrite à la main comme précédemment —
`migrate dev` refuse en environnement non-interactif ici — SQL généré
via `prisma migrate diff`).
- reference-seed-data.ts: classification par substance (Gluten et
Sulfites = INTOLERANCE, les 12 autres = ALLERGY — réaction
non-immunitaire documentée vs réaction immunitaire classique).
Corrige au passage l'upsert : `update: { kind }` au lieu de `update:
{}` — un reseed doit pouvoir corriger `kind` sur une Category déjà
existante, pas juste no-op.
- reference.service.ts / packages/shared: AllergyView gagne `kind`.
PATCH /profile/allergies ne change pas (une seule liste d'IDs, kind
ne sert qu'au groupement d'affichage côté client).
- Tests Mocha (29 passing) + Cucumber (15 scenarios, inchangés).
Classifié par substance (pas par utilisateur) — documenté comme
limitation connue dans le README. Web (split UI + hot saving sur
/foyer) dans le commit suivant.
- apps/web/cypress/e2e/onboarding.cy.ts — parcours complet (rempli et
entièrement skippé) signup → 3 étapes → home, mêmes conventions
cy.intercept que le reste.
- apps/web/cypress/e2e/household.cy.ts — /foyer : préremplissage, et
sauvegarde indépendante de chacune des 3 sections.
- specs/frontend-architecture.md : nouvelle section "Parcours profil —
foyer, régime, allergènes" (diagramme mermaid, les deux bugs de state
trouvés en testant dans le navigateur), arborescence et namespaces
i18n à jour.
- README.md : nouvelle section "Parcours profil — foyer, régime,
allergènes", section sidebar mise à jour (Foyer & profil n'est plus
un stub).
Cypress lui-même ne peut pas tourner en local dans ce sandbox (voir la
note existante dans le README) — vérifié via `tsc --noEmit` sur les
specs + parcours manuel complet dans le navigateur (les deux à travers
les 5 commits précédents de cette feature).
Clôt la feature profil/foyer/régime/allergènes (6 commits, cette PR) :
seed+référence -> endpoints foyer/profil -> composants partagés ->
wizard d'inscription -> page /foyer -> ce commit.
- HouseholdPage remplace le stub ComingSoonPage : 3 sections
indépendamment sauvegardées (nom du foyer, régime, allergènes/
intolérances), mêmes composants partagés que le wizard d'inscription.
Chaque section a son propre bouton "Enregistrer" (3 ressources API
distinctes, pas de raison qu'une modification attende les autres).
- AuthContext : ajout de refreshUser() — re-fetch GET /auth/me et met
à jour `user`.
- Bug trouvé et corrigé en testant l'aller-retour SPA dans le
navigateur (sidebar → Recettes → Foyer, sans rechargement complet) :
la valeur du régime revenait à l'ancienne après sauvegarde. Cause :
la page initialisait dietId depuis useAuth().user.dietId, un instantané
jamais rafraîchi après une modification faite directement via
apiClient (qui ne touche pas AuthContext). Fix : la page fetch son
propre profil frais (apiClient.me()) au montage plutôt que de
dépendre du contexte, et appelle refreshUser() après une sauvegarde
réussie du régime pour que le reste de l'app reste cohérent aussi.
- household.comingSoon (clé i18n) supprimée, plus utilisée.
Vérifié dans le navigateur : préremplissage, sauvegarde par section,
persistance après rechargement ET après navigation SPA aller-retour ;
nettoyage du compte de test.
- SignupPage: après signup(), navigate("/onboarding/foyer") au lieu de
"/" — la home reste inchangée, seule la destination change.
- pages/onboarding/: 3 routes top-level RequireAuth-gated (PAS nichées
sous AppLayout — wizard plein écran sans sidebar, même langage visuel
que /login|/signup) :
- /onboarding/foyer — HouseNameField, préremplie avec le nom
auto-généré du foyer (continuer sans éditer = skip implicite).
- /onboarding/regime — DietSelect, valeur initiale depuis
useAuth().user.dietId (pas de fetch supplémentaire nécessaire).
- /onboarding/allergenes — AllergySelect, termine sur navigate("/").
- Bug trouvé et corrigé en testant dans le navigateur : RedirectIfAuthenticated
redirigeait vers "/" en course avec le navigate() explicite de
SignupPage — `user` devient non-null (via signup()) pendant que
SignupPage est encore monté sous ce guard, qui réagit et redirige
avant que le navigate("/onboarding/foyer") ne prenne effet. Latent
depuis le début (invisible avant car l'ancien SignupPage naviguait
aussi vers "/", donc les deux redirections concordaient). Fix : la
décision de redirection est verrouillée une seule fois, au moment où
la vérification initiale (`isLoading`) se termine, plutôt que
réévaluée à chaque changement de `user`.
Vérifié de bout en bout dans le navigateur (inscription → 3 étapes →
home), données confirmées en base (foyer renommé, régime + 2 allergènes
enregistrés), puis nettoyage des comptes de test.
- ApiClient: getDiets/getAllergies (référence), getCurrentHouse/
renameHouse, updateDiet, getAllergyIds/updateAllergyIds.
- features/profile/: HouseNameField, DietSelect (toujours une option
"aucun régime" -> null, étape skippable), AllergySelect (checkboxes
en grille + fieldset/legend, pas un <select multiple> — plus
tapable/accessible, notamment sur mobile). Tous "dumb"/contrôlés :
reçoivent leurs données (diets/allergies) en props plutôt que de les
fetcher eux-mêmes — le fetch/état de chargement reste à la page
appelante.
- profile-forms.scss partagé par les trois (même split que
features/auth/auth-form.scss vs Login/SignupPage : styles de champs
ici, layout de page dans chaque page consommatrice).
Pas encore utilisés (aucune page ne les importe) — le wizard
d'inscription (étape suivante) et la page /foyer les cablent.
- GET/PATCH /house/current — renomme le foyer de l'utilisateur connecté.
PATCH avec houseId null -> 404 HOUSE_NOT_FOUND.
- PATCH /profile/diet { dietId: number | null } — régime du profil ;
null l'efface (étape skippable du parcours). dietId invalide ->
404 DIET_NOT_FOUND.
- GET/PATCH /profile/allergies — allergènes/intolérances, liste d'IDs ;
PATCH remplace l'ensemble complet (pas une fusion, cohérent avec un
multi-select). ID invalide -> 404 ALLERGY_NOT_FOUND.
- 3 nouveaux ErrorCode (4041-4043) + libellés fr.
- Extraction de toSafeProfile() dans src/lib/safe-profile.ts —
auparavant dupliqué dans auth.service.ts et require-auth.ts,
profile.service.ts le réutilise aussi.
- Tests Mocha (28 passing) + Cucumber (15 scenarios) — même convention
que le reste, doc README.
Deuxième commit de la feature profil/foyer/régime/allergènes —
composants front partagés dans le commit suivant.
- schema.prisma: Diet.name/Category.name deviennent @unique (pas dans le
doc spec d'origine — ajouté pour que le seed soit idempotent par
upsert). Migration écrite à la main + appliquée via `migrate deploy`
(`migrate dev` refuse en environnement non-interactif ici) — SQL
généré via `prisma migrate diff` pour matcher exactement les
conventions Prisma.
- src/db/reference-seed-data.ts: seedReferenceData() — 5 régimes, 14
allergènes (règlement UE 1169/2011 annexe II). Chaque allergène = une
Category (upsert par nom) + une unique Allergy sous cette catégorie
(Allergy elle-même ne porte pas de nom, voir schema.prisma).
Réutilisée par prisma/seed.ts (CLI, `prisma db seed`) ET
test-support/reset-db.ts (chaque test repart avec ces données de
référence, pas des tables vides).
- modules/reference/: GET /reference/diets, GET /reference/allergies —
publics (pas de requireAuth), lisibles avant qu'un compte existe
(wizard d'inscription).
- packages/shared: DietView, AllergyView (name résolu côté serveur
depuis Category, le split Allergy/Category reste invisible du client).
- Tests Mocha + Cucumber, doc README.
Premier commit de la feature profil/foyer/régime/allergènes (planifiée
en chat) — endpoints foyer/profil dans le commit suivant.
- New apps/web/cypress/e2e/home-planning.cy.ts (mocked API, same
cy.intercept convention as auth.cy.ts):
- sidebar nav between sections + active-link highlighting
- user name + logout from the sidebar footer
- home planning: empty / loaded (table rows) / error states
- specs/frontend-architecture.md: documents AppLayout (single
RequireAuth+AppLayout parent route, nested routes via <Outlet />),
the ComingSoonPage stub pattern, updated folder tree and i18n
namespace list, updated routing diagram.
- README.md: new "Accueil, sidebar & sections" section; notes the
Cypress-can't-run-headless-here environment limitation (confirmed
pre-existing on main) and the docker-vs-local-dev CORS_ORIGIN gotcha
hit while manually verifying this feature.
Closes out the home-page-after-login feature (5 commits, this PR):
GET /planning/current -> AppLayout -> routing/stub pages -> HomePage
planning view -> this commit.
- ApiClient.getCurrentPlanning() — GET /planning/current.
- HomePage.tsx: replaces the old greeting card (now redundant with
AppLayout's sidebar) with the household's current planning — loading /
error / empty ("aucun planning pour cette semaine") / loaded (table of
weekDay/meal/recipe) states, modeled as a discriminated union so an
impossible combination (e.g. loading with data) can't be represented.
No invented weekday/meal grid — the API's `weekDay`/`meal` are free-form
strings (no enum exists yet in the schema), so this renders the items
as returned rather than assuming a specific vocabulary.
- locales/fr/translation.json: home.* replaced (title/loading/error/
empty/table.*), old greeting/logout keys removed (superseded by
layout.greeting/layout.logout from the AppLayout commit).
- cypress/e2e/auth.cy.ts: updated the now-stale "Bonjour Alice Martin"
assertions (greeting moved to the sidebar, first name only) and added
GET /planning/current intercepts so these specs don't depend on a real
backend. Manually verified end-to-end against a real API in the browser
preview (empty state + a seeded planning) — Cypress itself can't run
headless Chromium in this sandboxed dev environment (confirmed
pre-existing on main, unrelated to this change); CI runs the real
suite.
Verified with `pnpm --filter web build` after each of steps 2-4 to keep
every commit in this sequence independently buildable.
- App.tsx: every authenticated route now nests under one
RequireAuth + AppLayout parent route (react-router nested
routes/<Outlet />) instead of each page wrapping its own guard.
- New routes/pages: /recettes, /liste-de-courses, /foyer, each a thin
wrapper around a shared ComingSoonPage ("cette section arrive
bientôt.") — no backend behind them yet, matches the "pages stub
dédiées" choice discussed in chat.
- locales/fr/translation.json: nav labels + stub copy.
HomePage still shows its own greeting/logout card here (unchanged,
now nested inside AppLayout's <Outlet /> — briefly duplicating the
sidebar's greeting/logout) — replaced by the actual planning view in
the next commit.
Adds the shell for authenticated pages: a fixed-width sidebar with
brand, section nav (Planning/Recettes/Liste de courses/Foyer & profil),
and the signed-in user's name + logout at the bottom, plus a main
content area rendering the matched child route via react-router's
<Outlet />. Follows the "Mise en Place" theme tokens; collapses to a
top bar under 640px (this app is meant to be embedded via Capacitor
later, see the root README).
Not wired into App.tsx yet (next commit) — self-contained and builds/
typechecks on its own.