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 (
+
+
+
+
+ );
+};
+
+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?.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?.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}`)}>
+
+
+
+
+ {menuItems?.menuitems_child?.map(renderMenuItem)}
+
+
+
+ )}
+
+
+ {/* Modal */}
+ {menuFieldsModel && (
+
+
+
+
+
{isMenuFieldEditMode ? "Edit Menu" : "Create Menu"}
+
+
+
+
+
+
+
+
+ )}
+
+ {/* 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"}
+
+
+
+
+
+
+
+
+ )}
+
+ {/* 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
+
+
+
+
+
+
+
+
+ );
+};
+
+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 && (
-

-
+
})
+
)}
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 (
<>