commit
c3e368913e
88
payment-iq/app/api/dashboard/audits/mockData.ts
Normal file
88
payment-iq/app/api/dashboard/audits/mockData.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { GridColDef } from "@mui/x-data-grid";
|
||||||
|
|
||||||
|
export const AuditColumns: GridColDef[] = [
|
||||||
|
{ field: "actionType", headerName: "Action Type", width: 130 },
|
||||||
|
{
|
||||||
|
field: "timeStampOfTheAction",
|
||||||
|
headerName: "Timestamp of the action",
|
||||||
|
width: 130,
|
||||||
|
},
|
||||||
|
{ field: "adminUsername", headerName: "Admin username", width: 130 },
|
||||||
|
{ field: "adminId", headerName: "Admin ID", width: 130 },
|
||||||
|
{ field: "affectedUserId", headerName: "Affected user ID", width: 130 },
|
||||||
|
{ field: "adminIPAddress", headerName: "Admin IP address", width: 130 },
|
||||||
|
{ field: "reasonNote", headerName: "Reason/Note", width: 130 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AuditData = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
actionType: "Create",
|
||||||
|
timeStampOfTheAction: "2023-03-01T12:00:00",
|
||||||
|
adminUsername: "admin1",
|
||||||
|
adminId: "12345",
|
||||||
|
affectedUserId: "67890",
|
||||||
|
adminIPAddress: "192.168.1.1",
|
||||||
|
reasonNote: "New user created",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
actionType: "Update",
|
||||||
|
timeStampOfTheAction: "2023-03-02T12:00:00",
|
||||||
|
adminUsername: "admin2",
|
||||||
|
adminId: "54321",
|
||||||
|
affectedUserId: "09876",
|
||||||
|
adminIPAddress: "192.168.2.2",
|
||||||
|
reasonNote: "User details updated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
actionType: "Delete",
|
||||||
|
timeStampOfTheAction: "2023-03-03T12:00:00",
|
||||||
|
adminUsername: "admin3",
|
||||||
|
adminId: "98765",
|
||||||
|
affectedUserId: "45678",
|
||||||
|
adminIPAddress: "192.168.3.3",
|
||||||
|
reasonNote: "User deleted",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
actionType: "Create",
|
||||||
|
timeStampOfTheAction: "2023-03-04T12:00:00",
|
||||||
|
adminUsername: "admin4",
|
||||||
|
adminId: "98765",
|
||||||
|
affectedUserId: "45678",
|
||||||
|
adminIPAddress: "192.168.3.3",
|
||||||
|
reasonNote: "New user created",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "5",
|
||||||
|
actionType: "Update",
|
||||||
|
timeStampOfTheAction: "2023-03-05T12:00:00",
|
||||||
|
adminUsername: "admin2",
|
||||||
|
adminId: "98765",
|
||||||
|
affectedUserId: "45678",
|
||||||
|
adminIPAddress: "192.168.3.3",
|
||||||
|
reasonNote: "User details updated",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AuditSearchLabels = [
|
||||||
|
{ label: "Action Type", field: "actionType", type: "text" },
|
||||||
|
{ label: "Date / Time", field: "dateTime", type: "date" },
|
||||||
|
{
|
||||||
|
label: "affectedUserId",
|
||||||
|
field: "Affected user ID",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Admin ID",
|
||||||
|
field: "adminId",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Admin username",
|
||||||
|
field: "adminUsername",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
];
|
||||||
52
payment-iq/app/api/dashboard/audits/route.ts
Normal file
52
payment-iq/app/api/dashboard/audits/route.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { AuditColumns, AuditData, AuditSearchLabels } from "./mockData";
|
||||||
|
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
|
||||||
|
const actionType = searchParams.get("actionType");
|
||||||
|
const affectedUserId = searchParams.get("affectedUserId");
|
||||||
|
const adminId = searchParams.get("adminId");
|
||||||
|
const adminUsername = searchParams.get("adminUsername");
|
||||||
|
const timeStampOfTheAction = searchParams.get("dateTime");
|
||||||
|
|
||||||
|
let filteredRows = [...AuditData];
|
||||||
|
|
||||||
|
if (actionType) {
|
||||||
|
filteredRows = filteredRows.filter(
|
||||||
|
(tx) => tx.actionType.toLocaleLowerCase() === actionType.toLocaleLowerCase(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (affectedUserId) {
|
||||||
|
filteredRows = filteredRows.filter(
|
||||||
|
(tx) => tx.affectedUserId.toLowerCase() === affectedUserId.toLowerCase(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adminId) {
|
||||||
|
filteredRows = filteredRows.filter(
|
||||||
|
(tx) => tx.adminId === adminId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (adminUsername) {
|
||||||
|
filteredRows = filteredRows.filter(
|
||||||
|
(tx) => tx.adminUsername === adminUsername,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeStampOfTheAction) {
|
||||||
|
filteredRows = filteredRows.filter(
|
||||||
|
(tx) =>
|
||||||
|
tx.timeStampOfTheAction.split(" ")[0] ===
|
||||||
|
formatToDateTimeString(timeStampOfTheAction).split(" ")[0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
tableRows: filteredRows,
|
||||||
|
tableColumns: AuditColumns,
|
||||||
|
tableSearchLabels: AuditSearchLabels,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,5 +1,9 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { depositTransactionDummyData, depositTransactionsColumns, depositTransactionsSearchLabels } from "./mockData";
|
import {
|
||||||
|
depositTransactionDummyData,
|
||||||
|
depositTransactionsColumns,
|
||||||
|
depositTransactionsSearchLabels,
|
||||||
|
} from "./mockData";
|
||||||
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
@ -12,46 +16,47 @@ export async function GET(request: NextRequest) {
|
|||||||
const transactionId = searchParams.get("transactionId");
|
const transactionId = searchParams.get("transactionId");
|
||||||
const dateTime = searchParams.get("dateTime");
|
const dateTime = searchParams.get("dateTime");
|
||||||
|
|
||||||
|
|
||||||
let filteredTransactions = [...depositTransactionDummyData];
|
let filteredTransactions = [...depositTransactionDummyData];
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.userId.toString() === userId
|
(tx) => tx.userId.toString() === userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.status.toLowerCase() === status.toLowerCase()
|
(tx) => tx.status.toLowerCase() === status.toLowerCase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depositMethod) {
|
if (depositMethod) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.depositMethod.toLowerCase() === depositMethod.toLowerCase()
|
(tx) => tx.depositMethod.toLowerCase() === depositMethod.toLowerCase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (merchandId) {
|
if (merchandId) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.merchandId.toString() === merchandId
|
(tx) => tx.merchandId.toString() === merchandId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (transactionId) {
|
if (transactionId) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.transactionId.toString() === transactionId
|
(tx) => tx.transactionId.toString() === transactionId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateTime) {
|
if (dateTime) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.dateTime.split(" ")[0] === formatToDateTimeString(dateTime).split(" ")[0]
|
(tx) =>
|
||||||
|
tx.dateTime.split(" ")[0] ===
|
||||||
|
formatToDateTimeString(dateTime).split(" ")[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
filteredTransactions: filteredTransactions,
|
tableRows: filteredTransactions,
|
||||||
transactionsSearchLabels: depositTransactionsSearchLabels,
|
tableSearchLabels: depositTransactionsSearchLabels,
|
||||||
transactionsColumns: depositTransactionsColumns
|
tableColumns: depositTransactionsColumns,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1,5 +1,9 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { withdrawalTransactionDummyData, withdrawalTransactionsColumns, withdrawalTransactionsSearchLabels } from "./mockData"
|
import {
|
||||||
|
withdrawalTransactionDummyData,
|
||||||
|
withdrawalTransactionsColumns,
|
||||||
|
withdrawalTransactionsSearchLabels,
|
||||||
|
} from "./mockData";
|
||||||
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
@ -10,36 +14,38 @@ export async function GET(request: NextRequest) {
|
|||||||
const dateTime = searchParams.get("dateTime");
|
const dateTime = searchParams.get("dateTime");
|
||||||
const withdrawalMethod = searchParams.get("withdrawalMethod");
|
const withdrawalMethod = searchParams.get("withdrawalMethod");
|
||||||
|
|
||||||
|
|
||||||
let filteredTransactions = [...withdrawalTransactionDummyData];
|
let filteredTransactions = [...withdrawalTransactionDummyData];
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.userId.toString() === userId
|
(tx) => tx.userId.toString() === userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.status.toLowerCase() === status.toLowerCase()
|
(tx) => tx.status.toLowerCase() === status.toLowerCase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (withdrawalMethod) {
|
if (withdrawalMethod) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.withdrawalMethod.toLowerCase() === withdrawalMethod.toLowerCase()
|
(tx) =>
|
||||||
|
tx.withdrawalMethod.toLowerCase() === withdrawalMethod.toLowerCase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateTime) {
|
if (dateTime) {
|
||||||
filteredTransactions = filteredTransactions.filter(
|
filteredTransactions = filteredTransactions.filter(
|
||||||
(tx) => tx.dateTime.split(" ")[0] === formatToDateTimeString(dateTime).split(" ")[0]
|
(tx) =>
|
||||||
|
tx.dateTime.split(" ")[0] ===
|
||||||
|
formatToDateTimeString(dateTime).split(" ")[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
filteredTransactions: filteredTransactions,
|
tableRows: filteredTransactions,
|
||||||
transactionsColumns: withdrawalTransactionsColumns,
|
tableSearchLabels: withdrawalTransactionsSearchLabels,
|
||||||
transactionsSearchLabels: withdrawalTransactionsSearchLabels
|
tableColumns: withdrawalTransactionsColumns,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
22
payment-iq/app/dashboard/audits/page.tsx
Normal file
22
payment-iq/app/dashboard/audits/page.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import DataTable from "@/app/features/DataTable/DataTable";
|
||||||
|
import { getAudits } from "@/app/services/audits";
|
||||||
|
|
||||||
|
export default async function AuditPage({
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
||||||
|
}) {
|
||||||
|
// Await searchParams before processing
|
||||||
|
const params = await searchParams;
|
||||||
|
// Create a safe query string by filtering only string values
|
||||||
|
const safeParams: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
safeParams[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const query = new URLSearchParams(safeParams).toString();
|
||||||
|
const data = await getAudits({ query });
|
||||||
|
|
||||||
|
return <DataTable data={data} />;
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
|
import DataTable from "@/app/features/DataTable/DataTable";
|
||||||
import TransactionsTable from "@/app/features/pages/transactions/TransactionsTable";
|
|
||||||
import { getTransactions } from "@/app/services/transactions";
|
import { getTransactions } from "@/app/services/transactions";
|
||||||
|
|
||||||
export default async function DepositTransactionPage({
|
export default async function DepositTransactionPage({
|
||||||
@ -17,8 +16,8 @@ export default async function DepositTransactionPage({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const query = new URLSearchParams(safeParams).toString();
|
const query = new URLSearchParams(safeParams).toString();
|
||||||
const transactionType = 'deposit';
|
const transactionType = "deposits";
|
||||||
const data = await getTransactions({ transactionType, query });
|
const data = await getTransactions({ transactionType, query });
|
||||||
|
|
||||||
return <TransactionsTable res={data}/>;
|
return <DataTable data={data} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,3 @@
|
|||||||
import TransactionsTable from "@/app/features/pages/transactions/TransactionsTable";
|
export default async function HistoryTransactionPage() {
|
||||||
import { getTransactions } from "@/app/services/transactions";
|
return <div>History Transactions Page</div>;
|
||||||
|
|
||||||
export default async function DepositTransactionPage({
|
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
|
||||||
}) {
|
|
||||||
// Await searchParams before processing
|
|
||||||
const params = await searchParams;
|
|
||||||
// Create a safe query string by filtering only string values
|
|
||||||
const safeParams: Record<string, string> = {};
|
|
||||||
for (const [key, value] of Object.entries(params)) {
|
|
||||||
if (typeof value === "string") {
|
|
||||||
safeParams[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const query = new URLSearchParams(safeParams).toString();
|
|
||||||
const transactionType = 'deposit';
|
|
||||||
const data = await getTransactions({ transactionType, query });
|
|
||||||
|
|
||||||
return <TransactionsTable res={data}/>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import TransactionsTable from "@/app/features/pages/transactions/TransactionsTable";
|
import DataTable from "@/app/features/DataTable/DataTable";
|
||||||
import { getTransactions } from "@/app/services/transactions";
|
import { getTransactions } from "@/app/services/transactions";
|
||||||
|
|
||||||
export default async function WithdrawalTransactionPage({
|
export default async function WithdrawalTransactionPage({
|
||||||
@ -16,8 +16,8 @@ export default async function WithdrawalTransactionPage({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const query = new URLSearchParams(safeParams).toString();
|
const query = new URLSearchParams(safeParams).toString();
|
||||||
const transactionType = 'withdrawal';
|
const transactionType = "withdrawal";
|
||||||
const data = await getTransactions({ transactionType, query });
|
const data = await getTransactions({ transactionType, query });
|
||||||
|
|
||||||
return <TransactionsTable res={data}/>;
|
return <DataTable data={data} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,16 +17,9 @@ import SearchIcon from "@mui/icons-material/Search";
|
|||||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
import { useSearchParams, useRouter } from "next/navigation";
|
import { useSearchParams, useRouter } from "next/navigation";
|
||||||
import { useState, useEffect, useMemo } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
|
import { ISearchLabel } from "../pages/transactions/types";
|
||||||
|
|
||||||
|
export default function AdvancedSearch({ labels }: { labels: ISearchLabel[] }) {
|
||||||
interface ILabel {
|
|
||||||
label: string;
|
|
||||||
field: string;
|
|
||||||
type: string;
|
|
||||||
options?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@ -37,7 +30,6 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
setFormValues(initialParams);
|
setFormValues(initialParams);
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
|
|
||||||
const updateURL = useMemo(
|
const updateURL = useMemo(
|
||||||
() =>
|
() =>
|
||||||
debounce((newValues: Record<string, string>) => {
|
debounce((newValues: Record<string, string>) => {
|
||||||
@ -47,7 +39,7 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
});
|
});
|
||||||
router.push(`?${updatedParams.toString()}`);
|
router.push(`?${updatedParams.toString()}`);
|
||||||
}, 500),
|
}, 500),
|
||||||
[router]
|
[router],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFieldChange = (field: string, value: string) => {
|
const handleFieldChange = (field: string, value: string) => {
|
||||||
@ -61,19 +53,20 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
router.push("?");
|
router.push("?");
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDrawer = (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
|
const toggleDrawer =
|
||||||
if (
|
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
|
||||||
event.type === "keydown" &&
|
if (
|
||||||
((event as React.KeyboardEvent).key === "Tab" ||
|
event.type === "keydown" &&
|
||||||
(event as React.KeyboardEvent).key === "Shift")
|
((event as React.KeyboardEvent).key === "Tab" ||
|
||||||
) {
|
(event as React.KeyboardEvent).key === "Shift")
|
||||||
return;
|
) {
|
||||||
}
|
return;
|
||||||
setOpen(open);
|
}
|
||||||
};
|
setOpen(open);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: '185px' }}>
|
<Box sx={{ width: "185px" }}>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
@ -137,7 +130,9 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
value={formValues[field] || ""}
|
value={formValues[field] || ""}
|
||||||
onChange={(e) => handleFieldChange(field, e.target.value)}
|
onChange={(e) =>
|
||||||
|
handleFieldChange(field, e.target.value)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -145,7 +140,9 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
<FormControl fullWidth size="small">
|
<FormControl fullWidth size="small">
|
||||||
<Select
|
<Select
|
||||||
value={formValues[field] || ""}
|
value={formValues[field] || ""}
|
||||||
onChange={(e) => handleFieldChange(field, e.target.value)}
|
onChange={(e) =>
|
||||||
|
handleFieldChange(field, e.target.value)
|
||||||
|
}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
>
|
>
|
||||||
<MenuItem value="">
|
<MenuItem value="">
|
||||||
@ -162,15 +159,17 @@ export default function AdvancedSearch({ labels }: { labels: ILabel[] }) {
|
|||||||
|
|
||||||
{type === "date" && (
|
{type === "date" && (
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={formValues[field] ? new Date(formValues[field]) : null}
|
value={
|
||||||
|
formValues[field] ? new Date(formValues[field]) : null
|
||||||
|
}
|
||||||
onChange={(newValue) =>
|
onChange={(newValue) =>
|
||||||
handleFieldChange(
|
handleFieldChange(
|
||||||
field,
|
field,
|
||||||
newValue?.toISOString() || ""
|
newValue?.toISOString() || "",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
textField: { fullWidth: true, size: "small" }
|
textField: { fullWidth: true, size: "small" },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -18,27 +18,28 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import FileUploadIcon from "@mui/icons-material/FileUpload";
|
import FileUploadIcon from "@mui/icons-material/FileUpload";
|
||||||
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
||||||
import AdvancedSearch from "../../AdvancedSearch/AdvancedSearch";
|
import AdvancedSearch from "../AdvancedSearch/AdvancedSearch";
|
||||||
import SearchFilters from "@/app/components/searchFilter/SearchFilters";
|
import SearchFilters from "@/app/components/searchFilter/SearchFilters";
|
||||||
import { exportData } from "@/app/utils/exportData";
|
import { exportData } from "@/app/utils/exportData";
|
||||||
import { ITransaction } from "./types";
|
import { IDataTable } from "./types";
|
||||||
|
|
||||||
interface IDepositProps {
|
|
||||||
res: ITransaction;
|
interface IDataTableProps<TRow, TColumn> {
|
||||||
|
data: IDataTable<TRow, TColumn>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransactionsTable = ({ res }: IDepositProps) => {
|
export type TWithId = { id: number };
|
||||||
const {
|
const DataTable = <TRow extends TWithId, TColumn extends GridColDef>(
|
||||||
filteredTransactions,
|
data: IDataTableProps<TRow, TColumn>,
|
||||||
transactionsColumns,
|
) => {
|
||||||
transactionsSearchLabels,
|
const { tableRows, tableColumns, tableSearchLabels } = data.data;
|
||||||
} = res;
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv");
|
const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv");
|
||||||
const [onlyCurrentTable, setOnlyCurrentTable] = useState(false);
|
const [onlyCurrentTable, setOnlyCurrentTable] = useState(false);
|
||||||
|
const [rows, setRows] = useState<TRow[]>(tableRows);
|
||||||
|
|
||||||
const filters = Object.fromEntries(searchParams.entries());
|
const filters = Object.fromEntries(searchParams.entries());
|
||||||
|
|
||||||
@ -55,16 +56,19 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [rows, setRows] = useState(filteredTransactions);
|
const getColumnsWithDropdown = (columns: TColumn[]): GridColDef[] => {
|
||||||
const getColumnsWithDropdown = (columns: GridColDef[]) => {
|
return columns?.map((col) => {
|
||||||
return columns.map((col) => {
|
|
||||||
if (col.field === "actions") {
|
if (col.field === "actions") {
|
||||||
return {
|
return {
|
||||||
...col,
|
...col,
|
||||||
renderCell: (params: GridRenderCellParams) => {
|
renderCell: (params: GridRenderCellParams) => {
|
||||||
const row = filteredTransactions.find((r) => r.id === params.id);
|
const row = tableRows.find((r) => r.id === params.id) as {
|
||||||
const options = row?.options;
|
id: number;
|
||||||
|
status?: string;
|
||||||
|
options?: { value: string; label: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = row?.options;
|
||||||
if (!options) return params.value;
|
if (!options) return params.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -79,9 +83,9 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
||||||
"& .MuiSelect-select": { py: 0.5 },
|
"& .MuiSelect-select": { py: 0.5 },
|
||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()} // Prevent row click when selecting
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{options.map((option: { value: string; label: string }) => (
|
{options.map((option) => (
|
||||||
<MenuItem key={option.value} value={option.value}>
|
<MenuItem key={option.value} value={option.value}>
|
||||||
{option.label}
|
{option.label}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -91,6 +95,7 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return col;
|
return col;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -110,7 +115,7 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
onChange={(e) => console.log(`setSearchQuery(${e.target.value})`)}
|
onChange={(e) => console.log(`setSearchQuery(${e.target.value})`)}
|
||||||
sx={{ width: 300 }}
|
sx={{ width: 300 }}
|
||||||
/>
|
/>
|
||||||
<AdvancedSearch labels={transactionsSearchLabels} />
|
<AdvancedSearch labels={tableSearchLabels} />
|
||||||
<SearchFilters filters={filters} />
|
<SearchFilters filters={filters} />
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@ -122,8 +127,8 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={filteredTransactions}
|
rows={tableRows}
|
||||||
columns={getColumnsWithDropdown(transactionsColumns) as GridColDef[]}
|
columns={getColumnsWithDropdown(tableColumns)}
|
||||||
initialState={{
|
initialState={{
|
||||||
pagination: { paginationModel: { pageSize: 50 } },
|
pagination: { paginationModel: { pageSize: 50 } },
|
||||||
}}
|
}}
|
||||||
@ -175,8 +180,8 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
exportData(
|
exportData(
|
||||||
filteredTransactions as unknown as ITransaction[],
|
tableRows,
|
||||||
transactionsColumns,
|
tableColumns,
|
||||||
fileType,
|
fileType,
|
||||||
onlyCurrentTable,
|
onlyCurrentTable,
|
||||||
setOpen,
|
setOpen,
|
||||||
@ -191,4 +196,4 @@ const TransactionsTable = ({ res }: IDepositProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TransactionsTable;
|
export default DataTable;
|
||||||
12
payment-iq/app/features/DataTable/types.ts
Normal file
12
payment-iq/app/features/DataTable/types.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
interface ISearchLabel {
|
||||||
|
label: string;
|
||||||
|
field: string;
|
||||||
|
type: string;
|
||||||
|
options?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDataTable<TRow, TColumn> {
|
||||||
|
tableRows: TRow[];
|
||||||
|
tableColumns: TColumn[];
|
||||||
|
tableSearchLabels: ISearchLabel[];
|
||||||
|
}
|
||||||
@ -1,168 +0,0 @@
|
|||||||
import { GridColDef } from "@mui/x-data-grid";
|
|
||||||
|
|
||||||
export const columns: GridColDef[] = [
|
|
||||||
{ field: "merchandId", headerName: "Merchant ID", width: 130 },
|
|
||||||
{ field: "transactionID", headerName: "Transaction ID", width: 130 },
|
|
||||||
{ field: "user", headerName: "User", width: 75 },
|
|
||||||
{ field: "created", headerName: "Created", type: "number", width: 130 },
|
|
||||||
{ field: "state", headerName: "State", type: "number", width: 130 },
|
|
||||||
{
|
|
||||||
field: "statusDescription",
|
|
||||||
headerName: "Status Description",
|
|
||||||
type: "number",
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: "statusCode",
|
|
||||||
headerName: "Status Code",
|
|
||||||
type: "number",
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: "pspStatusCode",
|
|
||||||
headerName: "PSP Status Code",
|
|
||||||
type: "number",
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: "pspStatusMessage",
|
|
||||||
headerName: "PSP Status Message",
|
|
||||||
type: "number",
|
|
||||||
width: 90,
|
|
||||||
},
|
|
||||||
{ field: "psp", headerName: "PSP", type: "number", width: 90 },
|
|
||||||
{ field: "pspAccount", headerName: "PSP Account", type: "number", width: 90 },
|
|
||||||
{ field: "initPSP", headerName: "Init PSP", type: "number", width: 90 },
|
|
||||||
{
|
|
||||||
field: "initPSPAccout",
|
|
||||||
headerName: "Init PSP Account",
|
|
||||||
type: "number",
|
|
||||||
width: 90,
|
|
||||||
},
|
|
||||||
{ field: "pspService", headerName: "PSP Service", type: "number", width: 90 },
|
|
||||||
{
|
|
||||||
field: "transactionType",
|
|
||||||
headerName: "Transaction Type",
|
|
||||||
type: "number",
|
|
||||||
width: 90,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: "paymentMethod",
|
|
||||||
headerName: "Payment Method",
|
|
||||||
type: "number",
|
|
||||||
width: 90,
|
|
||||||
},
|
|
||||||
{ field: "rules", headerName: "Rules", type: "number", width: 90 },
|
|
||||||
{ field: "amount", headerName: "Amount", type: "number", width: 90 },
|
|
||||||
{ field: "fee", headerName: "Fee", type: "number", width: 90 },
|
|
||||||
{
|
|
||||||
field: "transactionAmount",
|
|
||||||
headerName: "Transaction Amount",
|
|
||||||
type: "number",
|
|
||||||
width: 90,
|
|
||||||
},
|
|
||||||
// { field: 'baseAmount', headerName: 'Base Amount', type: 'number', width: 90 },
|
|
||||||
// { field: 'baseFee', headerName: 'Base Fee', type: 'number', width: 90 },
|
|
||||||
// { field: 'baseTransaction', headerName: 'Base Transaction', type: 'number', width: 90 },
|
|
||||||
// { field: 'pspFee', headerName: 'PSP Fee', type: 'number', width: 90 },
|
|
||||||
// { field: 'basePspFee', headerName: 'Base PSP Fee', type: 'number', width: 90 },
|
|
||||||
// { field: 'authAmount', headerName: 'Auth Amount', type: 'number', width: 90 },
|
|
||||||
// { field: 'baseAuthAmount', headerName: 'Base Auth Amount', type: 'number', width: 90 },
|
|
||||||
// { field: 'userBalance', headerName: 'User Balance', type: 'number', width: 90 },
|
|
||||||
// { field: 'updated', headerName: 'Updated', type: 'number', width: 90 },
|
|
||||||
// { field: 'userIp', headerName: 'User IP', type: 'number', width: 90 },
|
|
||||||
// { field: 'channel', headerName: 'Channel', type: 'number', width: 90 },
|
|
||||||
// { field: 'depositType', headerName: 'Deposit Type', type: 'number', width: 90 },
|
|
||||||
// { field: 'userEmal', headerName: 'User Emal', type: 'number', width: 90 },
|
|
||||||
// { field: 'userCategory', headerName: 'User Category', type: 'number', width: 90 },
|
|
||||||
// { field: 'userCountry', headerName: 'User Country', type: 'number', width: 90 },
|
|
||||||
// { field: 'userAccount', headerName: 'User Account', type: 'number', width: 90 },
|
|
||||||
// { field: 'bankName', headerName: 'Bank Name', type: 'number', width: 90 },
|
|
||||||
// { field: 'pspUserReference', headerName: 'PSP User Reference', type: 'number', width: 90 },
|
|
||||||
// { field: 'pspFraudScore', headerName: 'PSP Fraud Score', type: 'number', width: 90 },
|
|
||||||
// { field: 'fraudStatus', headerName: 'FraudStatus', type: 'number', width: 90 },
|
|
||||||
// { field: 'blocked', headerName: 'Blocked', type: 'number', width: 90 },
|
|
||||||
// { field: 'abuse', headerName: 'Abuse', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycStatus', headerName: 'KYC Status', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycPSPName', headerName: 'KYC PSP Name', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycPSPStatus', headerName: 'KYC PSP Status', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycIdStatus', headerName: 'KYC ID Status', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycAddressStatus', headerName: 'KYC Address Status', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycAgeStatus', headerName: 'KYC Age Status', type: 'number', width: 90 },
|
|
||||||
// { field: 'kycPEPAndSanction', headerName: 'KYC PEP And Sanction', type: 'number', width: 90 },
|
|
||||||
// { field: 'pspReferenceId', headerName: 'PSPReferenceID', type: 'number', width: 90 },
|
|
||||||
// { field: 'siteReferenceId', headerName: 'Site Reference ID', type: 'number', width: 90 },
|
|
||||||
// { field: 'info', headerName: 'Info', type: 'number', width: 90 },
|
|
||||||
// { field: 'accountHolder', headerName: 'Account Holder', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstName', headerName: 'First Name', type: 'number', width: 90 },
|
|
||||||
// { field: 'lastName', headerName: 'Last Name', type: 'number', width: 90 },
|
|
||||||
// { field: 'street', headerName: 'Street', type: 'number', width: 90 },
|
|
||||||
// { field: 'city', headerName: 'City', type: 'number', width: 90 },
|
|
||||||
// { field: 'zip', headerName: 'ZIP', type: 'number', width: 90 },
|
|
||||||
// { field: 'dob', headerName: 'DOB', type: 'number', width: 90 },
|
|
||||||
// { field: 'mobile', headerName: 'Mobile', type: 'number', width: 90 },
|
|
||||||
// { field: 'lastUpdatedBy', headerName: 'Last Updated By', type: 'number', width: 90 },
|
|
||||||
// { field: 'ipCity', headerName: 'IP City', type: 'number', width: 90 },
|
|
||||||
// { field: 'ipRegion', headerName: 'IP Region', type: 'number', width: 90 },
|
|
||||||
// { field: 'ipCountry', headerName: 'IP Country', type: 'number', width: 90 },
|
|
||||||
// { field: 'cardIssuerCountry', headerName: 'Card Issuer Country', type: 'number', width: 90 },
|
|
||||||
// { field: 'cardBand', headerName: 'Card Band', type: 'number', width: 90 },
|
|
||||||
// { field: 'cardCategory', headerName: 'Card Category', type: 'number', width: 90 },
|
|
||||||
// { field: 'cardIssuerName', headerName: 'Card Issuer Name', type: 'number', width: 90 },
|
|
||||||
// { field: 'inn', headerName: 'INN', type: 'number', width: 90 },
|
|
||||||
// { field: 'cardType', headerName: 'Card Type', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstAttempt', headerName: 'First Attempt', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstSuccessful', headerName: 'First Successful', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstTransaction', headerName: 'First Transaction', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstPspAcountAttempt', headerName: 'First PSP Acount Attempt', type: 'number', width: 90 },
|
|
||||||
// { field: 'firstPspAcountSuccessful', headerName: 'First PSP Acount Successful', type: 'number', width: 90 },
|
|
||||||
// { 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","Inprogress", "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: "Status",
|
|
||||||
field: "status",
|
|
||||||
type: "select",
|
|
||||||
options: states,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Payment Method",
|
|
||||||
field: "depositMethod",
|
|
||||||
type: "select",
|
|
||||||
options: depositMethod,
|
|
||||||
},
|
|
||||||
{ label: "Date / Time", field: "dateTime", type: "date" },
|
|
||||||
]
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
interface IDepositTransactionsColumns {
|
|
||||||
field: string;
|
|
||||||
headerName: string;
|
|
||||||
width: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IOptions {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFilteredTransactions {
|
|
||||||
id: number;
|
|
||||||
userId: number;
|
|
||||||
merchandId: number;
|
|
||||||
transactionId: number;
|
|
||||||
depositMethod: string;
|
|
||||||
status: string;
|
|
||||||
options: IOptions[];
|
|
||||||
amount: number;
|
|
||||||
currency: string;
|
|
||||||
dateTime: string;
|
|
||||||
errorInfo: string;
|
|
||||||
fraudScore: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDepositTransactionsSearchLabels {
|
|
||||||
label: string;
|
|
||||||
field: string;
|
|
||||||
type: string;
|
|
||||||
options?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITransaction {
|
|
||||||
filteredTransactions: IFilteredTransactions[];
|
|
||||||
transactionsColumns: IDepositTransactionsColumns[];
|
|
||||||
transactionsSearchLabels: IDepositTransactionsSearchLabels[];
|
|
||||||
}
|
|
||||||
@ -14,6 +14,8 @@ import SettingsIcon from "@mui/icons-material/Settings";
|
|||||||
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||||
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
|
import FactCheckIcon from "@mui/icons-material/FactCheck";
|
||||||
|
|
||||||
|
|
||||||
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
|
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
|
||||||
|
|
||||||
@ -75,6 +77,7 @@ export const PAGE_LINKS: ISidebarLink[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ title: "Account IQ", path: "/dashboard/account-iq", icon: InsightsIcon },
|
{ title: "Account IQ", path: "/dashboard/account-iq", icon: InsightsIcon },
|
||||||
|
{ title: "Audits", path: "/dashboard/audits", icon: FactCheckIcon },
|
||||||
// { title: 'Documentation', path: '/documentation', icon: DescriptionIcon },
|
// { title: 'Documentation', path: '/documentation', icon: DescriptionIcon },
|
||||||
// { title: 'Support', path: '/support', icon: SupportAgentIcon },
|
// { title: 'Support', path: '/support', icon: SupportAgentIcon },
|
||||||
// { title: 'System Status', path: '/system-status', icon: WarningAmberIcon },
|
// { title: 'System Status', path: '/system-status', icon: WarningAmberIcon },
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
import advancedSearchReducer from './advanedSearch/advancedSearchSlice';
|
import advancedSearchReducer from "./advanedSearch/advancedSearchSlice";
|
||||||
import transactionsReducer from './transactions/transactionsSlice';
|
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
advancedSearch: advancedSearchReducer,
|
advancedSearch: advancedSearchReducer,
|
||||||
transactions: transactionsReducer,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
import { RootState } from "../types";
|
|
||||||
|
|
||||||
export const selectTransactions = (state: RootState) => state.transactions.data;
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
|
||||||
|
|
||||||
interface ITransactionsData {
|
|
||||||
id: number;
|
|
||||||
merchandId: number;
|
|
||||||
transactionID: number;
|
|
||||||
user: number;
|
|
||||||
created: string;
|
|
||||||
state: string;
|
|
||||||
statusDescription: string;
|
|
||||||
pspStatusCode: number;
|
|
||||||
|
|
||||||
}
|
|
||||||
interface ITransactionsState {
|
|
||||||
data: ITransactionsData[];
|
|
||||||
loading: boolean;
|
|
||||||
error: null | string;
|
|
||||||
totalTransactions: number
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const initialState: ITransactionsState = {
|
|
||||||
data: [],
|
|
||||||
loading: false,
|
|
||||||
error: null,
|
|
||||||
totalTransactions: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionsSlice = createSlice({
|
|
||||||
name: 'transactions',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
},
|
|
||||||
extraReducers: (builder) => {
|
|
||||||
builder
|
|
||||||
.addCase(getTransactions.pending, (state) => {
|
|
||||||
state.loading = true;
|
|
||||||
state.error = null;
|
|
||||||
})
|
|
||||||
.addCase(getTransactions.fulfilled, (state, action) => {
|
|
||||||
state.data = action.payload.transactions;
|
|
||||||
state.totalTransactions = action.payload.count;
|
|
||||||
state.loading = false;
|
|
||||||
})
|
|
||||||
.addCase(getTransactions.rejected, (state, action) => {
|
|
||||||
state.error = action.error.message || "Failed to fetch categories";
|
|
||||||
state.loading = false;
|
|
||||||
state.data = [];
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export default transactionsSlice.reducer;
|
|
||||||
|
|
||||||
|
|
||||||
export const getTransactions = createAsyncThunk(
|
|
||||||
'transactions/getTransactions',
|
|
||||||
async (
|
|
||||||
{
|
|
||||||
userId = '',
|
|
||||||
state = '',
|
|
||||||
statusCode = '',
|
|
||||||
}: { userId?: string; state?: string; statusCode?: string } = {}
|
|
||||||
) => {
|
|
||||||
const url = new URL('https://api.example.com/transactions');
|
|
||||||
|
|
||||||
if (userId) url.searchParams.append('userId', userId);
|
|
||||||
if (state) url.searchParams.append('state', state);
|
|
||||||
if (statusCode) url.searchParams.append('statusCode', statusCode);
|
|
||||||
|
|
||||||
const response = await fetch(url.toString());
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to fetch transactions');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data; // Let the reducer store this
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
22
payment-iq/app/services/audits.ts
Normal file
22
payment-iq/app/services/audits.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export async function getAudits({
|
||||||
|
query,
|
||||||
|
}: {
|
||||||
|
query: string;
|
||||||
|
}) {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://localhost:3000/api/dashboard/audits?${query}`,
|
||||||
|
{
|
||||||
|
cache: "no-store",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
// Handle error from the API
|
||||||
|
const errorData = await res
|
||||||
|
.json()
|
||||||
|
.catch(() => ({ message: "Unknown error" }));
|
||||||
|
throw new Error(errorData.message || `HTTP error! status: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
@ -1,11 +1,23 @@
|
|||||||
export async function getTransactions({ transactionType, query }: { transactionType: string, query: string }) {
|
export async function getTransactions({
|
||||||
const res = await fetch(`http://localhost:3000/api/transactions/${transactionType}?${query}`, {
|
transactionType,
|
||||||
cache: "no-store",
|
query,
|
||||||
});
|
}: {
|
||||||
|
transactionType: string;
|
||||||
|
query: string;
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const res = await fetch(
|
||||||
|
`http://localhost:3000/api/dashboard/transactions/${transactionType}?${query}`,
|
||||||
|
{
|
||||||
|
cache: "no-store",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
// Handle error from the API
|
// Handle error from the API
|
||||||
const errorData = await res.json().catch(() => ({ message: 'Unknown error' }));
|
const errorData = await res
|
||||||
|
.json()
|
||||||
|
.catch(() => ({ message: "Unknown error" }));
|
||||||
throw new Error(errorData.message || `HTTP error! status: ${res.status}`);
|
throw new Error(errorData.message || `HTTP error! status: ${res.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,39 +1,34 @@
|
|||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
||||||
// @ts-nocheck
|
|
||||||
import * as XLSX from "xlsx";
|
import * as XLSX from "xlsx";
|
||||||
import { GridColDef } from "@mui/x-data-grid";
|
|
||||||
export type FileType = "csv" | "xls" | "xlsx";
|
export type FileType = "csv" | "xls" | "xlsx";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
|
import { GridColDef } from "@mui/x-data-grid";
|
||||||
|
|
||||||
import type { ITransaction } from "../features/pages/transactions/types";
|
export const exportData = <TRow, TColumn extends GridColDef>(
|
||||||
|
rows: TRow[],
|
||||||
|
columns: TColumn[],
|
||||||
export const exportData = (
|
|
||||||
transactions: ITransaction[],
|
|
||||||
columns: GridColDef[],
|
|
||||||
fileType: FileType = "csv",
|
fileType: FileType = "csv",
|
||||||
onlyCurrentTable = false,
|
onlyCurrentTable = false,
|
||||||
setOpen: (open: boolean) => void
|
setOpen: (open: boolean) => void,
|
||||||
) => {
|
) => {
|
||||||
const exportRows = onlyCurrentTable ? transactions.slice(0, 5) : transactions;
|
const exportRows = onlyCurrentTable ? rows.slice(0, 5) : rows;
|
||||||
const exportData = [
|
const exportData = [
|
||||||
columns.map((col) => col.headerName),
|
columns.map((col) => col.headerName),
|
||||||
...exportRows.map((row) => columns.map((col) => row[col.field] ?? "")),
|
...exportRows.map((row) => columns.map((col) => (row as Record<string, unknown>)[col.field] ?? "")),
|
||||||
];
|
];
|
||||||
|
|
||||||
const worksheet = XLSX.utils.aoa_to_sheet(exportData);
|
const worksheet = XLSX.utils.aoa_to_sheet(exportData);
|
||||||
const workbook = XLSX.utils.book_new();
|
const workbook = XLSX.utils.book_new();
|
||||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Transactions");
|
XLSX.utils.book_append_sheet(workbook, worksheet, "Transactions");
|
||||||
|
|
||||||
if (fileType === "csv") {
|
if (fileType === "csv") {
|
||||||
const csv = XLSX.utils.sheet_to_csv(worksheet);
|
const csv = XLSX.utils.sheet_to_csv(worksheet);
|
||||||
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
||||||
saveAs(blob, "transactions.csv");
|
saveAs(blob, "transactions.csv");
|
||||||
} else {
|
} else {
|
||||||
XLSX.writeFile(workbook, `transactions.${fileType}`, {
|
XLSX.writeFile(workbook, `transactions.${fileType}`, {
|
||||||
bookType: fileType,
|
bookType: fileType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user