This commit is contained in:
Cayden Schwechten 2026-10-31 20:44:29 -05:00
commit c19b910c36
80 changed files with 16134 additions and 0 deletions

54
app/lib/units.ts Normal file
View file

@ -0,0 +1,54 @@
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<Record<RecipeUnit, number>> = { g: 1, oz: 28.35, lb: 453.6 };
const ML_PER: Partial<Record<RecipeUnit, number>> = { 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" });
}