diff --git a/package.json b/package.json index 497456f..18a472f 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "react-dom": "^18.0.0", "react-draft-wysiwyg": "^1.15.0", "react-hot-toast": "^2.4.0", + "react-modal": "^3.16.1", "react-qr-code": "^2.0.11", "react-quill": "^2.0.0", "react-redux": "^7.2.9", diff --git a/public/index.html b/public/index.html index 1d602d2..ca6a3bf 100644 --- a/public/index.html +++ b/public/index.html @@ -7,37 +7,26 @@ * License MIT --> - - - - - - - - Smellika Admin - - - - - + + + + - - + - + - - - + + + - - -
- - - - + + + + + \ No newline at end of file diff --git a/src/_nav.js b/src/_nav.js index b0c36b0..ca0faf1 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -152,6 +152,18 @@ const _nav = [ icon: , to: "/orders/cancelled", }, + { + component: CNavItem, + name: "In Store Cash Orders", + icon: , + to: "/inStoreCashOrders/new", + }, + { + component: CNavItem, + name: "In Store QRCode Orders", + icon: , + to: "/InStoreQRCodeOrders/new", + }, ], }, { @@ -315,7 +327,13 @@ const _nav = [ icon: , to: "/employee", }, - +//Point of Sale start +{ + component: CNavItem, + name: "Point of Sale", + icon: , + to: "/pos", +}, // { // component: CNavGroup, // name: "Blog", diff --git a/src/index.js b/src/index.js index 466639c..3e4516a 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,7 @@ import { cibGmail } from "@coreui/icons"; import { createRoot } from "react-dom/client"; const setupAxios = () => { - //axios.defaults.baseURL = "http://localhost:5000"; + // axios.defaults.baseURL = "http://localhost:5000"; axios.defaults.baseURL = "https://api.smellika.com"; axios.defaults.headers = { diff --git a/src/routes.js b/src/routes.js index 4f3aeb7..6c8307a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -137,6 +137,10 @@ import { element } from "prop-types"; import OrderdayChart from "./views/Charts/OrderDaywise"; import RevenueCharts from "./views/Charts/RevenueCharts"; import AddCustomer from "./views/customerDetails/addCustomer"; +import Pos from "./views/PointOfSale/Pos"; +import InStoreCashOrders from "./views/orders/InStoreCashOrders"; +import POSViewOrders from "./views/orders/POSViewOrders"; +import InStoreQRCodeOrders from "./views/orders/InStoreQRCodeOrders"; import EmailCms from "./views/CustomerSupport/EmailCMS/EmailCms"; import RegistrationEmail from "./views/CustomerSupport/EmailCMS/RegistrationEmail"; import Employee from "./views/EmployeeAccess/Employee"; @@ -454,6 +458,10 @@ const routes = [ name: "Returned Orders", element: ReturnedOrders, }, + //Point of sale orders + { path: "/inStoreCashOrders/new", name: "In Store Cash Orders", element:InStoreCashOrders }, + { path: "/InStoreQRCodeOrders/new", name: "In Store QR Code Orders", element:InStoreQRCodeOrders }, + { path: "/inStoreOrders/:status/:id", name: "View In Store Cash Orders", element: POSViewOrders }, // { path: "/order/:status/:id", name: "View Order", element: ViewOdr }, //dashboard @@ -625,6 +633,12 @@ const routes = [ name: "Revenue (Day Wise)", element: RevenueCharts, }, + //Point of Sale Section + { + path: "/pos", + name: "Point of Sale", + element: Pos, + }, ]; export default routes; diff --git a/src/views/PointOfSale/AddressSelectionModal.js b/src/views/PointOfSale/AddressSelectionModal.js new file mode 100644 index 0000000..32cf183 --- /dev/null +++ b/src/views/PointOfSale/AddressSelectionModal.js @@ -0,0 +1,77 @@ +import React from "react"; +import Modal from "react-modal"; + +const AddressSelectionModal = ({ isOpen, onClose, addresses, onSelect }) => { + const modalStyle = { + overlay: { + backgroundColor: "rgba(0, 0, 0, 0.5)", + }, + content: { + top: "50%", + left: "50%", + right: "auto", + bottom: "auto", + marginRight: "-50%", + transform: "translate(-50%, -50%)", + maxWidth: "800px", + width: "90%", + }, + }; + + const tableStyle = { + width: "100%", + borderCollapse: "collapse", + }; + + const thTdStyle = { + border: "1px solid #ddd", + padding: "8px", + }; + + const thStyle = { + ...thTdStyle, + backgroundColor: "#f2f2f2", + }; +// console.log(addresses); + return ( + +

Select Address

+ + + + + + + + + + + {/* Add additional columns as needed */} + + + + {addresses.map((address, index) => ( + onSelect(address)} + style={{ cursor: "pointer", ...thTdStyle }} + > + + + + + + + + {/* Add additional columns as needed */} + + ))} + +
First NameLast NamePhone NumberStreetCityPostal CodeState
{address.first_Name}{address.last_Name}{address.phone_Number}{address.street}{address.city}{address.postalCode}{address.state}
+ +
+ ); +}; + +export default AddressSelectionModal; + diff --git a/src/views/PointOfSale/Pos.js b/src/views/PointOfSale/Pos.js new file mode 100644 index 0000000..3ed84b2 --- /dev/null +++ b/src/views/PointOfSale/Pos.js @@ -0,0 +1,1320 @@ +import React, { useState, useEffect } from "react"; +import { Link } from "react-router-dom"; +import Button from "@material-ui/core/Button"; +import { useNavigate } from "react-router-dom"; +import axios from "axios"; +import { isAutheticated } from "src/auth"; +import swal from "sweetalert"; +import { + Box, + FormControl, + IconButton, + InputLabel, + MenuItem, + Select, + TextField, + Container, + Grid, + Paper, + Typography, +} from "@mui/material"; +import toast, { Toaster } from "react-hot-toast"; +import SearchIcon from "@mui/icons-material/Search"; +import ClearIcon from "@mui/icons-material/Clear"; +// import PercentIcon from "@mui/icons-material/Percent"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import AddressSelectionModal from "./AddressSelectionModal"; + +const Pos = () => { + const token = isAutheticated(); + const [query, setQuery] = useState(""); + const navigate = useNavigate(); + const [loading, setLoading] = useState(true); + + const [usersWithAddresses, setUsersWithAddresses] = useState([]); + + const getUsersWithAddresses = async () => { + try { + const usersResponse = await axios.get("/api/v1/admin/users", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const users = usersResponse.data.users; + + const usersWithAddressesPromises = users.map(async (user) => { + try { + const addressResponse = await axios.get( + `/api/shipping/address/user/address/${user._id}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + const userWithAddress = { + ...user, + address: addressResponse.data?.UserShippingAddress || [], + }; + return userWithAddress; + } catch (error) { + throw new Error(`Error fetching address for user ${user._id}`); + } + }); + + const usersWithAddresses = await Promise.all(usersWithAddressesPromises); + setUsersWithAddresses(usersWithAddresses); + } catch (error) { + swal({ + title: error, + text: "Please login to access the resource or refresh the page.", + icon: "error", + button: "Retry", + dangerMode: true, + }); + } + }; + + useEffect(() => { + getUsersWithAddresses(); + }, [token]); + const [showData, setShowData] = useState(usersWithAddresses); + const [currentUser, setCurrentUser] = useState(null); + const [storedelivery, setStoreDelivery] = useState(""); + + // Function to handle change in radio button selection + const handleSalesTypeChange = (event) => { + setSalesType(event.target.value); + }; + const handlestoredeliveryChange = (event) => { + setStoreDelivery(event.target.value); + }; + useEffect(() => { + setTimeout(() => { + if (query !== "") { + setCurrentUser(null); + const lowerCaseQuery = query.toLowerCase(); // Convert query to lowercase + + const searchedResult = usersWithAddresses.filter((item) => + item.name.toString().toLowerCase().includes(lowerCaseQuery) + ); + + setShowData(searchedResult); + setLoading(false); + } + }, 100); + }, [query]); + + const handleClick = (id) => { + setQuery(""); + const customer = usersWithAddresses.find((user) => user._id === id); + setCurrentUser(customer); + }; + + // part 2*****************************8 + const [productData, setProductData] = useState([]); + const [filteredItems, setFilteredItems] = useState([]); + const [categories, setCategories] = useState([]); + const [selectedCategories, setSelectedCategories] = useState([]); + const [categoryValue, setCategoryValue] = useState("All"); + const [cartItem, setCartItem] = useState([]); + const [individualSubtotals, setIndividualSubtotals] = useState([]); + const [total, setTotal] = useState(0); + + const getAllProducts = async () => { + try { + const response = await axios.get("/api/product/getAll/user/"); + if (response.status === 200) { + // setProductData(response?.data?.product); + const activeProducts = response?.data?.product.filter( + (product) => product.product_Status === "Active" + ); + setProductData(activeProducts); + } + } catch (error) { + console.error("Error fetching products:", error); + } + }; + + const getCaterogy = async () => { + try { + const response = await axios.get("/api/category/getCategories"); + if (response.status === 200) { + setCategories(response?.data?.categories); + } + } catch (error) { + console.error("Error fetching categories:", error); + } + }; + const handleChange = (event) => { + const { name, value } = event.target; + + if (name === "category") { + setCategoryValue(value); + setSelectedCategories((prevCategories) => { + if (prevCategories.includes(value)) { + return prevCategories.filter((category) => category !== value); + } else { + return [...prevCategories, value]; + } + }); + } + }; + const items = () => { + setFilteredItems( + productData?.filter((item) => { + const categoryMatch = + categoryValue === "All" || + item.category.categoryName === categoryValue; + return categoryMatch; + }) + ); + }; + + useEffect(() => { + setLoading(true); + getAllProducts() + .then(() => { + getCaterogy(); + }) + .catch((error) => { + console.error("Error fetching products:", error); + }) + .finally(() => { + setLoading(false); // Set loading to false after data fetching + }); + }, [token]); + + useEffect(() => { + items(); + }, [categoryValue, productData]); + const styles = { + selectHeading: { + fontFamily: "inter", + fontWeight: "600", + fontSize: "16px", + color: "#6C7275", + marginBottom: ".5rem", + }, + tableContainer: { + maxHeight: 360, + height: 360, + overflowY: "auto", // Enable vertical scrolling + }, + headingStyle: { + fontFamily: "inter", + fontWeight: "600", + fontSize: "16px", + color: "#121212", + width: "70%", + borderBottom: "1px solid black", + }, + }; + + const addToCart = (item) => { + // Check if the item is already in the cart with the same product ID and variant + const existingCartItemIndex = cartItem.findIndex( + (cartItem) => + cartItem.product._id === item._id && + cartItem.variant._id === selectedVariants[item._id]?._id + ); + + if (existingCartItemIndex !== -1) { + // Item with the same product ID and variant already exists in the cart, update its quantity + const newCart = [...cartItem]; + const existingCartItem = newCart[existingCartItemIndex]; + const selectedVariant = selectedVariants[item._id]; + const price = selectedVariant ? selectedVariant.price : item.price; + + existingCartItem.quantity += 1; + existingCartItem.subtotal += + parseFloat(price) + parseFloat(item.gst_amount); + + setCartItem(newCart); + // Show a success message + swal("Item quantity updated in cart", "", "success"); + } else { + // Item is not in the cart, add it + const selectedVariant = selectedVariants[item._id]; + const price = selectedVariant ? selectedVariant.price : item.price; + const totalAmount = parseFloat(price) + parseFloat(item.gst_amount); + + setCartItem([ + ...cartItem, + { + product: item, + quantity: 1, + variant: { + _id: selectedVariant?._id, + gst_Id: selectedVariant?.gst_Id, + price: selectedVariant?.price, + volume: selectedVariant?.volume, + weight: selectedVariant?.weight, + variant_Name: selectedVariant?.variant_Name, + }, + subtotal: totalAmount, + }, + ]); + // Show a success message + swal("Item added to cart", "", "success"); + } + }; + + const handleIncrease = (index) => { + const newCart = [...cartItem]; + const item = newCart[index]; + + const selectedVariant = selectedVariants[item.product._id]; + const price = selectedVariant ? selectedVariant.price : item.product.price; + const totalAmount = + (item.quantity + 1) * parseFloat(price) + + parseFloat(item.product.gst_amount); + + newCart[index].quantity += 1; + newCart[index].subtotal = totalAmount; + + setCartItem(newCart); + }; + + const handleDecrease = (index) => { + const newCart = [...cartItem]; + const item = newCart[index]; + + if (item.quantity > 1) { + const selectedVariant = selectedVariants[item.product._id]; + const price = selectedVariant + ? selectedVariant.price + : item.product.price; + const totalAmount = + (item.quantity - 1) * parseFloat(price) + + parseFloat(item.product.gst_amount); + + newCart[index].quantity -= 1; + newCart[index].subtotal = totalAmount; + + setCartItem(newCart); + } + }; + + // console.log(cartItem) + const removeCartItemHandler = (id, variant) => { + const newCart = cartItem.filter( + (item) => item?.product._id !== id || item.variant._id !== variant + ); + setCartItem(newCart); + }; + + // Calculate subtotal of all items in cart + const calculateTotal = () => { + let subtotal = 0; + cartItem.forEach((item) => { + subtotal += item.subtotal; + }); + setTotal(subtotal); + }; + + useEffect(() => { + calculateTotal(); + }, [cartItem]); + // console.log(usersWithAddresses); + const [showChangeAddress, setShowChangeAddress] = useState(false); + const [selectedAddress, setSelectedAddress] = useState( + currentUser?.address[0] + ); + useEffect(() => { + setSelectedAddress(currentUser?.address[0]); + }, [currentUser]); + const handleChangeAddress = () => { + setShowChangeAddress(true); + }; + + const handleSelectAddress = (address) => { + setSelectedAddress(address); + setShowChangeAddress(false); + }; + + // const checkoutCash = async () => { + // // console.log("Checkout button clicked"); + // try { + // const config = { + // headers: { + // Authorization: `Bearer ${token}`, + // }, + // }; + // const cartData = cartItem.map((item) => ({ + // product: item.product, // Entire product object + // quantity: item.quantity, + // variant: item.variant, // Entire variant object + // subtotal: item.subtotal, + // })); + // const order = { + // userr: currentUser._id, + // address: selectedAddress._id, + // cart: cartData, + // subtotal: total, + // orderType: "PointOfSale", + // }; + + // // Send POST request to backend API endpoint + // const response = await axios.post( + // "/api/order/pos-checkout/", + // order, + // config + // ); + + // toast.success("Order Placed! Your order has been successfully placed."); + // swal({ + // title: "Order Placed!", + // text: `Order ID: ${ + // response.data.order.orderID + // }\nDate and Time: ${new Date( + // response.data.order.createdAt + // ).toLocaleString("en-IN", { + // month: "short", + // day: "numeric", + // year: "numeric", + // hour: "2-digit", + // minute: "numeric", + // hour12: true, + // })}`, + // icon: "success", + // button: "OK", + // }); + + // // Clear cart items, reset current user and address, and reset radio button states + // setCartItem([]); + // setCurrentUser(null); + // setSelectedAddress(null); + // setSalesType(""); + // setStoreDelivery(""); + // setTotal(0); + // setCategoryValue("All"); + // } catch (error) { + // // Handle errors + // console.error("Error placing order:", error); + + // toast.error( + // "Error! There was an error placing your order. Please try again later." + // ); + // } + // }; + const checkoutCash = async () => { + const config = { + headers: { + Authorization: `Bearer ${token}`, + }, + }; + + const cartData = cartItem.map((item) => ({ + product: item.product, // Entire product object + quantity: item.quantity, + variant: item.variant, // Entire variant object + subtotal: item.subtotal, + })); + + const order = { + userr: currentUser._id, + address: selectedAddress._id, + cart: cartData, + subtotal: total, + orderType: "PointOfSale", + }; + + // Send POST request to backend API endpoint + axios + .post("/api/order/pos-checkout/", order, config) + .then((response) => { + // Handle successful response + swal({ + title: "Order Placed!", + text: `Order ID: ${ + response.data.order.orderID + }\nDate and Time: ${new Date( + response.data.order.createdAt + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + })}`, + icon: "success", + button: "OK", + }); + + // Clear cart items, reset current user and address, and reset radio button states + setCartItem([]); + setCurrentUser(null); + setSelectedAddress(null); + setStoreDelivery(""); + setTotal(0); + setCategoryValue("All"); + }) + .catch((error) => { + // Handle errors + console.error("Error placing order:", error); + + toast.error( + "Error! There was an error placing your order. Please try again later." + ); + }); + }; + + // for QR Code + const checkoutQRCode = async () => { + try { + const { + data: { key }, + } = await axios.get( + `/api/order/getRzpKey/${currentUser.name}/${currentUser.email}`, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ); + + const cartData = cartItem.map((item) => ({ + product: item.product, // Entire product object + quantity: item.quantity, + variant: item.variant, // Entire variant object + subtotal: item.subtotal, + })); + + const { + data: { order }, + } = await axios.post( + "/api/order/Rzpcheckout", + { + userr: currentUser._id, + address: selectedAddress._id, + cart: cartData, + subtotal: total, + orderType: "PointOfSale", + }, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ); + + const options = { + key, + amount: order.amount, + currency: "INR", + name: "Smellika", + description: "Smellika RazorPay", + image: + "https://res.cloudinary.com/dnmgivd1x/image/upload/v1707058241/bolo/Logo/aasy4ulmbbtqmcxi64j0.jpg", + order_id: order.id, + // callback_url: + // "http://localhost:5000/api/order/pos-paymentverification/", + callback_url: + "https://api.smellika.com/api/order/pos-paymentverification/", + + prefill: { + name: currentUser.name, + email: currentUser.email, + }, + notes: { + address: "Razorpay Corporate Office", + }, + theme: { + color: "#121212", + }, + }; + + const razor = new window.Razorpay(options); + + razor.on("payment.success", async function (response) { + // Handle successful payment + console.log("Payment successful:", response); + }); + + razor.open(); + } catch (error) { + if ( + error.response && + error.response.data && + error.response.data.message + ) { + // If error.response and its properties exist, handle the error message + toast.error(error.response.data.message); + } else { + // If error.response or its properties are undefined, handle the error generically + toast.error("An error occurred. Please try again later."); + } + } + }; + // varient + const [selectedVariants, setSelectedVariants] = useState({}); + + // Function to set default variant for each product + const setDefaultVariants = () => { + const defaultVariants = {}; + filteredItems.forEach((item) => { + defaultVariants[item._id] = + item.variants && item.variants.length > 0 ? item.variants[0] : null; + }); + setSelectedVariants(defaultVariants); + }; + + // Function to handle variant change + const handleVariantChange = (productId, event) => { + const selectedVariantName = event.target.value; + const selectedVariant = filteredItems + .find((item) => item._id === productId) + ?.variants.find( + (variant) => variant.variant_Name === selectedVariantName + ); + setSelectedVariants((prevState) => ({ + ...prevState, + [productId]: selectedVariant, + })); + }; + // console.log(selectedVariants); + // console.log(cartItem); + // Call setDefaultVariants when the component mounts + useEffect(() => { + setDefaultVariants(); + }, [filteredItems]); + + // console.log("currentUser", currentUser); + return ( +
+
+
+ {/* Part 1: Top Part */} +
+
+
+
+ {/* Customer search */} +
+ + Select Customer: + + setQuery(e.target.value)} + InputProps={{ + endAdornment: ( + handleSearchClick(query)} + > + + + ), + disableUnderline: true, + }} + /> +
+ {query !== "" && ( +
+ + + + + + + + + + {!loading && showData.length === 0 && ( + + + + )} + {loading ? ( + + + + ) : ( + showData.map((user, index) => ( + { + if (user.address.length === 0) { + toast.error( + "Please add an address for shopping." + ); + } else { + handleClick(user?._id); + } + }} + className={`cursor-pointer hover:bg-gray-100 ${ + user.address.length === 0 ? "opacity-50" : "" + }`} + > + + {user.address.length === 0 ? ( + + ) : ( + <> + + + + )} + + )) + )} + +
Customer NameAddressMobile No.
+
+ +
+
+ Loading... +
{user.name} + Add address for shopping + + {`${user?.address[0]?.street}, ${user?.address[0]?.city}, ${user?.address[0]?.state}, ${user?.address[0]?.country}, ${user?.address[0]?.postalCode}`} + {`${user?.address[0]?.phone_Number}`}
+
+ )} + {/* Display selected customer */} +
+ {currentUser && ( +
+
+
+
+ + Customer Name: + + {`${currentUser?.name}`} +
+
+ + Mobile No.: + + {`${selectedAddress?.phone_Number}`} +
+
+ + Address: + + {`${selectedAddress?.street}, ${selectedAddress?.city}, ${selectedAddress?.state}, ${selectedAddress?.country}, ${selectedAddress?.postalCode}`} +
+ +
+
+
+ )} + {/* Render AddressSelectionModal only when currentUser exists */} + {currentUser && ( + setShowChangeAddress(false)} + addresses={currentUser.address} + onSelect={handleSelectAddress} + /> + )} +
+
+
+
+
+ {/* Part 2: Panel 1 and Panel 2 */} +
+ {/* Panel 1 (Left Hand Side) */} +
+
+ {/* Category selection */} +
+ + Categories: + + + + +
+ {/* Product display */} +
+
+ + + + + + + + + + + + {filteredItems.map((item, index) => ( + + + + + + + + + ))} + +
Product ImageProduct NamevariantPriceAction
+ {item.image && item.image.length > 0 && ( + Product Image + )} + {item.name} + {item.variants && item.variants.length > 0 ? ( + + + + ) : ( + "No Variant" + )} + + {selectedVariants[item._id] + ? selectedVariants[item._id].price + : item.price} + + +
+
+
+
+
+ {/* Panel 2 (Right Hand Side) */} +
+
+ {/* Display added products */} + + Added Products: + + {/* Display added products */} +
+ + + + + + + + + + Product + + + Quantity + + + Price + + + GST + + + + Subtotal + + + + + {cartItem.length === 0 ? ( + + + + Add products for shopping + + + + ) : ( + cartItem.map((row, index) => ( + + + {/* {row.product} */} + + + + + + + {row?.product.name} + + + removeCartItemHandler( + row?.product._id, + row.variant._id + ) + } + sx={{ + color: "#6C7275", + width: "105%", + display: "flex", + alignItems: "center", + // justifyContent: "space-between", + cursor: "pointer", + ml: "10px", + + // border: 'solid' + }} + > + + + Remove + + + + + + + + + handleDecrease(index) + } + > + - + + + {row && row.quantity} + + handleIncrease(index) + } + > + + + + + + + ₹ + {row?.variant.price + ? row.variant.price + : row.product.price} + {/* {selectedVariants[row._id] + ? selectedVariants[row._id].price + : row.price} */} + + + ₹{row?.product.gst_amount} + + + + ₹{row.subtotal} + + + )) + )} + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* {salesType === "" ? null : salesType === + "inStoreDelivery" ? ( */} +
+ + In-Store delivery: + +
+ + +
+
+ + +
+ {storedelivery && ( + + )} +
+ {/* ) : ( +
+ {salesType === "shipToCustomer" && ( + + )} + {salesType !== "shipToCustomer" && ( + + )} +
+ )} */} +
+ + {/* Total Section */} +
+ + Total: + + + ₹{total} + +
+
+
+
+
+
+
+
+
+ ); +}; + +export default Pos; diff --git a/src/views/orders/InStoreCashOrders.js b/src/views/orders/InStoreCashOrders.js new file mode 100644 index 0000000..7cd0221 --- /dev/null +++ b/src/views/orders/InStoreCashOrders.js @@ -0,0 +1,386 @@ +import React, { useState, useEffect } from "react"; +import { Link } from "react-router-dom"; +import axios from "axios"; + +import { isAutheticated } from "src/auth"; +import Button from "@material-ui/core/Button"; + +function InStoreCashOrders() { + const token = isAutheticated(); + const [loading, setLoading] = useState(true); + const [success, setSuccess] = useState(true); + const [newOrdersData, setNewOrdersData] = useState([]); + console.log(newOrdersData); + + const [currentPage, setCurrentPage] = useState(1); + const [itemPerPage, setItemPerPage] = useState(10); + const [showData, setShowData] = useState(newOrdersData); + + const handleShowEntries = (e) => { + setCurrentPage(1); + setItemPerPage(e.target.value); + }; + + useEffect(() => { + function getNewOrder() { + axios + .get(`/api/order/getAll/new`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + const filteredOrders = res.data.order.filter( + (order) => order.orderType === "PointOfSale" && order.paymentMode === "cod" + ); + + // Set the filtered orders data + setNewOrdersData(filteredOrders); + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + } + getNewOrder(); + }, [success]); + + + useEffect(() => { + const loadData = () => { + const indexOfLastPost = currentPage * itemPerPage; + const indexOfFirstPost = indexOfLastPost - itemPerPage; + setShowData(newOrdersData.slice(indexOfFirstPost, indexOfLastPost)); + }; + loadData(); + }, [currentPage, itemPerPage, newOrdersData]); + + const handleDelete = (id) => { + console.log(id); + swal({ + title: "Are you sure?", + icon: "error", + buttons: { + Yes: { text: "Yes", value: true }, + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((value) => { + if (value === true) { + axios + .delete(`/api/order/delete/${id}`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + }); + }; + + return ( +
+
+
+
+
+
+
+ New Orders +
+ + {/*
+ + + +
*/} +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + + + + + {!loading && showData.length === 0 && ( + + + + )} + {loading ? ( + + + + ) : ( + showData.map((order, i) => { + return ( + + + + + + + + + ); + }) + )} + +
Order IDCustomerOrder valueOrder AtStatusActions
+
No Data Available
+
+ Loading... +
{order?.orderID} + {order?.user?.name} + + ₹{order?.total_amount} + + {new Date(order?.createdAt).toLocaleString( + "en-IN", + { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + } + )} + + + {order?.orderStatus} + + + {/* */} + + + + {/* + + */} + + {/* */} +
+
+ +
+
+
+ Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "} + {Math.min( + currentPage * itemPerPage, + newOrdersData.length + )}{" "} + of {newOrdersData.length} entries +
+
+ +
+
+
    +
  • + setCurrentPage((prev) => prev - 1)} + > + Previous + +
  • + + {!(currentPage - 1 < 1) && ( +
  • + + setCurrentPage((prev) => prev - 1) + } + > + {currentPage - 1} + +
  • + )} + +
  • + + {currentPage} + +
  • + + {!( + (currentPage + 1) * itemPerPage - itemPerPage > + newOrdersData.length - 1 + ) && ( +
  • + { + setCurrentPage((prev) => prev + 1); + }} + > + {currentPage + 1} + +
  • + )} + +
  • + newOrdersData.length - 1 + ) + ? "paginate_button page-item next" + : "paginate_button page-item next disabled" + } + > + setCurrentPage((prev) => prev + 1)} + > + Next + +
  • +
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export default InStoreCashOrders; diff --git a/src/views/orders/InStoreQRCodeOrders.js b/src/views/orders/InStoreQRCodeOrders.js new file mode 100644 index 0000000..1892060 --- /dev/null +++ b/src/views/orders/InStoreQRCodeOrders.js @@ -0,0 +1,386 @@ +import React, { useState, useEffect } from "react"; +import { Link } from "react-router-dom"; +import axios from "axios"; + +import { isAutheticated } from "src/auth"; +import Button from "@material-ui/core/Button"; + +function InStoreQRCodeOrders() { + const token = isAutheticated(); + const [loading, setLoading] = useState(true); + const [success, setSuccess] = useState(true); + const [newOrdersData, setNewOrdersData] = useState([]); + console.log(newOrdersData); + + const [currentPage, setCurrentPage] = useState(1); + const [itemPerPage, setItemPerPage] = useState(10); + const [showData, setShowData] = useState(newOrdersData); + + const handleShowEntries = (e) => { + setCurrentPage(1); + setItemPerPage(e.target.value); + }; + + useEffect(() => { + function getNewOrder() { + axios + .get(`/api/order/getAll/new`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + const filteredOrders = res.data.order.filter( + (order) => order.orderType === "PointOfSale" && order.paymentMode === "online" + ); + + // Set the filtered orders data + setNewOrdersData(filteredOrders); + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + } + getNewOrder(); + }, [success]); + + + useEffect(() => { + const loadData = () => { + const indexOfLastPost = currentPage * itemPerPage; + const indexOfFirstPost = indexOfLastPost - itemPerPage; + setShowData(newOrdersData.slice(indexOfFirstPost, indexOfLastPost)); + }; + loadData(); + }, [currentPage, itemPerPage, newOrdersData]); + + const handleDelete = (id) => { + console.log(id); + swal({ + title: "Are you sure?", + icon: "error", + buttons: { + Yes: { text: "Yes", value: true }, + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((value) => { + if (value === true) { + axios + .delete(`/api/order/delete/${id}`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => { + setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + }); + }; + + return ( +
+
+
+
+
+
+
+ New Orders +
+ + {/*
+ + + +
*/} +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + + + + + {!loading && showData.length === 0 && ( + + + + )} + {loading ? ( + + + + ) : ( + showData.map((order, i) => { + return ( + + + + + + + + + ); + }) + )} + +
Order IDCustomerOrder valueOrder AtStatusActions
+
No Data Available
+
+ Loading... +
{order?.orderID} + {order?.user?.name} + + ₹{order?.total_amount} + + {new Date(order?.createdAt).toLocaleString( + "en-IN", + { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + } + )} + + + {order?.orderStatus} + + + {/* */} + + + + {/* + + */} + + {/* */} +
+
+ +
+
+
+ Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "} + {Math.min( + currentPage * itemPerPage, + newOrdersData.length + )}{" "} + of {newOrdersData.length} entries +
+
+ +
+
+
    +
  • + setCurrentPage((prev) => prev - 1)} + > + Previous + +
  • + + {!(currentPage - 1 < 1) && ( +
  • + + setCurrentPage((prev) => prev - 1) + } + > + {currentPage - 1} + +
  • + )} + +
  • + + {currentPage} + +
  • + + {!( + (currentPage + 1) * itemPerPage - itemPerPage > + newOrdersData.length - 1 + ) && ( +
  • + { + setCurrentPage((prev) => prev + 1); + }} + > + {currentPage + 1} + +
  • + )} + +
  • + newOrdersData.length - 1 + ) + ? "paginate_button page-item next" + : "paginate_button page-item next disabled" + } + > + setCurrentPage((prev) => prev + 1)} + > + Next + +
  • +
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export default InStoreQRCodeOrders; diff --git a/src/views/orders/NewOrders.js b/src/views/orders/NewOrders.js index 43c2621..f6acb81 100644 --- a/src/views/orders/NewOrders.js +++ b/src/views/orders/NewOrders.js @@ -31,7 +31,11 @@ function NewOrders() { }, }) .then((res) => { - setNewOrdersData(res.data.order); + const filteredOrders = res.data.order.filter( + (order) => order.orderType === "WebSite" + ); + + setNewOrdersData(filteredOrders); setLoading(false); }) .catch((err) => { @@ -41,6 +45,7 @@ function NewOrders() { } getNewOrder(); }, [success]); + useEffect(() => { const loadData = () => { diff --git a/src/views/orders/POSViewOrders.js b/src/views/orders/POSViewOrders.js new file mode 100644 index 0000000..7af4c55 --- /dev/null +++ b/src/views/orders/POSViewOrders.js @@ -0,0 +1,946 @@ +import React, { useState, useEffect, useRef } from "react"; +import axios from "axios"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import QRCode from "react-qr-code"; +import { isAutheticated } from "src/auth"; +import { useDispatch, useSelector } from "react-redux"; +import { addItemsToCart } from "src/redux/Actions/cartAction"; +import toast from "react-hot-toast"; +import { cibBlackberry } from "@coreui/icons"; +import Button from "@material-ui/core/Button"; + +function POSViewOrders() { + const { status, id } = useParams(); + const [success, setSuccess] = useState(true); + + const { cartItems, subTotal, shippingCharge, tax, shipingInfo, total } = + useSelector((state) => state.cart); + + const AllStates = useSelector((state) => state); + const getValue = useRef(); + const getFranchiseeID = useRef(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const printOrderRef = useRef(); + const token = isAutheticated(); + const [productData, setProductData] = useState([]); + const [allFranchisee, setAllFranchisee] = useState([]); + const [allTax, setAllTax] = useState([]); + const [orderDetails, setOrderDetails] = useState(); + + const [productDetails, setProductDetails] = useState(); + const [loading, setLoading] = useState(true); + const [orderId, setOrderId] = useState(null); + const [orderStatus, setOrderStatus] = useState(""); + // const [data, setData] = useState({ + // product_Name: '', + // address: '', + // quantity: '', + // contact_Number: '', + // total_Price: '', + // }) + useEffect(() => { + const getSingleOrder = async () => { + setLoading(true); + const res = await axios.get(`/api/order/getOne/${id}`, { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }); + if (res.data) { + setLoading(false); + setOrderId(res.data?.order?.order_id); + setOrderDetails(res.data?.order); + console.log(res.data); + // let options = { + // Franchisee: res.data?.order?.shippingInfo?.Franchisee?._id, + // name: res.data?.order?.shippingInfo?.name, + + // contact_Number: res.data?.order?.shippingInfo?.contact_Number, + // contact_Person_Name: res.data?.order?.shippingInfo?.contact_Person_Name, + // address: res.data?.order?.shippingInfo?.address, + // city: res.data?.order?.shippingInfo?.city, + // price_Lable: res.data?.order?.shippingInfo?.Franchisee?.price_Lable, + // state: res.data?.order?.shippingInfo?.state, + // banner: res.data?.order?.shippingInfo?.Franchisee?.banner?.url, + // // Franchisee_Url: res?.data?.data?.url + // } + // dispatch({ type: "addShippingInfo", payload: options }); + // if (res.data?.order?.orderItems) { + // res.data?.order?.orderItems.map((i, ind) => { + // dispatch({ type: "addToCart", payload: i }); + // dispatch({ type: "calculatePrice" }); + + // }) + // } + } + }; + getSingleOrder(); + }, [token]); + + const handleChange = (e) => { + if (e.target.type === "text") { + setData((prev) => ({ ...prev, [e.target.id]: e.target.value })); + } else { + if (e.target.value === "") toast.error("please select status"); + setOrderStatus(e.target.value); + } + }; + const handleQuantityChange = (e) => { + setData((prev) => ({ + ...prev, + quantity: e.target.value, + total_Price: productDetails?.base_Price * e.target.value, + })); + }; + // ------------------------------------------------------ + + const handlechangestatus = () => { + if (orderStatus === "dispatched") { + swal({ + title: `Are you sure for ${orderStatus}?`, + icon: "warning", + content: { + element: "div", + attributes: { + innerHTML: + '' + + '', + }, + }, + buttons: { + Yes: { text: "Submit", value: true }, + + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((result) => { + if (result === true) { + // You have the input values, you can use them in your API call + const courierName = document.getElementById("input1").value.trim(); + const TrackingID = document.getElementById("input2").value.trim(); + + // Check if values are entered + if (courierName === "" || TrackingID === "") { + swal({ + title: "Warning", + text: "Please enter values Courier Name And Tracking ID", + icon: "warning", + button: "Ok", + dangerMode: true, + }); + } else { + axios + .patch( + `/api/order/change/status/${id}`, + { + status: orderStatus, + courierName, + TrackingID, + sendemail: orderDetails?.user?.email, + customerName: orderDetails?.user?.name, + }, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ) + .then((res) => { + console.log("status"); + toast.success( + `Order status change ${status} to ${orderStatus}` + ); + // setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + } + // else { + // swal.close(); // Close the popup if canceled + // } + }); + } else if (orderStatus === "cancelled") { + swal({ + title: `Are you sure for ${orderStatus}?`, + icon: "warning", + content: { + element: "div", + attributes: { + innerHTML: + '

Reson for cancellation.?

', + }, + }, + buttons: { + Yes: { text: "Submit", value: true }, + + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((result) => { + if (result === true) { + // You have the input values, you can use them in your API call + const ReasonforCancellation = document + .getElementById("input1") + .value.trim(); + + // Check if values are entered + if (ReasonforCancellation === "") { + swal({ + title: "Warning", + text: "Please enter Reason for Cancellation", + icon: "warning", + button: "Ok", + dangerMode: true, + }); + } else { + axios + .patch( + `/api/order/change/status/${id}`, + { + status: orderStatus, + ReasonforCancellation, + }, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ) + .then((res) => { + console.log("status"); + toast.success( + `Order status change ${status} to ${orderStatus}` + ); + // setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + } + // else { + // swal.close(); // Close the popup if canceled + // } + }); + } else if (orderStatus === "delivered") { + swal({ + title: `Are you sure for ${orderStatus}?`, + icon: "warning", + content: { + element: "div", + attributes: { + innerHTML: + '', + // '', + }, + }, + buttons: { + Yes: { text: "Submit", value: true }, + + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((result) => { + if (result === true) { + // You have the input values, you can use them in your API call + const DDate = document.getElementById("input1").value.trim(); + + // Check if values are entered + if (DDate === "") { + swal({ + title: "Warning", + text: "Please enter Delivered Date", + icon: "warning", + button: "Ok", + dangerMode: true, + }); + } else { + axios + .patch( + `/api/order/change/status/${id}`, + { + status: orderStatus, + DDate, + }, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ) + .then((res) => { + console.log("status"); + toast.success( + `Order status change ${status} to ${orderStatus}` + ); + // setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + } + // else { + // swal.close(); // Close the popup if canceled + // } + }); + } else { + swal({ + title: `Are you sure for ${orderStatus}?`, + icon: "warning", + + buttons: { + Yes: { text: "Yes", value: true }, + Cancel: { text: "Cancel", value: "cancel" }, + }, + }).then((value) => { + if (value === true) { + axios + .patch( + `/api/order/change/status/${id}`, + { status: orderStatus }, + { + headers: { + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + } + ) + .then((res) => { + console.log("status"); + toast.success(`order status change ${status} to ${orderStatus}`); + // setSuccess((prev) => !prev); + }) + .catch((err) => { + swal({ + title: "Warning", + text: err.response.data.message + ? err.response.data.message + : "Something went wrong!", + icon: "error", + button: "Retry", + dangerMode: true, + }); + }); + } + }); + } + }; + + function getBack() { + navigate(`/orders/${status}`, { replace: true }); + } + + return ( + <> + {" "} +
+
+
+
+
+
+
+

View Order

+
+
+ {orderDetails?.orderID && ( + +
Order ID : {orderDetails?.orderID}
{" "} +
+ )} +
+ {orderDetails?.courier_name && ( +
+ +
+ Courier Name: {orderDetails?.courier_name} +
{" "} +
+ Tracking ID : {orderDetails?.courier_tracking_id} +
+
+
+ )} + {orderDetails?.isDelivered && ( +
+ +
Delivered: Yes
{" "} +
+ Delivered Date: {orderDetails?.DeliveredDate} +
+
+
+ )} +
+ {/* */} + + + + +
+
+
+
+ {loading ? ( +
+
+ Loading... +
+
+ ) : ( +
+
+ {orderDetails?.shipingInfo !== null && ( +
+
+ {/*
+ +
+ + + +
+ +
*/} + +
+
+ Products : {orderDetails?.orderItems?.length} +
+
+ + {orderDetails?.orderItems && + orderDetails?.orderItems.map( + (productDetails, i) => ( +
+
+
+ {productDetails?.name} +
+
+
+ {productDetails?.name} +
+
+
+
+ + {" "} + Quantity:{" "} + {productDetails?.quantity} + +
+ +

+ Subtotal: ₹ + {productDetails?.quantity * + productDetails?.price} +

+

+ Variant:{" "} + {productDetails?.variant_Name} +

+
+
+

+ Price: ₹ + {productDetails?.price} +

+

+ GST: ₹ + {productDetails?.gst_amount} +

+
+
+
+
+
+
+ ) + )} +
+ Shipping Charge: ₹ + {orderDetails?.shipping_charge} +
+ Total Order Value: ₹ + {orderDetails?.total_amount} +
+
+
+
+ )} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + {orderDetails?.status_timeline?.cancelled && ( + + + + + + )} + {/* + + + + */} + +
Order Placed On : + {orderDetails?.createdAt + ? new Date( + orderDetails?.createdAt + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : new Date( + productData?.placed_on + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + })} +
+ Processing Started + : + {orderDetails?.status_timeline?.processing + ? new Date( + orderDetails?.status_timeline?.processing + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : "-"} +
+ Dispatched On + : + {orderDetails?.status_timeline?.dispatched + ? new Date( + orderDetails?.status_timeline?.dispatched + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : "-"} +
+ Delivered On + : + {orderDetails?.status_timeline?.delivered + ? new Date( + orderDetails?.status_timeline?.delivered + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : "-"} +
+ Cancelled On + : + {orderDetails?.status_timeline?.cancelled + ? new Date( + orderDetails?.status_timeline?.cancelled + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : "-"} +
Returned On : + {orderDetails?.status_timeline?.returned + ? new Date( + orderDetails?.status_timeline?.returned + ).toLocaleString("en-IN", { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit", + minute: "numeric", + hour12: true, + }) + : "-"} +
+
+
+
+
+
+
+
+ {orderDetails?.orderStatus !== "cancelled" ? ( +
+ Order Status: {orderDetails?.orderStatus} +
+ ) : ( + <> +
+ Order Status: {orderDetails?.orderStatus} +
+

+ {" "} + Order Cancelled Reason:{" "} + {orderDetails?.order_Cancelled_Reason} +

+ + )} + {/* order status change */}{" "} +
+ {" "} + {status !== "cancelled" && + status !== "returned" && + status !== "delivered" && ( +
+ +
+
+ +
+ {orderStatus && ( +
+ +
+ )} +
+
+ )} +
+ {/* */} + + {/*
+ + +
*/} +
+ {orderDetails?.shipingInfo !== null && ( +
+
+ {/*
+ {orderDetails?.shippingInfo?.name} +
*/} +
+
+ Name: {orderDetails?.shippingInfo?.first_Name}{" "} + {orderDetails?.shippingInfo?.last_Name} +
+ +

+ Contact No. :{" "} + {orderDetails?.shippingInfo?.phone_Number} +

+ + street. : {orderDetails?.shippingInfo?.street} + + +

+ City : {orderDetails?.shippingInfo?.city} +

+

+ State : {orderDetails?.shippingInfo?.state} +

+

+ country : {orderDetails?.shippingInfo?.country} +

+

+ Postal Code. :{" "} + {orderDetails?.shippingInfo?.postalCode} +

+
+
+
+
+ )} +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ )} +
+
+
+
+ {/* */} +
+ + ); +} + +export default POSViewOrders;