Created
February 22, 2025 09:33
-
-
Save amritmaurya1504/b6846836a67cc0fb483b5d5c7222893d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // INVOICE COMPONENT | |
| import React, { useRef } from "react"; | |
| import { motion } from "framer-motion"; | |
| import { FaCheck } from "react-icons/fa6"; | |
| const Invoice = ({ orderInfo, setShowInvoice }) => { | |
| const invoiceRef = useRef(null); | |
| const handlePrint = () => { | |
| const printContent = invoiceRef.current.innerHTML; | |
| const WinPrint = window.open("", "", "width=900,height=650"); | |
| WinPrint.document.write(` | |
| <html> | |
| <head> | |
| <title>Order Receipt</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; padding: 20px; } | |
| .receipt-container { width: 300px; border: 1px solid #ddd; padding: 10px; } | |
| h2 { text-align: center; } | |
| </style> | |
| </head> | |
| <body> | |
| ${printContent} | |
| </body> | |
| </html> | |
| `); | |
| WinPrint.document.close(); | |
| WinPrint.focus(); | |
| setTimeout(() => { | |
| WinPrint.print(); | |
| WinPrint.close(); | |
| }, 1000); | |
| }; | |
| return ( | |
| <div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center"> | |
| <div className="bg-white p-4 rounded-lg shadow-lg w-[400px]"> | |
| {/* Receipt Content for Printing */} | |
| <div ref={invoiceRef} className="p-4"> | |
| {/* Receipt Header */} | |
| <div className="flex justify-center mb-4"> | |
| <motion.div | |
| initial={{ scale: 0, opacity: 0 }} | |
| animate={{ scale: 1.2, opacity: 1 }} | |
| transition={{ duration: 0.5, type: "spring", stiffness: 150 }} | |
| className="w-12 h-12 border-8 border-green-500 rounded-full flex items-center justify-center shadow-lg bg-green-500" | |
| > | |
| <motion.span | |
| initial={{ scale: 0 }} | |
| animate={{ scale: 1 }} | |
| transition={{ delay: 0.3, duration: 0.3 }} | |
| className="text-2xl" | |
| > | |
| <FaCheck className="text-white" /> | |
| </motion.span> | |
| </motion.div> | |
| </div> | |
| <h2 className="text-xl font-bold text-center mb-2">Order Receipt</h2> | |
| <p className="text-gray-600 text-center">Thank you for your order!</p> | |
| {/* Order Details */} | |
| <div className="mt-4 border-t pt-4 text-sm text-gray-700"> | |
| <p> | |
| <strong>Order ID:</strong>{" "} | |
| {Math.floor(new Date(orderInfo.orderDate).getTime())} | |
| </p> | |
| <p> | |
| <strong>Name:</strong> {orderInfo.customerDetails.name} | |
| </p> | |
| <p> | |
| <strong>Phone:</strong> {orderInfo.customerDetails.phone} | |
| </p> | |
| <p> | |
| <strong>Guests:</strong> {orderInfo.customerDetails.guests} | |
| </p> | |
| </div> | |
| {/* Items Summary */} | |
| <div className="mt-4 border-t pt-4"> | |
| <h3 className="text-sm font-semibold">Items Ordered</h3> | |
| <ul className="text-sm text-gray-700"> | |
| {orderInfo.items.map((item, index) => ( | |
| <li | |
| key={index} | |
| className="flex justify-between items-center text-xs" | |
| > | |
| <span> | |
| {item.name} x{item.quantity} | |
| </span> | |
| <span>₹{item.price.toFixed(2)}</span> | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| {/* Bills Summary */} | |
| <div className="mt-4 border-t pt-4 text-sm"> | |
| <p> | |
| <strong>Subtotal:</strong> ₹{orderInfo.bills.total.toFixed(2)} | |
| </p> | |
| <p> | |
| <strong>Tax:</strong> ₹{orderInfo.bills.tax.toFixed(2)} | |
| </p> | |
| <p className="text-md font-semibold"> | |
| <strong>Grand Total:</strong> ₹ | |
| {orderInfo.bills.totalWithTax.toFixed(2)} | |
| </p> | |
| </div> | |
| {/* Payment Details */} | |
| <div className="mb-2 mt-2 text-xs"> | |
| {orderInfo.paymentMethod === "Cash" ? ( | |
| <p> | |
| <strong>Payment Method:</strong> {orderInfo.paymentMethod} | |
| </p> | |
| ) : ( | |
| <> | |
| <p> | |
| <strong>Payment Method:</strong> {orderInfo.paymentMethod} | |
| </p> | |
| <p> | |
| <strong>Razorpay Order ID:</strong>{" "} | |
| {orderInfo.paymentData?.razorpay_order_id} | |
| </p> | |
| <p> | |
| <strong>Razorpay Payment ID:</strong>{" "} | |
| {orderInfo.paymentData?.razorpay_payment_id} | |
| </p> | |
| </> | |
| )} | |
| </div> | |
| </div> | |
| {/* Buttons */} | |
| <div className="flex justify-between mt-4"> | |
| <button | |
| onClick={handlePrint} | |
| className="text-blue-500 hover:underline text-xs px-4 py-2 rounded-lg" | |
| > | |
| Print Receipt | |
| </button> | |
| <button | |
| onClick={() => setShowInvoice(false)} | |
| className="text-red-500 hover:underline text-xs px-4 py-2 rounded-lg" | |
| > | |
| Close | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default Invoice; | |
| // BILL COMPONENT | |
| import React, { useState } from "react"; | |
| import { useDispatch, useSelector } from "react-redux"; | |
| import { getTotalPrice } from "../../redux/slices/cartSlice"; | |
| import { | |
| addOrder, | |
| createOrderRazorpay, | |
| updateTable, | |
| verifyPaymentRazorpay, | |
| } from "../../https/index"; | |
| import { enqueueSnackbar } from "notistack"; | |
| import { useMutation } from "@tanstack/react-query"; | |
| import { removeAllItems } from "../../redux/slices/cartSlice"; | |
| import { removeCustomer } from "../../redux/slices/customerSlice"; | |
| import Invoice from "../invoice/Invoice"; | |
| function loadScript(src) { | |
| return new Promise((resolve) => { | |
| const script = document.createElement("script"); | |
| script.src = src; | |
| script.onload = () => { | |
| resolve(true); | |
| }; | |
| script.onerror = () => { | |
| resolve(false); | |
| }; | |
| document.body.appendChild(script); | |
| }); | |
| } | |
| const Bill = () => { | |
| const dispatch = useDispatch(); | |
| const customerData = useSelector((state) => state.customer); | |
| const cartData = useSelector((state) => state.cart); | |
| const total = useSelector(getTotalPrice); | |
| const taxRate = 5.25; | |
| const tax = (total * taxRate) / 100; | |
| const totalPriceWithTax = total + tax; | |
| const [paymentMethod, setPaymentMethod] = useState(); | |
| const [showInvoice, setShowInvoice] = useState(false); | |
| const [orderInfo, setOrderInfo] = useState(); | |
| const handlePlaceOrder = async () => { | |
| if (!paymentMethod) { | |
| enqueueSnackbar("Please select a payment method!", { | |
| variant: "warning", | |
| }); | |
| return; | |
| } | |
| if (paymentMethod === "Online") { | |
| // load the script | |
| try { | |
| const res = await loadScript( | |
| "https://checkout.razorpay.com/v1/checkout.js" | |
| ); | |
| if (!res) { | |
| enqueueSnackbar("Razorpay SDK failed to load. Are you online?", { | |
| variant: "warning", | |
| }); | |
| return; | |
| } | |
| // create order | |
| const reqData = { | |
| amount: totalPriceWithTax.toFixed(2), | |
| }; | |
| const { data } = await createOrderRazorpay(reqData); | |
| const options = { | |
| key: `${import.meta.env.VITE_RAZORPAY_KEY_ID}`, | |
| amount: data.order.amount, | |
| currency: data.order.currency, | |
| name: "RESTRO", | |
| description: "Secure Payment for Your Meal", | |
| order_id: data.order.id, | |
| handler: async function (response) { | |
| const verification = await verifyPaymentRazorpay(response); | |
| console.log(verification); | |
| enqueueSnackbar(verification.data.message, { variant: "success" }); | |
| // Place the order | |
| const orderData = { | |
| customerDetails: { | |
| name: customerData.customerName, | |
| phone: customerData.customerPhone, | |
| guests: customerData.guests, | |
| }, | |
| orderStatus: "In Progress", | |
| bills: { | |
| total: total, | |
| tax: tax, | |
| totalWithTax: totalPriceWithTax, | |
| }, | |
| items: cartData, | |
| table: customerData.table.tableId, | |
| paymentMethod: paymentMethod, | |
| paymentData: { | |
| razorpay_order_id: response.razorpay_order_id, | |
| razorpay_payment_id: response.razorpay_payment_id, | |
| }, | |
| }; | |
| setTimeout(() => { | |
| orderMutation.mutate(orderData); | |
| }, 1500); | |
| }, | |
| prefill: { | |
| name: customerData.name, | |
| email: "", | |
| contact: customerData.phone, | |
| }, | |
| theme: { color: "#025cca" }, | |
| }; | |
| const rzp = new window.Razorpay(options); | |
| rzp.open(); | |
| } catch (error) { | |
| console.log(error); | |
| enqueueSnackbar("Payment Failed!", { | |
| variant: "error", | |
| }); | |
| } | |
| } else { | |
| // Place the order | |
| const orderData = { | |
| customerDetails: { | |
| name: customerData.customerName, | |
| phone: customerData.customerPhone, | |
| guests: customerData.guests, | |
| }, | |
| orderStatus: "In Progress", | |
| bills: { | |
| total: total, | |
| tax: tax, | |
| totalWithTax: totalPriceWithTax, | |
| }, | |
| items: cartData, | |
| table: customerData.table.tableId, | |
| paymentMethod: paymentMethod, | |
| }; | |
| orderMutation.mutate(orderData); | |
| } | |
| }; | |
| const orderMutation = useMutation({ | |
| mutationFn: (reqData) => addOrder(reqData), | |
| onSuccess: (resData) => { | |
| const { data } = resData.data; | |
| console.log(data); | |
| setOrderInfo(data); | |
| // Update Table | |
| const tableData = { | |
| status: "Booked", | |
| orderId: data._id, | |
| tableId: data.table, | |
| }; | |
| setTimeout(() => { | |
| tableUpdateMutation.mutate(tableData); | |
| }, 1500); | |
| enqueueSnackbar("Order Placed!", { | |
| variant: "success", | |
| }); | |
| setShowInvoice(true); | |
| }, | |
| onError: (error) => { | |
| console.log(error); | |
| }, | |
| }); | |
| const tableUpdateMutation = useMutation({ | |
| mutationFn: (reqData) => updateTable(reqData), | |
| onSuccess: (resData) => { | |
| console.log(resData); | |
| dispatch(removeCustomer()); | |
| dispatch(removeAllItems()); | |
| }, | |
| onError: (error) => { | |
| console.log(error); | |
| }, | |
| }); | |
| return ( | |
| <> | |
| <div className="flex items-center justify-between px-5 mt-2"> | |
| <p className="text-xs text-[#ababab] font-medium mt-2"> | |
| Items({cartData.lenght}) | |
| </p> | |
| <h1 className="text-[#f5f5f5] text-md font-bold"> | |
| ₹{total.toFixed(2)} | |
| </h1> | |
| </div> | |
| <div className="flex items-center justify-between px-5 mt-2"> | |
| <p className="text-xs text-[#ababab] font-medium mt-2">Tax(5.25%)</p> | |
| <h1 className="text-[#f5f5f5] text-md font-bold">₹{tax.toFixed(2)}</h1> | |
| </div> | |
| <div className="flex items-center justify-between px-5 mt-2"> | |
| <p className="text-xs text-[#ababab] font-medium mt-2"> | |
| Total With Tax | |
| </p> | |
| <h1 className="text-[#f5f5f5] text-md font-bold"> | |
| ₹{totalPriceWithTax.toFixed(2)} | |
| </h1> | |
| </div> | |
| <div className="flex items-center gap-3 px-5 mt-4"> | |
| <button | |
| onClick={() => setPaymentMethod("Cash")} | |
| className={`bg-[#1f1f1f] px-4 py-3 w-full rounded-lg text-[#ababab] font-semibold ${ | |
| paymentMethod === "Cash" ? "bg-[#383737]" : "" | |
| }`} | |
| > | |
| Cash | |
| </button> | |
| <button | |
| onClick={() => setPaymentMethod("Online")} | |
| className={`bg-[#1f1f1f] px-4 py-3 w-full rounded-lg text-[#ababab] font-semibold ${ | |
| paymentMethod === "Online" ? "bg-[#383737]" : "" | |
| }`} | |
| > | |
| Online | |
| </button> | |
| </div> | |
| <div className="flex items-center gap-3 px-5 mt-4"> | |
| <button className="bg-[#025cca] px-4 py-3 w-full rounded-lg text-[#f5f5f5] font-semibold text-lg"> | |
| Print Receipt | |
| </button> | |
| <button | |
| onClick={handlePlaceOrder} | |
| className="bg-[#f6b100] px-4 py-3 w-full rounded-lg text-[#1f1f1f] font-semibold text-lg" | |
| > | |
| Place Order | |
| </button> | |
| </div> | |
| {showInvoice && ( | |
| <Invoice orderInfo={orderInfo} setShowInvoice={setShowInvoice} /> | |
| )} | |
| </> | |
| ); | |
| }; | |
| export default Bill; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment