import React, { useState, useEffect } from 'react'; import { Box, Typography, Grid, Card, Button, TextField, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress, Alert, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Chip, Select, MenuItem, InputLabel, FormControl, List, ListItem, ListItemText, Divider, } from '@mui/material'; import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; import AddIcon from '@mui/icons-material/Add'; import RefreshIcon from '@mui/icons-material/Refresh'; import SaveIcon from '@mui/icons-material/Save'; import { api } from '../../services/api'; import type { StaffMenuItem } from '../../services/api'; export const InventoryView: React.FC = () => { const [ingredients, setIngredients] = useState([]); const [menuDishes, setMenuDishes] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Ingredient Form Dialog States const [openIngForm, setOpenIngForm] = useState(false); const [editingIng, setEditingIng] = useState(null); const [ingName, setIngName] = useState(''); const [ingStock, setIngStock] = useState(0); const [ingUnit, setIngUnit] = useState('kg'); const [ingMinStock, setIngMinStock] = useState(2); // Recipe Builder States const [selectedMenuId, setSelectedMenuId] = useState(5); // Default to item 5 // Backend schema: { ingredient_id, quantity_required } const [recipeIngredients, setRecipeIngredients] = useState<{ ingredient_id: number; quantity_required: number }[]>([]); const [recipeLoading, setRecipeLoading] = useState(false); // Adding single ingredient to recipe const [addIngId, setAddIngId] = useState(''); const [addIngQty, setAddIngQty] = useState(0.1); const fetchIngredients = async () => { setLoading(true); setError(null); try { const [data, dishes] = await Promise.all([ api.getIngredients(), api.listMenuItems().catch(() => []), ]); setIngredients(Array.isArray(data) ? data : []); const dishList = Array.isArray(dishes) ? dishes : []; setMenuDishes(dishList); if (dishList.length && !dishList.some((d) => d.id === selectedMenuId)) { setSelectedMenuId(dishList[0].id); } } catch (err: any) { setError(err.message || 'Failed to fetch ingredients'); } finally { setLoading(false); } }; const fetchRecipe = async (menuId: number) => { setRecipeLoading(true); try { const data = await api.getRecipe(menuId); // Backend returns list[RecipeIngredientRead] - an array directly setRecipeIngredients(Array.isArray(data) ? data : (data?.ingredients || [])); } catch (err) { console.error('Failed to load recipe', err); } finally { setRecipeLoading(false); } }; useEffect(() => { fetchIngredients(); }, []); useEffect(() => { fetchRecipe(selectedMenuId); }, [selectedMenuId]); const handleSaveIngredient = async () => { setError(null); try { if (editingIng) { // Edit existing — backend only allows: name, unit, reorder_threshold, unit_cost const updated = await api.updateIngredient(editingIng.id, { name: ingName, reorder_threshold: ingMinStock, }); setIngredients((prev) => prev.map((i) => (i.id === editingIng.id ? updated : i)) ); } else { // Create new — backend schema: name, unit, current_stock, reorder_threshold, unit_cost const created = await api.createIngredient(ingName, ingStock, ingUnit, ingMinStock); setIngredients((prev) => [...prev, created]); } setOpenIngForm(false); setEditingIng(null); setIngName(''); setIngStock(0); setIngMinStock(2); } catch (err: any) { setError(err.message || 'Failed to save ingredient'); } }; const handleDeleteIngredient = async (id: string) => { if (!window.confirm('Delete this ingredient?')) return; try { await api.deleteIngredient(id); setIngredients((prev) => prev.filter((i) => i.id !== id)); } catch (err: any) { alert(err.message || 'Failed to delete ingredient'); } }; const handleSaveRecipe = async () => { try { await api.saveRecipe(selectedMenuId, recipeIngredients); alert('Recipe updated successfully!'); } catch (err: any) { alert(err.message || 'Failed to save recipe'); } }; const handleAddRecipeIngredient = () => { if (!addIngId || addIngQty <= 0) return; if (recipeIngredients.some(r => r.ingredient_id === Number(addIngId))) { alert('Ingredient already added to recipe!'); return; } setRecipeIngredients(prev => [...prev, { ingredient_id: Number(addIngId), quantity_required: addIngQty }]); setAddIngId(''); }; const handleRemoveRecipeIngredient = (ingId: number) => { setRecipeIngredients(prev => prev.filter(r => r.ingredient_id !== ingId)); }; return ( Kitchen Stock & Recipes Keep track of ingredient raw stocks, identify low stock warnings, and link menu items to recipes. {error && {error}} {/* Ingredients Grid */} Ingredient Stocks List {loading ? ( ) : ( Ingredient Stock Qty Alert Level Actions {ingredients.map((ing) => { // Backend fields: current_stock, reorder_threshold const currentStock = Number(ing.current_stock ?? ing.stock ?? 0); const threshold = Number(ing.reorder_threshold ?? ing.minStock ?? 0); const isLow = currentStock <= threshold; return ( {ing.name} {currentStock} {ing.unit} {isLow ? ( ) : ( )} { setEditingIng(ing); setIngName(ing.name); setIngStock(currentStock); setIngUnit(ing.unit); setIngMinStock(threshold); setOpenIngForm(true); }} > handleDeleteIngredient(ing.id)}> ); })}
)}
{/* Recipes Linker Panel */} Dish Recipes Linking Select Menu Dish Recipe Ingredients needed: {recipeLoading ? ( ) : ( {recipeIngredients.length === 0 ? ( No ingredients linked to this dish recipe yet. Link below. ) : ( {recipeIngredients.map((item) => { // Backend fields: ingredient_id, quantity_required const detail = ingredients.find((i: any) => i.id === item.ingredient_id); return ( handleRemoveRecipeIngredient(item.ingredient_id)}> } sx={{ px: 0, py: 0.5 }} > {detail ? detail.name : `Ingredient #${item.ingredient_id}`} } secondary={`Required: ${item.quantity_required} ${detail ? detail.unit : 'kg'}`} /> ); })} )} LINK ANOTHER STOCK COMPONENT: setAddIngQty(parseFloat(e.target.value) || 0)} /> )}
{/* Ingredient Form Dialog */} setOpenIngForm(false)} maxWidth="xs" fullWidth> {editingIng ? 'Modify Raw Ingredient Stock' : 'Add Raw Stock Ingredient'} setIngName(e.target.value)} fullWidth size="small" /> setIngStock(parseFloat(e.target.value) || 0)} fullWidth size="small" /> Measurement Unit setIngMinStock(parseFloat(e.target.value) || 0)} fullWidth size="small" />
); };