classify web

This commit is contained in:
Husanjonazamov
2026-02-24 12:52:49 +05:00
commit 64af77101f
310 changed files with 45449 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
"use client";
import { formatDateMonthYear, t } from "@/utils";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSelector } from "react-redux";
import { useEffect, useState } from "react";
import { CurrentLanguageData } from "@/redux/reducer/languageSlice";
import NoData from "@/components/EmptyStates/NoData";
import { Badge } from "@/components/ui/badge";
import TransactionSkeleton from "@/components/Skeletons/TransactionSkeleton";
import { paymentTransactionApi } from "@/utils/api";
import { toast } from "sonner";
import Pagination from "@/components/Common/Pagination";
import UploadReceiptModal from "./UploadReceiptModal";
const Transactions = () => {
const CurrentLanguage = useSelector(CurrentLanguageData);
const [transactions, setTransactions] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(null);
const [transactionId, setTransactionId] = useState("");
const [IsUploadRecipt, setIsUploadRecipt] = useState(false);
const handleUploadReceipt = (id) => {
setTransactionId(id);
setIsUploadRecipt(true);
};
const fetchTransactions = async () => {
try {
setIsLoading(true);
const res = await paymentTransactionApi.transaction({
page: currentPage,
});
setTotalPages(res.data.data.last_page);
setCurrentPage(res.data.data.current_page);
if (res?.data?.error === false) {
setTransactions(res?.data?.data?.data);
} else {
toast.error(res?.data?.message);
}
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const handlePageChange = (page) => {
setCurrentPage(page);
};
useEffect(() => {
fetchTransactions();
}, [currentPage]);
const getStatusBadge = (status) => {
switch (status) {
case "succeed":
return <Badge className="bg-green-500">{t("completed")}</Badge>;
case "pending":
return <Badge className="bg-yellow-500">{t("pending")}</Badge>;
case "failed":
return <Badge className="bg-red-500">{t("failed")}</Badge>;
case "under review":
return <Badge className="bg-blue-500">{t("underReview")}</Badge>;
default:
return <Badge>{status}</Badge>;
}
};
return (
<>
{isLoading ? (
<TransactionSkeleton />
) : transactions.length > 0 ? (
<>
<div className="overflow-hidden border rounded-md">
<Table>
<TableHeader className="bg-muted">
<TableRow className="text-xs sm:text-sm">
<TableHead>{t("id")}</TableHead>
<TableHead>{t("paymentMethod")}</TableHead>
<TableHead>{t("transactionId")}</TableHead>
<TableHead>{t("date")}</TableHead>
<TableHead>{t("price")}</TableHead>
<TableHead>{t("status")}</TableHead>
</TableRow>
</TableHeader>
<TableBody className="text-xs sm:text-sm">
{transactions.map((transaction) => (
<TableRow
key={transaction?.id}
className="hover:bg-muted text-center"
>
<TableCell>{transaction?.id}</TableCell>
<TableCell>{transaction?.payment_gateway}</TableCell>
<TableCell>{transaction?.order_id}</TableCell>
<TableCell>
{formatDateMonthYear(transaction?.created_at)}
</TableCell>
<TableCell>{transaction?.amount}</TableCell>
<TableCell>
{transaction?.payment_status === "pending" &&
transaction?.payment_gateway === "BankTransfer" ? (
<button
onClick={() => handleUploadReceipt(transaction?.id)}
className="py-2 px-4 rounded whitespace-nowrap text-white bg-primary"
>
{t("uploadReceipt")}
</button>
) : (
getStatusBadge(transaction?.payment_status)
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex justify-end items-center mb-2">
<Pagination
className="mt-7"
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
/>
</div>
</div>
</>
) : (
<NoData name={t("transaction")} />
)}
<UploadReceiptModal
key={IsUploadRecipt}
IsUploadRecipt={IsUploadRecipt}
setIsUploadRecipt={setIsUploadRecipt}
transactionId={transactionId}
setData={setTransactions}
/>
</>
);
};
export default Transactions;

View File

@@ -0,0 +1,136 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { t } from "@/utils";
import { updateBankTransferApi } from "@/utils/api";
import { useState, useCallback } from "react";
import { useDropzone } from "react-dropzone";
import { MdClose } from "react-icons/md";
import { toast } from "sonner";
const UploadReceiptModal = ({
IsUploadRecipt,
setIsUploadRecipt,
transactionId,
setData,
}) => {
const [selectedFile, setSelectedFile] = useState(null);
const [isUploading, setIsUploading] = useState(false);
const [preview, setPreview] = useState(null);
const onDrop = useCallback((acceptedFiles) => {
const file = acceptedFiles[0];
setSelectedFile(file);
// Create preview URL
const previewUrl = URL.createObjectURL(file);
setPreview(previewUrl);
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
"image/*": [".png", ".jpg", ".jpeg"],
},
maxFiles: 1,
multiple: false,
});
const clearSelection = () => {
if (preview) {
URL.revokeObjectURL(preview);
}
setSelectedFile(null);
setPreview(null);
};
const handleReceiptSubmit = async () => {
try {
setIsUploading(true);
const res = await updateBankTransferApi.updateBankTransfer({
payment_transection_id: transactionId,
payment_receipt: selectedFile,
});
if (res?.data?.error === false) {
setData((prevData) =>
prevData.map((item) =>
item.id === transactionId ? res?.data?.data : item
)
);
toast.success(t("receiptUploaded"));
setIsUploadRecipt(false);
} else {
toast.error(res?.data?.message);
}
} catch (error) {
console.log("Failed To Upload Receipt", error);
} finally {
setIsUploading(false);
}
};
return (
<Dialog open={IsUploadRecipt} onOpenChange={setIsUploadRecipt}>
<DialogContent onInteractOutside={(e) => e.preventDefault()}>
<DialogHeader>
<DialogTitle className="text-xl font-semibold">
{t("uploadPaymentReceipt")}
</DialogTitle>
<DialogDescription className="!text-base">
{t("uploadReceiptDescription")}
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4 mt-4">
{!selectedFile ? (
<div
{...getRootProps()}
className={`border-2 border-dashed border-gray-300 rounded-lg p-8 text-center transition-all duration-200 cursor-pointer hover:border-primary hover:bg-gray-50 ${
isDragActive ? "border-primary bg-gray-50" : ""
}`}
>
<input {...getInputProps()} />
{/* <FileImage className="receipt-icon" /> */}
<p className="text-sm text-black opacity-60">
{isDragActive
? t("dropYourReceiptHere")
: t("dragAndDropReceipt")}
</p>
<p className="text-sm text-black opacity-60">
{t("clickToSelect")}
</p>
</div>
) : (
<div className="relative">
<img
src={preview}
alt="Receipt preview"
className="w-full h-48 object-contain rounded-lg border border-gray-200"
/>
<button
className="absolute top-2 right-2 bg-red-500 text-white border-none rounded flex items-center justify-center w-8 h-8 cursor-pointer"
onClick={clearSelection}
>
<MdClose size={24} />
</button>
</div>
)}
<button
className="border-0 bg-primary text-white px-3 py-1.5 rounded w-max self-end disabled:opacity-65"
onClick={handleReceiptSubmit}
disabled={!selectedFile || isUploading}
>
{t("submit")}
</button>
</div>
</DialogContent>
</Dialog>
);
};
export default UploadReceiptModal;