// frontend/src/components/recipes/IngredientList.tsx import React from 'react'; import { Plus, Minus, Edit2, Trash2, GripVertical, Info, AlertCircle, Package, Droplets, Scale, Euro } from 'lucide-react'; import { RecipeIngredient } from '../../api/services/recipes.service'; interface IngredientListProps { ingredients: RecipeIngredient[]; editable?: boolean; showCosts?: boolean; showGroups?: boolean; batchMultiplier?: number; onAddIngredient?: () => void; onEditIngredient?: (ingredient: RecipeIngredient) => void; onRemoveIngredient?: (ingredientId: string) => void; onReorderIngredients?: (ingredients: RecipeIngredient[]) => void; className?: string; } const IngredientList: React.FC = ({ ingredients, editable = false, showCosts = false, showGroups = true, batchMultiplier = 1, onAddIngredient, onEditIngredient, onRemoveIngredient, onReorderIngredients, className = '' }) => { // Group ingredients by ingredient_group const groupedIngredients = React.useMemo(() => { if (!showGroups) { return { 'All Ingredients': ingredients }; } const groups: Record = {}; ingredients.forEach(ingredient => { const group = ingredient.ingredient_group || 'Other'; if (!groups[group]) { groups[group] = []; } groups[group].push(ingredient); }); // Sort ingredients within each group by order Object.keys(groups).forEach(group => { groups[group].sort((a, b) => a.ingredient_order - b.ingredient_order); }); return groups; }, [ingredients, showGroups]); // Get unit icon const getUnitIcon = (unit: string) => { switch (unit.toLowerCase()) { case 'g': case 'kg': return ; case 'ml': case 'l': return ; case 'units': case 'pieces': case 'pcs': return ; default: return ; } }; // Format quantity with multiplier const formatQuantity = (quantity: number, unit: string) => { const adjustedQuantity = quantity * batchMultiplier; return `${adjustedQuantity} ${unit}`; }; // Calculate total cost const getTotalCost = () => { return ingredients.reduce((total, ingredient) => { const cost = ingredient.total_cost || 0; return total + (cost * batchMultiplier); }, 0); }; return (
{/* Header */}

Ingredients

{ingredients.length} ingredient{ingredients.length !== 1 ? 's' : ''} {batchMultiplier !== 1 && ( ×{batchMultiplier} batch )}

{showCosts && (
Total Cost
{getTotalCost().toFixed(2)}
)} {editable && onAddIngredient && ( )}
{/* Ingredients List */}
{Object.entries(groupedIngredients).map(([groupName, groupIngredients]) => (
{/* Group Header */} {showGroups && Object.keys(groupedIngredients).length > 1 && (

{groupName}

)} {/* Group Ingredients */} {groupIngredients.map((ingredient, index) => (
{/* Drag Handle */} {editable && onReorderIngredients && (
)} {/* Order Number */}
{ingredient.ingredient_order}
{/* Ingredient Info */}

{ingredient.ingredient_id} {/* This would be ingredient name from inventory */}

{ingredient.is_optional && ( Optional )}
{/* Quantity */}
{getUnitIcon(ingredient.unit)} {formatQuantity(ingredient.quantity, ingredient.unit)} {ingredient.alternative_quantity && ingredient.alternative_unit && ( (≈ {formatQuantity(ingredient.alternative_quantity, ingredient.alternative_unit)}) )}
{/* Preparation Method */} {ingredient.preparation_method && (
Prep: {ingredient.preparation_method}
)} {/* Notes */} {ingredient.ingredient_notes && (
{ingredient.ingredient_notes}
)} {/* Substitutions */} {ingredient.substitution_options && (
Substitutions available
)}
{/* Cost */} {showCosts && (
€{((ingredient.total_cost || 0) * batchMultiplier).toFixed(2)}
{ingredient.unit_cost && (
€{ingredient.unit_cost.toFixed(2)}/{ingredient.unit}
)} {ingredient.cost_updated_at && (
{new Date(ingredient.cost_updated_at).toLocaleDateString()}
)}
)} {/* Actions */} {editable && (
)}
))}
))} {/* Empty State */} {ingredients.length === 0 && (

No ingredients yet

Add ingredients to start building your recipe

{editable && onAddIngredient && ( )}
)}
{/* Summary */} {ingredients.length > 0 && (
{ingredients.length} total ingredients {ingredients.filter(i => i.is_optional).length > 0 && ( {ingredients.filter(i => i.is_optional).length} optional )} {ingredients.some(i => i.substitution_options) && ( {ingredients.filter(i => i.substitution_options).length} with substitutions )}
{showCosts && (
Total: €{getTotalCost().toFixed(2)}
)}
)}
); }; export default IngredientList;