import type { IngredientView, UnitView } from "@batch-cooking/shared"; import { useTranslation } from "react-i18next"; import { AllergenBadges } from "../badges/AllergenBadges"; import { DietBadges } from "../badges/DietBadges"; import { ReproducibleBadge } from "../badges/ReproducibleBadge"; import { IngredientTypeIcon } from "./ingredient-icons"; import { ingredientLabel } from "./ingredient-label"; import "../recipes.scss"; /** One selected ingredient line in the recipe form — the ingredient itself (picked via `IngredientPicker`) plus its quantity/unit for this recipe. Quantity is kept as a raw string while editing (not parsed to a number until submit) so an in-progress/invalid value doesn't fight the input; `unit` picks from `unitsCatalog` (a closed reference list, see `Unit` in schema.prisma) rather than free text. */ export function IngredientRow({ ingredient, quantity, unitId, unitsCatalog, duplicate = false, onQuantityChange, onUnitChange, onRemove, }: { ingredient: IngredientView; quantity: string; unitId: number | null; unitsCatalog: UnitView[]; /** This ingredient is also picked by another line in the same form — `RecipeIngredient`'s primary key is `(recipeId, ingredientId)`, one row per ingredient (schema.prisma), so submitting two lines for it would fail. Never true for a manually-added line (`IngredientPicker`'s `excludeIds` already prevents picking the same ingredient twice) — only reachable via a source import, whose raw lines aren't deduplicated (e.g. "Egg Yolks" and "Eggs" both resolving to "Egg" — see issue #53's `RecipeImportForm`). */ duplicate?: boolean; onQuantityChange: (quantity: string) => void; onUnitChange: (unitId: number) => void; onRemove: () => void; }) { const { t } = useTranslation(); // No unit picked yet — surfaced with a highlighted ` onQuantityChange(e.target.value)} aria-label={t("recipes.form.quantityLabel")} /> {!isPlaceholder && ( <> )} {unitMissing && (

{t("recipes.form.unitMissingHint")}

)} {duplicate && (

{t("recipes.form.duplicateIngredientHint")}

)} ); }