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 index 830714f..8a1b250 100644 --- a/src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx +++ b/src/app/admin/(pos-system)/pos/(product)/product-list/page.jsx @@ -386,7 +386,7 @@ const ProductListInner = () => { className="dropdown-item px-16 py-8 d-flex align-items-center gap-2 rounded text-secondary-light bg-hover-neutral-200 text-hover-neutral-900" onClick={(e) => { e.preventDefault(); - router.push(`/admin/pos/update-product?menuitemname=${menu.name}`) + router.push(`/admin/pos/update-product?category=${activeCategory}&menuitemname=${menu.name}`) // handleFloorEdit(menu); }} > 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 index efd67fa..daa4576 100644 --- a/src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx +++ b/src/app/admin/(pos-system)/pos/(product)/update-product/page.jsx @@ -1,20 +1,22 @@ "use client"; -import { useState } from "react"; +import { useEffect, 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"; +import axios from "axios"; +import { Baseurl } from "@utils/BaseUrl.utils"; const UpdateProduct = () => { - - const searchParams = useSearchParams() - const category = searchParams.get('category') - console.log("category", category) + const searchParams = useSearchParams(); + const category = searchParams.get("category"); + const MenuItemName = searchParams.get("menuitemname"); const [imagePreview, setImagePreview] = useState(null); const [imageFile, setImageFile] = useState(null); + const [errors, setErrors] = useState({}); const [formData, setFormData] = useState({ menuitemname: "", @@ -26,7 +28,33 @@ const UpdateProduct = () => { description: "", }); - const [errors, setErrors] = useState({}); + useEffect(() => { + if (category) { + getMenuFoodItems(); + } + }, [category]); + + const getMenuFoodItems = async () => { + try { + const res = await client.get( + `/Dine360%20Menu%20Category/${category}?fields=["*"]&limit_page_length=100` + ); + const item = res?.data?.data?.menuitems_child?.[0]; + if (item) { + setFormData((prev) => ({ + ...prev, + ...item, + is_active: !!item.is_active, + is_special: !!item.is_special, + })); + if (item.image_item) { + setImagePreview(`${process.env.NEXT_PUBLIC_IMAGE_BASE}/${item.image_item}`); + } + } + } catch (error) { + console.error("Error fetching menu items:", error); + } + }; const handleChange = (e) => { const { name, value, type, checked } = e.target; @@ -66,7 +94,7 @@ const UpdateProduct = () => { if (!formData.description || formData.description.trim().length < 5) { newErrors.description = "Description is too short"; } - if (!imageFile) { + if (!imageFile && !imagePreview) { newErrors.image_item = "Image is required"; } return newErrors; @@ -81,19 +109,6 @@ const UpdateProduct = () => { } 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, @@ -107,41 +122,47 @@ const UpdateProduct = () => { 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) + 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"); + formDataToSend.append("data", JSON.stringify({ name: MenuItemName })); + const response = await axios.put(`${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); } - catch (error) { - console.log("error", error) - } - }; - return ( - {/* Breadcrumb */} - +
-
-
-
- -
-
Add New Product
-
+
+
+
+
+
Add New Product
+
-
- + - {errors.price &&
{errors.price}
} + {errors.price && ( +
+ {errors.price} +
+ )}
-
- + {errors.description && ( -
{errors.description}
+
+ {errors.description} +
)}
-
- {/* Is Active */} -
+
{ onChange={handleChange} id="is_active" /> -
- - {/* Is Special */} -
+
{ onChange={handleChange} id="is_special" /> -
@@ -221,75 +254,76 @@ const UpdateProduct = () => {
- + {errors.availability_time && ( -
{errors.availability_time}
+
+ {errors.availability_time} +
)}
-
- + {errors.preparation_time && ( -
{errors.preparation_time}
+
+ {errors.preparation_time} +
)}
-
-
-
diff --git a/src/app/admin/(pos-system)/pos/sides/page.jsx b/src/app/admin/(pos-system)/pos/sides/page.jsx index 8393113..81fe370 100644 --- a/src/app/admin/(pos-system)/pos/sides/page.jsx +++ b/src/app/admin/(pos-system)/pos/sides/page.jsx @@ -28,6 +28,7 @@ const SidesPageInner = () => { description: "", price: "", item_image: null, + image_preview: null, }); const [errors, setErrors] = useState({}); const [deleteConfirm, setDeleteConfirm] = useState({ show: false, id: null }); @@ -75,15 +76,12 @@ const SidesPageInner = () => { const handleSubmit = async (e) => { e.preventDefault(); const newErrors = {}; - if (!formData.sidename.trim()) newErrors.sidename = "Side Name is required"; if (!formData.description.trim()) newErrors.description = "Description is required"; if (!formData.price) newErrors.price = "Price is required"; - if (!editMode && !formData.item_image) { newErrors.item_image = "Item image is required"; } - setErrors(newErrors); if (Object.keys(newErrors).length > 0) return; @@ -96,50 +94,27 @@ const SidesPageInner = () => { }; try { - let itemId; + const formDataToSend = new FormData(); + formDataToSend.append("endpoint", "Dine360 Food Sides"); + formDataToSend.append("body", JSON.stringify(body)); + if (formData.item_image instanceof File) { + formDataToSend.append("file", formData.item_image); + formDataToSend.append("fileid", "item_image"); + } if (editMode) { - itemId = editingRoomId; - - // Step 2: Upload new image if updated - if (formData.item_image?.startsWith("data:image")) { - const uploadPath = await uploadImage(formData.item_image, "Dine360 Food Sides", itemId); - body.item_image = uploadPath; - } - - // Step 3: Update - await client.put(`/Dine360%20Food%20Sides/${itemId}`, body); - } else { - // Step 1: Create new item with image - const formDataToSend = new FormData(); - formDataToSend.append("endpoint", "Dine360 Food Sides"); - formDataToSend.append("body", JSON.stringify(body)); - - // 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"); - - const response = await axios.post(`${Baseurl}/Upload-Image-To-Frappe`, formDataToSend, { + formDataToSend.append("data", JSON.stringify({ name: editingRoomId })); + await client.put(`${Baseurl}/Upload-Image-To-Frappe`, formDataToSend, { headers: { - "Authorization": "token 482beca79d9c005:b8778f51fcca82b", + Authorization: "token 482beca79d9c005:b8778f51fcca82b", + }, + }); + } else { + 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(); @@ -147,56 +122,45 @@ const SidesPageInner = () => { } catch (error) { console.error("❌ Submission error:", error); const backendError = error?.response?.data?.exception || ""; - if (backendError.includes("Data too long for column 'item_image'")) { setErrors((prev) => ({ ...prev, item_image: "Data too long for column 'item_image'", })); - } else if (error?.response?.status === 500) { - alert("Server error occurred. Please try again later."); } else { alert("Submission failed. Please try again."); } } }; - const uploadImage = async (base64Data, doctype, docname) => { - const blob = await (await fetch(base64Data)).blob(); - const form = new FormData(); - form.append("file", blob, "side.jpg"); - form.append("is_private", "0"); - form.append("doctype", doctype); - form.append("docname", docname); - - const res = await fetch(`${ImageBase}/api/method/upload_file`, { - method: "POST", - headers: { - "Authorization": "token 482beca79d9c005:b8778f51fcca82b", - // ❌ Don't set Content-Type when using FormData — let the browser set it - }, - body: form, - }); - - const result = await res.json(); - - if (!result.message?.file_url) throw new Error("Upload failed"); - return result.message.file_url; - }; - - const handleEdit = (room) => { setFormData({ sidename: room.sidename || "", description: room.description || "", price: room.price || "", item_image: room.item_image || null, + image_preview: null, }); setEditingRoomId(room.name); setEditMode(true); setShowModal(true); }; + const resetForm = () => { + if (formData.image_preview) URL.revokeObjectURL(formData.image_preview); + setFormData({ + sidename: "", + description: "", + price: "", + item_image: null, + image_preview: null, + }); + setErrors({}); + setEditMode(false); + setEditingRoomId(null); + setShowModal(false); + }; + const handleDelete = async () => { try { await client.delete(`/Dine360%20Food%20Sides/${deleteConfirm?.id}`); @@ -207,32 +171,15 @@ const SidesPageInner = () => { } }; - const resetForm = () => { - setFormData({ - sidename: "", - description: "", - price: "", - item_image: null, - }); - setErrors({}); - setEditMode(false); - setEditingRoomId(null); - setShowModal(false); - }; - return (
- {/* Header + Create Button */}
Sides
- +
- {/* Grid */}
{loading ? ( @@ -243,65 +190,31 @@ const SidesPageInner = () => { const gradientClass = gradientClasses[index % gradientClasses.length]; return (
-
+
-
- -
    -
  • - { - 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 + +
  • +
- -
-
{room?.sidename}
- {room?.description} -
${room.price.toFixed(2)}
- -
+ +
{room?.sidename}
+ {room?.description} +
${room.price.toFixed(2)}
@@ -318,12 +231,11 @@ const SidesPageInner = () => {
-
{editMode ? "Edit Side" : "Create Side"}
+
{editMode ? "Edit Side" : "Create Side"}
- {/* Form fields */}
@@ -345,38 +257,55 @@ const SidesPageInner = () => {
- { const file = e.target.files[0]; if (!file) return; if (!file.type.startsWith("image/")) { - setErrors(prev => ({ ...prev, item_image: "Only image files are allowed." })); + setErrors(prev => ({ ...prev, item_image: "Only image files allowed." })); return; } - if (file.size / (1024 * 1024) > 1) { + if (file.size / 1024 / 1024 > 1) { setErrors(prev => ({ ...prev, item_image: "Max size 1MB" })); return; } - setFormData(prev => ({ ...prev, item_image: file })); // Save raw File + const previewURL = URL.createObjectURL(file); + setFormData(prev => ({ + ...prev, + item_image: file, + image_preview: previewURL, + })); setErrors(prev => ({ ...prev, item_image: null })); }} /> - {formData.item_image && ( + {(formData.image_preview || formData.item_image) && (
preview @@ -419,7 +348,6 @@ const SidesPageInner = () => {
); }; - const SidesPage = () => (