import type { BaseUnit, RecipeUnit, StoreItem } from "@/app/types"; // --------------------------------------------------------------------------- // Unit conversion: recipe units → the catalog item's base unit. // // Weight ↔ volume crossings (e.g. "1 cup rice" for a rice bag sold by weight) // use the item's `gramsPerMl`. Anything else that doesn't line up is a data // bug, so we throw loudly instead of guessing. // --------------------------------------------------------------------------- const GRAMS_PER: Partial> = { g: 1, oz: 28.35, lb: 453.6 }; const ML_PER: Partial> = { ml: 1, tsp: 4.93, tbsp: 14.79, cup: 236.6 }; export function toBaseUnit(amount: number, unit: RecipeUnit, item: StoreItem): number { const base = item.baseUnit; if (base === "each") { if (unit === "each") return amount; throw new Error(`"${item.id}" is sold by count; recipe used "${unit}"`); } if (base === "g") { if (GRAMS_PER[unit] !== undefined) return amount * GRAMS_PER[unit]!; if (ML_PER[unit] !== undefined && item.gramsPerMl) return amount * ML_PER[unit]! * item.gramsPerMl; } if (base === "ml") { if (ML_PER[unit] !== undefined) return amount * ML_PER[unit]!; } throw new Error(`Can't convert ${unit} → ${base} for "${item.id}" (missing gramsPerMl?)`); } /** Pretty-print a base-unit amount using US kitchen units. */ export function formatAmount(amount: number, base: BaseUnit): string { if (base === "each") { const rounded = Math.round(amount * 10) / 10; return `${rounded}`; } if (base === "g") { const lb = amount / 453.6; return lb >= 1 ? `${lb.toFixed(1)} lb` : `${(amount / 28.35).toFixed(1)} oz`; } // ml const cups = amount / 236.6; // Number(...) drops trailing zeros: 2.50 → 2.5, 2.00 → 2 if (cups >= 0.25) return `${Number(cups.toFixed(cups >= 4 ? 0 : 2))} cup`; const tbsp = amount / 14.79; return tbsp >= 1 ? `${tbsp.toFixed(1)} tbsp` : `${(amount / 4.93).toFixed(1)} tsp`; } export function formatUsd(value: number): string { return value.toLocaleString("en-US", { style: "currency", currency: "USD" }); }