import { Soup, Salad, Pizza, Sandwich, IceCream, Cookie, Beef, Fish, Cherry, Carrot, Egg, Drumstick, Croissant, Donut, Cake, ChefHat, Banana, Grape, Citrus, Hamburger, Popcorn, Bean, Vegan, UtensilsCrossed, Leaf, Shrimp, CookingPot, Candy, Nut, Coffee, Wine, Beer, GlassWater, Martini, CupSoda, Milk, type LucideIcon, } from "lucide-react"; // Muted on purpose — these sit behind a lucide icon on every photo-less card // in a grid, so anything more saturated than "barely-there tint" overpowers // real cover photos next to it and reads as loud rather than as a placeholder. export const GRADIENT_OPTIONS = [ { key: "orange-rose", classes: "from-orange-100/70 to-rose-100/70 dark:from-orange-950/40 dark:to-rose-950/40" }, { key: "amber-lime", classes: "from-amber-100/70 to-lime-100/70 dark:from-amber-950/40 dark:to-lime-950/40" }, { key: "sky-indigo", classes: "from-sky-100/70 to-indigo-100/70 dark:from-sky-950/40 dark:to-indigo-950/40" }, { key: "emerald-teal", classes: "from-emerald-100/70 to-teal-100/70 dark:from-emerald-950/40 dark:to-teal-950/40" }, { key: "fuchsia-purple", classes: "from-fuchsia-100/70 to-purple-100/70 dark:from-fuchsia-950/40 dark:to-purple-950/40" }, { key: "yellow-orange", classes: "from-yellow-100/70 to-orange-100/70 dark:from-yellow-950/40 dark:to-orange-950/40" }, { key: "rose-pink", classes: "from-rose-100/70 to-pink-100/70 dark:from-rose-950/40 dark:to-pink-950/40" }, { key: "teal-cyan", classes: "from-teal-100/70 to-cyan-100/70 dark:from-teal-950/40 dark:to-cyan-950/40" }, ] as const; export const DISH_ICON_OPTIONS = [ { key: "chef-hat", Icon: ChefHat }, { key: "soup", Icon: Soup }, { key: "salad", Icon: Salad }, { key: "pizza", Icon: Pizza }, { key: "sandwich", Icon: Sandwich }, { key: "ice-cream", Icon: IceCream }, { key: "cookie", Icon: Cookie }, { key: "beef", Icon: Beef }, { key: "fish", Icon: Fish }, { key: "cherry", Icon: Cherry }, { key: "carrot", Icon: Carrot }, { key: "egg", Icon: Egg }, { key: "drumstick", Icon: Drumstick }, { key: "croissant", Icon: Croissant }, { key: "donut", Icon: Donut }, { key: "cake", Icon: Cake }, { key: "banana", Icon: Banana }, { key: "grape", Icon: Grape }, { key: "citrus", Icon: Citrus }, { key: "hamburger", Icon: Hamburger }, { key: "popcorn", Icon: Popcorn }, { key: "bean", Icon: Bean }, { key: "vegan", Icon: Vegan }, { key: "utensils-crossed", Icon: UtensilsCrossed }, { key: "leaf", Icon: Leaf }, { key: "shrimp", Icon: Shrimp }, { key: "cooking-pot", Icon: CookingPot }, { key: "candy", Icon: Candy }, { key: "nut", Icon: Nut }, ] as const; export const DRINK_ICON_OPTIONS = [ { key: "coffee", Icon: Coffee }, { key: "wine", Icon: Wine }, { key: "beer", Icon: Beer }, { key: "glass-water", Icon: GlassWater }, { key: "martini", Icon: Martini }, { key: "cup-soda", Icon: CupSoda }, { key: "milk", Icon: Milk }, ] as const; export const ALL_ICON_OPTIONS: { key: string; Icon: LucideIcon }[] = [...DISH_ICON_OPTIONS, ...DRINK_ICON_OPTIONS]; const ICON_BY_KEY = new Map(ALL_ICON_OPTIONS.map((o) => [o.key, o.Icon])); const GRADIENT_BY_KEY = new Map(GRADIENT_OPTIONS.map((o) => [o.key, o.classes])); /** Small, fast, deterministic string hash (djb2) — same recipe always gets the same placeholder. */ function hashString(s: string): number { let hash = 5381; for (let i = 0; i < s.length; i++) { hash = (hash * 33) ^ s.charCodeAt(i); } return hash >>> 0; } /** Resolves a recipe's cover placeholder — user-chosen coverIcon/coverColor * win when set and recognized, else falls back to a deterministic pick * from the recipe id so it's still stable without a fresh hash on rename. */ export function getRecipePlaceholder(recipe: { id: string; recipeType?: "dish" | "drink"; coverIcon?: string | null; coverColor?: string | null; }): { gradient: string; Icon: LucideIcon } { const hash = hashString(recipe.id); const dishOrDrinkIcons = recipe.recipeType === "drink" ? DRINK_ICON_OPTIONS : DISH_ICON_OPTIONS; const gradient = (recipe.coverColor ? GRADIENT_BY_KEY.get(recipe.coverColor) : undefined) ?? GRADIENT_OPTIONS[hash % GRADIENT_OPTIONS.length]!.classes; const Icon = (recipe.coverIcon ? ICON_BY_KEY.get(recipe.coverIcon) : undefined) ?? // Different modulus base than gradient's so icon/color pairing doesn't // repeat in lockstep across ids that happen to share a gradient. dishOrDrinkIcons[Math.floor(hash / GRADIENT_OPTIONS.length) % dishOrDrinkIcons.length]!.Icon; return { gradient, Icon }; }