The 'Sides' category and option to add sides are enabled, but the image upload feature for sides is still pending.
This commit is contained in:
parent
6df04ed684
commit
179975947e
@ -13179,12 +13179,12 @@ select option {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
background-color: var(--primary-600);
|
background-color: var(--theme-color);
|
||||||
transition: 0.2s linear;
|
transition: 0.2s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered-tab .nav-link.active {
|
.bordered-tab .nav-link.active {
|
||||||
color: var(--primary-600);
|
color: var(--theme-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered-tab .nav-link.active::before {
|
.bordered-tab .nav-link.active::before {
|
||||||
@ -13194,7 +13194,7 @@ select option {
|
|||||||
/* Bordered Tab Css End */
|
/* Bordered Tab Css End */
|
||||||
/* Pill Tab Css Start */
|
/* Pill Tab Css Start */
|
||||||
.pill-tab .nav-link.active {
|
.pill-tab .nav-link.active {
|
||||||
background-color: var(--primary-600);
|
background-color: var(--theme-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-tab.style-three {
|
.pill-tab.style-three {
|
||||||
|
|||||||
67
src/app/admin/(pos-system)/pos/sides-category/page.jsx
Normal file
67
src/app/admin/(pos-system)/pos/sides-category/page.jsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
"use client"
|
||||||
|
import MasterLayout from "@/masterLayout/MasterLayout";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import client from "@auth";
|
||||||
|
import PageLoader from "@/components/common-component/PageLoader";
|
||||||
|
import Breadcrumb from "@/components/Breadcrumb";
|
||||||
|
import SidesCategoryComponent from "@/components/admin/SidesCategoryComponent";
|
||||||
|
|
||||||
|
// export const metadata = {
|
||||||
|
// title: "WowDash NEXT JS - Admin Dashboard Multipurpose Bootstrap 5 Template",
|
||||||
|
// description:
|
||||||
|
// "Wowdash NEXT JS is a developer-friendly, ready-to-use admin template designed for building attractive, scalable, and high-performing web applications.",
|
||||||
|
// };
|
||||||
|
|
||||||
|
const SidesCategoryPage = () => {
|
||||||
|
|
||||||
|
const [sidesCategoryData, setSidesCategoryData] = useState(null);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [restaruntBranch, setRestaruntBranch] = useState("")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const restarunt = localStorage.getItem("restaurantbranch")
|
||||||
|
setRestaruntBranch(restarunt)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (restaruntBranch && restaruntBranch !== "")
|
||||||
|
getSidesCategory();
|
||||||
|
}, [restaruntBranch]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const getSidesCategory = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
// const res = await client?.get(`/Dine360 Floor?fields=[\"*\"]&limit_page_length=100`);
|
||||||
|
const res = await client?.get(`/Dine360%20Food%20Sides%20Category?fields=[%22*%22]&limit_page_length=100&filters=[["restaurantbranch","=","${restaruntBranch}"]]`);
|
||||||
|
setSidesCategoryData(res?.data?.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching floor data:", error);
|
||||||
|
setError(error?.message || "Failed to fetch floor data");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("")
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MasterLayout>
|
||||||
|
|
||||||
|
<Breadcrumb title='Floor' />
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<PageLoader />
|
||||||
|
) : (
|
||||||
|
<SidesCategoryComponent sidesCategoryData={sidesCategoryData} getSidesCategory={getSidesCategory} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</MasterLayout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidesCategoryPage;
|
||||||
416
src/app/admin/(pos-system)/pos/sides/page.jsx
Normal file
416
src/app/admin/(pos-system)/pos/sides/page.jsx
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
"use client";
|
||||||
|
import React, { Suspense, useEffect, useState } from "react";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import MasterLayout from "@/masterLayout/MasterLayout";
|
||||||
|
import client from "@auth";
|
||||||
|
import { gradientClasses } from "@utils/constant.utils";
|
||||||
|
import PageLoader from "@/components/common-component/PageLoader";
|
||||||
|
import PageNoData from "@/components/common-component/PageNoData";
|
||||||
|
import Breadcrumb from "@/components/Breadcrumb";
|
||||||
|
import { Icon } from "@iconify/react";
|
||||||
|
|
||||||
|
const SidesPageInner = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const sidesCategoryName = decodeURIComponent(searchParams.get("sidescategoryname"));
|
||||||
|
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [editMode, setEditMode] = useState(false);
|
||||||
|
const [editingRoomId, setEditingRoomId] = useState(null);
|
||||||
|
const [roomData, setRoomData] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
sidename: "",
|
||||||
|
description: "",
|
||||||
|
price: "",
|
||||||
|
item_image: null,
|
||||||
|
});
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = 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(() => {
|
||||||
|
if (sidesCategoryName) {
|
||||||
|
getSideData();
|
||||||
|
}
|
||||||
|
}, [sidesCategoryName]);
|
||||||
|
|
||||||
|
const getSideData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
const roomRes = await client.get(
|
||||||
|
`/Dine360%20Food%20Sides?fields=[%22*%22]&limit_page_length=100&filters=[["sidecategoryid","=","${sidesCategoryName}"]]`
|
||||||
|
);
|
||||||
|
setRoomData(roomRes?.data?.data || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
setError(error?.message || "Failed to fetch side data");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (!formData.item_image) newErrors.item_image = "Item image is required";
|
||||||
|
|
||||||
|
setErrors(newErrors);
|
||||||
|
if (Object.keys(newErrors).length > 0) return;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
sidename: formData.sidename,
|
||||||
|
description: formData.description,
|
||||||
|
price: parseFloat(formData.price),
|
||||||
|
item_image: formData.item_image,
|
||||||
|
sidecategoryid: sidesCategoryName,
|
||||||
|
restaurantbranch: restaruntBranch,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (editMode) {
|
||||||
|
await client.put(`/Dine360%20Food%20Sides/${editingRoomId}`, body);
|
||||||
|
} else {
|
||||||
|
await client.post(`/Dine360%20Food%20Sides`, body);
|
||||||
|
}
|
||||||
|
getSideData();
|
||||||
|
resetForm();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Submission error:", error);
|
||||||
|
|
||||||
|
// Backend MySQL error handling
|
||||||
|
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 or contact support.");
|
||||||
|
} else {
|
||||||
|
alert("Submission failed. Please check your input and try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleEdit = (room) => {
|
||||||
|
setFormData({
|
||||||
|
sidename: room.sidename || "",
|
||||||
|
description: room.description || "",
|
||||||
|
price: room.price || "",
|
||||||
|
item_image: room.item_image || null,
|
||||||
|
});
|
||||||
|
setEditingRoomId(room.name);
|
||||||
|
setEditMode(true);
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
try {
|
||||||
|
await client.delete(`/Dine360%20Food%20Sides/${deleteConfirm?.id}`);
|
||||||
|
setDeleteConfirm({ show: false, id: null });
|
||||||
|
getSideData();
|
||||||
|
} catch (error) {
|
||||||
|
alert("Error deleting. It might be linked to other data.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
setFormData({
|
||||||
|
sidename: "",
|
||||||
|
description: "",
|
||||||
|
price: "",
|
||||||
|
item_image: null,
|
||||||
|
});
|
||||||
|
setErrors({});
|
||||||
|
setEditMode(false);
|
||||||
|
setEditingRoomId(null);
|
||||||
|
setShowModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid" style={{ marginBottom: "100px" }}>
|
||||||
|
<div className="card h-100 p-0 radius-12">
|
||||||
|
<div className="card-body p-3 p-lg-5">
|
||||||
|
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h6 className="mb-0">Sides</h6>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-bg-theme radius-8 px-20 py-11"
|
||||||
|
onClick={() => setShowModal(true)}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3 gx-3 gx-lg-5 gy-lg-5 justify-content-center">
|
||||||
|
{loading ? (
|
||||||
|
<PageLoader />
|
||||||
|
) : roomData?.length === 0 ? (
|
||||||
|
<PageNoData />
|
||||||
|
) : (
|
||||||
|
roomData?.map((room, index) => {
|
||||||
|
const gradientClass = gradientClasses[index % gradientClasses.length];
|
||||||
|
return (
|
||||||
|
<div className="col-xxl-3 col-lg-4 col-sm-6 cursor-pointer" key={room?.name}>
|
||||||
|
<div className={`card p-3 shadow-2 radius-8 h-100 bg-theme border border-white position-relative`}>
|
||||||
|
<div className="position-absolute top-0 end-0 me-1 mt-1 d-flex gap-2">
|
||||||
|
<div className='dropdown'>
|
||||||
|
<button
|
||||||
|
className='btn px-1 py-1 d-flex align-items-center text-primary-light'
|
||||||
|
type='button'
|
||||||
|
data-bs-toggle='dropdown'
|
||||||
|
aria-expanded='false'
|
||||||
|
>
|
||||||
|
<Icon icon='entypo:dots-three-vertical' className='menu-icon' />
|
||||||
|
</button>
|
||||||
|
<ul className='dropdown-menu'>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className='dropdown-item px-16 py-8 d-flex align-items-center gap-2 rounded text-secondary-light'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleEdit(room);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon='lucide:edit' className='menu-icon' />
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className='dropdown-item px-16 py-8 d-flex align-items-center gap-2 rounded text-secondary-light'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDeleteConfirm({ show: true, id: room.name });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon='fluent:delete-24-regular' className='menu-icon' />
|
||||||
|
Delete
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-body text-center p-0">
|
||||||
|
<h6 className="text-lg mt-0 mb-0">{room?.sidename}</h6>
|
||||||
|
<p className="mb-0 text-sm">{room?.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Modal */}
|
||||||
|
{showModal && (
|
||||||
|
<div className="modal fade show" style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-header border-0 pb-0">
|
||||||
|
<h6 className="modal-title text-lg">{editMode ? "Edit Side" : "Create Side"}</h6>
|
||||||
|
<button type="button" className="btn-close" onClick={resetForm}></button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Side Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={`form-control ${errors.sidename ? "is-invalid" : ""}`}
|
||||||
|
name="sidename"
|
||||||
|
value={formData.sidename}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{errors.sidename && <div className="invalid-feedback">{errors.sidename}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Description</label>
|
||||||
|
<textarea
|
||||||
|
className={`form-control ${errors.description ? "is-invalid" : ""}`}
|
||||||
|
name="description"
|
||||||
|
value={formData.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
{errors.description && <div className="invalid-feedback">{errors.description}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Price</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className={`form-control ${errors.price ? "is-invalid" : ""}`}
|
||||||
|
name="price"
|
||||||
|
value={formData.price}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{errors.price && <div className="invalid-feedback">{errors.price}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Item Image</label>
|
||||||
|
<div className="d-flex align-items-center gap-3">
|
||||||
|
{/* Input on the left */}
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
className={`form-control ${errors.item_image ? "is-invalid" : ""}`}
|
||||||
|
style={{ maxWidth: "350px" }}
|
||||||
|
onChange={(e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const allowedTypes = ["image/jpeg", "image/png", "image/jpg"];
|
||||||
|
const maxFileSizeMB = 1;
|
||||||
|
|
||||||
|
if (!allowedTypes.includes(file.type)) {
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
item_image: "Only JPG, JPEG, and PNG files are allowed.",
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.size / (1024 * 1024) > maxFileSizeMB) {
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
item_image: "Max file size is 1MB.",
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
item_image: reader.result,
|
||||||
|
}));
|
||||||
|
setErrors((prev) => ({ ...prev, item_image: null }));
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Image on the right */}
|
||||||
|
{formData.item_image && (
|
||||||
|
<div className="position-relative" style={{ width: "50px", height: "50px" }}>
|
||||||
|
<img
|
||||||
|
src={formData.item_image}
|
||||||
|
alt="Preview"
|
||||||
|
style={{
|
||||||
|
width: "50px",
|
||||||
|
height: "50px",
|
||||||
|
objectFit: "cover",
|
||||||
|
borderRadius: "8px",
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Delete icon top-right */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-sm btn-danger position-absolute top-0 end-0 p-1"
|
||||||
|
style={{
|
||||||
|
transform: "translate(50%, -50%)",
|
||||||
|
borderRadius: "50%",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "1",
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
setFormData((prev) => ({ ...prev, item_image: null }))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{errors.item_image && <div className="invalid-feedback d-block">{errors.item_image}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<button type="submit" className="btn btn-bg-theme">
|
||||||
|
{editMode ? "Update" : "Submit"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Delete Confirmation */}
|
||||||
|
{deleteConfirm.show && (
|
||||||
|
<div className="modal fade show" style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="d-flex justify-content-between mb-1">
|
||||||
|
<h6 className="text-lg mb-0">Confirm Delete</h6>
|
||||||
|
<button type="button" className="btn-close" onClick={() => setDeleteConfirm({ show: false, id: null })}></button>
|
||||||
|
</div>
|
||||||
|
<p className="mb-0">Are you sure you want to delete this item?</p>
|
||||||
|
<div className="d-flex justify-content-end gap-2 mt-1">
|
||||||
|
<button className="btn btn-outline-danger px-14 py-6 text-sm" onClick={() => setDeleteConfirm({ show: false, id: null })}>Cancel</button>
|
||||||
|
<button className="btn btn-danger px-14 py-6 text-sm" onClick={handleDelete}>Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SidesPage = () => {
|
||||||
|
return (
|
||||||
|
<MasterLayout>
|
||||||
|
<Breadcrumb title="Sides Management" />
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<SidesPageInner />
|
||||||
|
</Suspense>
|
||||||
|
</MasterLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidesPage;
|
||||||
@ -33,73 +33,155 @@ const MenuItemsCategory = () => {
|
|||||||
const [orderStatus, setOrderStatus] = useState('pending');
|
const [orderStatus, setOrderStatus] = useState('pending');
|
||||||
const HST_TAX_RATE = 0.13; // 13% HST tax rate
|
const HST_TAX_RATE = 0.13; // 13% HST tax rate
|
||||||
|
|
||||||
|
const [catMenu, setCatMenu] = useState(null);
|
||||||
|
const [catMenuActive, setCatMenuActive] = useState(null)
|
||||||
|
const [selectedItem, setSelectedItem] = useState(null);
|
||||||
|
const [showItemModal, setShowItemModal] = useState(false);
|
||||||
|
const [selectedExtras, setSelectedExtras] = useState([]);
|
||||||
|
const [selectedDrink, setSelectedDrink] = useState(null);
|
||||||
|
const [orderItems, setOrderItems] = useState([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
getMenuItem();
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (menuName) {
|
const getMenuItem = async () => {
|
||||||
getMenuItems();
|
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);
|
||||||
}
|
}
|
||||||
}, [menuName]);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (menuData) {
|
if (catMenu?.length > 0) {
|
||||||
getMenuFoodItems(menuData)
|
getMenuItems(catMenu[0]?.menuname); // auto-load first menu's categories
|
||||||
setActiveCategory(menuData[0]?.name)
|
|
||||||
}
|
}
|
||||||
}, [menuData])
|
}, [catMenu]);
|
||||||
|
|
||||||
const getMenuItems = async () => {
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (menuData?.length > 0) {
|
||||||
|
getMenuFoodItems(menuData[0]);
|
||||||
|
setActiveCategory(menuData[0]?.name);
|
||||||
|
}
|
||||||
|
}, [menuData]);
|
||||||
|
|
||||||
|
const getMenuItems = async (menuname) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Fetch floor data using name
|
const menuRes = await client.get(
|
||||||
const menuRes = await client.get(`/Dine360%20Menu%20Category%20Link?fields=[\"*\"]&limit_page_length=100&filters=[["menu","=","${menuName}"]]`);
|
`/Dine360%20Menu%20Category%20Link?fields=["*"]&limit_page_length=100&filters=[["menu","=","${menuname}"]]`
|
||||||
console.log("menuRes", menuRes?.data?.data);
|
);
|
||||||
|
|
||||||
// Get menu categories based on the menucategory from menuRes
|
const menuLinks = menuRes?.data?.data || [];
|
||||||
const menuCategories = menuRes?.data?.data || [];
|
|
||||||
const menuCategoryPromises = menuCategories.map(async (menuItem) => {
|
const menuCategoryPromises = menuLinks.map(async (menuItem) => {
|
||||||
const menuCategoryRes = await client.get(`/Dine360%20Menu%20Category?fields=[\"*\"]&limit_page_length=100&filters=[["name","=","${menuItem.menucategory}"]]`);
|
const res = await client.get(
|
||||||
console.log("menuCategoryRes", menuCategoryRes?.data?.data);
|
`/Dine360%20Menu%20Category?fields=["*"]&limit_page_length=100&filters=[["name","=","${menuItem.menucategory}"]]`
|
||||||
return menuCategoryRes?.data?.data?.[0] || null;
|
);
|
||||||
|
return res?.data?.data?.[0] || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuCategoryResults = await Promise.all(menuCategoryPromises);
|
const categories = await Promise.all(menuCategoryPromises);
|
||||||
const filteredMenuCategories = menuCategoryResults.filter(category => category !== null);
|
const validCategories = categories.filter(Boolean);
|
||||||
|
|
||||||
setMenuData(filteredMenuCategories);
|
setMenuData(validCategories);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching data:", error);
|
console.error("Error fetching menu categories:", error);
|
||||||
setError(error?.message || "Failed to fetch floor data");
|
setError(error?.message || "Failed to fetch menu categories");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMenuFoodItems = (async (menuData) => {
|
const getMenuFoodItems = async (category) => {
|
||||||
console.log("menuDataaaa", menuData)
|
if (!category?.name) return;
|
||||||
try {
|
try {
|
||||||
const res = await client.get(`/Dine360%20Menu%20Category/${menuData[0].name}?fields=[\"*\"]&limit_page_length=100`);
|
const res = await client.get(
|
||||||
console.log("res", res)
|
`/Dine360%20Menu%20Category/${category.name}?fields=["*"]&limit_page_length=100`
|
||||||
setMenuItems(res?.data?.data)
|
);
|
||||||
|
setMenuItems(res?.data?.data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error)
|
console.error("Error fetching menu items:", error);
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleMenuClick = async (menu) => {
|
|
||||||
try {
|
|
||||||
const menuItemsRes = await client.get(`/Dine360%20Menu%20Category/${menu.name}?fields=[\"*\"]&limit_page_length=100`);
|
|
||||||
console.log("menuItemsRes", menuItemsRes?.data?.data);
|
|
||||||
setMenuItems(menuItemsRes?.data?.data);
|
|
||||||
setActiveCategory(menu?.name);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching data:", error);
|
|
||||||
setError(error?.message || "Failed to fetch floor data");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch sides linked to a specific food item
|
||||||
|
const getLinkedSides = async (foodItemName) => {
|
||||||
|
try {
|
||||||
|
const res = await client.get(
|
||||||
|
`/Dine360 FoodItem Sides Link?fields=["*"]&filters=[["menuitemname","=","${foodItemName}"]]`
|
||||||
|
);
|
||||||
|
return res.data.data; // Sides linked to this menu item
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching linked sides for item: ${foodItemName}`, error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch and group all sides by their category
|
||||||
|
const getAllSidesGroupedByCategory = async (foodItemName) => {
|
||||||
|
try {
|
||||||
|
const res = await client.get(`/Dine360 FoodItem Sides Link?fields=["*"]&filters=[["menuitemname","=","${foodItemName}"]]`);
|
||||||
|
console.log("Grouped Sides:", res.data.data);
|
||||||
|
|
||||||
|
const SliderLink = res?.data?.data || [];
|
||||||
|
|
||||||
|
const SidesList = SliderLink.map(async (sideItem) => {
|
||||||
|
const res = await client.get(
|
||||||
|
`/Dine360 Food Sides?fields=["*"]&filters=[["name","=","${sideItem.menucategory}"]]`
|
||||||
|
);
|
||||||
|
return res?.data?.data?.[0] || null;
|
||||||
|
});
|
||||||
|
const categories = await Promise.all(SidesList);
|
||||||
|
const validSides = categories.filter(Boolean);
|
||||||
|
console.log("validSides", validSides)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching grouped sides:", err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddToOrder = () => {
|
||||||
|
const cartItem = cart.find(item => item.name === selectedItem.name);
|
||||||
|
|
||||||
|
if (!cartItem) {
|
||||||
|
alert("Please add the item to cart before adding to order.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrderItems(cart);
|
||||||
|
setShowItemModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// For user clicking category from catMenu
|
||||||
|
const handleCatMenuClick = async (menuname) => {
|
||||||
|
await getMenuItems(menuname);
|
||||||
|
setCatMenuActive(menuname)
|
||||||
|
};
|
||||||
|
|
||||||
|
// For user clicking individual menu category
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
console.log("menuItems", menuItems);
|
console.log("menuItems", menuItems);
|
||||||
const addToCart = (item) => {
|
const addToCart = (item) => {
|
||||||
const existingItem = cart.find(cartItem => cartItem.name === item.name);
|
const existingItem = cart.find(cartItem => cartItem.name === item.name);
|
||||||
@ -112,24 +194,40 @@ const MenuItemsCategory = () => {
|
|||||||
} else {
|
} else {
|
||||||
setCart([...cart, { ...item, quantity: 1 }]);
|
setCart([...cart, { ...item, quantity: 1 }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFromCart = (itemName) => {
|
const removeFromCart = (itemName) => {
|
||||||
setCart(cart.filter(item => item.name !== itemName));
|
setCart(prev => prev.filter(item => item.name !== itemName));
|
||||||
|
setOrderItems(prev => prev.filter(item => item.name !== itemName));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateQuantity = (itemName, newQuantity) => {
|
const updateQuantity = (itemName, newQuantity) => {
|
||||||
if (newQuantity < 1) {
|
if (newQuantity < 1) {
|
||||||
removeFromCart(itemName);
|
removeItem(itemName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCart(cart.map(item =>
|
|
||||||
|
// Update cart
|
||||||
|
setCart(prev =>
|
||||||
|
prev.map(item =>
|
||||||
item.name === itemName
|
item.name === itemName
|
||||||
? { ...item, quantity: newQuantity }
|
? { ...item, quantity: newQuantity }
|
||||||
: item
|
: item
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update orderItems
|
||||||
|
setOrderItems(prev =>
|
||||||
|
prev.map(item =>
|
||||||
|
item.name === itemName
|
||||||
|
? { ...item, quantity: newQuantity }
|
||||||
|
: item
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const calculateSubtotal = () => {
|
const calculateSubtotal = () => {
|
||||||
return cart.reduce((total, item) => total + (item.price * item.quantity), 0);
|
return cart.reduce((total, item) => total + (item.price * item.quantity), 0);
|
||||||
};
|
};
|
||||||
@ -146,7 +244,19 @@ const MenuItemsCategory = () => {
|
|||||||
setShowOrderModal(true);
|
setShowOrderModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log
|
// Toggle extras like checkbox buttons
|
||||||
|
const toggleExtra = (extra) => {
|
||||||
|
setSelectedExtras(prev =>
|
||||||
|
prev.includes(extra)
|
||||||
|
? prev.filter(item => item !== extra)
|
||||||
|
: [...prev, extra]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select one drink at a time
|
||||||
|
const selectDrink = (drink) => {
|
||||||
|
setSelectedDrink(drink === selectedDrink ? null : drink);
|
||||||
|
};
|
||||||
|
|
||||||
const confirmOrder = async () => {
|
const confirmOrder = async () => {
|
||||||
// Get current time in YYYY-MM-DD HH:mm:ss format (Canada Eastern Time)
|
// Get current time in YYYY-MM-DD HH:mm:ss format (Canada Eastern Time)
|
||||||
@ -164,7 +274,7 @@ const MenuItemsCategory = () => {
|
|||||||
console.log(orderStartTime);
|
console.log(orderStartTime);
|
||||||
|
|
||||||
// Prepare formatted order data
|
// Prepare formatted order data
|
||||||
const formattedOrder = cart.map((item) => ({
|
const formattedOrder = orderItems.map((item) => ({
|
||||||
menuitem: item.name,
|
menuitem: item.name,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
rate: item.price,
|
rate: item.price,
|
||||||
@ -212,21 +322,36 @@ const MenuItemsCategory = () => {
|
|||||||
|
|
||||||
|
|
||||||
const renderMenuItem = (menu) => {
|
const renderMenuItem = (menu) => {
|
||||||
|
console.log("menu", menu)
|
||||||
const cartItem = cart.find(item => item.name === menu.name);
|
const cartItem = cart.find(item => item.name === menu.name);
|
||||||
const isInCart = !!cartItem;
|
const isInCart = !!cartItem;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={menu.name} className="col-xxl-3 col-md-6 user-grid-card">
|
<div key={menu.name} className="col-xxl-3 col-md-6 user-grid-card">
|
||||||
<div className="position-relative border bg-tb-lilac radius-16 overflow-hidden">
|
<div className="position-relative border radius-16 overflow-hidden">
|
||||||
<div className="p-16 text-center">
|
<div className="card cursor-pointer" onClick={() => {
|
||||||
|
setSelectedItem(menu);
|
||||||
|
getAllSidesGroupedByCategory(menu?.name)
|
||||||
|
setShowItemModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="card-body text-center p-0">
|
||||||
<img
|
<img
|
||||||
src={menu.profileImg}
|
src={menu.profileImg}
|
||||||
alt=""
|
alt=""
|
||||||
className="border br-white border-width-2-px w-100-px h-100-px rounded-circle object-fit-cover"
|
className="border br-white border-width-2-px w-100-px h-100-px rounded-circle object-fit-cover"
|
||||||
/>
|
/>
|
||||||
<h6 className="text-md mb-0 mt-3">{menu.menuitemname}</h6>
|
{/* <div className="w-100 max-h-150-px radius-0 overflow-hidden" >
|
||||||
<span className="text-secondary-light text-sm mb-16">${menu.price.toFixed(2)}</span>
|
<img alt="" className="w-100 h-150-px object-fit-cover" src="/assets/images/blog/blog5.png" style={{ height: "150px" }} />
|
||||||
{isInCart ? (
|
</div> */}
|
||||||
|
<div className="p-3 pt-0">
|
||||||
|
<h6 className="text-lg mb-1 mt-1">{menu.menuitemname}</h6>
|
||||||
|
<span className="text-secondary-light text-sm lh-sm mb-1">{menu.parent}</span>
|
||||||
|
<h6 className="text-md mb-0 mt-1">${menu.price.toFixed(2)}</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* {isInCart ? (
|
||||||
<div className="d-flex align-items-center justify-content-center gap-2">
|
<div className="d-flex align-items-center justify-content-center gap-2">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-outline-danger-500"
|
className="btn btn-sm btn-outline-danger-500"
|
||||||
@ -249,7 +374,8 @@ const MenuItemsCategory = () => {
|
|||||||
>
|
>
|
||||||
Add to Cart
|
Add to Cart
|
||||||
</button>
|
</button>
|
||||||
)}
|
)} */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -257,12 +383,18 @@ const MenuItemsCategory = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const cartItem = cart.find(item => item.name === selectedItem.name);
|
||||||
|
const isInCart = !!cartItem;
|
||||||
|
const quantity = cartItem?.quantity || 1;
|
||||||
|
|
||||||
|
console.log("catMenuActive", catMenuActive)
|
||||||
|
console.log("cartItem?.quantity", cartItem)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MasterLayout>
|
<MasterLayout>
|
||||||
<Breadcrumb title={`Menu Items`} />
|
<Breadcrumb title={`Menu Items`} />
|
||||||
|
|
||||||
<div className="container-fluid">
|
<div className="container-fluid" style={{ marginBottom: "100px" }}>
|
||||||
|
|
||||||
{
|
{
|
||||||
loading ? (
|
loading ? (
|
||||||
@ -273,12 +405,42 @@ const MenuItemsCategory = () => {
|
|||||||
<div className="row gy-4">
|
<div className="row gy-4">
|
||||||
{/* Menu Category - Always col-xxl-2 */}
|
{/* Menu Category - Always col-xxl-2 */}
|
||||||
<div className="col-xxl-2">
|
<div className="col-xxl-2">
|
||||||
|
<div className="d-inline-block w-100">
|
||||||
|
|
||||||
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
||||||
<div className="card-body p-24">
|
<div className="card-body p-24">
|
||||||
<ul className="d-flex flex-column gap-2">
|
<ul className="d-flex flex-column gap-2">
|
||||||
|
{catMenu.map((menu) => (
|
||||||
|
<li
|
||||||
|
className={`nav-item border rounded-2 px-3 py-3 bg-border-theme d-flex align-items-center gap-3 ${catMenuActive === menu.menuname ? "bg-theme " : ""
|
||||||
|
}`}
|
||||||
|
role="presentation"
|
||||||
|
key={menu?.name}
|
||||||
|
onClick={() => handleCatMenuClick(menu?.name)}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/assets/images/menu/menu-icons/all-menu.png"
|
||||||
|
alt="all menu"
|
||||||
|
className="w-28-px h-28-px"
|
||||||
|
/>
|
||||||
|
<span className="line-height-1">{menu?.menuname}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Menu Items - col-xxl-10 if cart is empty, else col-xxl-7 */}
|
||||||
|
<div className="col-xxl-7">
|
||||||
|
<div className="card p-0 overflow-hidden position-relative radius-12 mb-3">
|
||||||
|
<div className="card-body p-24">
|
||||||
|
<ul className="d-flex gap-2 mb-0">
|
||||||
{menuData.map((menu) => (
|
{menuData.map((menu) => (
|
||||||
<li
|
<li
|
||||||
className={`nav-item border rounded-2 px-3 py-3 bg-tb-lilac d-flex align-items-center gap-3 ${activeCategory === menu.name ? "bg-danger-500 text-white" : ""
|
className={`nav-item border rounded-2 px-3 py-1 bg-outline-theme d-flex align-items-center gap-3 ${activeCategory === menu.name ? "bg-theme" : ""
|
||||||
}`}
|
}`}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
key={menu?.name}
|
key={menu?.name}
|
||||||
@ -296,27 +458,83 @@ const MenuItemsCategory = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Menu Items - col-xxl-10 if cart is empty, else col-xxl-7 */}
|
|
||||||
<div className={cart.length === 0 ? "col-xxl-10" : "col-xxl-7"}>
|
|
||||||
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
|
||||||
<div className="card-body p-24">
|
|
||||||
<div className="row gy-4">
|
<div className="row gy-4">
|
||||||
{menuItems?.menuitems_child?.map(renderMenuItem)}
|
{menuItems?.menuitems_child?.map(renderMenuItem)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Cart - Show only if cart has items */}
|
{/* Cart - Show only if cart has items */}
|
||||||
{cart.length > 0 && (
|
{/* {cart.length > 0 && ( */}
|
||||||
<div className="col-xxl-3">
|
<div className="col-xxl-3">
|
||||||
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
||||||
<div className="card-body p-24">
|
<div className="card-body p-24">
|
||||||
<h6 className="text-lg mb-3">Order Details</h6>
|
<h6 className="text-lg mb-3">POS Dine-in</h6>
|
||||||
{cart.map((item) => (
|
<div className="d-flex flex-wrap align-items-center gap-4 mb-3">
|
||||||
<div key={item.name} className="card bg-tb-lilac mb-3 border">
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
||||||
|
>
|
||||||
|
Items
|
||||||
|
<span className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary-600 border border-white">
|
||||||
|
{orderItems.length}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-warning-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
||||||
|
>
|
||||||
|
Table Name
|
||||||
|
{/* <span className="position-absolute top-0 end-0 translate-middle-y badge rounded-pill bg-danger-600">
|
||||||
|
99+
|
||||||
|
</span> */}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-success-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
||||||
|
>
|
||||||
|
Members
|
||||||
|
<span className="position-absolute top-0 end-0 translate-middle-y badge rounded-pill bg-success-600 border border-white ">
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="mb-1 border-bottom pb-3 mb-3">
|
||||||
|
<ul className="list-group radius-8">
|
||||||
|
<li className="list-group-item d-flex align-items-center justify-content-between border text-secondary-light p-6 bg-base border-bottom-0">
|
||||||
|
<div className="d-flex text-sm align-items-center gap-2">
|
||||||
|
<img className="w-20-px h-20-px rounded-circle" alt="" src="/assets/images/lists/list-img1.png" />
|
||||||
|
Contact Number
|
||||||
|
</div>
|
||||||
|
<span className="text-xs bg-success-100 text-success-600 radius-4 px-10 py-2 fw-semibold cursor-pointer" data-bs-toggle="tooltip" title="This is the contact number">Show</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li className="list-group-item d-flex align-items-center justify-content-between border text-secondary-light p-6 bg-base">
|
||||||
|
<div className="d-flex align-items-center text-sm gap-2">
|
||||||
|
<img className="w-20-px h-20-px rounded-circle" alt="" src="/assets/images/lists/list-img2.png" />
|
||||||
|
Customer Info
|
||||||
|
</div>
|
||||||
|
<span className="text-xs bg-success-100 text-success-600 radius-4 px-10 py-2 fw-semibold cursor-pointer" data-bs-toggle="tooltip" title="Customer details here">Show</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{orderItems.length === 0 ? (
|
||||||
|
<p className="text-muted">Your cart is empty.</p>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{orderItems.map((item) => (
|
||||||
|
<div key={item.name} className="card bg-theme mb-3 border">
|
||||||
<div className="card-body p-3">
|
<div className="card-body p-3">
|
||||||
<div className="d-flex align-items-center gap-3">
|
<div className="d-flex align-items-center gap-3">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
@ -343,7 +561,7 @@ const MenuItemsCategory = () => {
|
|||||||
<div className="text-end mb-2">
|
<div className="text-end mb-2">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-link text-danger-500 p-0 ms-2"
|
className="btn btn-sm btn-link text-danger-500 p-0 ms-2"
|
||||||
onClick={() => removeFromCart(item.id)}
|
onClick={() => removeFromCart(item.name)}
|
||||||
>
|
>
|
||||||
<Icon icon="mdi:delete" className="fs-5" />
|
<Icon icon="mdi:delete" className="fs-5" />
|
||||||
</button>
|
</button>
|
||||||
@ -373,22 +591,160 @@ const MenuItemsCategory = () => {
|
|||||||
|
|
||||||
<div className="order-summary border-top pt-3">
|
<div className="order-summary border-top pt-3">
|
||||||
<button
|
<button
|
||||||
className="btn bg-danger-500 text-white w-100"
|
className="btn btn-bg-theme w-100"
|
||||||
onClick={handlePlaceOrder}
|
onClick={handlePlaceOrder}
|
||||||
>
|
>
|
||||||
Place Order
|
Place Order
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* )} */}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{showItemModal && selectedItem && (
|
||||||
|
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className="modal-backdrop fade show"
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0, left: 0, right: 0, bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
|
zIndex: 1040
|
||||||
|
}}
|
||||||
|
onClick={() => setShowItemModal(false)}
|
||||||
|
/>
|
||||||
|
<div className="modal fade show" style={{ display: 'block', zIndex: 1050 }}>
|
||||||
|
<div className="modal-dialog modal-xl modal-dialog-centered">
|
||||||
|
<div className="modal-content radius-12 overflow-hidden">
|
||||||
|
<div className="modal-header border-0">
|
||||||
|
<h6 className="modal-title text-lg">{selectedItem.menuitemname}</h6>
|
||||||
|
<button type="button" className="btn-close" onClick={() => setShowItemModal(false)} />
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="row gy-3">
|
||||||
|
{/* Col 1 - Image */}
|
||||||
|
<div className="col-md-4">
|
||||||
|
<img
|
||||||
|
src="https://images.immediate.co.uk/production/volatile/sites/30/2020/08/chorizo-mozarella-gnocchi-bake-cropped-9ab73a3.jpg?quality=90&resize=700,636"
|
||||||
|
alt={selectedItem.menuitemname}
|
||||||
|
className="w-100 rounded"
|
||||||
|
style={{ objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Col 2 - Size + Quantity */}
|
||||||
|
<div className="col-md-4">
|
||||||
|
<h6 className="mb-2 text-lg">Choose Size</h6>
|
||||||
|
<select className="form-select mb-3">
|
||||||
|
<option>Regular</option>
|
||||||
|
<option>Large</option>
|
||||||
|
<option>Family</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<h6 className="mb-1 text-lg">Quantity</h6>
|
||||||
|
{isInCart ? (
|
||||||
|
<div className="d-flex align-items-center gap-2">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-danger-500"
|
||||||
|
onClick={() => updateQuantity(selectedItem.name, cartItem.quantity - 1)}
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<span className="mx-2 border border-white px-3 py-1 radius-6">{cartItem.quantity}</span>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-success-500"
|
||||||
|
onClick={() => updateQuantity(selectedItem.name, cartItem.quantity + 1)}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => addToCart(selectedItem)}
|
||||||
|
className="btn-bg-theme text-white p-10 text-sm btn-sm px-14 py-12 radius-8 d-flex align-items-center justify-content-center mt-2 fw-medium gap-2 w-100"
|
||||||
|
>
|
||||||
|
Add to Cart
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Col 3 - Extras */}
|
||||||
|
{/* Col 3 - Extras + Order Button + Total */}
|
||||||
|
<div className="col-md-4">
|
||||||
|
|
||||||
|
<h6 className="mb-2 text-lg">Add Extras</h6>
|
||||||
|
<div className="d-flex flex-wrap gap-3 mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn text-sm btn-sm px-14 py-6 radius-8 d-flex justify-content-between align-items-center gap-2 border ${selectedExtras.includes('cheese') ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
||||||
|
onClick={() => toggleExtra('cheese')}
|
||||||
|
>
|
||||||
|
<span>Extra Cheese</span>
|
||||||
|
<span>$1.50</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center gap-2 border ${selectedExtras.includes('sauce') ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
||||||
|
onClick={() => toggleExtra('sauce')}
|
||||||
|
>
|
||||||
|
<span>Spicy Sauce</span>
|
||||||
|
<span>$0.75</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6 className="mb-1 text-lg">Drinks</h6>
|
||||||
|
<div className="d-flex flex-column gap-2 mb-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center border ${selectedDrink === 'coke' ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
||||||
|
onClick={() => selectDrink('coke')}
|
||||||
|
>
|
||||||
|
<span>Coke</span>
|
||||||
|
<span>$2.00</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center border ${selectedDrink === 'pepsi' ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
||||||
|
onClick={() => selectDrink('pepsi')}
|
||||||
|
>
|
||||||
|
<span>Pepsi</span>
|
||||||
|
<span>$2.00</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="bg-success-100 p-3 mb-3 mt-3 rounded">
|
||||||
|
<h6 className="mb-2">Total: <span className="text-success-500">$12.99</span></h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="btn btn-bg-theme w-100 mb-3 text-white" onClick={handleAddToOrder}>
|
||||||
|
Add to Order
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*
|
||||||
|
<div className="modal-footer border-0">
|
||||||
|
<button className="btn btn-danger-500 w-100 text-white">Add to Order</button>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* Order Confirmation Modal */}
|
{/* Order Confirmation Modal */}
|
||||||
{
|
{
|
||||||
showOrderModal && (
|
showOrderModal && (
|
||||||
|
|||||||
@ -158,7 +158,7 @@ const TableOrderInner = () => {
|
|||||||
|
|
||||||
// Push the route with formatted timestamp
|
// Push the route with formatted timestamp
|
||||||
router.push(
|
router.push(
|
||||||
`/waiter/menu-category?restaurantbranch=${restaruntBranch}&table=${encodeURIComponent(selectedTable.name)}&seats=${seatCount}&time=${encodeURIComponent(formattedTime)}`
|
`/waiter/menu-items?restaurantbranch=${restaruntBranch}&table=${encodeURIComponent(selectedTable.name)}&seats=${seatCount}&time=${encodeURIComponent(formattedTime)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -525,7 +525,7 @@ const TableOrderInner = () => {
|
|||||||
<div className="modal-dialog modal-dialog-centered">
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
<div className="modal-header border-0 pb-0">
|
<div className="modal-header border-0 pb-0">
|
||||||
<h6 className="modal-title text-lg">Enter Seat Count</h6>
|
<h6 className="modal-title text-lg">Enter Seat Count - {selectedTable.tablename}</h6>
|
||||||
<button type="button" className="btn-close" onClick={() => setShowModalTable(false)}></button>
|
<button type="button" className="btn-close" onClick={() => setShowModalTable(false)}></button>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
@ -552,8 +552,8 @@ const TableOrderInner = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-footer border-0 pt-0">
|
<div className="modal-footer border-0 pt-0">
|
||||||
<button type="button" className="btn btn-sm btn-outline-danger" onClick={() => setShowModalTable(false)}>Cancel</button>
|
<button type="button" className="btn btn-sm btn-outline-theme" onClick={() => setShowModalTable(false)}>Cancel</button>
|
||||||
<button type="button" className="btn btn-sm btn-danger-500" onClick={handleSeatSubmit}>Proceed to Menu</button>
|
<button type="button" className="btn btn-sm btn-bg-theme" onClick={handleSeatSubmit}>Proceed to Menu</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
323
src/components/admin/SidesCategoryComponent.jsx
Normal file
323
src/components/admin/SidesCategoryComponent.jsx
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { gradientClasses } from "../../../utils/constant.utils";
|
||||||
|
import client from "../../../Auth";
|
||||||
|
import { Icon } from "@iconify/react";
|
||||||
|
import PageNoData from "../common-component/PageNoData";
|
||||||
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
const SidesCategoryComponent = ({ sidesCategoryData, getSidesCategory }) => {
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams();
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
|
const [selectedSidesCategoryId, setSelectedSidesCategoryId] = useState(null);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
sidesCategoryname: "",
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = 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]);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const newErrors = {};
|
||||||
|
Object.entries(formData).forEach(([key, value]) => {
|
||||||
|
if (!value.trim()) newErrors[key] = `${key} is required`;
|
||||||
|
});
|
||||||
|
setErrors(newErrors);
|
||||||
|
if (Object.keys(newErrors).length > 0) return;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
// ...(isEditMode && { name: selectedFloorId }), // only adds `name` if editing
|
||||||
|
sidecategoryname: formData?.sidesCategoryname,
|
||||||
|
description: formData?.description,
|
||||||
|
restaurantbranch: restaruntBranch
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
if (isEditMode) {
|
||||||
|
await client.put(`/Dine360%20Food%20Sides%20Category/${selectedSidesCategoryId}`, body);
|
||||||
|
} else {
|
||||||
|
await client.post(`/Dine360%20Food%20Sides%20Category`, body);
|
||||||
|
}
|
||||||
|
getSidesCategory();
|
||||||
|
resetForm();
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error?.response?.data?.exception?.includes("DuplicateEntryError") ||
|
||||||
|
error?.response?.data?.message?.includes("Duplicate entry")
|
||||||
|
) {
|
||||||
|
alert("SidesCategory with this name already exists. Please use a different name.");
|
||||||
|
} else if (
|
||||||
|
error?.response?.data?.exception?.includes("UniqueValidationError") ||
|
||||||
|
error?.response?.data?.message?.includes("UniqueValidationError entry")
|
||||||
|
) {
|
||||||
|
alert("SidesCategory with this name already exists. Please use a different name.");
|
||||||
|
} else {
|
||||||
|
alert("An error occurred. Please try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
setFormData({
|
||||||
|
sidesCategoryname: "", description: "",
|
||||||
|
});
|
||||||
|
setShowModal(false);
|
||||||
|
setIsEditMode(false);
|
||||||
|
setSelectedSidesCategoryId(null);
|
||||||
|
setErrors({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (sidesCategory) => {
|
||||||
|
setIsEditMode(true);
|
||||||
|
setSelectedSidesCategoryId(sidesCategory.name);
|
||||||
|
setFormData({
|
||||||
|
sidesCategoryname: sidesCategory.sidesCategoryname || "",
|
||||||
|
description: sidesCategory.description || "",
|
||||||
|
});
|
||||||
|
setShowModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
try {
|
||||||
|
await client.delete(`/Dine360%20Food%20Sides%20Category/${deleteConfirm.id}`);
|
||||||
|
setDeleteConfirm({ show: false, id: null });
|
||||||
|
getSidesCategory();
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error?.response?.data?.exception?.includes("DuplicateEntryError") ||
|
||||||
|
error?.response?.data?.message?.includes("Duplicate entry")
|
||||||
|
) {
|
||||||
|
alert("SidesCategory with this 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 SidesCategory three is linked with Dine360 Room ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid" style={{ marginBottom: "100px" }}>
|
||||||
|
<div className="card h-100 p-0 radius-12">
|
||||||
|
<div className="card-body p-3 p-lg-5">
|
||||||
|
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h6 className="mb-0">SidesCategory</h6>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-bg-theme radius-8 px-20 py-11"
|
||||||
|
onClick={() => setShowModal(true)}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row gy-3 gx-3 gx-lg-5 gy-lg-5 justify-content-center">
|
||||||
|
{
|
||||||
|
sidesCategoryData?.lenght === 0 ? (
|
||||||
|
<PageNoData />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{sidesCategoryData.map((sidesCategory, index) => {
|
||||||
|
const gradientClass = gradientClasses[index % gradientClasses.length];
|
||||||
|
return (
|
||||||
|
<div className="col-xxl-3 col-lg-4 col-sm-6" key={sidesCategory.name}>
|
||||||
|
<div className={`card p-3 shadow-2 radius-8 h-100 bg-theme border border-white position-relative`}>
|
||||||
|
|
||||||
|
{/* Top-right action buttons */}
|
||||||
|
<div className="position-absolute top-0 end-0 me-1 mt-1 ">
|
||||||
|
<div className='dropdown'>
|
||||||
|
<button
|
||||||
|
className='btn px-1 py-1 d-flex align-items-center text-primary-light'
|
||||||
|
type='button'
|
||||||
|
data-bs-toggle='dropdown'
|
||||||
|
aria-expanded='false'
|
||||||
|
>
|
||||||
|
<Icon icon='entypo:dots-three-vertical' className='menu-icon' />
|
||||||
|
</button>
|
||||||
|
<ul className='dropdown-menu'>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
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();
|
||||||
|
handleEdit(sidesCategory);
|
||||||
|
}}
|
||||||
|
> <Icon icon='lucide:edit' className='menu-icon' />
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
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();
|
||||||
|
setDeleteConfirm({ show: true, id: sidesCategory.name });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon='fluent:delete-24-regular'
|
||||||
|
className='menu-icon'
|
||||||
|
/>
|
||||||
|
Delete
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Centered content */}
|
||||||
|
<Link href={`/admin/pos/sides?restaurantbranch=${restaruntBranch}&sidescategoryname=${sidesCategory.name}`} className="text-decoration-none text-dark">
|
||||||
|
<div className="card-body text-center d-flex flex-column justify-content-center align-items-center p-0 h-100">
|
||||||
|
<h6 className="text-lg mb-0">{sidesCategory.sidecategoryname}</h6>
|
||||||
|
<p className="mb-0 text-sm">{sidesCategory.description}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Create/Edit Modal */}
|
||||||
|
{showModal && (
|
||||||
|
<div className="modal fade show" style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-header border-0 pb-0">
|
||||||
|
<h6 className="modal-title text-lg">
|
||||||
|
{isEditMode ? "Edit SidesCategory" : "Create SidesCategory"}
|
||||||
|
</h6>
|
||||||
|
<button type="button" className="btn-close" onClick={resetForm}></button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">SidesCategory Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={`form-control ${errors.sidesCategoryname ? "is-invalid" : ""}`}
|
||||||
|
name="sidesCategoryname"
|
||||||
|
value={formData.sidesCategoryname}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{errors.sidesCategoryname && (
|
||||||
|
<div className="invalid-feedback">{errors.sidesCategoryname}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="form-label">Description</label>
|
||||||
|
<textarea
|
||||||
|
className={`form-control ${errors.description ? "is-invalid" : ""}`}
|
||||||
|
name="description"
|
||||||
|
value={formData.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
{errors.description && (
|
||||||
|
<div className="invalid-feedback">{errors.description}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <div className="mb-3">
|
||||||
|
<label className="form-label">Branch</label>
|
||||||
|
<select
|
||||||
|
className={`form-select ${errors.branch ? "is-invalid" : ""}`}
|
||||||
|
name="branch"
|
||||||
|
value={formData.branch}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<option value="">Select Branch</option>
|
||||||
|
{branchData.map((branch) => (
|
||||||
|
<option key={branch.name} value={branch.name}>
|
||||||
|
{branch.branch}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{errors.branch && (
|
||||||
|
<div className="invalid-feedback">{errors.branch}</div>
|
||||||
|
)}
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
|
<button type="submit" className="btn btn-bg-theme">
|
||||||
|
{isEditMode ? "Update" : "Submit"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Delete Confirmation Modal */}
|
||||||
|
{deleteConfirm.show && (
|
||||||
|
<div className="modal fade show" style={{ display: "block", backgroundColor: "rgba(0,0,0,0.5)" }}>
|
||||||
|
<div className="modal-dialog modal-dialog-centered">
|
||||||
|
<div className="modal-content">
|
||||||
|
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="d-flex justify-content-between mb-1">
|
||||||
|
<h6 className="text-lg mb-0">Confirm Delete</h6>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-close"
|
||||||
|
onClick={() => setDeleteConfirm({ show: false, id: null })}
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
<p className="m-0">Are you sure you want to delete this sidesCategory?</p>
|
||||||
|
<div className="d-flex justify-content-end gap-2 mt-1 ">
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-danger px-14 py-6 text-sm"
|
||||||
|
onClick={() => setDeleteConfirm({ show: false, id: null })}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-danger px-14 py-6 text-sm" onClick={handleDelete}>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidesCategoryComponent;
|
||||||
Loading…
x
Reference in New Issue
Block a user