Untitled
unknown
jsx
10 months ago
26 kB
5
Indexable
import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { pdf } from "@react-pdf/renderer"; import axiosInstance from "../../utils/axiosInstance"; import BarcodeCheckerPDF from "./SellProductPdf"; import ConfirmationModal from "./ConfirmationModal"; import { toast, ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; const BarcodeChecker = () => { const navigate = useNavigate(); const [barcode, setBarcode] = useState(""); const [id, setId] = useState(""); const [items, setItems] = useState([]); const [customerName, setCustomerName] = useState(""); const [customerDetails, setCustomerDetails] = useState(null); const [loading, setLoading] = useState(false); const [errors, setErrors] = useState({}); const [quantity, setQuantity] = useState(0); const [name, setName] = useState(""); const [price, setPrice] = useState(0); const [payment, setPayment] = useState(0); const [paidAmount, setPaidAmount] = useState(0); const [remainingAmount, setRemainingAmount] = useState(0); const [suggestions, setSuggestions] = useState([]); const [customers, setCustomers] = useState([]); const [debt, setDebt] = useState(0); const [availableQuantity, setAvailableQuantity] = useState(0); const [editIndex, setEditIndex] = useState(null); const [editQuantity, setEditQuantity] = useState(0); const [editPrice, setEditPrice] = useState(0); const [discount, setDiscount] = useState(0); const [showModal, setShowModal] = useState(false); const [deleteIndex, setDeleteIndex] = useState(null); useEffect(() => { const fetchCustomers = async () => { setLoading(true); try { const response = await axiosInstance.get("/customers?perPage=100"); setCustomers(response.data); } catch (error) { console.error("Error fetching customers:", error); } finally { setLoading(false); } }; fetchCustomers(); }, []); useEffect(() => { if (barcode) { const fetchItem = async () => { setLoading(true); try { const response = await axiosInstance.get(`/items/${barcode}`); const item = response.data.data; setName(item.name); setPrice(item.salePrice); setId(item.id); setAvailableQuantity(item.quantity); } catch (error) { console.error("Error fetching item:", error); setName(""); setPrice(0); setId(null); setAvailableQuantity(0); } finally { setLoading(false); } }; fetchItem(); } }, [barcode]); useEffect(() => { if (customerName) { const filteredSuggestions = customers.data.filter((customer) => customer.username.toLowerCase().startsWith(customerName.toLowerCase()) ); setSuggestions(filteredSuggestions); } else { setSuggestions([]); } }, [customerName, customers]); useEffect(() => { if (customerDetails && customerDetails.id) { const fetchDebt = async () => { setLoading(true); try { const response = await axiosInstance.get( `/debts/${customerDetails.id}` ); const debt = response.data.data.total; setDebt(debt); } catch (error) { console.error("Error fetching customer debt:", error); } finally { setLoading(false); } }; fetchDebt(); } }, [customerDetails]); const validate = () => { const newErrors = {}; if (!barcode) newErrors.barcode = "بارکۆد پێویستە"; if (!name) newErrors.name = "ناوی بەرهەم پێویستە"; if (!price) newErrors.price = "نرخ پێویستە"; if (!quantity) newErrors.quantity = "دانە پێویستە"; return newErrors; }; const formatPhoneNumber = (phone) => { const part1 = phone.slice(7, 11); const part2 = phone.slice(4, 7); const part3 = phone.slice(0, 4); return `${part1} ${part2} ${part3}`; }; const formatNumber = (value) => { if (!value) return ""; const parts = value.toString().split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return parts.join("."); }; const handlePaymentChange = (e) => { const value = e.target.value.replace(/,/g, ""); // Remove commas for parsing setPayment(value ? parseFloat(value) : 0); // Update state with numeric value }; const handlePriceChange = (e) => { const value = e.target.value.replace(/,/g, ""); // Remove commas for parsing setPrice(value ? parseFloat(value) : 0); // Update state with numeric value }; const handleDiscountChange = (e) => { const value = e.target.value.replace(/,/g, ""); // Remove commas for parsing setDiscount(value ? parseFloat(value) : 0); // Update state with numeric value }; const handleEditPriceChange = (e) => { const value = e.target.value.replace(/,/g, ""); // Remove commas for parsing setEditPrice(value ? parseFloat(value) : 0); // Update state with numeric value }; const handleAddItem = () => { const validationErrors = validate(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } setErrors({}); const newItem = { barcode, name, price: parseFloat(price), quantity: parseInt(quantity), id, availableQuantity, }; setItems([...items, newItem]); setBarcode(""); setName(""); setPrice(0); setQuantity(0); }; const handleDeleteItem = () => { const newItems = items.filter((_, i) => i !== deleteIndex); setItems(newItems); setShowModal(false); }; const handleShowModal = (index) => { setDeleteIndex(index); setShowModal(true); }; const handleSearchCustomer = (customer) => { const selectedCustomer = customers.data.find( (c) => c.username === customer ); if (selectedCustomer) { setCustomerDetails(selectedCustomer); setCustomerName(selectedCustomer.username); setSuggestions([]); setPaidAmount(0); setRemainingAmount(totalPriceAfterDiscount); } else { setCustomerDetails(null); setErrors({ customerName: "Customer not found" }); } }; const handleSuggestionClick = (customer) => { setCustomerName(customer.username); handleSearchCustomer(customer.username); }; const handleKeyDown = (e) => { if (e.key === "Enter") { handleSearchCustomer(customerName); } }; const totalPrice = items.reduce( (sum, item) => sum + item.price * item.quantity, 0 ); const totalPriceAfterDiscount = totalPrice - discount; const handlePayment = async () => { const newPaidAmount = paidAmount + payment; const newRemainingAmount = totalPriceAfterDiscount - newPaidAmount; const newAmountLent = customerDetails.amountLent + newRemainingAmount; setPaidAmount(newPaidAmount); setRemainingAmount(newRemainingAmount > 0 ? newRemainingAmount : 0); setCustomerDetails((prevDetails) => ({ ...prevDetails, amountLent: newAmountLent, })); try { const paymentData = { customer_id: customerDetails.id, paidAmount: payment, soldItems: items.map((item) => ({ item_id: item.id, quantity: item.quantity, salePrice: parseFloat(item.price), })), discount, }; await axiosInstance.post("/sales", paymentData); console.log("Payment recorded successfully"); // Show success toast toast.success("وەسڵەکە بە سەرکەوتوویی دروستکرا!", { position: "top-right", autoClose: 2000, // 2 seconds }); } catch (error) { console.error("Error recording payment:", error); toast.error("هەڵەیەک ڕویدا لە دروستکردنی وەسڵەکە."); } setPayment(0); }; const handlePrint = async () => { const doc = ( <BarcodeCheckerPDF customerDetails={customerDetails} items={items} totalPrice={totalPriceAfterDiscount} paidAmount={paidAmount} remainingAmount={remainingAmount} discount={discount} customers={customers} /> ); const blob = await pdf(doc).toBlob(); const url = URL.createObjectURL(blob); const printWindow = window.open(url); if (printWindow) { printWindow.onload = () => { printWindow.print(); }; } else { console.error("Failed to open new window for printing."); } }; const handleEditRow = (index) => { setEditIndex(index); setEditQuantity(items[index].quantity); setEditPrice(items[index].price); }; const handleSaveEdit = (index) => { const updatedItems = items.map((item, i) => { if (i === index) { return { ...item, quantity: editQuantity, price: parseFloat(editPrice), }; } return item; }); setItems(updatedItems); // Reset editing state setEditIndex(null); setEditQuantity(0); setEditPrice(0); }; const handleCancelEdit = () => { setEditIndex(null); setEditQuantity(0); setEditPrice(0); }; return ( <div className="flex items-center justify-center min-h-screen overflow-hidden bg-secondary"> <ToastContainer /> <div className="flex flex-col w-full p-8 m-2 border-2 rounded-lg shadow-lg bg-accent border-primary"> <button className="flex text-invert border border-primary py-2 px-4 rounded-md w-[10%] text-center text-lg mb-4" onClick={() => { navigate("/"); }} > گەڕانەوە </button> <div className="relative flex mb-4"> <input type="text" value={customerName} onChange={(e) => setCustomerName(e.target.value)} placeholder="ناوی کڕیار" onKeyDown={handleKeyDown} className={`flex-grow px-4 py-2 text-xl border rounded-md bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.customerName ? "border-red-500" : "border-primary" }`} /> <button onClick={() => handleSearchCustomer(customerName)} disabled={loading} className="px-10 py-[10px] mr-4 bg-primary text-invert rounded-md hover:bg-primary focus:outline-none focus:ring-2 focus:ring-invert focus:ring-opacity-75 text-xl font-medium" > گەڕان </button> {suggestions.length > 0 && ( <ul className="absolute z-10 w-full mt-1 text-xl rounded-md shadow-lg top-full bg-accent"> {suggestions.map((suggestion) => ( <li key={suggestion.id} onClick={() => handleSuggestionClick(suggestion)} className="px-4 py-2 text-xl text-right cursor-pointer hover:bg-primary text-invert" > {suggestion.username} - {suggestion.city} - {suggestion.phone} </li> ))} </ul> )} </div> {errors.customerName && ( <p className="mt-1 text-sm text-right text-red-500"> {errors.customerName} </p> )} <h2 className="mb-6 text-2xl font-bold text-center text-invert"> زانیاری بەرهەم </h2> <div className="grid grid-cols-1 gap-4 mb-4 text-2xl md:grid-cols-2 lg:grid-cols-4"> <div className="flex flex-col"> <label className="mb-2 text-invert">بارکۆد</label> <input type="text" value={barcode} onChange={(e) => setBarcode(e.target.value)} placeholder="بارکۆد" className={`px-4 py-2 border rounded-md text-xl bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.barcode ? "border-red-500" : "border-primary" }`} /> </div> <div className="flex flex-col"> <label className="mb-2 text-invert">ناوی بەرهەم</label> <input type="text" value={name} readOnly placeholder="ناوی بەرهەم" className={`px-4 py-2 border rounded-md text-xl bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.name ? "border-red-500" : "border-primary" }`} /> </div> <div className="flex flex-col"> <label className="mb-2 text-invert">نرخ</label> <input type="text" value={formatNumber(price)} onChange={handlePriceChange} placeholder="نرخ" className={`px-4 py-2 border rounded-md text-xl bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.price ? "border-red-500" : "border-primary" }`} /> </div> <div className="flex flex-col"> <label className="mb-2 text-invert">عدد</label> <input type="number" value={quantity} onChange={(e) => setQuantity(parseInt(e.target.value))} placeholder="دانە" className={`w-16 px-4 py-2 border rounded-md text-xl bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.quantity ? "border-red-500" : "border-primary" }`} /> </div> </div> <div className="flex justify-center"> <button onClick={handleAddItem} disabled={quantity === 0} className="w-40 px-4 py-2 mt-2 text-4xl rounded-md bg-primary disabled:bg-gray-900 disabled:cursor-not-allowed text-invert hover:bg-primary focus:outline-none focus:ring-2 focus:ring-invert focus:ring-opacity-75" > + </button> </div> {errors.barcode && ( <p className="mt-1 text-xl text-right text-red-500"> {errors.barcode} </p> )} <div className="flex flex-1"> <div className="w-1/4 pl-4"> {customerDetails && ( <div className=""> <h3 className="text-2xl font-semibold text-right text-invert"> زانیاری کڕیار </h3> <div className="p-4 px-2 my-4 border-2 rounded-lg shadow bg-secondary border-primary"> <div className="grid grid-cols-2 gap-8"> <div> <p className="py-2 m-2 mb-2 text-xl text-invert"> {customerDetails.username} </p> <p className={`m-2 mb-2 py-2 ${ customerDetails.amountLent <= 1500 ? "text-green-500 text-xl" : "text-red-500 text-xl" }`} > قەرز : ${debt?.toLocaleString("en-US")} </p> <p className="py-2 m-2 mb-2 text-xl text-invert"> {customerDetails.phone1 && formatPhoneNumber(customerDetails.phone1)} </p> </div> <div> <p className="py-2 m-2 mb-2 text-xl text-invert"> {customerDetails.province} </p> <p className="py-2 m-2 mb-2 text-xl text-invert"> {customerDetails.city} </p> <p className="py-2 m-2 mb-2 text-xl text-invert"> {customerDetails.address} </p> </div> </div> <div className="p-2 mb-4 border-2 rounded-lg shadow bg-secondary border-primary"> <p className="py-2 text-xl text-invert"> پارەی دراو: {paidAmount.toLocaleString("en-US")} $ </p> <p className="py-2 text-xl text-invert"> ماوە: {remainingAmount.toLocaleString("en-US")} $ </p> </div> </div> </div> )} {customerDetails && ( <> {" "} <div className="flex items-center justify-center w-full gap-4 mb-4 text-2xl"> <label className="w-3/5 ml-4 mr-2 text-sm font-semibold text-invert"> بڕی داشکاندن </label> <input type="text" value={formatNumber(discount)} onChange={handleDiscountChange} placeholder="(دینار)" className={`px-4 py-2 border rounded-md text-xl w-full bg-secondary text-invert text-right focus:outline-none focus:ring focus:ring-primary ${ errors.discount ? "border-red-500" : "border-primary" }`} /> </div> <div className="w-full p-4 ml-auto text-right border-2 rounded-lg shadow bg-secondary border-primary"> <div className="flex items-center mb-2 text-right"> <label className="ml-4 mr-2 text-xl font-semibold text-invert"> بڕی پارە </label> <input type="text" value={formatNumber(payment)} onChange={handlePaymentChange} className="w-40 p-4 py-2 text-2xl text-right border rounded-md border-primary bg-secondary text-invert focus:outline-none focus:ring focus:ring-primary" /> </div> <button onClick={handlePayment} disabled={loading} className="w-full px-4 py-2 mt-2 mb-4 text-xl font-semibold rounded-md bg-primary text-invert hover:bg-primary focus:outline-none focus:ring-2 focus:ring-invert focus:ring-opacity-75" > پارەدان </button> </div> </> )} </div> <div className="flex flex-col w-3/4 pr-4"> {items.length > 0 && ( <> <div className="flex-1 "> <h3 className="text-2xl font-semibold text-right text-invert"> زانیاری بەرهەمەکان </h3> <div className="mt-[23px] overflow-y-auto max-h-96 text-xl flex-1 border-1 border-primary rounded-md"> <table className="min-w-full border-2 bg-secondary text-invert border-primary"> <thead> <tr> <th className="w-1/12 px-4 py-2 text-xl text-right border-2 border-primary"> # </th> <th className="w-3/12 px-4 py-2 text-xl text-right border-2 border-primary"> بەرهەم </th> <th className="w-2/12 px-4 py-2 text-xl text-right border-2 border-primary"> دانە </th> <th className="w-2/12 px-4 py-2 text-xl text-right border-2 border-primary"> نرخ </th> <th className="w-2/12 px-4 py-2 text-xl text-right border-2 border-primary"> کۆ </th> <th className="w-2/12 px-4 py-2 text-xl text-right border-2 border-primary"> هەڵەبوو </th> </tr> </thead> <tbody> {items.map((item, index) => ( <tr key={index}> <td className="px-4 py-2 text-xl text-right border"> {index + 1} </td> <td className="px-4 py-2 text-xl text-right border"> {item.name} </td> <td className="px-4 py-2 text-xl text-right border"> {editIndex === index ? ( <input type="number" value={editQuantity} onChange={(e) => setEditQuantity(parseInt(e.target.value)) } className="w-full p-2 text-right text-black rounded-md" /> ) : ( item.quantity )} </td> <td className="px-4 py-2 text-xl text-right border"> {editIndex === index ? ( <input type="text" value={formatNumber(editPrice)} onChange={handleEditPriceChange} className="w-full p-2 text-right text-black rounded-md" /> ) : ( formatNumber(item.price) // Ensure the price is formatted correctly )} </td> <td className="px-4 py-2 text-xl text-right border"> {formatNumber(item.price * item.quantity)} </td> <td className="px-4 py-2 text-xl text-right border"> {editIndex === index ? ( <div className="flex justify-end gap-2"> <button onClick={() => handleSaveEdit(index)} className="px-2 py-1 text-white rounded bg-primary" > نوێکردنەوە </button> <button onClick={handleCancelEdit} className="px-2 py-1 text-white bg-red-600 rounded" > هەڵوەشاندنەوە </button> </div> ) : ( <div className="flex justify-center gap-2"> <button onClick={() => handleEditRow(index)} className="px-2 py-1 text-white rounded bg-primary" > گۆڕین </button> <button onClick={() => handleShowModal(index)} className="px-2 py-1 text-white bg-red-600 rounded" > سڕینەوە </button> </div> )} </td> </tr> ))} </tbody> </table> </div> <h3 className="mt-4 text-2xl font-semibold text-right text-invert"> کۆی نرخ: {totalPrice.toLocaleString("en-US")} </h3> <h3 className="mt-4 text-2xl font-semibold text-right text-invert"> بڕی داشکاندن: {discount.toLocaleString("en-US")} </h3> <h3 className="mt-4 text-2xl font-semibold text-right text-invert"> کۆی نرخ دوای داشکاندن:{" "} {totalPriceAfterDiscount.toLocaleString("en-US")} </h3> </div> </> )} </div> </div> <button onClick={handlePrint} className="flex justify-center w-full px-4 py-2 mt-8 text-2xl rounded-md bg-primary text-invert hover:bg-primary focus:outline-none focus:ring-2 focus:ring-invert focus:ring-opacity-75" > پرینت کردن </button> </div> <ConfirmationModal show={showModal} onClose={() => setShowModal(false)} onConfirm={handleDeleteItem} /> </div> ); }; export default BarcodeChecker;
Editor is loading...
Leave a Comment