feat(recipes): ingrédients d'assaisonnement « selon le goût » (#128) #136

Merged
kyuno merged 1 commit from feat/seasoning-ingredients into main 2026-09-09 14:35:25 +02:00
Owner

Contexte

Closes #128 (puce 1 de #3). Aujourd'hui toute ligne d'ingrédient porte une quantité + une unité obligatoires (RecipeIngredient.quantity/unitId NOT NULL, recipeIngredientInputSchema, canSubmit), impossible de saisir « sel — selon le goût ».

Changements

Modèle — RecipeIngredient.quantity/unitId deviennent nullable + relation unit optionnelle. Migration 20260909120000_recipe_ingredient_quantity_optional : deux DROP NOT NULL, purement additive, aucune ligne réécrite.

Contrat partagé — recipeIngredientInputSchema : quantity/unitId optionnels + .refine « les deux ou aucun » (rejette quantité-sans-unité). RecipeIngredientView.quantity/unit et ShoppingListItemView.quantity/unit deviennent … | null.

API — toRecipeView renvoie null/null pour une ligne d'assaisonnement ; resolveIngredientLines/assertUnitsExist gèrent l'absence. aggregateShoppingList : une ligne sans quantité apparaît une fois (clé :toTaste, dédupliquée par ingrédient), jamais sommée ni mise à l'échelle. Session de cuisine : le chemin clause-de-technique tolérait déjà quantity null — inchangé (ne lit pas la liste d'en-tête).

Frontend — IngredientRow : case « Selon le goût » qui masque quantité/unité et n'est jamais --incomplete. RecipeFormPage + RecipeImportForm : IngredientLine.toTaste, canSubmit/payload adaptés, et un import dont la source laisse une ligne résolue sans quantité (« salt to taste », déjà produit par translateRecipeIngredients) arrive directement en toTaste. ShoppingListPage affiche « selon le goût ». i18n : recipes.form.toTasteToggle, shoppingList.toTaste.

Vérification locale

pnpm lint ✓ · pnpm build ✓ (Prisma client régénéré, tsc api/web OK) · Cypress recipe-form.feature 9/9 + shopping-list.feature 5/5 + régression recipes/planning/recipe-sources/layout 38/38. Mocha non exécutable ici (pas de Postgres local) — 4 tests ajoutés (recipe.test.ts ×3, shopping-list.test.ts ×1), à valider en CI.

🤖 Generated with Claude Code

## Contexte `Closes #128` (puce 1 de #3). Aujourd'hui toute ligne d'ingrédient porte une quantité + une unité obligatoires (`RecipeIngredient.quantity`/`unitId` `NOT NULL`, `recipeIngredientInputSchema`, `canSubmit`), impossible de saisir « sel — selon le goût ». ## Changements **Modèle** — `RecipeIngredient.quantity`/`unitId` deviennent nullable + relation `unit` optionnelle. Migration `20260909120000_recipe_ingredient_quantity_optional` : deux `DROP NOT NULL`, purement additive, aucune ligne réécrite. **Contrat partagé** — `recipeIngredientInputSchema` : `quantity`/`unitId` optionnels + `.refine` « les deux ou aucun » (rejette quantité-sans-unité). `RecipeIngredientView.quantity`/`unit` et `ShoppingListItemView.quantity`/`unit` deviennent `… | null`. **API** — `toRecipeView` renvoie `null`/`null` pour une ligne d'assaisonnement ; `resolveIngredientLines`/`assertUnitsExist` gèrent l'absence. **`aggregateShoppingList`** : une ligne sans quantité apparaît une fois (clé `:toTaste`, dédupliquée par ingrédient), jamais sommée ni mise à l'échelle. Session de cuisine : le chemin clause-de-technique tolérait déjà `quantity` null — inchangé (ne lit pas la liste d'en-tête). **Frontend** — `IngredientRow` : case « Selon le goût » qui masque quantité/unité et n'est jamais `--incomplete`. `RecipeFormPage` + `RecipeImportForm` : `IngredientLine.toTaste`, `canSubmit`/payload adaptés, et un import dont la source laisse une ligne résolue sans quantité (« salt to taste », déjà produit par `translateRecipeIngredients`) arrive directement en `toTaste`. `ShoppingListPage` affiche « selon le goût ». i18n : `recipes.form.toTasteToggle`, `shoppingList.toTaste`. ## Vérification locale `pnpm lint` ✓ · `pnpm build` ✓ (Prisma client régénéré, tsc api/web OK) · Cypress `recipe-form.feature` 9/9 + `shopping-list.feature` 5/5 + régression `recipes`/`planning`/`recipe-sources`/`layout` 38/38. Mocha non exécutable ici (pas de Postgres local) — 4 tests ajoutés (`recipe.test.ts` ×3, `shopping-list.test.ts` ×1), à valider en CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-09 14:15:56 +02:00
feat(recipes): ingrédients d'assaisonnement « selon le goût » sans quantité (#128)
All checks were successful
CI / lint (push) Successful in 3m5s
CI / build (push) Successful in 3m15s
CI / intent-service-test (push) Successful in 5m12s
CI / e2e (push) Successful in 11m1s
CI / test (push) Successful in 19m13s
CI / build-android (push) Has been skipped
ba030ec18c
`RecipeIngredient.quantity`/`unitId` deviennent nullable (migration purement
additive) : une ligne de recette peut n'avoir ni quantité ni unité — sel,
poivre… ajoutés selon le goût. Le schéma partagé impose « quantité et unité,
ou aucune des deux » ; `toRecipeView` renvoie `null`/`null`.

`aggregateShoppingList` fait apparaître une telle ligne une seule fois, sans
quantité ni unité, jamais sommée ni mise à l'échelle. `ShoppingListPage`
affiche « selon le goût » à la place de la quantité.

Formulaire : `IngredientRow` gagne une case « Selon le goût » qui masque
quantité/unité ; `RecipeFormPage` et `RecipeImportForm` n'exigent plus
quantité/unité pour ces lignes et les envoient sans. Un import dont la source
laisse une ligne résolue sans quantité (« salt to taste ») arrive
directement en « selon le goût ».

Le chemin clause-de-technique (session de cuisine) tolérait déjà `quantity`
null — inchangé.

Tests : Mocha (création/édition d'une ligne sans quantité, rejet
quantité-sans-unité, liste de courses non sommée) ; Cypress (case à cocher +
parcours), régression recette/liste de courses OK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno merged commit 019e3fb5e5 into main 2026-09-09 14:35:25 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/batchCooking#136
No description provided.