From 6ac670303fc8a0d89df4ddbe0ed498632848b84f Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Jun 2026 19:07:59 +0530 Subject: [PATCH 1/3] Integrate category CRUD APIs --- .../src/components/admin/menu/Menus.jsx | 23 +- .../admin/menu/menuCard/CategoryFormModal.jsx | 279 +++++++++++------- .../admin/menu/menuCard/CategorySidebar.jsx | 244 ++++++++++----- 3 files changed, 362 insertions(+), 184 deletions(-) diff --git a/RestroHub-FrontEnd/src/components/admin/menu/Menus.jsx b/RestroHub-FrontEnd/src/components/admin/menu/Menus.jsx index ec48b848..a57531e7 100644 --- a/RestroHub-FrontEnd/src/components/admin/menu/Menus.jsx +++ b/RestroHub-FrontEnd/src/components/admin/menu/Menus.jsx @@ -1,5 +1,6 @@ // Menus.jsx import { useRef, useState, useEffect } from 'react'; +import toast from 'react-hot-toast'; import MenuHeader from './menuCard/Header'; import BulkActions from './menuCard/BulkActions'; import CategorySidebar from './menuCard/CategorySidebar'; @@ -16,6 +17,7 @@ const Menus = () => { const [allCategories, setAllCategories] = useState([]); const menuGridRef = useRef(null); const menusGridRef = useRef(null); + const categorySidebarRef = useRef(null); // FOOD ITEM MODAL STATE const [isModalOpen, setIsModalOpen] = useState(false); @@ -23,6 +25,7 @@ const Menus = () => { // CATEGORY MODAL STATE const [isCategoryModalOpen, setIsCategoryModalOpen] = useState(false); + const [editingCategory, setEditingCategory] = useState(null); // MENU CREATION MODAL STATE const [isMenuModalOpen, setIsMenuModalOpen] = useState(false); @@ -65,11 +68,26 @@ const Menus = () => { // CATEGORY MODAL HANDLERS const openCategoryModal = () => { + setEditingCategory(null); setIsCategoryModalOpen(true); }; + const openEditCategoryModal = async (category) => { + try { + const response = await api.get(`/secure/api/v1/categories/${category.categoryId}`); + setEditingCategory(response.data?.data || response.data); + setIsCategoryModalOpen(true); + } catch (err) { + console.error('Failed to fetch category:', err.response?.data || err); + toast.error(err.response?.data?.message || 'Failed to load category details'); + } + }; + const closeCategoryModal = () => { setIsCategoryModalOpen(false); + setEditingCategory(null); + categorySidebarRef.current?.refreshCategories(); + menuGridRef.current?.refreshFoods(); }; // MENU CREATION MODAL HANDLERS @@ -141,9 +159,11 @@ const Menus = () => {
{ {/* Menu Creation Modal */} @@ -192,4 +213,4 @@ const Menus = () => { ); }; -export default Menus; \ No newline at end of file +export default Menus; diff --git a/RestroHub-FrontEnd/src/components/admin/menu/menuCard/CategoryFormModal.jsx b/RestroHub-FrontEnd/src/components/admin/menu/menuCard/CategoryFormModal.jsx index e1effa26..6523a1f4 100644 --- a/RestroHub-FrontEnd/src/components/admin/menu/menuCard/CategoryFormModal.jsx +++ b/RestroHub-FrontEnd/src/components/admin/menu/menuCard/CategoryFormModal.jsx @@ -1,148 +1,213 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Dialog } from "@headlessui/react"; -import { X, Loader2, Tag, Type, FileText } from "lucide-react"; +import { AlertCircle, FileText, Loader2, Tag, Type, X } from "lucide-react"; +import toast from "react-hot-toast"; import api from "@services/common/api"; -const CategoryFormModal = ({ isOpen, onClose }) => { - const [formData, setFormData] = useState({ - name: "", - description: "" - }); +const getErrorMessage = (err, fallback) => + err.response?.data?.message || err.response?.data?.error || fallback; +const initialFormData = { + name: "", + description: "" +}; + +const CategoryFormModal = ({ isOpen, onClose, editingCategory }) => { + const [formData, setFormData] = useState(initialFormData); + const [errors, setErrors] = useState({}); + const [submitError, setSubmitError] = useState(""); const [submitting, setSubmitting] = useState(false); + const isEditing = Boolean(editingCategory?.categoryId); + + useEffect(() => { + if (!isOpen) return; + + setFormData({ + name: editingCategory?.name || "", + description: editingCategory?.description || "" + }); + setErrors({}); + setSubmitError(""); + }, [editingCategory, isOpen]); + const updateField = (field, value) => { setFormData(prev => ({ ...prev, [field]: value })); + setErrors(prev => ({ ...prev, [field]: undefined })); + }; + + const validateForm = () => { + const nextErrors = {}; + const name = formData.name.trim(); + const description = formData.description.trim(); + + if (!name) nextErrors.name = "Category name is required."; + if (name && name.length < 2) nextErrors.name = "Category name must be at least 2 characters."; + if (name.length > 50) nextErrors.name = "Category name must be 50 characters or less."; + if (description.length > 255) nextErrors.description = "Description must be 255 characters or less."; + + setErrors(nextErrors); + return Object.keys(nextErrors).length === 0; }; const handleSubmit = async (e) => { e.preventDefault(); + setSubmitError(""); + + if (!validateForm()) return; + try { setSubmitting(true); const payload = { - name: formData.name, - description: formData.description, + name: formData.name.trim(), + description: formData.description.trim(), isDelete: false }; - await api.post("/secure/api/v1/categories/addCategory", payload); + if (isEditing) { + await api.put(`/secure/api/v1/categories/update/${editingCategory.categoryId}`, payload); + toast.success("Category updated successfully"); + } else { + await api.post("/secure/api/v1/categories/addCategory", payload); + toast.success("Category created successfully"); + } + setFormData(initialFormData); onClose(); - setFormData({ name: "", description: "" }); - } catch (err) { - console.error("Category create failed:", err.response?.data || err); + console.error("Category save failed:", err.response?.data || err); + const message = getErrorMessage(err, "Failed to save category"); + setSubmitError(message); + toast.error(message); } finally { setSubmitting(false); } }; return ( - - {/* Backdrop */} -