Untitled
unknown
jsx
a year ago
26 kB
6
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, index) => {
const value = e.target.value.replace(/,/g, ""); // Remove commas for parsing
const updatedPrice = value ? parseFloat(value) : 0; // Update state with numeric value
const updatedItems = items.map((item, i) => {
if (i === index) {
return { ...item, price: updatedPrice };
}
return item;
});
setItems(updatedItems);
};
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: 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">
<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"
}`}
/>
<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"
}`}
/>
<input
type="text"
value={formatNumber(price)}
onChange={(e) => handlePriceChange(e, -1)}
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"
}`}
/>
<input
type="number"
value={quantity}
onChange={(e) => setQuantity(parseInt(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.quantity ? "border-red-500" : "border-primary"
}`}
/>
</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="number"
value={discount}
onChange={(e) => setDiscount(parseFloat(e.target.value))}
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-32 px-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}
className={
item.quantity < 0 ||
item.quantity > item.availableQuantity
? "bg-red-300"
: ""
}
>
<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={(e) =>
handlePriceChange(e, index)
}
className="w-full p-2 text-right text-black rounded-md"
/>
) : (
formatNumber(item.price)
)}
</td>
<td className="px-4 py-2 text-xl text-right border">
{(item.price * item.quantity).toLocaleString(
"en-US"
)}
</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