From a75de8c589a9d1d8bef844681a57dcb19f64d462 Mon Sep 17 00:00:00 2001 From: alaguraj Date: Wed, 23 Jul 2025 20:28:19 +0530 Subject: [PATCH] menu, menu category modulefully completed menu item create and update page ui updated --- .../pos/(product)/create-product/page.jsx | 309 +++++++ .../pos/(product)/product-list/page.jsx | 851 ++++++++++++++++++ .../pos/(product)/update-product/page.jsx | 326 +++++++ src/app/admin/(pos-system)/pos/sides/page.jsx | 172 ++-- .../DayReservationTableEnable.jsx | 201 ++--- 5 files changed, 1673 insertions(+), 186 deletions(-) create mode 100644 src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx create mode 100644 src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx create mode 100644 src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx diff --git a/src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx b/src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx new file mode 100644 index 0000000..f1707a3 --- /dev/null +++ b/src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx @@ -0,0 +1,309 @@ +"use client"; +import { useState } from "react"; +import "highlight.js/styles/github.css"; +import client from "@auth"; +import { Icon } from "@iconify/react"; +import MasterLayout from "@/masterLayout/MasterLayout"; +import Breadcrumb from "@/components/Breadcrumb"; +import { useRouter, useSearchParams } from "next/navigation"; +import axios from "axios"; +import { Baseurl } from "@utils/BaseUrl.utils"; + +const AddNewProduct = () => { + const searchParams = useSearchParams(); + const category = searchParams.get("category"); + const router = useRouter(); + + const [imagePreview, setImagePreview] = useState(null); + const [imageFile, setImageFile] = useState(null); + + const [formData, setFormData] = useState({ + menuitemname: "", + price: 0, + is_active: false, + is_special: false, + availability_time: "", + preparation_time: 0, + description: "", + }); + + const [errors, setErrors] = useState({}); + + const handleChange = (e) => { + const { name, value, type, checked } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: type === "checkbox" ? checked : value, + })); + }; + + const handleFileChange = (e) => { + const file = e.target.files?.[0]; + if (file) { + setImageFile(file); + setImagePreview(URL.createObjectURL(file)); + } + }; + + const handleRemoveImage = () => { + setImageFile(null); + setImagePreview(null); + }; + + const validate = () => { + const newErrors = {}; + if (!formData.menuitemname || formData.menuitemname.trim().length < 3) { + newErrors.menuitemname = "Menu Item Name is required"; + } + if (!formData.price || Number(formData.price) <= 0) { + newErrors.price = "Price must be greater than 0"; + } + if (!formData.availability_time) { + newErrors.availability_time = "Availability Time is required"; + } + if (!formData.preparation_time || Number(formData.preparation_time) <= 0) { + newErrors.preparation_time = "Preparation Time must be greater than 0"; + } + if (!formData.description || formData.description.trim().length < 5) { + newErrors.description = "Description is too short"; + } + if (!imageFile) { + newErrors.image_item = "Image is required"; + } + return newErrors; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + const validationErrors = validate(); + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + return; + } + setErrors({}); + + const body = { + menucategoryname: category, + description: formData.description, + is_active: 0, + doctype: "Dine360 Menu Category", + menuitems_child: [ + { + menuitemname: formData.menuitemname, + price: formData.price, + is_active: formData.is_active ? 1 : 0, + is_special: formData.is_special ? 1 : 0, + availability_time: formData.availability_time, + preparation_time: formData.preparation_time, + }, + ], + }; + + try { + const formDataToSend = new FormData(); + formDataToSend.append("endpoint", `Dine360%20Menu%20Category/${category}`); + formDataToSend.append("body", JSON.stringify(body)); + formDataToSend.append("file", imageFile); // ✅ use imageFile + formDataToSend.append("fileid", "image_item"); + + const response = await axios.post(`${Baseurl}/Upload-Image-To-Frappe`, formDataToSend, { + headers: { + Authorization: "token 482beca79d9c005:b8778f51fcca82b", + }, + }); + + console.log("response", response); + alert("Form submitted successfully!"); + router.push("/admin/pos/product-list"); + } catch (error) { + console.log("error", error); + } + }; + + return ( + + +
+
+
+
+
+
Add New Product
+
+
+
+ + + {errors.menuitemname && ( +
{errors.menuitemname}
+ )} +
+
+ + + {errors.price && ( +
{errors.price}
+ )} +
+
+ +
+ + + {errors.description && ( +
{errors.description}
+ )} +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + + {errors.availability_time && ( +
{errors.availability_time}
+ )} +
+
+ + + {errors.preparation_time && ( +
{errors.preparation_time}
+ )} +
+
+ +
+ +
+ {imagePreview ? ( +
+ + Uploaded +
+ ) : ( + + )} + {errors.image_item && ( +
{errors.image_item}
+ )} +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ ); +}; + +export default AddNewProduct; diff --git a/src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx b/src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx new file mode 100644 index 0000000..830714f --- /dev/null +++ b/src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx @@ -0,0 +1,851 @@ +"use client"; +import React, { Suspense, useEffect, useState } from "react"; +import MasterLayout from "@/masterLayout/MasterLayout"; +import client from "@auth"; +import { Icon } from "@iconify/react"; +import PageLoader from "@/components/common-component/PageLoader"; +import PageNoData from "@/components/common-component/PageNoData"; +import Breadcrumb from "@/components/Breadcrumb"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; + +const ProductListInner = () => { + + const router = useRouter() + + const [menuData, setMenuData] = useState(null); + const [menuItems, setMenuItems] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [activeCategory, setActiveCategory] = useState(null); + const [catMenu, setCatMenu] = useState(null); + const [catMenuActive, setCatMenuActive] = useState(null); + + const [menuFieldsModel, setMenuFieldsModel] = useState(false); + const [isMenuFieldEditMode, setIsMenuFieldEditMode] = useState(false); + const [editingMenuId, setEditingMenuId] = useState(null); + const [formErrors, setFormErrors] = useState({}); + const [menuFieldDeleteConfirm, setMenuFieldDeleteConfirm] = useState({ show: false, id: null }); + const [menuFieldsFormData, setMenuFieldsFormData] = useState({ + menuname: "", + description: "", + is_active: 0, + }); + + const [menuCategoryModel, setMenuCategoryModel] = useState(false); + const [isMenuCategoryEditMode, setIsMenuCategoryEditMode] = useState(false); + const [editingMenuCategoryId, setEditingMenuCategoryId] = useState(null); + const [formMenuCategoryErrors, setFormMenuCategoryErrors] = useState({}); + const [menuCategoryDeleteConfirm, setMenuCategoryDeleteConfirm] = useState({ show: false, id: null }); + const [menuCategoryFormData, setMenuCategoryFormData] = useState({ + menucategoryname: "", + description: "", + is_active: 0, + }); + + const [menuItemDeleteConfirm, setMenuItemDeleteConfirm] = useState({ show: false, id: null }); + + + const [restaruntBranch, setRestaruntBranch] = useState("") + + useEffect(() => { + const restarunt = localStorage.getItem("restaurantbranch") + setRestaruntBranch(restarunt) + }, []) + + useEffect(() => { + const isLogin = JSON.parse(localStorage.getItem("isLogin")); + if (!isLogin) { + router.push(`/admin?restaurantbranch=${restaruntBranch}`); + } + }, [router]); + + useEffect(() => { + getMenuItem(); + }, []); + + const getMenuItem = async () => { + try { + const res = await client?.get(`/Dine360 Menu?fields=["*"]&limit_page_length=100`); + setCatMenu(res?.data?.data || []); + setCatMenuActive(res?.data?.data[0]?.name || []); + } catch (error) { + console.error("Error fetching menu list:", error); + } + }; + + useEffect(() => { + if (catMenu?.length > 0) { + getMenuCategoryItems(catMenu[0]?.menuname); + } + }, [catMenu]); + + useEffect(() => { + if (menuData?.length > 0) { + getMenuFoodItems(menuData[0]); + setActiveCategory(menuData[0]?.name); + } + }, [menuData]); + + const getMenuCategoryItems = async (menuname) => { + try { + setLoading(true); + setError(null); + + const menuRes = await client.get( + `/Dine360%20Menu%20Category%20Link?fields=["*"]&limit_page_length=100&filters=[["menu","=","${menuname}"]]` + ); + + const menuLinks = menuRes?.data?.data || []; + console.log("menuLinks", menuLinks) + + const menuCategoryPromises = menuLinks.map(async (menuItem) => { + const res = await client.get( + `/Dine360%20Menu%20Category?fields=["*"]&limit_page_length=100&filters=[["name","=","${menuItem.menucategory}"]]` + ); + return res?.data?.data?.[0] || null; + }); + + const categories = await Promise.all(menuCategoryPromises); + const validCategories = categories.filter(Boolean); + + setMenuData(validCategories); + } catch (error) { + console.error("Error fetching menu categories:", error); + setError(error?.message || "Failed to fetch menu categories"); + } finally { + setLoading(false); + } + }; + + const getMenuFoodItems = async (category) => { + if (!category?.name) return; + try { + const res = await client.get( + `/Dine360%20Menu%20Category/${category.name}?fields=["*"]&limit_page_length=100` + ); + setMenuItems(res?.data?.data || []); + } catch (error) { + console.error("Error fetching menu items:", error); + } + }; + + const handleCatMenuClick = async (menuname) => { + await getMenuCategoryItems(menuname); + setCatMenuActive(menuname); + }; + + const handleMenuClick = async (menu) => { + try { + const res = await client.get( + `/Dine360%20Menu%20Category/${menu.name}?fields=["*"]&limit_page_length=100` + ); + setMenuItems(res?.data?.data || []); + setActiveCategory(menu?.name); + } catch (error) { + console.error("Error fetching menu data:", error); + setError(error?.message || "Failed to fetch menu data"); + } + }; + + + const handleInputChange = (e) => { + const { name, value, type, checked } = e.target; + setMenuFieldsFormData((prev) => ({ + ...prev, + [name]: type === "checkbox" ? (checked ? 1 : 0) : value, + })); + }; + + const openCreateModal = () => { + setIsMenuFieldEditMode(false); + setEditingMenuId(null); + setMenuFieldsFormData({ menuname: "", description: "", is_active: 0 }); + setFormErrors({}); + setMenuFieldsModel(true); + }; + + const openEditModal = (menu) => { + setIsMenuFieldEditMode(true); + setEditingMenuId(menu.name); + setMenuFieldsFormData({ + menuname: menu.menuname || "", + description: menu.description || "", + is_active: menu.is_active || 0, + }); + setFormErrors({}); + setMenuFieldsModel(true); + }; + + const closeModal = () => { + setMenuFieldsModel(false); + setFormErrors({}); + }; + + const handleFormSubmit = async (e) => { + e.preventDefault(); + + const errors = {}; + if (!menuFieldsFormData.menuname.trim()) errors.menuname = "Menu name is required"; + if (!menuFieldsFormData.description.trim()) errors.description = "Description is required"; + + if (Object.keys(errors).length > 0) { + setFormErrors(errors); + return; + } + + const body = { + restaurantbranch: restaruntBranch, + menuname: menuFieldsFormData?.menuname, + description: menuFieldsFormData?.description, + is_active: menuFieldsFormData?.is_active + } + + try { + if (isMenuFieldEditMode && editingMenuId) { + await client.put(`/Dine360 Menu/${editingMenuId}`, body); + } else { + await client.post(`/Dine360 Menu`, body); + } + closeModal(); + getMenuItem(); + } catch (error) { + console.error("Error saving menu:", error); + } + }; + + const handleMenuFieldDelete = async () => { + try { + await client.delete(`/Dine360 Menu/${menuFieldDeleteConfirm.id}`); + setMenuFieldDeleteConfirm({ show: false, id: null }); + getMenuItem(); + } catch (error) { + if ( + error?.response?.data?.exception?.includes("DuplicateEntryError") || + error?.response?.data?.message?.includes("Duplicate entry") + ) { + alert("Menu Name already exists. Please use a different name."); + } else if (error?.response?.data?.exception?.includes("LinkExistsError") || + error?.response?.data?.message?.includes("LinkExistsError")) { + alert(" Cannot delete or cancel because Dine360 Menu three is linked with Dine360 Menu Category "); + } + } + }; + + + // MenuCategory + const handleMenuCategoryInputChange = (e) => { + const { name, value, type, checked } = e.target; + setMenuCategoryFormData((prev) => ({ + ...prev, + [name]: type === "checkbox" ? (checked ? 1 : 0) : value, + })); + }; + + const openMenuCategoryCreateModal = () => { + setIsMenuCategoryEditMode(false); + setEditingMenuCategoryId(null); + setMenuCategoryFormData({ menucategoryname: "", description: "", is_active: 0 }); + setFormMenuCategoryErrors({}); + setMenuCategoryModel(true); + }; + + const openMenuCategoryEditModal = (menu) => { + setIsMenuCategoryEditMode(true); + setEditingMenuCategoryId(menu.name); + setMenuCategoryFormData({ + menucategoryname: menu.menucategoryname || "", + description: menu.description || "", + is_active: menu.is_active || 0, + }); + setFormMenuCategoryErrors({}); + setMenuCategoryModel(true); + }; + + const closeMenuCategoryModal = () => { + setMenuCategoryModel(false); + setFormMenuCategoryErrors({}); + }; + + const handleMenuCategoryFormSubmit = async (e) => { + e.preventDefault(); + + const errors = {}; + if (!menuCategoryFormData.menucategoryname.trim()) errors.menucategoryname = "Menu Category name is required"; + if (!menuCategoryFormData.description.trim()) errors.description = "Description is required"; + + if (Object.keys(errors).length > 0) { + setFormMenuCategoryErrors(errors); + return; + } + + const body = { + restaurantbranch: restaruntBranch, + menucategoryname: menuCategoryFormData?.menucategoryname, + description: menuCategoryFormData?.description, + is_active: menuCategoryFormData?.is_active + } + try { + if (isMenuCategoryEditMode && editingMenuCategoryId) { + await client.put(`/Dine360%20Menu%20Category/${editingMenuCategoryId}`, body); + } else { + const res = await client.post(`/Dine360%20Menu%20Category`, body); + const CategoryLinkBody = { + menu: catMenuActive, + menucategory: res?.data?.data?.name, + restaurantbranch: restaruntBranch, + } + await client.post(`/Dine360%20Menu%20Category%20Link`, CategoryLinkBody) + } + closeMenuCategoryModal(); + getMenuCategoryItems(catMenuActive); + } catch (error) { + if ( + error?.response?.data?.exc_type?.includes("UniqueValidationError") || + error?.response?.data?.message?.includes("Duplicate entry") + ) { + alert("Menu Category Name already exists. Please use a different name."); + } else if (error?.response?.data?.exception?.includes("LinkExistsError") || + error?.response?.data?.message?.includes("LinkExistsError")) { + alert("Cannot delete or cancel because Dine360 Menu three is linked with Dine360 Menu Category "); + } + } + }; + console.log("catMenuActive", catMenuActive) + + const handleMenuCategoryDelete = async () => { + try { + await client.delete(`/Dine360%20Menu%20Category%20Link/${catMenuActive} - ${menuCategoryDeleteConfirm.id}`) + + await client.delete(`/Dine360%20Menu%20Category/${menuCategoryDeleteConfirm.id}`); + setMenuCategoryDeleteConfirm({ show: false, id: null }); + getMenuItem(); + } catch (error) { + if ( + error?.response?.data?.exception?.includes("DuplicateEntryError") || + error?.response?.data?.message?.includes("Duplicate entry") + ) { + alert("Menu Name already exists. Please use a different name."); + } else if (error?.response?.data?.exception?.includes("LinkExistsError") || + error?.response?.data?.message?.includes("LinkExistsError")) { + alert("Cannot delete or cancel because Dine360 Menu three is linked with Dine360 Menu Category "); + } + } + }; + + + const handleMenuItemDelete = async () => { + try { + await client.delete(`/Dine360%20Menu%20Category/${activeCategory}/${menuItemDeleteConfirm.id}`); + setMenuItemDeleteConfirm({ show: false, id: null }); + getMenuItem(); + } catch (error) { + if ( + error?.response?.data?.exception?.includes("DuplicateEntryError") || + error?.response?.data?.message?.includes("Duplicate entry") + ) { + alert("Menu Name already exists. Please use a different name."); + } else if (error?.response?.data?.exception?.includes("LinkExistsError") || + error?.response?.data?.message?.includes("LinkExistsError")) { + alert("Cannot delete or cancel because Dine360 Menu three is linked with Dine360 Menu Category "); + } + } + }; + + + const renderMenuItem = (menu) => ( +
+
+
+
+ +
+
{menu.menuitemname}
+ {menu.parent} +
${menu.price?.toFixed(2)}
+
+
+
e.stopPropagation()}> +
+ +
    +
  • + { + e.preventDefault(); + router.push(`/admin/pos/update-product?menuitemname=${menu.name}`) + // handleFloorEdit(menu); + }} + > + + Edit + +
  • +
  • + { + e.preventDefault(); + e.stopPropagation(); // optional + setMenuItemDeleteConfirm({ show: true, id: menu.name }); + }} + > + + Delete + +
  • +
+
+
+ +
+
+
+ ); + + + return ( + <> +
+ {loading ? ( + + ) : ( +
+
+
+
+
    +
  • +
    + + New +
    +
  • + {catMenu.map((menu) => ( +
  • handleCatMenuClick(menu?.menuname)} + style={{ cursor: "pointer" }} + > + menu + {menu?.menuname} +
    e.stopPropagation()}> +
    + +
      +
    • + { + e.preventDefault(); + openEditModal(menu);// optional: prevents bubbling from link + // handleFloorEdit(menu); + }} + > + + Edit + +
    • +
    • + { + e.preventDefault(); + e.stopPropagation(); // optional + setMenuFieldDeleteConfirm({ show: true, id: menu.name }); + }} + > + + Delete + +
    • +
    +
    +
    +
  • + ))} +
+
+
+
+ +
+
+
+
    + {/* Create New Category Button */} +
  • + + New +
  • + + {/* Menu Items */} + {menuData.map((menu) => ( +
  • handleMenuClick(menu)} // ✅ Keep this + style={{ cursor: "pointer", minWidth: "150px", maxWidth: "220px" }} + > + {/* Left: Icon and Name */} +
    + menu icon + {menu?.menucategoryname} +
    + + {/* Three Dots Menu (Absolute) */} +
    e.stopPropagation()} // ✅ Prevent parent click + > +
    + +
      +
    • + { + e.preventDefault(); + openMenuCategoryEditModal(menu); + }} + > + Edit + +
    • +
    • + { + e.preventDefault(); + setMenuCategoryDeleteConfirm({ show: true, id: menu.name }); // 👈 use menu + }} + > + Delete + +
    • +
    +
    +
    +
  • + ))} + +
+ + +
+
+ +
+
+
+
router.push(`/admin/pos/create-product?category=${activeCategory}`)}> +
+
+ +
Create
+
+
+
+
+
+ {menuItems?.menuitems_child?.map(renderMenuItem)} +
+
+
+ )} +
+ + {/* Modal */} + {menuFieldsModel && ( +
+
+
+
+
{isMenuFieldEditMode ? "Edit Menu" : "Create Menu"}
+ +
+
+
+
+ + + {formErrors.menuname && ( +
{formErrors.menuname}
+ )} +
+ +
+ + + {formErrors.description && ( +
{formErrors.description}
+ )} +
+ +
+ + +
+ +
+ +
+
+
+
+
+
+ )} + + {/* Delete Confirmation Modal */} + {menuFieldDeleteConfirm.show && ( +
+
+
+ +
+
+
Confirm Delete
+ +
+

Are you sure you want to delete this Menu?

+
+ + +
+
+
+
+
+ )} + + + {/* Menu category create & Upadte */} + {menuCategoryModel && ( +
+
+
+
+
{isMenuCategoryEditMode ? "Edit Menu Category" : "Create Menu Category"}
+ +
+
+
+
+ + + {formMenuCategoryErrors.menucategoryname && ( +
{formMenuCategoryErrors.menucategoryname}
+ )} +
+ +
+ + + {formMenuCategoryErrors.description && ( +
{formMenuCategoryErrors.description}
+ )} +
+ +
+ + +
+ +
+ +
+
+
+
+
+
+ )} + + {/* Delete Confirmation Modal */} + {menuCategoryDeleteConfirm.show && ( +
+
+
+ +
+
+
Confirm Delete
+ +
+

Are you sure you want to delete this Menu?

+
+ + +
+
+
+
+
+ )} + + {/* Delete Confirmation Modal */} + {menuItemDeleteConfirm.show && ( +
+
+
+ +
+
+
Confirm Delete
+ +
+

Are you sure you want to delete this Menu?

+
+ + +
+
+
+
+
+ )} + + ); +}; + +const ProductList = () => ( + + + }> + + + +); + +export default ProductList; diff --git a/src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx b/src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx new file mode 100644 index 0000000..efd67fa --- /dev/null +++ b/src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx @@ -0,0 +1,326 @@ +"use client"; +import { useState } from "react"; +import "highlight.js/styles/github.css"; +import client from "@auth"; +import { Icon } from "@iconify/react"; +import MasterLayout from "@/masterLayout/MasterLayout"; +import Breadcrumb from "@/components/Breadcrumb"; +import { useSearchParams } from "next/navigation"; + +const UpdateProduct = () => { + + const searchParams = useSearchParams() + const category = searchParams.get('category') + console.log("category", category) + + const [imagePreview, setImagePreview] = useState(null); + const [imageFile, setImageFile] = useState(null); + + const [formData, setFormData] = useState({ + menuitemname: "", + price: 0, + is_active: false, + is_special: false, + availability_time: "", + preparation_time: 0, + description: "", + }); + + const [errors, setErrors] = useState({}); + + const handleChange = (e) => { + const { name, value, type, checked } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: type === "checkbox" ? checked : value, + })); + }; + + const handleFileChange = (e) => { + const file = e.target.files?.[0]; + if (file) { + setImageFile(file); + setImagePreview(URL.createObjectURL(file)); + } + }; + + const handleRemoveImage = () => { + setImageFile(null); + setImagePreview(null); + }; + + const validate = () => { + const newErrors = {}; + if (!formData.menuitemname || formData.menuitemname.trim().length < 3) { + newErrors.menuitemname = "Menu Item Name is required"; + } + if (!formData.price || Number(formData.price) <= 0) { + newErrors.price = "Price must be greater than 0"; + } + if (!formData.availability_time) { + newErrors.availability_time = "Availability Time is required"; + } + if (!formData.preparation_time || Number(formData.preparation_time) <= 0) { + newErrors.preparation_time = "Preparation Time must be greater than 0"; + } + if (!formData.description || formData.description.trim().length < 5) { + newErrors.description = "Description is too short"; + } + if (!imageFile) { + newErrors.image_item = "Image is required"; + } + return newErrors; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + const validationErrors = validate(); + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + return; + } + setErrors({}); + + const data = new FormData(); + data.append("menuitemname", formData.menuitemname); + data.append("price", formData.price.toString()); + data.append("is_active", formData.is_active ? "1" : "0"); + data.append("is_special", formData.is_special ? "1" : "0"); + data.append("availability_time", formData.availability_time); + data.append("preparation_time", formData.preparation_time.toString()); + data.append("description", formData.description); + if (imageFile) data.append("image_item", imageFile); + + // for (let pair of data.entries()) { + // console.log(pair[0], pair[1]); + // } + const body = { + menucategoryname: category, + description: formData.description, + is_active: 0, + doctype: "Dine360 Menu Category", + menuitems_child: [ + { + menuitemname: formData.menuitemname, + price: formData.price, + is_active: formData.is_active ? 1 : 0, + is_special: formData.is_special ? 1 : 0, + availability_time: formData.availability_time, + preparation_time: formData.preparation_time, + } + ] + } + + try { + const res = await client.post(`/Dine360%20Menu%20Category/${category}`, body); + console.log('res', res) + alert("Form submitted successfully!"); + + } + catch (error) { + console.log("error", error) + } + + }; + + + return ( + + {/* Breadcrumb */} + +
+
+
+
+ +
+
Add New Product
+
+
+
+ + + {errors.menuitemname && ( +
{errors.menuitemname}
+ )} + +
+
+ + + {errors.price &&
{errors.price}
} +
+
+ + +
+ + + {errors.description && ( +
{errors.description}
+ )} +
+ + +
+ {/* Is Active */} +
+ + +
+ + {/* Is Special */} +
+ + +
+
+ +
+
+ + + {errors.availability_time && ( +
{errors.availability_time}
+ )} +
+ +
+ + + {errors.preparation_time && ( +
{errors.preparation_time}
+ )} +
+ +
+ +
+ +
+ {imagePreview ? ( +
+ + Uploaded +
+ ) : ( + + )} + {errors.image_item && ( +
{errors.image_item}
+ )} +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ ); +}; + +export default UpdateProduct; diff --git a/src/app/admin/(pos-system)/pos/sides/page.jsx b/src/app/admin/(pos-system)/pos/sides/page.jsx index b36b716..8393113 100644 --- a/src/app/admin/(pos-system)/pos/sides/page.jsx +++ b/src/app/admin/(pos-system)/pos/sides/page.jsx @@ -10,6 +10,7 @@ import PageNoData from "@/components/common-component/PageNoData"; import Breadcrumb from "@/components/Breadcrumb"; import { Icon } from "@iconify/react"; import { Baseurl, ImageBase } from "@utils/BaseUrl.utils"; +import axios from "axios"; const SidesPageInner = () => { const router = useRouter(); @@ -109,17 +110,36 @@ const SidesPageInner = () => { // Step 3: Update await client.put(`/Dine360%20Food%20Sides/${itemId}`, body); } else { - // Step 1: Create without image - const res = await client.post(`/Dine360%20Food%20Sides`, body); - itemId = res?.data?.data?.name; + // Step 1: Create new item with image + const formDataToSend = new FormData(); + formDataToSend.append("endpoint", "Dine360 Food Sides"); + formDataToSend.append("body", JSON.stringify(body)); - // Step 2: Upload image - if (formData.item_image) { - const uploadPath = await uploadImage(formData.item_image, "Dine360 Food Sides", itemId); + // Append image file (should be File object, not base64) + formDataToSend.append("file", formData.item_image); // `imageFile` should be a real File from + formDataToSend.append("fileid", "item_image"); - // Step 3: Update with image - await client.put(`/Dine360%20Food%20Sides/${itemId}`, { item_image: uploadPath }); - } + const response = await axios.post(`${Baseurl}/Upload-Image-To-Frappe`, formDataToSend, { + headers: { + "Authorization": "token 482beca79d9c005:b8778f51fcca82b", + }, + }); + + // Handle response if needed + console.log("Upload success:", response.data); + + + // // Step 1: Create without image + // const res = await client.post(`/Dine360%20Food%20Sides`, body); + // itemId = res?.data?.data?.name; + + // // Step 2: Upload image + // if (formData.item_image) { + // const uploadPath = await uploadImage(formData.item_image, "Dine360 Food Sides", itemId); + + // // Step 3: Update with image + // await client.put(`/Dine360%20Food%20Sides/${itemId}`, { item_image: uploadPath }); + // } } getSideData(); @@ -226,53 +246,53 @@ const SidesPageInner = () => {
-
- -
    -
  • - { - e.preventDefault(); - handleEdit(room); - }} - > - Edit - -
  • -
  • - { - e.preventDefault(); - setDeleteConfirm({ show: true, id: room.name }); - }} - > - - Delete - -
  • -
- -
- +
+ +
    +
  • + { + e.preventDefault(); + handleEdit(room); + }} + > + Edit + +
  • +
  • + { + e.preventDefault(); + setDeleteConfirm({ show: true, id: room.name }); + }} + > + + Delete + +
  • +
+ +
+
@@ -325,31 +345,41 @@ const SidesPageInner = () => {
- { - const file = e.target.files[0]; - if (!file) return; + { + const file = e.target.files[0]; + if (!file) return; - if (!file.type.startsWith("image/")) { - setErrors(prev => ({ ...prev, item_image: "Only image files are allowed." })); - return; - } + if (!file.type.startsWith("image/")) { + setErrors(prev => ({ ...prev, item_image: "Only image files are allowed." })); + return; + } - if (file.size / (1024 * 1024) > 1) { - setErrors(prev => ({ ...prev, item_image: "Max size 1MB" })); - return; - } + if (file.size / (1024 * 1024) > 1) { + setErrors(prev => ({ ...prev, item_image: "Max size 1MB" })); + return; + } - const reader = new FileReader(); - reader.onloadend = () => { - setFormData(prev => ({ ...prev, item_image: reader.result })); + setFormData(prev => ({ ...prev, item_image: file })); // Save raw File setErrors(prev => ({ ...prev, item_image: null })); - }; - reader.readAsDataURL(file); - }} /> + }} + /> {formData.item_image && (
- preview - + preview +
)}
diff --git a/src/components/admin/(reservation)/DayReservationTableEnable.jsx b/src/components/admin/(reservation)/DayReservationTableEnable.jsx index c59362f..5138adc 100644 --- a/src/components/admin/(reservation)/DayReservationTableEnable.jsx +++ b/src/components/admin/(reservation)/DayReservationTableEnable.jsx @@ -8,96 +8,74 @@ import { useRouter, useSearchParams } from "next/navigation"; const DayReservationTableEnableInner = () => { const router = useRouter(); - - // const day_name = params.name; const searchParams = useSearchParams(); const day = searchParams.get('day'); const day_name = searchParams.get('name'); const [tables, setTables] = useState([]); - const [showModal, setShowModal] = useState(false); - const [selectedTable, setSelectedTable] = useState(null); - const [error, setError] = useState(''); const [tableConfigs, setTableConfigs] = useState([]); + const [selectedTable, setSelectedTable] = useState(null); + const [showModal, setShowModal] = useState(false); + const [formData, setFormData] = useState({ enabled: false, start_time: '', end_time: '', interval: '' }); const [errors, setErrors] = useState({}); + const [restaurantBranch, setRestaurantBranch] = useState(''); const [loading, setLoading] = useState(true); - const [formData, setFormData] = useState({ - enabled: false, - start_time: '', - end_time: '', - interval: '' - }); - const [restaruntBranch, setRestaruntBranch] = useState("") + useEffect(() => { + const branch = localStorage.getItem('restaurantbranch'); + setRestaurantBranch(branch); + }, []); useEffect(() => { - const restarunt = localStorage.getItem("restaurantbranch") - setRestaruntBranch(restarunt) - }, []) - - useEffect(() => { - const isLogin = JSON.parse(localStorage.getItem("isLogin")); + const isLogin = JSON.parse(localStorage.getItem('isLogin')); if (!isLogin) { - router.push(`/admin?restaurantbranch=${restaruntBranch}`); + router.push(`/admin?restaurantbranch=${restaurantBranch}`); } - }, [router]); - - + }, [router, restaurantBranch]); useEffect(() => { - if (restaruntBranch && restaruntBranch !== "") - getTableData(); - }, [restaruntBranch]); - + if (restaurantBranch) getTableData(); + }, [restaurantBranch]); const getTableData = async () => { try { - setLoading(true) - const [tableRes, enableTableRes] = await Promise.all([ - // client.get(`/Dine360%20Table?fields=["*"]`), - client.get(`/Dine360%20Table?fields=["*"]&filters=[["restaurantbranch","=","${restaruntBranch}"]]`), + setLoading(true); + const [tableRes, configRes] = await Promise.all([ + client.get(`/Dine360%20Table?fields=["*"]&filters=[["restaurantbranch","=","${restaurantBranch}"]]`), client.get(`/Dine360%20Days/${day_name}?fields=["*"]`) ]); const allTables = tableRes.data.data || []; - const enabledTables = enableTableRes.data.data?.table_configuration || []; + const savedConfigs = configRes.data.data?.table_configuration || []; - setTableConfigs(enabledTables); + setTableConfigs(savedConfigs); - const enabledNames = new Set(enabledTables.map((t) => t.table)); + const enabledNames = new Set(savedConfigs.filter(t => t.enabled).map(t => t.table)); - const mergedTables = allTables.map((table) => ({ + const merged = allTables.map(table => ({ ...table, enabled: enabledNames.has(table.name) })); - setTables(mergedTables); - setLoading(false) + setTables(merged); + setLoading(false); } catch (error) { - console.error("Error fetching table or enabled list:", error); - setLoading(false) + console.error('Error loading table data:', error); + setLoading(false); } }; - console.log("tableConfigs", tableConfigs) - console.log("tables", tables) - - const formatTime = (timeStr) => { - if (!timeStr) return ''; - const parts = timeStr.split(':'); - return parts.length >= 2 ? `${parts[0].padStart(2, '0')}:${parts[1].padStart(2, '0')}` : ''; - }; const handleTableClick = (table) => { setSelectedTable(table); - setError(''); - const existingConfig = tableConfigs.find(cfg => cfg.table === table.name); - if (existingConfig) { + const existing = tableConfigs.find(cfg => cfg.table === table.name); + + if (existing && existing.enabled) { setFormData({ enabled: true, - start_time: formatTime(existingConfig.start_time), - end_time: formatTime(existingConfig.end_time), - interval: existingConfig.interval?.toString() || '' + start_time: existing.start_time?.slice(0, 5) || '', + end_time: existing.end_time?.slice(0, 5) || '', + interval: existing.interval?.toString() || '' }); } else { setFormData({ @@ -109,72 +87,59 @@ const DayReservationTableEnableInner = () => { } setShowModal(true); + setErrors({}); }; + const handleChange = (e) => { const { name, value, type, checked } = e.target; - setFormData((prev) => ({ + setFormData(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value })); }; - const handleSubmit = async (e) => { - e.preventDefault(); + const handleSubmit = () => { + const errs = {}; + if (formData.enabled) { + if (!formData.start_time) errs.start_time = 'Start time required'; + if (!formData.end_time) errs.end_time = 'End time required'; + if (!formData.interval || isNaN(formData.interval) || formData.interval <= 0) { + errs.interval = 'Positive interval required'; + } + } - const newErrors = {}; - - if (!formData.enabled) { - // Remove any config for this table when disabling - setTableConfigs(prev => prev.filter(cfg => cfg.table !== selectedTable?.name)); - setShowModal(false); + if (Object.keys(errs).length > 0) { + setErrors(errs); return; } - if (!formData.start_time) { - newErrors.start_time = 'Start time is required.'; - } - if (!formData.end_time) { - newErrors.end_time = 'End time is required.'; - } - if (!formData.interval) { - newErrors.interval = 'Interval is required.'; - } else if (isNaN(formData.interval) || formData.interval <= 0) { - newErrors.interval = 'Interval must be a positive number.'; - } + const updatedConfig = formData.enabled + ? { + day, + table: selectedTable.name, + enabled: 1, + start_time: `${formData.start_time}:00`, + end_time: `${formData.end_time}:00`, + interval: Number(formData.interval) + } + : { + day, + table: selectedTable.name, + enabled: 0, + start_time: null, + end_time: null, + interval: null + }; - setErrors(newErrors); - if (Object.keys(newErrors).length > 0) return; - - const formatTime = (timeStr) => { - if (!timeStr) return ''; - const [hour, minute] = timeStr.split(':'); - const hh = hour.padStart(2, '0'); - const mm = minute.padStart(2, '0'); - return `${hh}:${mm}:00`; - }; - - const newConfig = { - day, - table: selectedTable?.name, - enabled: 1, - start_time: formatTime(formData.start_time), - end_time: formatTime(formData.end_time), - interval: Number(formData.interval) - }; - - // Replace or add only enabled configs - setTableConfigs(prevConfigs => { - const filtered = prevConfigs.filter(cfg => cfg.table !== newConfig.table); - return [...filtered, newConfig]; + setTableConfigs(prev => { + const others = prev.filter(cfg => cfg.table !== selectedTable.name); + return [...others, updatedConfig]; }); setShowModal(false); - setErrors({}); - setFormData({ enabled: false, start_time: '', end_time: '', interval: '' }); }; - const handleModelClose = () => { setShowModal(false); setErrors({}); @@ -182,34 +147,40 @@ const DayReservationTableEnableInner = () => { }; const handleSaveAllChanges = async () => { - if (tableConfigs.length === 0) { - alert("No changes to save."); - return; - } + const confirm = window.confirm('Are you sure you want to save all changes?'); + if (!confirm) return; - const confirmed = window.confirm("Are you sure you want to save all changes?"); - if (!confirmed) return; - - const payload = { - name: day_name, - table_configuration: tableConfigs - }; + const fullConfigs = tables.map(table => { + const cfg = tableConfigs.find(c => c.table === table.name); + return cfg + ? cfg + : { + day, + table: table.name, + enabled: 0, + start_time: null, + end_time: null, + interval: null + }; + }); try { - await client.put(`/Dine360%20Days/${day_name}`, payload, { + await client.put(`/Dine360%20Days/${day_name}`, { + name: day_name, + table_configuration: fullConfigs + }, { headers: { 'Content-Type': 'application/json' } }); - alert("All table configurations saved successfully!"); + alert('Saved successfully!'); setTableConfigs([]); getTableData(); } catch (error) { - console.error("Error saving all changes:", error); - alert("Failed to save configurations."); + console.error('Save failed:', error); + alert('Failed to save changes.'); } }; const sortedTables = [...tables].sort((a, b) => a.tablename.localeCompare(b.tablename)); - console.log("sortedTables", sortedTables) return ( <>