From ab8ed33cd2f615e276c3f0d251865e33e19d5119 Mon Sep 17 00:00:00 2001 From: alaguraj Date: Thu, 7 Aug 2025 16:14:34 +0530 Subject: [PATCH] create product sides added configure updated --- .../pos/(product)/create-product/page.jsx | 916 ++++++++++++------ 1 file changed, 628 insertions(+), 288 deletions(-) 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 index 2dca08b..ace1974 100644 --- a/src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx +++ b/src/app/admin/(pos-system)/pos/(product)/create-product/page.jsx @@ -1,317 +1,657 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState, useRef } 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"; +import { Baseurl, ImageBase } from "@utils/BaseUrl.utils"; +import client from "@auth"; const AddNewProduct = () => { - const searchParams = useSearchParams(); - const category = searchParams.get("category"); - const router = useRouter(); + const searchParams = useSearchParams(); + const category = searchParams.get("category"); + const router = useRouter(); - const [imagePreview, setImagePreview] = useState(null); - const [imageFile, setImageFile] = useState(null); + const [imagePreview, setImagePreview] = useState(null); + const [imageFile, setImageFile] = useState(null); + const [restaruntBranch, setRestaruntBranch] = useState(""); + const [formData, setFormData] = useState({ + menuitemname: "", + price: 0, + is_active: false, + is_special: false, + availability_time: "", + preparation_time: 0, + description: "", + }); - const [formData, setFormData] = useState({ - menuitemname: "", - price: 0, - is_active: false, - is_special: false, - availability_time: "", - preparation_time: 0, - description: "", + const [errors, setErrors] = useState({}); + const [showModal, setShowModal] = useState(false); + + const [activeCategory, setActiveCategory] = useState(""); + const [sidesCategoryData, setSidesCategoryData] = useState(null); + const [sidesDataByCategory, setSidesDataByCategory] = useState({}); // cache per category + const [sidesData, setSidesData] = useState(null); + + // selection stored per category + const [selectionByCategory, setSelectionByCategory] = useState({}); // { [categoryName]: Set } + const selectAllRef = useRef(null); + + // derive current category selected set + const selectedSides = selectionByCategory[activeCategory] || new Set(); + + const isAllSelected = + sidesData && sidesData.length > 0 && selectedSides.size === sidesData.length; + const isPartialSelected = + sidesData && + selectedSides.size > 0 && + selectedSides.size < (sidesData?.length || 0); + + useEffect(() => { + if (selectAllRef.current) { + selectAllRef.current.indeterminate = !!isPartialSelected; + } + }, [isPartialSelected]); + + const toggleSide = (name) => { + setSelectionByCategory((prev) => { + const prevSet = new Set(prev[activeCategory] || []); + if (prevSet.has(name)) prevSet.delete(name); + else prevSet.add(name); + return { ...prev, [activeCategory]: prevSet }; }); + }; - const [errors, setErrors] = useState({}); + const toggleSelectAll = () => { + if (!sidesData) return; + setSelectionByCategory((prev) => { + const copy = { ...prev }; + if (isAllSelected) { + copy[activeCategory] = new Set(); // clear + } else { + copy[activeCategory] = new Set(sidesData.map((s) => s.name)); + } + return copy; + }); + }; - const handleChange = (e) => { - const { name, value, type, checked } = e.target; - setFormData((prev) => ({ - ...prev, - [name]: type === "checkbox" ? checked : value, - })); + useEffect(() => { + const restarunt = localStorage.getItem("restaurantbranch"); + setRestaruntBranch(restarunt); + }, []); + + useEffect(() => { + const isLogin = JSON.parse(localStorage.getItem("isLogin")); + if (!isLogin) { + router.push(`/admin?restaurantbranch=${restaruntBranch}`); + } + }, [router, restaruntBranch]); + + useEffect(() => { + if (restaruntBranch && restaruntBranch !== "") getSidesCategory(); + }, [restaruntBranch]); + + const getSidesCategory = async () => { + try { + const res = await client?.get( + `/Dine360%20Food%20Sides%20Category?fields=[%22*%22]&limit_page_length=100&filters=[["restaurantbranch","=","${restaruntBranch}"]]` + ); + setSidesCategoryData(res?.data?.data); + if (res?.data?.data?.[0]) { + const first = res?.data?.data[0]?.name; + setActiveCategory(first); + await getSideData(first); + } + } catch (error) { + console.error("Error fetching category data:", error); + } + }; + + const getSideData = async (sidesCategoryName) => { + try { + const sideRes = await client.get( + `/Dine360%20Food%20Sides?fields=[%22*%22]&limit_page_length=100&filters=[["sidecategoryid","=","${sidesCategoryName}"]]` + ); + const data = sideRes?.data?.data || []; + setSidesData(data); + setSidesDataByCategory((prev) => ({ ...prev, [sidesCategoryName]: data })); + // keep existing selectionByCategory for that category (do not clear) + } catch (error) { + console.error("Error fetching sides data:", error); + } + }; + + const handleSidesCategoryClick = async (catname) => { + setActiveCategory(catname); + await getSideData(catname); + }; + + 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] ?? null; + if (file) { + setImageFile(file); + setImagePreview(URL.createObjectURL(file)); + setErrors((prev) => { + const { item_image, ...rest } = prev; + return rest; + }); + } + }; + + 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 (min 3 chars)"; + } + 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.item_image = "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 = { + menuitemname: formData.menuitemname, + price: Number(formData.price), + is_active: formData.is_active ? 1 : 0, + is_special: formData.is_special ? 1 : 0, + availability_time: formData.availability_time, + preparation_time: Number(formData.preparation_time), + description: formData.description, }; - const handleFileChange = (e) => { - const file = e.target.files?.[0]; - if (file) { - setImageFile(file); - setImagePreview(URL.createObjectURL(file)); - } - }; + try { + const formDataToSend = new FormData(); + formDataToSend.append("endpoint", "Dine360 Menu Category"); + if (imageFile) { + formDataToSend.append("file", imageFile); + } + formDataToSend.append("fileid", "item_image"); + formDataToSend.append("childjson", JSON.stringify(body)); + formDataToSend.append("childkey", "menuitems_child"); + formDataToSend.append("docname", category ?? ""); + formDataToSend.append("isimageupdateorcreate", "1"); - 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 = + await axios.post( + `${Baseurl}/Upload-Image-To-Frappe/parent-child`, + formDataToSend, { - 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, - description: formData.description, - }; - - 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", - // }, - // }); - const formDataToSend = new FormData(); - formDataToSend.append("endpoint", "Dine360 Menu Category"); - formDataToSend.append("file", imageFile); // ✅ use imageFile - formDataToSend.append("fileid", "item_image"); - formDataToSend.append("childjson", JSON.stringify(body)) - formDataToSend.append("childkey", "menuitems_child"); - formDataToSend.append("docname", category); - formDataToSend.append("isimageupdateorcreate", 1); - console.log(formDataToSend) - const response = await axios.post(`${Baseurl}/Upload-Image-To-Frappe/parent-child`, 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); + headers: { + Authorization: "token 482beca79d9c005:b8778f51fcca82b", + }, } - }; + ); - return ( - - -
-
-
-
-
-
Add New Product
-
-
-
- - - {errors.menuitemname && ( -
{errors.menuitemname}
- )} -
-
- - - {errors.price && ( -
{errors.price}
- )} -
-
+ alert("Form submitted successfully!"); + router.push("/admin/pos/product-list"); + } catch (error) { + console.error("Submission error:", error); + alert("Failed to submit. Please try again."); + } + }; -
- - - {errors.description && ( -
{errors.description}
- )} -
+ const handleSaveSides = () => { + // aggregate all selected sides across categories + const allSelected = []; + Object.entries(selectionByCategory).forEach(([cat, setOfNames]) => { + const list = sidesDataByCategory[cat] || []; + list.forEach((side) => { + if (setOfNames.has(side?.name)) { + allSelected.push({ ...side, category: cat }); + } + }); + }); + console.log("All selected sides (full objects):", allSelected); + const names = allSelected.map((s) => s.sidename || s.name); + console.log("Selected side names:", names); + alert(`Selected sides (${names.length}): ${names.join(", ")}`); + }; -
-
- - -
+ const resetForm = () => { + setShowModal(false); + }; -
- - -
-
- -
-
- - - {errors.availability_time && ( -
{errors.availability_time}
- )} -
-
- - - {errors.preparation_time && ( -
{errors.preparation_time}
- )} -
-
- -
- -
- {imagePreview ? ( -
- - Uploaded -
- ) : ( - - )} - {errors.image_item && ( -
{errors.image_item}
- )} -
-
- -
-
- -
-
- -
-
-
-
+ return ( + + +
+
+
+
+
+
Add New Product
+
+ {/* ... (form inputs same as before) ... */} +
+
+ + + {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.item_image && ( +
+ {errors.item_image} +
+ )} +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
- - ); +
+
+
+ + {showModal && ( +
setShowModal(false)} + > +
e.stopPropagation()} + > +
+
+
Link Sides
+ +
+ +
+ {/* Select All / Deselect All button */} +
+ +
+ +
+
+
+
+
+
    + {sidesCategoryData?.map((sidesCategory) => ( +
  • + handleSidesCategoryClick(sidesCategory?.name) + } + style={{ cursor: "pointer" }} + > + menu + + {sidesCategory?.sidecategoryname} + +
  • + ))} +
+
+
+
+
+ +
+
+ {sidesData?.map((side) => { + const selected = (selectionByCategory[activeCategory] || new Set()).has( + side?.name + ); + return ( +
toggleSide(side?.name)} + > +
+
{ + e.stopPropagation(); + toggleSide(side?.name); + }} + > + toggleSide(side?.name)} + /> +
+ +
+ {side?.sidename +
+ {side?.sidename} +
+ + ${side.price.toFixed(2)} + +
+
+
+ ); + })} +
+
+
+
+ + {/* footer actions */} +
+ +
+
+
+
+ )} + + ); }; export default AddNewProduct;