diff --git a/src/components/KitchenandInventory/Recipes/RecipeCard.jsx b/src/components/KitchenandInventory/Recipes/RecipeCard.jsx index a37f0c39c3..1dfcf4824c 100644 --- a/src/components/KitchenandInventory/Recipes/RecipeCard.jsx +++ b/src/components/KitchenandInventory/Recipes/RecipeCard.jsx @@ -52,7 +52,7 @@ const RecipeCard = ({ recipe, onViewDetails }) => { {/* View Button */} - @@ -61,7 +61,8 @@ const RecipeCard = ({ recipe, onViewDetails }) => { RecipeCard.propTypes = { recipe: PropTypes.shape({ - id: PropTypes.number.isRequired, + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + _id: PropTypes.string, name: PropTypes.string.isRequired, type: PropTypes.string.isRequired, description: PropTypes.string.isRequired, diff --git a/src/components/KitchenandInventory/Recipes/RecipesLandingPage.jsx b/src/components/KitchenandInventory/Recipes/RecipesLandingPage.jsx index dc7812f2c1..79b8785d97 100644 --- a/src/components/KitchenandInventory/Recipes/RecipesLandingPage.jsx +++ b/src/components/KitchenandInventory/Recipes/RecipesLandingPage.jsx @@ -1,18 +1,42 @@ import React, { useState, useEffect } from 'react'; +import axios from 'axios'; import RecipeCard from './RecipeCard'; import ViewRecipe from './ViewRecipe'; import { mockRecipes } from './mockRecipes'; import styles from './RecipesLandingPage.module.css'; +const API_URL = `${window.location.protocol}//${window.location.hostname}:4500/api/kitchenandinventory/recipes`; + const RecipesLandingPage = () => { const [recipes, setRecipes] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [filteredRecipes, setFilteredRecipes] = useState([]); const [selectedRecipe, setSelectedRecipe] = useState(null); + const [loading, setLoading] = useState(true); useEffect(() => { - setRecipes(mockRecipes); - setFilteredRecipes(mockRecipes); + const fetchRecipes = async () => { + try { + const token = localStorage.getItem('token'); + const response = await axios.get(API_URL, { + headers: { Authorization: token }, + }); + if (response.data && response.data.length > 0) { + setRecipes(response.data); + setFilteredRecipes(response.data); + } else { + setRecipes(mockRecipes); + setFilteredRecipes(mockRecipes); + } + } catch (err) { + // Fallback to mock data if API is unavailable + setRecipes(mockRecipes); + setFilteredRecipes(mockRecipes); + } finally { + setLoading(false); + } + }; + fetchRecipes(); }, []); useEffect(() => { @@ -31,7 +55,7 @@ const RecipesLandingPage = () => { }, [searchTerm, recipes]); const handleViewRecipe = recipeId => { - const recipe = recipes.find(r => r.id === recipeId); + const recipe = recipes.find(r => (r._id || r.id) === recipeId); setSelectedRecipe(recipe); }; @@ -39,11 +63,28 @@ const RecipesLandingPage = () => { setSelectedRecipe(null); }; + const handleRecipeUpdate = updatedRecipe => { + const recipeId = updatedRecipe._id || updatedRecipe.id; + setRecipes(prev => prev.map(r => ((r._id || r.id) === recipeId ? updatedRecipe : r))); + setSelectedRecipe(updatedRecipe); + }; + const handleAddRecipe = () => { // eslint-disable-next-line no-console console.log('Add new recipe'); }; + if (loading) { + return ( +
+
+

Recipes

+
+
Loading recipes...
+
+ ); + } + return ( <>
@@ -75,7 +116,11 @@ const RecipesLandingPage = () => {
{filteredRecipes.length > 0 ? ( filteredRecipes.map(recipe => ( - + )) ) : (
@@ -85,7 +130,13 @@ const RecipesLandingPage = () => {
- {selectedRecipe && } + {selectedRecipe && ( + + )} ); }; diff --git a/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.jsx b/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.jsx new file mode 100644 index 0000000000..4dcfc51820 --- /dev/null +++ b/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.jsx @@ -0,0 +1,259 @@ +import React, { useState, useEffect, useRef } from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; +import styles from './SubstituteIngredientModal.module.css'; + +// Mock inventory data - replace with real inventory API when available +const mockInventoryItems = [ + { id: 'inv1', name: 'White Pepper', category: 'Spices', quantityAvailable: '500g' }, + { id: 'inv2', name: 'Cayenne Pepper', category: 'Spices', quantityAvailable: '200g' }, + { id: 'inv3', name: 'Paprika', category: 'Spices', quantityAvailable: '350g' }, + { id: 'inv4', name: 'Crushed Crackers', category: 'Dry Goods', quantityAvailable: '1kg' }, + { id: 'inv5', name: 'Panko Flakes', category: 'Dry Goods', quantityAvailable: '750g' }, + { id: 'inv6', name: 'Cheddar Cheese', category: 'Dairy', quantityAvailable: '2kg' }, + { id: 'inv7', name: 'Mozzarella Cheese', category: 'Dairy', quantityAvailable: '1.5kg' }, + { id: 'inv8', name: 'Oats', category: 'Dry Goods', quantityAvailable: '3kg' }, + { id: 'inv9', name: 'Chia Seeds', category: 'Dry Goods', quantityAvailable: '400g' }, + { id: 'inv10', name: 'Coconut Flakes', category: 'Dry Goods', quantityAvailable: '600g' }, +]; + +const ENDPOINTS = { + substituteIngredient: recipeId => + `${window.location.protocol}//${window.location.hostname}:4500/api/kitchenandinventory/recipes/${recipeId}/substitute`, +}; + +const SubstituteIngredientModal = ({ ingredient, recipeId, onConfirm, onClose }) => { + const [selectedItem, setSelectedItem] = useState(''); + const [quantity, setQuantity] = useState(''); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [searchTerm, setSearchTerm] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(''); + const modalRef = useRef(null); + const dropdownRef = useRef(null); + + useEffect(() => { + const handleEscape = e => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [onClose]); + + useEffect(() => { + const handleClickOutside = e => { + if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { + setIsDropdownOpen(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const filteredItems = mockInventoryItems.filter( + item => + item.name.toLowerCase().includes(searchTerm.toLowerCase()) || + item.category.toLowerCase().includes(searchTerm.toLowerCase()), + ); + + const selectedInventoryItem = mockInventoryItems.find(item => item.id === selectedItem); + + const isValidObjectId = id => /^[a-fA-F0-9]{24}$/.test(id); + + const handleConfirm = async () => { + if (!selectedItem || !quantity) return; + + setIsSubmitting(true); + setError(''); + + const ingredientId = ingredient._id || ingredient.id; + const id = String(recipeId); + + // If recipeId is a valid MongoDB ObjectId, call the API + if (isValidObjectId(id)) { + try { + const token = localStorage.getItem('token'); + const url = ENDPOINTS.substituteIngredient(id); + + const response = await axios.put( + url, + { + ingredientId, + substituteName: selectedInventoryItem.name, + quantity, + }, + { + headers: { + Authorization: token, + }, + }, + ); + + onConfirm({ + ingredientId, + substituteId: selectedItem, + substituteName: selectedInventoryItem.name, + quantity, + updatedRecipe: response.data.recipe, + }); + return; + } catch (err) { + const message = + err.response?.data?.message || err.message || 'Failed to substitute ingredient'; + setError(message); + setIsSubmitting(false); + return; + } + } + + // Fallback: local-only update for mock data + onConfirm({ + ingredientId, + substituteId: selectedItem, + substituteName: selectedInventoryItem.name, + quantity, + }); + setIsSubmitting(false); + }; + + const handleSelectItem = itemId => { + setSelectedItem(itemId); + setIsDropdownOpen(false); + setSearchTerm(''); + }; + + return ( +
+ + +

Substitute Ingredient

+ +
+ Replacing +
+ {ingredient.name} + {ingredient.quantity} +
+
+ +
+ +
+ + + {isDropdownOpen && ( +
+ setSearchTerm(e.target.value)} + /> +
+ {filteredItems.length > 0 ? ( + filteredItems.map(item => ( + + )) + ) : ( +
No matching ingredients found
+ )} +
+
+ )} +
+
+ +
+ + setQuantity(e.target.value)} + /> +
+ + {error &&
{error}
} + +
+ + +
+
+
+ ); +}; + +SubstituteIngredientModal.propTypes = { + ingredient: PropTypes.shape({ + id: PropTypes.string, + _id: PropTypes.string, + name: PropTypes.string.isRequired, + quantity: PropTypes.string.isRequired, + }).isRequired, + recipeId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + onConfirm: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, +}; + +export default SubstituteIngredientModal; diff --git a/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.module.css b/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.module.css new file mode 100644 index 0000000000..eecd499961 --- /dev/null +++ b/src/components/KitchenandInventory/Recipes/SubstituteIngredientModal.module.css @@ -0,0 +1,479 @@ +/* ===== MODAL OVERLAY ===== */ +.modalOverlay { + position: fixed; + inset: 0; + background-color: rgb(0 0 0 / 60%); + display: flex; + align-items: center; + justify-content: center; + z-index: 1100; + animation: fade-in 0.2s ease; +} + +.modalBackdrop { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + background: transparent; + border: none; + cursor: default; + padding: 0; +} + +@keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes scale-in { + from { + transform: scale(0.95); + opacity: 0; + } + + to { + transform: scale(1); + opacity: 1; + } +} + +/* ===== MODAL ===== */ +.modal { + position: relative; + width: 90%; + max-width: 480px; + background-color: #fafaf5; + border-radius: 12px; + padding: 2rem; + box-shadow: 0 8px 30px rgb(0 0 0 / 20%); + animation: scale-in 0.2s ease; + z-index: 1101; +} + +/* ===== CLOSE BUTTON ===== */ +.closeButton { + position: absolute; + top: 0.75rem; + right: 0.75rem; + background: none; + border: none; + font-size: 1.25rem; + cursor: pointer; + color: #555; + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.2s, color 0.2s; +} + +.closeButton:hover { + background-color: #e0e0e0; + color: #222; +} + +/* ===== TITLE ===== */ +.modalTitle { + font-size: 1.25rem; + font-weight: 700; + color: #2d5016; + margin: 0 0 1.25rem; +} + +/* ===== CURRENT INGREDIENT ===== */ +.currentIngredient { + background-color: #fbe9e7; + border: 1px solid #ffccbc; + border-radius: 8px; + padding: 0.75rem 1rem; + margin-bottom: 1.5rem; +} + +.currentLabel { + font-size: 0.7rem; + font-weight: 600; + color: #c62828; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.currentInfo { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.25rem; +} + +.currentName { + font-weight: 600; + color: #333; + font-size: 0.95rem; +} + +.currentQty { + font-size: 0.85rem; + color: #888; +} + +/* ===== FORM GROUP ===== */ +.formGroup { + margin-bottom: 1.25rem; +} + +.label { + display: block; + font-size: 0.85rem; + font-weight: 600; + color: #3a3a3a; + margin-bottom: 0.4rem; +} + +/* ===== DROPDOWN ===== */ +.dropdownWrapper { + position: relative; +} + +.dropdownTrigger { + width: 100%; + padding: 0.65rem 1rem; + background-color: #fff; + border: 1px solid #d4cfbb; + border-radius: 8px; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.9rem; + text-align: left; + transition: border-color 0.2s; +} + +.dropdownTrigger:hover { + border-color: #2d5016; +} + +.selectedText { + display: flex; + flex-direction: column; + gap: 0.1rem; +} + +.selectedMeta { + font-size: 0.75rem; + color: #888; +} + +.placeholder { + color: #999; +} + +.chevron { + font-size: 0.65rem; + color: #888; + margin-left: 0.5rem; + flex-shrink: 0; +} + +.dropdownMenu { + position: absolute; + top: calc(100% + 4px); + left: 0; + right: 0; + background-color: #fff; + border: 1px solid #d4cfbb; + border-radius: 8px; + box-shadow: 0 4px 12px rgb(0 0 0 / 10%); + z-index: 10; + overflow: hidden; +} + +.searchInput { + width: 100%; + padding: 0.6rem 1rem; + border: none; + border-bottom: 1px solid #e0ddd3; + font-size: 0.85rem; + outline: none; + box-sizing: border-box; +} + +.searchInput::placeholder { + color: #aaa; +} + +.dropdownList { + max-height: 200px; + overflow-y: auto; +} + +.dropdownItem { + width: 100%; + padding: 0.6rem 1rem; + background: none; + border: none; + cursor: pointer; + text-align: left; + display: flex; + flex-direction: column; + gap: 0.1rem; + transition: background-color 0.15s; +} + +.dropdownItem:hover { + background-color: #f0ede6; +} + +.dropdownItemSelected { + background-color: #e8f5e9; +} + +.itemName { + font-size: 0.9rem; + font-weight: 500; + color: #333; +} + +.itemMeta { + font-size: 0.75rem; + color: #888; +} + +.noResults { + padding: 1rem; + text-align: center; + color: #999; + font-size: 0.85rem; + font-style: italic; +} + +/* ===== QUANTITY INPUT ===== */ +.quantityInput { + width: 100%; + padding: 0.65rem 1rem; + border: 1px solid #d4cfbb; + border-radius: 8px; + font-size: 0.9rem; + outline: none; + transition: border-color 0.2s; + box-sizing: border-box; + background-color: #fff; +} + +.quantityInput:focus { + border-color: #2d5016; +} + +.quantityInput::placeholder { + color: #aaa; +} + +/* ===== ERROR MESSAGE ===== */ +.errorMessage { + background-color: #fbe9e7; + color: #c62828; + padding: 0.5rem 0.75rem; + border-radius: 6px; + font-size: 0.85rem; + margin-bottom: 0.75rem; +} + +/* ===== MODAL ACTIONS ===== */ +.modalActions { + display: flex; + justify-content: flex-end; + gap: 0.75rem; + margin-top: 1.5rem; +} + +.cancelBtn, +.confirmBtn { + padding: 0.6rem 1.25rem; + border-radius: 8px; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + border: none; + transition: background-color 0.2s, opacity 0.2s; +} + +.cancelBtn { + background-color: #f0ede6; + color: #555; +} + +.cancelBtn:hover { + background-color: #e0ddd3; +} + +.confirmBtn { + background-color: #2d5016; + color: #fff; +} + +.confirmBtn:hover { + background-color: #3a6b1e; +} + +.confirmBtn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ===== RESPONSIVE ===== */ +@media (width <= 480px) { + .modal { + width: 95%; + padding: 1.25rem; + } + + .modalActions { + flex-direction: column; + } + + .cancelBtn, + .confirmBtn { + width: 100%; + text-align: center; + } +} + +/* ===== DARK MODE ===== */ +:global(.dark-mode) .modal { + background-color: #1e1e1e; +} + +:global(.dark-mode) .closeButton { + color: #ccc; +} + +:global(.dark-mode) .closeButton:hover { + background-color: #3a3a3a; + color: #fff; +} + +:global(.dark-mode) .modalTitle { + color: #8ba888; +} + +:global(.dark-mode) .currentIngredient { + background-color: #3d1e1e; + border-color: #5a2a2a; +} + +:global(.dark-mode) .currentLabel { + color: #ef9a9a; +} + +:global(.dark-mode) .currentName { + color: #ddd; +} + +:global(.dark-mode) .currentQty { + color: #999; +} + +:global(.dark-mode) .label { + color: #ccc; +} + +:global(.dark-mode) .dropdownTrigger { + background-color: #2a2a2a; + border-color: #444; + color: #ddd; +} + +:global(.dark-mode) .dropdownTrigger:hover { + border-color: #8ba888; +} + +:global(.dark-mode) .selectedMeta { + color: #999; +} + +:global(.dark-mode) .placeholder { + color: #777; +} + +:global(.dark-mode) .chevron { + color: #999; +} + +:global(.dark-mode) .dropdownMenu { + background-color: #2a2a2a; + border-color: #444; + box-shadow: 0 4px 12px rgb(0 0 0 / 30%); +} + +:global(.dark-mode) .searchInput { + background-color: #2a2a2a; + color: #ddd; + border-bottom-color: #444; +} + +:global(.dark-mode) .searchInput::placeholder { + color: #777; +} + +:global(.dark-mode) .dropdownItem:hover { + background-color: #333; +} + +:global(.dark-mode) .dropdownItemSelected { + background-color: #2a3a2a; +} + +:global(.dark-mode) .itemName { + color: #ddd; +} + +:global(.dark-mode) .itemMeta { + color: #999; +} + +:global(.dark-mode) .noResults { + color: #777; +} + +:global(.dark-mode) .quantityInput { + background-color: #2a2a2a; + border-color: #444; + color: #ddd; +} + +:global(.dark-mode) .quantityInput:focus { + border-color: #8ba888; +} + +:global(.dark-mode) .quantityInput::placeholder { + color: #777; +} + +:global(.dark-mode) .errorMessage { + background-color: #3d1e1e; + color: #ef9a9a; +} + +:global(.dark-mode) .cancelBtn { + background-color: #333; + color: #ccc; +} + +:global(.dark-mode) .cancelBtn:hover { + background-color: #444; +} + +:global(.dark-mode) .confirmBtn { + background-color: #4a7a3a; +} + +:global(.dark-mode) .confirmBtn:hover { + background-color: #5a8a4a; +} \ No newline at end of file diff --git a/src/components/KitchenandInventory/Recipes/ViewRecipe.jsx b/src/components/KitchenandInventory/Recipes/ViewRecipe.jsx index 9cbe7da458..51320bb2d4 100644 --- a/src/components/KitchenandInventory/Recipes/ViewRecipe.jsx +++ b/src/components/KitchenandInventory/Recipes/ViewRecipe.jsx @@ -1,17 +1,32 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import styles from './ViewRecipe.module.css'; +import SubstituteIngredientModal from './SubstituteIngredientModal'; -const ViewRecipe = ({ recipe, onClose }) => { +const ViewRecipe = ({ recipe, onClose, onRecipeUpdate }) => { const panelRef = useRef(null); + const [substituteTarget, setSubstituteTarget] = useState(null); + const [ingredients, setIngredients] = useState([]); + + useEffect(() => { + if (recipe && recipe.ingredients) { + setIngredients(recipe.ingredients); + } + }, [recipe]); useEffect(() => { const handleEscape = e => { - if (e.key === 'Escape') onClose(); + if (e.key === 'Escape') { + if (substituteTarget) { + setSubstituteTarget(null); + } else { + onClose(); + } + } }; document.addEventListener('keydown', handleEscape); return () => document.removeEventListener('keydown', handleEscape); - }, [onClose]); + }, [onClose, substituteTarget]); if (!recipe) return null; @@ -41,8 +56,38 @@ const ViewRecipe = ({ recipe, onClose }) => { }; const handleSubstitute = ingredientId => { - // eslint-disable-next-line no-console - console.log('Substitute ingredient:', ingredientId); + const target = ingredients.find(ing => (ing._id || ing.id) === ingredientId); + if (target) setSubstituteTarget(target); + }; + + const handleSubstituteConfirm = substitution => { + let updatedIngredients; + if (substitution.updatedRecipe) { + updatedIngredients = substitution.updatedRecipe.ingredients; + } else { + updatedIngredients = ingredients.map(ing => { + const ingId = ing._id || ing.id; + return ingId === substitution.ingredientId + ? { + ...ing, + name: substitution.substituteName, + quantity: substitution.quantity, + isAvailable: true, + isOnsite: false, + } + : ing; + }); + } + setIngredients(updatedIngredients); + + if (onRecipeUpdate) { + onRecipeUpdate({ + ...recipe, + ingredients: updatedIngredients, + }); + } + + setSubstituteTarget(null); }; const handleReorder = ingredientId => { @@ -122,9 +167,9 @@ const ViewRecipe = ({ recipe, onClose }) => {

Ingredients

- {recipe.ingredients && recipe.ingredients.length > 0 ? ( - recipe.ingredients.map(ingredient => ( -
+ {ingredients && ingredients.length > 0 ? ( + ingredients.map(ingredient => ( +
{ingredient.name} {ingredient.quantity} @@ -138,14 +183,14 @@ const ViewRecipe = ({ recipe, onClose }) => { @@ -165,9 +210,9 @@ const ViewRecipe = ({ recipe, onClose }) => {

Instructions

{recipe.instructions && recipe.instructions.length > 0 ? (
    - {recipe.instructions.map(step => ( + {recipe.instructions.map((step, index) => (
  1. - {recipe.instructions.indexOf(step) + 1} + {index + 1}

    {step}

  2. ))} @@ -177,12 +222,24 @@ const ViewRecipe = ({ recipe, onClose }) => { )}
+ + {/* Substitute Ingredient Modal */} + {substituteTarget && ( + setSubstituteTarget(null)} + /> + )}
); }; ViewRecipe.propTypes = { recipe: PropTypes.shape({ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + _id: PropTypes.string, name: PropTypes.string.isRequired, type: PropTypes.string.isRequired, description: PropTypes.string.isRequired, @@ -194,7 +251,8 @@ ViewRecipe.propTypes = { onsitePercentage: PropTypes.number, ingredients: PropTypes.arrayOf( PropTypes.shape({ - id: PropTypes.string.isRequired, + id: PropTypes.string, + _id: PropTypes.string, name: PropTypes.string.isRequired, quantity: PropTypes.string.isRequired, isOnsite: PropTypes.bool, @@ -204,6 +262,11 @@ ViewRecipe.propTypes = { instructions: PropTypes.arrayOf(PropTypes.string), }).isRequired, onClose: PropTypes.func.isRequired, + onRecipeUpdate: PropTypes.func, +}; + +ViewRecipe.defaultProps = { + onRecipeUpdate: null, }; export default ViewRecipe; diff --git a/src/routes.jsx b/src/routes.jsx index d7fa89f639..1a5fd3ca02 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -163,9 +163,9 @@ import CommunityCalendar from './components/CommunityPortal/Calendar/CommunityCa import KitchenandInventoryLogin from './components/KitchenandInventory/Login'; import KIProtectedRoute from './components/common/KitchenandInventory/KIProtectedRoute'; import KIDashboard from './components/KitchenandInventory/KIDashboard/KIDashboard'; -import RecipesLandingPage from "./components/KitchenandInventory/Recipes"; -import KIINVENTORY from "./components/KitchenandInventory/KIInventory/KIInventory"; -import KICalendar from "./components/KitchenandInventory/KICalendar/KICalendar"; +import RecipesLandingPage from './components/KitchenandInventory/Recipes'; +import KIINVENTORY from './components/KitchenandInventory/KIInventory/KIInventory'; +import KICalendar from './components/KitchenandInventory/KICalendar/KICalendar'; // Education Portal import EPProtectedRoute from './components/common/EPDashboard/EPProtectedRoute'; @@ -905,7 +905,11 @@ export default ( - + {/* ----- End of Kitchen and Inventory Portal Routes ----- */}