import type { Recipe, RecipeIngredient } from "@/app/types"; import { CATALOG_BY_ID } from "./catalog"; // --------------------------------------------------------------------------- // Human-friendly ingredient lines, scaled to a serving count. // // ing("white_rice", 1.5, "cup") for 4 → shown for 2 as "¾ cup Long Grain White Rice" // ing("black_beans", 1, "each") → "1 × Black Beans, canned (15 oz can)" // --------------------------------------------------------------------------- const FRACTIONS: [number, string][] = [ [0, ""], [1 / 8, "⅛"], [1 / 4, "¼"], [1 / 3, "⅓"], [1 / 2, "½"], [2 / 3, "⅔"], [3 / 4, "¾"], [1, ""], ]; /** 0.75 → "¾", 1.5 → "1½", 2.33 → "2⅓", 13.2 → "13". */ function formatQuantity(value: number): string { if (value >= 10) return String(Math.round(value)); const whole = Math.floor(value); const fraction = value - whole; const [nearest, glyph] = FRACTIONS.reduce((best, f) => (Math.abs(f[0] - fraction) < Math.abs(best[0] - fraction) ? f : best)); const total = whole + (nearest === 1 ? 1 : 0); if (total === 0 && glyph === "") return "⅛"; // never show "0" for a real ingredient return `${total > 0 ? total : ""}${glyph}` || "0"; } interface IngredientLine { quantity: string; /** e.g. "cup", "lb", or "×" for counted items. */ unit: string; name: string; /** Package hint for counted items, e.g. "15 oz can". */ detail?: string; } function ingredientLine(ingredient: RecipeIngredient, scale: number): IngredientLine { const item = CATALOG_BY_ID.get(ingredient.itemId); const name = item?.name ?? ingredient.itemId; const quantity = formatQuantity(ingredient.amount * scale); if (ingredient.unit === "each") { // Single-unit packages (a can, a jar) read better with the package size. const detail = item && item.packageSize === 1 && item.packageLabel !== "each" ? item.packageLabel : undefined; return { quantity, unit: "×", name, detail }; } return { quantity, unit: ingredient.unit, name }; } export function ingredientLines(recipe: Recipe, servings: number): IngredientLine[] { const scale = servings / recipe.servings; return recipe.ingredients.map((i) => ingredientLine(i, scale)); }