Ingrédients d'assaisonnement : lignes de recette « selon le goût » sans quantité ni unité #128
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Contexte
Origine : ticket #3 (« Améliorations diverses »), puce 1 — « Unités pour les ingrédients → Ajouter la possibilité de définir des ingrédients d'assaisonnement (sans quantité, selon le goût) ».
Aujourd'hui, toute ligne d'ingrédient d'une recette porte une quantité et une unité obligatoires :
RecipeIngredient.quantityestDecimal @db.Decimal(10, 2)NOT NULL etunitId IntNOT NULL (schema.prisma:700-712).recipeIngredientInputSchemaimposequantity: z.number().positive(...)etunitId: z.number().int().positive()(packages/shared/src/schemas/recipe.ts:22-34).quantity > 0et une unité :canSubmit(RecipeFormPage.tsx:180-187), etIngredientRowmarque--incompletesur l'unité manquante (IngredientRow.tsx:38-47).Number(recipeIngredient.quantity) * scalesans jamais tester un null (shopping-list.service.ts:70-87).Résultat : impossible de saisir « sel, poivre — selon le goût » sans inventer une quantité factice.
À noter : le chemin clause-de-technique tolère déjà
quantity/unitnuls (StepTechStepIngredient.quantityest nullable,toOptimizerRecipefaitline.quantity === null ? null : Number(...), cooking-session.service.ts:86-90). C'est seulement la liste d'ingrédients d'en-tête de recette qui est stricte.Comportement souhaité
Une ligne d'ingrédient peut être marquée « assaisonnement / selon le goût » : ni quantité, ni unité. Elle apparaît dans la recette et dans la liste de courses comme un simple rappel (« Sel — selon le goût »), sans quantité agrégée, et n'entre pas dans la mise à l'échelle portions.
Modèle de données
RecipeIngredient(apps/api/prisma/schema.prisma) —quantityetunitIddeviennent nullable :Migration purement additive :
ALTER TABLE "recipe_ingredient" ALTER COLUMN "quantity" DROP NOT NULL;+ idemunit_id. Aucune ligne existante touchée (toutes gardent leur quantité).Contrat partagé (
packages/shared)recipeIngredientInputSchema(schemas/recipe.ts:22-34) :quantity/unitIddeviennent optionnels, avec un.refine— soit les deux présents, soit les deux absents (jamais une quantité sans unité). L'exclusivitéingredientIdxorplaceholderNameexistante est conservée.RecipeIngredientView(types/recipe.ts:28-32) :quantity: number | null,unit: UnitView | null.ShoppingListItemView(types/shopping-list.ts:19-23) : voir « Points à trancher ».Backend
recipe.service.tstoRecipeView(recipe.service.ts:244-249) :quantity/unitconditionnels.resolveIngredientLines/createRecipeInternal/updateRecipe: stockernull/nullpour une ligne d'assaisonnement ;assertUnitsExistne reçoit que lesunitIdnon-null.shopping-list.service.tsaggregateShoppingList(shopping-list.service.ts:67-95) : une lignequantity === nullest listée une fois (dédupliquée paringredientId), sans somme ni unité. Test de non-régression obligatoire.cooking-session.service.ts: le chemin clause-de-technique tolère déjà les nuls — vérifier qu'aucun accès à la liste d'en-tête ne casse.Frontend
IngredientRow(IngredientRow.tsx) : un toggle « selon le goût » par ligne ; coché ⇒ champs quantité + unité masqués/grisés, la ligne n'est plus--incomplete.IngredientLine(state deRecipeFormPage+RecipeImportForm) :quantity/unitId« non applicables ».canSubmit: une ligne « selon le goût » est toujours valide (RecipeFormPage.tsx:180-187, RecipeImportForm.tsx:268-277).quantity/unitIdpour ces lignes.RecipeDetailPanel: afficher « selon le goût » au lieu de « 0 g ».ShoppingListPage: ces lignes à part / sans quantité.recipes.form.toTasteLabel,shoppingList.toTaste, etc.Découpage proposé
packages/shared,toRecipeView, création/édition, fixtures Cypress du formulaire à jour. Pas encore de toggle visible.IngredientRow, câblé dansRecipeFormPage+RecipeImportForm, e2e Gherkin.Tests (obligatoires par PR, cf.
specs/dev-conventions.md)createRecipe/updateRecipeacceptent une ligne sans quantité/unité et la relisent ennull;recipeIngredientInputSchemarejette quantité-sans-unité ;aggregateShoppingListignore les lignesnull(non-régression) ;getCookingPlanForDateidem.Documentation à mettre à jour
specs/batch-cooking-modele.md—recipe_ingredient(colonnes nullable + invariant).specs/backend-architecture.md— sections liste de courses & session de cuisine.Points restant à trancher en revue
toTastedans le catalogueUnit(éviterait une migration, mais mélange un concept d'unité avec une absence de quantité — a priori non).ShoppingListItemView: nouveau champtoTaste: booleanavecquantity/unitnullable, ou lignes regroupées à part dansShoppingListView..min(1)« au moins un ingrédient » ? (oui a priori.)🤖 Filed by Claude Code
Implémenté par la PR #136 (
feat/seasoning-ingredients), mergée dansmain(019e3fb).RecipeIngredient.quantity/unitIdsont désormais nullable (schema.prisma), avec la logique d'assaisonnement « selon le goût » côté schéma partagé, backend (liste de courses / session de cuisine) et formulaires. Vérifié présent surmain.