diff --git a/payment-iq/app/api/transactions/deposit/route.ts b/payment-iq/app/api/transactions/deposit/route.ts new file mode 100644 index 0000000..69369bd --- /dev/null +++ b/payment-iq/app/api/transactions/deposit/route.ts @@ -0,0 +1,55 @@ +import { depositTransactionDummyData } from "@/app/features/Pages/transactions/mockData"; +import { formatToDateTimeString } from "@/app/utils/formatDate"; +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + + const status = searchParams.get("status"); + const userId = searchParams.get("userId"); + const depositMethod = searchParams.get("depositMethod"); + const merchandId = searchParams.get("merchandId"); + const transactionId = searchParams.get("transactionId"); + const dateTime = searchParams.get("dateTime"); + + + let filteredTransactions = [...depositTransactionDummyData]; + + console.log(12345, dateTime?.split(" ")[0]); + if (userId) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.userId.toString() === userId + ); + } + + if (status) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.status.toLowerCase() === status.toLowerCase() + ); + } + + if (depositMethod) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.depositMethod.toLowerCase() === depositMethod.toLowerCase() + ); + } + if (merchandId) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.merchandId.toString() === merchandId + ); + } + if (transactionId) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.transactionId.toString() === transactionId + ); + } + + console.log(777, dateTime); + if (dateTime) { + filteredTransactions = filteredTransactions.filter( + (tx) => tx.dateTime.split(" ")[0] === formatToDateTimeString(dateTime).split(" ")[0] + ); + } + + return NextResponse.json(filteredTransactions); +} diff --git a/payment-iq/app/dashboard/transactions/deposits/page.tsx b/payment-iq/app/dashboard/transactions/deposits/page.tsx new file mode 100644 index 0000000..7ed98ea --- /dev/null +++ b/payment-iq/app/dashboard/transactions/deposits/page.tsx @@ -0,0 +1,7 @@ +import DepositsTransactionsTable from "@/app/features/Pages/transactions/DepositTransactionsTable"; + +export default function DepositTransactionPage() { + return ( + + ); +} diff --git a/payment-iq/app/dashboard/transactions/page.tsx b/payment-iq/app/dashboard/transactions/history/page.tsx similarity index 100% rename from payment-iq/app/dashboard/transactions/page.tsx rename to payment-iq/app/dashboard/transactions/history/page.tsx diff --git a/payment-iq/app/features/AdvancedSearch/AdvancedSearch1.tsx b/payment-iq/app/features/AdvancedSearch/AdvancedSearch1.tsx new file mode 100644 index 0000000..1050817 --- /dev/null +++ b/payment-iq/app/features/AdvancedSearch/AdvancedSearch1.tsx @@ -0,0 +1,172 @@ +import { useState } from "react"; +import { + Box, + TextField, + MenuItem, + Button, + Drawer, + FormControl, + Select, + Typography, + Stack, +} from "@mui/material"; +import { DatePicker } from "@mui/x-date-pickers/DatePicker"; +import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import SearchIcon from "@mui/icons-material/Search"; +import RefreshIcon from "@mui/icons-material/Refresh"; + + +interface IForm { + userId: string; + transactionId: string; + transactionReferenceId: string; + currency: string; + state: string; + depositMethod: string; + dateTime: string +} + +interface ILabel { + label: string; + field: string; + type: string; + options?: string[]; +} + +interface IAdvancedSearch { + setForm: () => void; + form: IForm[]; + resetForm: () => void; + labels: ILabel[] +} + +export default function AdvancedSearch1({ setForm, form, resetForm, labels }: IAdvancedSearch) { + const [open, setOpen] = useState(false); + + const handleChange = (field: string, value: any) => { + setForm((prev) => ({ ...prev, [field]: value })); + }; + + const toggleDrawer = + (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === "keydown" && + ((event as React.KeyboardEvent).key === "Tab" || + (event as React.KeyboardEvent).key === "Shift") + ) { + return; + } + + setOpen(open); + }; + + const list = () => ( + + + + + + Search + + {/* Buttons */} + + + + + + + {labels.map(({ label, field, type, options }) => ( + + + {label} + + {type === "text" && ( + handleChange(field, e.target.value)} + /> + )} + {type === "select" && ( + + + + )} + {type === "date" && ( + handleChange(field, newValue)} + renderInput={(params) => ( + + )} + /> + )} + + ))} + + + + + ); + return ( + + + {/* */} + + {list()} + + + ); +} diff --git a/payment-iq/app/features/Pages/transactions/DepositTransactionsTable.tsx b/payment-iq/app/features/Pages/transactions/DepositTransactionsTable.tsx new file mode 100644 index 0000000..ea55be8 --- /dev/null +++ b/payment-iq/app/features/Pages/transactions/DepositTransactionsTable.tsx @@ -0,0 +1,176 @@ +"use client"; +import { useCallback, useEffect, useState } from "react"; +import { + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + FormControl, + Select, + MenuItem, + FormControlLabel, + Checkbox, + Stack, + Paper, + styled, + TextField, +} from "@mui/material"; +import FileUploadIcon from "@mui/icons-material/FileUpload"; +import { DataGrid } from "@mui/x-data-grid"; +import { depositTransactionsColumns, Labels } from "./constants"; +import AdvancedSearch1 from "../../AdvancedSearch/AdvancedSearch1"; +import SearchFilters from "@/app/components/searchFilter/SearchFilters"; +import { exportData } from "@/app/utils/exportData"; +import { ITransaction } from "./types"; + +const paginationModel = { page: 0, pageSize: 50 }; + +export default function DepositsTransactionsTable() { + const [form, setForm] = useState({ + userId: "", + transactionId: "", + transactionReferenceId: "", + currency: "", + state: "", + depositMethod: "", + dateTime: "", + }); + + const [open, setOpen] = useState(false); + const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv"); + const [onlyCurrentTable, setOnlyCurrentTable] = useState(false); + const [transactions, setTransactions] = useState([]); + + + console.log(777, form) + + + + + const fetchTransactions = useCallback(async () => { + try { + const stringForm: Record = Object.fromEntries( + Object.entries(form).map(([key, value]) => [key, value === null ? "" : String(value)]) + ); + const query = new URLSearchParams(stringForm).toString(); + const res = await fetch(`/api/transactions/deposit?${query}`); + const data = await res.json(); + setTransactions(data); + } catch (error) { + console.error('Error fetching transactions:', error); + } + }, [form]); + + + const resetForm = () => { + setForm({ + userId: "", + transactionId: "", + transactionReferenceId: "", + currency: "", + state: "", + depositMethod: "", + dateTime: "", + }); + }; + + useEffect(() => { + fetchTransactions(); + }, [form, fetchTransactions]); + + const handleDeleteFilter = (key: string) => { + setForm((prev) => ({ ...prev, [key]: '' })); + }; + + const handleClearAll = () => { + resetForm() + fetchTransactions() + }; + + + const handleClickField = (field: string, value: string) => { + setForm((prev) => ({ ...prev, [field]: value })); + }; + + + return ( + + + console.log(`setSearchQuery(${e.target.value})`)} + sx={{ width: 300 }} + /> + + + + + + { + handleClickField(params.field, params.value as string) + }} + /> + + {/* Export Dialog */} + setOpen(false)}> + Export Transactions + + + + + setOnlyCurrentTable(e.target.checked)} + /> + } + label="Only export the results in the current table" + sx={{ mt: 2 }} + /> + + + + + + + + ); +} + +const StyledPaper = styled(Paper)(() => ({ + height: "90vh", + width: "80vw" +})); diff --git a/payment-iq/app/features/Pages/transactions/constants.ts b/payment-iq/app/features/Pages/transactions/constants.ts index ccccd1b..40be306 100644 --- a/payment-iq/app/features/Pages/transactions/constants.ts +++ b/payment-iq/app/features/Pages/transactions/constants.ts @@ -119,3 +119,50 @@ export const columns: GridColDef[] = [ // { field: 'originTransactionId', headerName: 'Origin Transaction ID', type: 'number', width: 90 }, // { field: 'transactionReferenceId', headerName: 'Transaction Reference ID', type: 'number', width: 90 }, ]; + +export const depositTransactionsColumns = [ + { field: "userId", headerName: "User ID", width: 130 }, + { field: "merchandId", headerName: "Merchant ID", width: 130 }, + { field: "transactionId", headerName: "Transaction ID", width: 130 }, + { field: "depositMethod", headerName: "Deposit Method", width: 130 }, + { field: "status", headerName: "Status", width: 130 }, + { field: "amount", headerName: "Amount", width: 130 }, + { field: "currency", headerName: "Currency", width: 130 }, + { field: "dateTime", headerName: "Date / Time", width: 130 }, + { field: "errorInfo", headerName: "Error Info", width: 130 }, + { field: "fraudScore", headerName: "Fraud Score", width: 130 }, +] + + +export const currencies = ["USD", "EUR", "GBP"]; +export const states = ["Pending", "Completed", "Failed"]; +export const depositMethod = ["Card", "Bank Transfer"]; + +export const Labels = [ + { label: "User", field: "userId", type: "text" }, + { label: "Transaction ID", field: "transactionId", type: "text" }, + { + label: "Transaction Reference ID", + field: "transactionReferenceId", + type: "text", + }, + { + label: "Currency", + field: "currency", + type: "select", + options: currencies, + }, + { + label: "State", + field: "state", + type: "select", + options: states, + }, + { + label: "Payment Method", + field: "depositMethod", + type: "select", + options: depositMethod, + }, + { label: "Date / Time", field: "dateTime", type: "date" }, +] diff --git a/payment-iq/app/features/Pages/transactions/mockData.ts b/payment-iq/app/features/Pages/transactions/mockData.ts index 6fac954..8699d2c 100644 --- a/payment-iq/app/features/Pages/transactions/mockData.ts +++ b/payment-iq/app/features/Pages/transactions/mockData.ts @@ -2691,11 +2691,12 @@ export const rows = [ // transactionReferenceId: "", // no value provided // }, // ]; + export const transactionDummyData = [ { id: 1, merchandId: 100987998, - transactionID: 1049131973, + transactionID: 1049136973, user: 1, created: "2025-06-18 10:10:30", state: "FAILED", @@ -2705,7 +2706,7 @@ export const transactionDummyData = [ { id: 2, merchandId: 100987998, - transactionID: 1049131973, + transactionID: 1049131975, user: 2, created: "2025-06-18 10:10:30", state: "FAILED", @@ -2715,7 +2716,7 @@ export const transactionDummyData = [ { id: 3, merchandId: 100987998, - transactionID: 1049131973, + transactionID: 1049131975, user: 3, created: "2025-06-18 10:10:30", state: "Completed", @@ -2725,7 +2726,7 @@ export const transactionDummyData = [ { id: 4, merchandId: 100987998, - transactionID: 1049131973, + transactionID: 1049131975, user: 4, created: "2025-06-18 10:10:30", state: "FAILED", @@ -2735,7 +2736,7 @@ export const transactionDummyData = [ { id: 5, merchandId: 100987998, - transactionID: 1049131973, + transactionID: 1049131975, user: 5, created: "2025-06-18 10:10:30", state: "FAILED", @@ -2743,3 +2744,149 @@ export const transactionDummyData = [ pspStatusCode: 100501, }, ]; + +export const depositTransactionDummyData = [ + { + id: 1, + userId: 17, + merchandId: 100987998, + transactionId: 1049131973, + depositMethod: "Card", + status: "Completed", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 2, + userId: 17, + merchandId: 100987998, + transactionId: 1049131973, + depositMethod: "Card", + status: "Completed", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 3, + userId: 17, + merchandId: 100987997, + transactionId: 1049131973, + depositMethod: "Card", + status: "Complete", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 4, + userId: 19, + merchandId: 100987997, + transactionId: 1049136973, + depositMethod: "Card", + status: "Completed", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 5, + userId: 19, + merchandId: 100987998, + transactionId: 1049131973, + depositMethod: "Card", + status: "Completed", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 6, + userId: 27, + merchandId: 100987997, + transactionId: 1049131973, + depositMethod: "Card", + status: "Pending", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 7, + userId: 175, + merchandId: 100987938, + transactionId: 1049136973, + depositMethod: "Card", + status: "Pending", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-18 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 8, + userId: 172, + merchandId: 100987938, + transactionId: 1049131973, + depositMethod: "Card", + status: "Pending", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-12 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 9, + userId: 174, + merchandId: 100987938, + transactionId: 1049131973, + depositMethod: "Bank Transfer", + status: "Inprogress", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-17 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 10, + userId: 7, + merchandId: 100987998, + transactionId: 1049131973, + depositMethod: "Bank Transfer", + status: "Inprogress", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-17 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, + { + id: 11, + userId: 1, + merchandId: 100987998, + transactionId: 1049131973, + depositMethod: "Bank Transfer", + status: "Error", + amount: 4000, + currency: "EUR", + dateTime: "2025-06-17 10:10:30", + errorInfo: "-", + fraudScore: "frad score 1234", + }, +]; diff --git a/payment-iq/app/features/Pages/transactions/types.ts b/payment-iq/app/features/Pages/transactions/types.ts new file mode 100644 index 0000000..eb53823 --- /dev/null +++ b/payment-iq/app/features/Pages/transactions/types.ts @@ -0,0 +1,3 @@ + export interface ITransaction { + [key: string]: string | number; // Replace with actual fields if known, e.g. id: string, amount: number, etc. + } diff --git a/payment-iq/app/features/dashboard/sidebar/SidebarLink.constants.ts b/payment-iq/app/features/dashboard/sidebar/SidebarLink.constants.ts index c732888..f7bb406 100644 --- a/payment-iq/app/features/dashboard/sidebar/SidebarLink.constants.ts +++ b/payment-iq/app/features/dashboard/sidebar/SidebarLink.constants.ts @@ -10,14 +10,36 @@ import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings"; import InsightsIcon from "@mui/icons-material/Insights"; import ListAltIcon from "@mui/icons-material/ListAlt"; import SettingsIcon from "@mui/icons-material/Settings"; + +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import HistoryIcon from '@mui/icons-material/History'; + import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces"; export const PAGE_LINKS: ISidebarLink[] = [ { title: "Home", path: "/dashboard", icon: HomeIcon }, + { title: "Transaction", path: "/dashboard/transactions", - icon: AccountBalanceWalletIcon, + icon: AccountBalanceWalletIcon, children: [ + { + title: "Deposits", + path: "/dashboard/transactions/deposits", + icon: ArrowDownwardIcon, + }, + { + title: "Withdrawals", + path: "/dashboard/transactions/withdrawals", + icon: ArrowUpwardIcon, + }, + { + title: "Transaction History", + path: "/dashboard/transactions/history", + icon: HistoryIcon, + }, + ], }, { title: "Approve", path: "/dashboard/approve", icon: CheckCircleIcon }, { title: "Investigate", path: "/dashboard/investigate", icon: SearchIcon }, diff --git a/payment-iq/app/utils/exportData.ts b/payment-iq/app/utils/exportData.ts new file mode 100644 index 0000000..d68b8b5 --- /dev/null +++ b/payment-iq/app/utils/exportData.ts @@ -0,0 +1,37 @@ +import * as XLSX from "xlsx"; +import { GridColDef } from "@mui/x-data-grid"; +export type FileType = "csv" | "xls" | "xlsx"; +import { saveAs } from "file-saver"; + +import type { ITransaction } from "../features/Pages/transactions/types"; + + +export const exportData = ( + transactions: ITransaction[], + columns: GridColDef[], + fileType: FileType = "csv", + onlyCurrentTable = false, + setOpen: (open: boolean) => void +) => { + const exportRows = onlyCurrentTable ? transactions.slice(0, 5) : transactions; + const exportData = [ + columns.map((col) => col.headerName), + ...exportRows.map((row) => columns.map((col) => row[col.field] ?? "")), + ]; + + const worksheet = XLSX.utils.aoa_to_sheet(exportData); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, "Transactions"); + + if (fileType === "csv") { + const csv = XLSX.utils.sheet_to_csv(worksheet); + const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); + saveAs(blob, "transactions.csv"); + } else { + XLSX.writeFile(workbook, `transactions.${fileType}`, { + bookType: fileType, + }); + } + + setOpen(false); + }; diff --git a/payment-iq/app/utils/formatDate.ts b/payment-iq/app/utils/formatDate.ts new file mode 100644 index 0000000..93c2071 --- /dev/null +++ b/payment-iq/app/utils/formatDate.ts @@ -0,0 +1,13 @@ +export const formatToDateTimeString = (dateString: string): string => { + const date = new Date(dateString); + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // months are 0-indexed + const day = String(date.getDate()).padStart(2, '0'); + + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} \ No newline at end of file