Fixing import issues
This commit is contained in:
parent
2e5788405c
commit
5779dd762f
@ -1,191 +0,0 @@
|
|||||||
import { GridColDef } from "@mui/x-data-grid";
|
|
||||||
|
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const depositTransactionsColumns: GridColDef[] = [
|
|
||||||
{ 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 depositTransactionsSearchLabels = [
|
|
||||||
{ 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: ["USD", "EUR", "GBP"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Status",
|
|
||||||
field: "status",
|
|
||||||
type: "select",
|
|
||||||
options: ["Pending","Inprogress", "Completed", "Failed"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Payment Method",
|
|
||||||
field: "depositMethod",
|
|
||||||
type: "select",
|
|
||||||
options: ["Card", "Bank Transfer"]
|
|
||||||
},
|
|
||||||
{ label: "Date / Time", field: "dateTime", type: "date" },
|
|
||||||
]
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
|
||||||
import { depositTransactionDummyData, depositTransactionsColumns, depositTransactionsSearchLabels } from "./mockData";
|
|
||||||
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dateTime) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.dateTime.split(" ")[0] === formatToDateTimeString(dateTime).split(" ")[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
filteredTransactions: filteredTransactions,
|
|
||||||
transactionsSearchLabels: depositTransactionsSearchLabels,
|
|
||||||
transactionsColumns: depositTransactionsColumns
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import { transactionDummyData } from "@/app/components/test/test2";
|
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
|
||||||
const { searchParams } = new URL(request.url);
|
|
||||||
|
|
||||||
const state = searchParams.get("state");
|
|
||||||
const user = searchParams.get("user");
|
|
||||||
|
|
||||||
let filteredTransactions = [...transactionDummyData];
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.user.toString() === user
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.state.toLowerCase() === state.toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(filteredTransactions);
|
|
||||||
}
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
import { GridColDef } from "@mui/x-data-grid";
|
|
||||||
|
|
||||||
export const withdrawalTransactionDummyData = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
userId: 17,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
userId: 17,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
userId: 17,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Complete",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-18 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
userId: 19,
|
|
||||||
transactionId: 1049136973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Completed",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-18 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
userId: 19,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
userId: 27,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
userId: 1,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
userId: 172,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Card",
|
|
||||||
status: "Pending",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-12 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
userId: 174,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Inprogress",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
userId: 1,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
userId: 1,
|
|
||||||
transactionId: 1049131973,
|
|
||||||
withdrawalMethod: "Bank Transfer",
|
|
||||||
status: "Error",
|
|
||||||
amount: 4000,
|
|
||||||
dateTime: "2025-06-17 10:10:30",
|
|
||||||
errorInfo: "-",
|
|
||||||
fraudScore: "frad score 1234",
|
|
||||||
manualCorrectionFlag: "-",
|
|
||||||
informationWhoApproved: "-",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const withdrawalTransactionsColumns: GridColDef[] = [
|
|
||||||
{ field: "userId", headerName: "User ID", width: 130 },
|
|
||||||
{ field: "transactionId", headerName: "Transaction ID", width: 130 },
|
|
||||||
{ field: "withdrawalMethod", headerName: "Withdrawal Method", width: 130 },
|
|
||||||
{ field: "status", headerName: "Status", width: 130 },
|
|
||||||
{ field: "amount", headerName: "Amount", width: 130 },
|
|
||||||
{ field: "dateTime", headerName: "Date / Time", width: 130 },
|
|
||||||
{ field: "errorInfo", headerName: "Error Info", width: 130 },
|
|
||||||
{ field: "fraudScore", headerName: "Fraud Score", width: 130 },
|
|
||||||
{
|
|
||||||
field: "manualCorrectionFlag",
|
|
||||||
headerName: "Manual Correction Flag",
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: "informationWhoApproved",
|
|
||||||
headerName: "Information who approved",
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const withdrawalTransactionsSearchLabels = [
|
|
||||||
{
|
|
||||||
label: "Status",
|
|
||||||
field: "status",
|
|
||||||
type: "select",
|
|
||||||
options: ["Pending", "Inprogress", "Completed", "Failed"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Payment Method",
|
|
||||||
field: "depositMethod",
|
|
||||||
type: "select",
|
|
||||||
options: ["Card", "Bank Transfer"],
|
|
||||||
},
|
|
||||||
{ label: "Date / Time", field: "dateTime", type: "date" },
|
|
||||||
];
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
|
||||||
import { withdrawalTransactionDummyData, withdrawalTransactionsColumns, withdrawalTransactionsSearchLabels } from "./mockData"
|
|
||||||
import { formatToDateTimeString } from "@/app/utils/formatDate";
|
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
|
||||||
const { searchParams } = new URL(request.url);
|
|
||||||
|
|
||||||
const userId = searchParams.get("userId");
|
|
||||||
const status = searchParams.get("status");
|
|
||||||
const dateTime = searchParams.get("dateTime");
|
|
||||||
const withdrawalMethod = searchParams.get("withdrawalMethod");
|
|
||||||
|
|
||||||
|
|
||||||
let filteredTransactions = [...withdrawalTransactionDummyData];
|
|
||||||
|
|
||||||
if (userId) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.userId.toString() === userId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.status.toLowerCase() === status.toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (withdrawalMethod) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.withdrawalMethod.toLowerCase() === withdrawalMethod.toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dateTime) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.dateTime.split(" ")[0] === formatToDateTimeString(dateTime).split(" ")[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
filteredTransactions: filteredTransactions,
|
|
||||||
transactionsColumns: withdrawalTransactionsColumns,
|
|
||||||
transactionsSearchLabels: withdrawalTransactionsSearchLabels
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { styled } from "@mui/material"
|
|
||||||
import { SectionCard } from "../SectionCard/SectionCard"
|
|
||||||
|
|
||||||
const AccountIQIcon = styled('div')(({ theme }) => ({
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#4ecdc4',
|
|
||||||
fontSize: '1rem',
|
|
||||||
marginRight: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const AccountIQ = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionCard
|
|
||||||
title="AccountIQ"
|
|
||||||
icon={<AccountIQIcon>AIQ</AccountIQIcon>
|
|
||||||
}
|
|
||||||
items={[
|
|
||||||
{ title: 'Automatically reconcile your transactions' },
|
|
||||||
{ title: 'Live wallet balances from providers' },
|
|
||||||
{ title: 'Gaming provider financial overviews' },
|
|
||||||
{ title: 'Learn more' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import DescriptionIcon from '@mui/icons-material/Description';
|
|
||||||
import { SectionCard } from '../SectionCard/SectionCard';
|
|
||||||
|
|
||||||
export const Documentation = () => {
|
|
||||||
return (
|
|
||||||
<SectionCard
|
|
||||||
title="Documentation"
|
|
||||||
icon={<DescriptionIcon fontSize="small" />}
|
|
||||||
items={[
|
|
||||||
{ title: 'Provider Integration Overview' },
|
|
||||||
{ title: 'APIs Introduction' },
|
|
||||||
{ title: 'Documentation Overview' },
|
|
||||||
{ title: 'How-Tos' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,155 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
Typography,
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
Button,
|
|
||||||
Stack,
|
|
||||||
Box,
|
|
||||||
Paper,
|
|
||||||
IconButton,
|
|
||||||
} from '@mui/material';
|
|
||||||
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
|
||||||
|
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
||||||
|
|
||||||
export const FetchReport = () => {
|
|
||||||
const [state, setState] = useState('');
|
|
||||||
const [psp, setPsp] = useState('');
|
|
||||||
const [reportType, setReportType] = useState('');
|
|
||||||
|
|
||||||
const handleDownload = () => {
|
|
||||||
// Download logic goes here
|
|
||||||
alert('Report downloaded');
|
|
||||||
};
|
|
||||||
|
|
||||||
const isDownloadEnabled = state && psp && reportType;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper elevation={3} sx={{ padding: 2, margin: 2, display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
||||||
<Typography variant="h6" fontWeight="bold">
|
|
||||||
Fetch Report
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<CalendarTodayIcon fontSize="small" />
|
|
||||||
<Typography variant="body2">Last 30 days</Typography>
|
|
||||||
<IconButton size="small">
|
|
||||||
<MoreVertIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Stack spacing={2}>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel>Select state (defaults to All)</InputLabel>
|
|
||||||
<Select value={state} onChange={(e) => setState(e.target.value)} label="Select state (defaults to All)">
|
|
||||||
<MenuItem value="successful">Successful</MenuItem>
|
|
||||||
<MenuItem value="failed">Failed</MenuItem>
|
|
||||||
<MenuItem value="canceled">Canceled</MenuItem>
|
|
||||||
|
|
||||||
{/* Add more states */}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel>Select PSPs (defaults to All)</InputLabel>
|
|
||||||
<Select value={psp} onChange={(e) => setPsp(e.target.value)} label="Select PSPs (defaults to All)">
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem value="a1">A1</MenuItem>
|
|
||||||
<MenuItem value="ahub">AHUB</MenuItem>
|
|
||||||
<MenuItem value="aibms">AIBMS</MenuItem>
|
|
||||||
|
|
||||||
|
|
||||||
{/* Add more PSPs */}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel>Select report type</InputLabel>
|
|
||||||
<Select value={reportType} onChange={(e) => setReportType(e.target.value)} label="Select report type">
|
|
||||||
<MenuItem value="allTransactionsReport">All Transactions Report</MenuItem>
|
|
||||||
<MenuItem value="depositReport">Deposit Report</MenuItem>
|
|
||||||
<MenuItem value="widthdrawReport">WithDraw Report</MenuItem>
|
|
||||||
{/* Add more types */}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<Box textAlign="center" mt={2}>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={handleDownload}
|
|
||||||
disabled={!isDownloadEnabled}
|
|
||||||
sx={{ minWidth: 200 }}
|
|
||||||
>
|
|
||||||
Download Report
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
IconButton,
|
|
||||||
Divider,
|
|
||||||
} from '@mui/material';
|
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
||||||
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
|
|
||||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
|
||||||
// import { ArrowDropUp } from '@mui/icons-material';
|
|
||||||
|
|
||||||
const stats = [
|
|
||||||
{ label: 'TOTAL', value: 5, change: '-84.85%' },
|
|
||||||
{ label: 'SUCCESSFUL', value: 10, change: '100%' },
|
|
||||||
{ label: 'ACCEPTANCE RATE', value: '0%', change: '-100%' },
|
|
||||||
{ label: 'AMOUNT', value: '€0.00', change: '-100%' },
|
|
||||||
{ label: 'ATV', value: '€0.00', change: '-100%' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const StatItem = ({ label, value, change }: { label: string, value: string | number, change: string }) => (
|
|
||||||
<Box sx={{ textAlign: 'center', px: 2 }}>
|
|
||||||
<Typography variant="body2" fontWeight="bold" color="text.secondary">
|
|
||||||
{label}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6" fontWeight="bold" mt={0.5}>
|
|
||||||
{value}
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'error.main' }}>
|
|
||||||
<ArrowDropDownIcon fontSize="small" />
|
|
||||||
{/* <ArrowDropUp fontSize='small' /> */}
|
|
||||||
<Typography variant="caption">{change}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const GeneralHealthCard = () => {
|
|
||||||
return (
|
|
||||||
<Card sx={{ borderRadius: 3, p: 2 }}>
|
|
||||||
<CardContent sx={{ pb: '16px !important' }}>
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
||||||
<Typography variant="subtitle1" fontWeight="bold">
|
|
||||||
General Health
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<CalendarTodayIcon fontSize="small" />
|
|
||||||
<Typography variant="body2">Last 24h</Typography>
|
|
||||||
<IconButton size="small">
|
|
||||||
<MoreVertIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-around', mt: 2 }}>
|
|
||||||
{stats.map((item) => (
|
|
||||||
<StatItem key={item.label} {...item} />
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { Box } from "@mui/material";
|
|
||||||
import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts";
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{ name: "Group A", value: 100 },
|
|
||||||
{ name: "Group B", value: 200 },
|
|
||||||
{ name: "Group C", value: 400 },
|
|
||||||
{ name: "Group D", value: 300 }
|
|
||||||
];
|
|
||||||
|
|
||||||
const COLORS = ["#4caf50", "#ff9800", "#f44336", "#9e9e9e"];
|
|
||||||
|
|
||||||
const RADIAN = Math.PI / 180;
|
|
||||||
const renderCustomizedLabel = ({
|
|
||||||
cx,
|
|
||||||
cy,
|
|
||||||
midAngle,
|
|
||||||
innerRadius,
|
|
||||||
outerRadius,
|
|
||||||
percent,
|
|
||||||
// index
|
|
||||||
}: any) => {
|
|
||||||
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
|
|
||||||
const x = cx + radius * Math.cos(-midAngle * RADIAN);
|
|
||||||
const y = cy + radius * Math.sin(-midAngle * RADIAN);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<text
|
|
||||||
x={x}
|
|
||||||
y={y}
|
|
||||||
fill="white"
|
|
||||||
textAnchor={x > cx ? "start" : "end"}
|
|
||||||
dominantBaseline="central"
|
|
||||||
>
|
|
||||||
{`${(percent * 100).toFixed(0)}%`}
|
|
||||||
</text>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export const PieCharts = () => {
|
|
||||||
return (
|
|
||||||
<Box sx={{
|
|
||||||
width: {
|
|
||||||
xs: '100%',
|
|
||||||
md: '60%'
|
|
||||||
}, height: '300px'
|
|
||||||
}}>
|
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
|
||||||
<PieChart>
|
|
||||||
<Pie
|
|
||||||
data={data}
|
|
||||||
cx="50%"
|
|
||||||
cy="50%"
|
|
||||||
labelLine={false}
|
|
||||||
label={renderCustomizedLabel}
|
|
||||||
outerRadius="80%" // Percentage-based radius
|
|
||||||
fill="#8884d8"
|
|
||||||
dataKey="value"
|
|
||||||
>
|
|
||||||
{data.map((entry, index) => (
|
|
||||||
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
|
|
||||||
))}
|
|
||||||
</Pie>
|
|
||||||
</PieChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</Box >
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
import { CardContent, Typography, Divider, List, ListItem, ListItemText, Paper, Box, IconButton } from "@mui/material";
|
|
||||||
|
|
||||||
|
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
||||||
|
|
||||||
interface SectionCardItem {
|
|
||||||
title: string;
|
|
||||||
date?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SectionCardProps {
|
|
||||||
title: string;
|
|
||||||
icon?: React.ReactNode;
|
|
||||||
items: SectionCardItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SectionCard: React.FC<SectionCardProps> = ({ title, icon, items }) => (
|
|
||||||
<Paper elevation={3} sx={{ padding: 2, margin: 2, display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<CardContent>
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
||||||
<Box sx={{ display: 'flex' }}>
|
|
||||||
{icon}
|
|
||||||
<Typography variant="subtitle1" fontWeight="bold">{title}</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<IconButton size="small">
|
|
||||||
<MoreVertIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box >
|
|
||||||
<Divider />
|
|
||||||
<List dense disablePadding>
|
|
||||||
{items.map((item, index) => (
|
|
||||||
<ListItem key={index} disableGutters>
|
|
||||||
<ListItemText
|
|
||||||
primary={item.title}
|
|
||||||
secondary={item.date}
|
|
||||||
primaryTypographyProps={{ fontSize: 14 }}
|
|
||||||
secondaryTypographyProps={{ fontSize: 12 }}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</CardContent>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Paper,
|
|
||||||
Box,
|
|
||||||
Button
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
const data1 = [
|
|
||||||
{ state: 'Success', count: 120, percentage: '60%', color: 'green' },
|
|
||||||
{ state: 'Pending', count: 50, percentage: '25%', color: 'orange' },
|
|
||||||
{ state: 'Failed', count: 20, percentage: '10%', color: 'red' },
|
|
||||||
{ state: 'Other', count: 10, percentage: '5%', color: 'gray' }
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TransactionsOverviewTable = () => {
|
|
||||||
return (
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell align="center">State</TableCell>
|
|
||||||
<TableCell align="center">Count</TableCell>
|
|
||||||
<TableCell align="center">Percentage</TableCell>
|
|
||||||
<TableCell align="center">Action</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{data1.map((row) => (
|
|
||||||
<TableRow key={row.state}>
|
|
||||||
<TableCell align="center">
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
alignItems: 'center',
|
|
||||||
mx: 'auto', // center the flexbox itself
|
|
||||||
width: '73px' // consistent width for alignment
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
borderRadius: '50%',
|
|
||||||
bgcolor: row.color,
|
|
||||||
mr: 1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{row.state}
|
|
||||||
</Box>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="center">{row.count}</TableCell>
|
|
||||||
<TableCell align="center">{row.percentage}</TableCell>
|
|
||||||
<TableCell align="center">
|
|
||||||
<Button variant="outlined" size="small">
|
|
||||||
View
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { Box, Button, IconButton, Paper, Typography } from "@mui/material"
|
|
||||||
import { PieCharts } from "../PieCharts/PieCharts"
|
|
||||||
|
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
||||||
import { TransactionsOverviewTable } from "./TransactionsOverViewTable";
|
|
||||||
|
|
||||||
export const TransactionsOverview = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
return (
|
|
||||||
<Paper elevation={3} sx={{ padding: 2, margin: 2, display: 'flex', flexDirection: 'column' }}>
|
|
||||||
|
|
||||||
{/* Title and All Transactions Button */}
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', px: 1 }}>
|
|
||||||
<Typography variant="h6">
|
|
||||||
Transactions Overview (Last 24h)
|
|
||||||
</Typography>
|
|
||||||
<Box>
|
|
||||||
<Button variant="contained" color="primary" onClick={() => router.push('dashboard/transactions')}>
|
|
||||||
All Transactions
|
|
||||||
</Button>
|
|
||||||
<IconButton size="small">
|
|
||||||
<MoreVertIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Chart and Table */}
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
padding: 2,
|
|
||||||
margin: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: {
|
|
||||||
xs: 'wrap', // Wrap on small screens
|
|
||||||
md: 'nowrap' // No wrap on medium and up
|
|
||||||
},
|
|
||||||
gap: {
|
|
||||||
xs: 4, // Add spacing on small screens
|
|
||||||
md: 0 // No spacing on larger screens
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PieCharts />
|
|
||||||
<TransactionsOverviewTable />
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Paper,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
|
||||||
|
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
||||||
|
|
||||||
const transactions = [
|
|
||||||
|
|
||||||
{
|
|
||||||
id: '1049078821',
|
|
||||||
user: '17',
|
|
||||||
created: '2025-06-17 16:45',
|
|
||||||
type: 'BestPayWithdrawal',
|
|
||||||
amount: '-787.49 TRY',
|
|
||||||
psp: 'BestPay'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1049078822',
|
|
||||||
user: '17',
|
|
||||||
created: '2025-06-17 16:45',
|
|
||||||
type: 'BestPayWithdrawal',
|
|
||||||
amount: '-787.49 TRY',
|
|
||||||
psp: 'BestPay'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1049078823',
|
|
||||||
user: '17',
|
|
||||||
created: '2025-06-17 16:45',
|
|
||||||
type: 'BestPayWithdrawal',
|
|
||||||
amount: '-787.49 TRY',
|
|
||||||
psp: 'BestPay'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1049078824',
|
|
||||||
user: '17',
|
|
||||||
created: '2025-06-17 16:45',
|
|
||||||
type: 'BestPayWithdrawal',
|
|
||||||
amount: '-787.49 TRY',
|
|
||||||
psp: 'BestPay'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TransactionsWaitingApproval = () => {
|
|
||||||
return (
|
|
||||||
|
|
||||||
<Paper elevation={3} sx={{ padding: 2, margin: 2, display: 'flex', flexDirection: 'column' }}>
|
|
||||||
|
|
||||||
<Box sx={{ p: 3 }}>
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
||||||
<Typography variant="h6" fontWeight="bold">
|
|
||||||
Transactions Waiting for Approval
|
|
||||||
</Typography>
|
|
||||||
<Box>
|
|
||||||
<Button variant="outlined">All Pending Withdrawals</Button>
|
|
||||||
|
|
||||||
<IconButton size="small">
|
|
||||||
<MoreVertIcon fontSize="small" />
|
|
||||||
</IconButton> </Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell><strong>ID</strong></TableCell>
|
|
||||||
<TableCell><strong>User</strong></TableCell>
|
|
||||||
<TableCell><strong>Created</strong></TableCell>
|
|
||||||
<TableCell><strong>Type</strong></TableCell>
|
|
||||||
<TableCell><strong>Amount</strong></TableCell>
|
|
||||||
<TableCell><strong>PSP</strong></TableCell>
|
|
||||||
<TableCell><strong>Action</strong></TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{transactions.map((tx) => (
|
|
||||||
<TableRow key={tx.id}>
|
|
||||||
<TableCell>{tx.id}</TableCell>
|
|
||||||
<TableCell>{tx.user}</TableCell>
|
|
||||||
<TableCell>{tx.created}</TableCell>
|
|
||||||
<TableCell>{tx.type}</TableCell>
|
|
||||||
<TableCell>{tx.amount}</TableCell>
|
|
||||||
<TableCell>{tx.psp}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<IconButton color="success">
|
|
||||||
<CheckCircleIcon />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton color="error">
|
|
||||||
<CancelIcon />
|
|
||||||
</IconButton>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { SectionCard } from "../SectionCard/SectionCard";
|
|
||||||
import WifiIcon from '@mui/icons-material/Wifi';
|
|
||||||
|
|
||||||
export const WhatsNew = () => {
|
|
||||||
return (
|
|
||||||
<SectionCard
|
|
||||||
title="What’s New"
|
|
||||||
icon={<WifiIcon fontSize="small" />}
|
|
||||||
items={[
|
|
||||||
{ title: 'Sneak Peek – Discover the New Rules Hub Feature', date: '13 May 2025' },
|
|
||||||
{ title: 'New security measures for anonymizing sensitive configuration values, effective December 2nd', date: '31 Oct 2024' },
|
|
||||||
{ title: 'Introducing Our New Transactions and Rule Views', date: '23 Oct 2024' },
|
|
||||||
{ title: 'Introducing Our New Status Page', date: '09 Sept 2024' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
SelectChangeEvent,
|
|
||||||
ListItemText,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { SIDEBAR_LINKS } from '@/constants/SidebarLink.constants';
|
|
||||||
import { ISidebarLink } from '@/interfaces/SidebarLink.interfaces';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
onChange?: (event: SelectChangeEvent<string>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SidebarDropdown({ onChange }: Props) {
|
|
||||||
const [value, setValue] = React.useState('');
|
|
||||||
|
|
||||||
const handleChange = (event: SelectChangeEvent<string>) => {
|
|
||||||
setValue(event.target.value);
|
|
||||||
onChange?.(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormControl fullWidth variant="outlined" sx={{ maxWidth: 200 }}>
|
|
||||||
<InputLabel id="sidebar-dropdown-label">Navigate To</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="sidebar-dropdown-label"
|
|
||||||
value={value}
|
|
||||||
onChange={handleChange}
|
|
||||||
label="Navigate To"
|
|
||||||
>
|
|
||||||
{SIDEBAR_LINKS.map((link:ISidebarLink) => (
|
|
||||||
<MenuItem key={link.path} value={link.path}>
|
|
||||||
<ListItemText primary={link.title} />
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { AppBar, Toolbar, IconButton, Menu, MenuItem } from '@mui/material';
|
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
|
||||||
import Dropdown from './DropDown';
|
|
||||||
|
|
||||||
const Header = () => {
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
||||||
|
|
||||||
// Handle menu close
|
|
||||||
const handleMenuClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = () => {
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppBar position="sticky" color="transparent" elevation={0} sx={{ borderBottom: '1px solid #22242626' }}>
|
|
||||||
<Toolbar>
|
|
||||||
{/* Burger Menu */}
|
|
||||||
<IconButton edge="start" color="inherit" aria-label="menu">
|
|
||||||
<MenuIcon />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
{/* Dropdown Button */}
|
|
||||||
<Dropdown onChange={handleChange}/>
|
|
||||||
|
|
||||||
{/* Dropdown Menu */}
|
|
||||||
<Menu
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
open={Boolean(anchorEl)}
|
|
||||||
onClose={handleMenuClose}
|
|
||||||
>
|
|
||||||
<MenuItem onClick={handleMenuClose}>Option 1</MenuItem>
|
|
||||||
<MenuItem onClick={handleMenuClose}>Option 2</MenuItem>
|
|
||||||
<MenuItem onClick={handleMenuClose}>Option 3</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { styled } from '@mui/system';
|
|
||||||
|
|
||||||
export const LayoutWrapper = styled('div')({
|
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
|
||||||
height: '100vh',
|
|
||||||
// overflow: 'hidden',
|
|
||||||
});
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { styled } from '@mui/system';
|
|
||||||
|
|
||||||
export const MainContent = styled('div')(({ theme }) => ({
|
|
||||||
marginLeft: '240px',
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
minHeight: '100vh',
|
|
||||||
width: 'calc(100% - 240px)',
|
|
||||||
}));
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { styled } from '@mui/system';
|
|
||||||
import { ISidebarLink } from '@/interfaces/SidebarLink.interfaces';
|
|
||||||
|
|
||||||
const LinkContainer = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '12px 1px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
color: theme.palette.text.tertiary,
|
|
||||||
textDecoration: 'none',
|
|
||||||
transition: 'background 0.2s ease-in-out',
|
|
||||||
|
|
||||||
'&:hover': {
|
|
||||||
color: 'rgb(255, 255, 255)',
|
|
||||||
background: 'rgba(255, 255, 255, 0.08)',
|
|
||||||
backgroundColor: theme.palette.action.hover,
|
|
||||||
cursor: 'pointer',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const LinkText = styled('span')(({ theme }) => ({
|
|
||||||
color: theme.palette.text.tertiary,
|
|
||||||
marginLeft: '12px',
|
|
||||||
fontWeight: 500,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function SidebarLink({ title, path, icon: Icon }: ISidebarLink) {
|
|
||||||
return (
|
|
||||||
<Link href={path} passHref legacyBehavior>
|
|
||||||
<a style={{ textDecoration: 'none' }}>
|
|
||||||
<LinkContainer>
|
|
||||||
{Icon && <Icon />}
|
|
||||||
<LinkText>{title}</LinkText>
|
|
||||||
</LinkContainer>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
|
||||||
import { styled } from '@mui/system';
|
|
||||||
import { SIDEBAR_LINKS } from '@/constants/SidebarLink.constants';
|
|
||||||
import SidebarLink from './SideBarLink';
|
|
||||||
|
|
||||||
const SideBarContainer = styled('aside')(({ theme }) => ({
|
|
||||||
position: 'fixed',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: 240,
|
|
||||||
height: '100vh',
|
|
||||||
backgroundColor: theme.palette.background.primary,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
padding: theme.spacing(2),
|
|
||||||
zIndex: 1100,
|
|
||||||
borderRight: `1px solid ${theme.palette.divider}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SidebarHeader = styled('div')(({ theme }) => ({
|
|
||||||
fontSize: '20px',
|
|
||||||
fontWeight: 600,
|
|
||||||
marginBottom: theme.spacing(3),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const IconSpacing = styled(DashboardIcon)(({ theme }) => ({
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SideBar = () => {
|
|
||||||
return (
|
|
||||||
<SideBarContainer>
|
|
||||||
<SidebarHeader>
|
|
||||||
<span style={{color: '#fff'}}>Betrise cashir <IconSpacing fontSize="small" /></span>
|
|
||||||
</SidebarHeader>
|
|
||||||
{SIDEBAR_LINKS.map((link) => (
|
|
||||||
<SidebarLink
|
|
||||||
key={link.path}
|
|
||||||
title={link.title}
|
|
||||||
path={link.path}
|
|
||||||
icon={link.icon}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</SideBarContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SideBar;
|
|
||||||
@ -1,162 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
TextField,
|
|
||||||
IconButton,
|
|
||||||
InputAdornment,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Checkbox,
|
|
||||||
Paper,
|
|
||||||
MenuItem,
|
|
||||||
InputLabel,
|
|
||||||
Select,
|
|
||||||
FormControl,
|
|
||||||
SelectChangeEvent
|
|
||||||
} from '@mui/material';
|
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
|
||||||
|
|
||||||
const rows = [
|
|
||||||
{
|
|
||||||
merchantId: '100987998',
|
|
||||||
txId: '1049078821',
|
|
||||||
userId: 17,
|
|
||||||
userEmail: 'dhkheni1@yopmail.com',
|
|
||||||
kycStatus: 'N/A',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
merchantId: '100987998',
|
|
||||||
txId: '1049078821',
|
|
||||||
userId: 18,
|
|
||||||
userEmail: 'dhkheni1@yopmail.com',
|
|
||||||
kycStatus: 'N/A',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
merchantId: '100987998',
|
|
||||||
txId: '1049078821',
|
|
||||||
userId: 19,
|
|
||||||
userEmail: 'dhkheni1@yopmail.com',
|
|
||||||
kycStatus: 'N/A',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const Approve = () => {
|
|
||||||
const [age, setAge] = useState('');
|
|
||||||
const [selectedRows, setSelectedRows] = useState<number[]>([]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleCheckboxChange = (userId: number) => (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const isChecked = event.target.checked;
|
|
||||||
setSelectedRows((prevSelected: number[]) =>
|
|
||||||
isChecked
|
|
||||||
? [...prevSelected, userId]
|
|
||||||
: prevSelected.filter((id) => id !== userId)
|
|
||||||
);
|
|
||||||
console.log('Selected IDs:', isChecked
|
|
||||||
? [...selectedRows, userId]
|
|
||||||
: selectedRows.filter((id) => id !== userId));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangeAge = (event: SelectChangeEvent) => {
|
|
||||||
setAge(event.target.value as string);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box p={2}>
|
|
||||||
<Box mb={2} display="flex" justifyContent="space-between" alignItems="center">
|
|
||||||
<TextField
|
|
||||||
variant="outlined"
|
|
||||||
placeholder="Filter by tags or search by keyword"
|
|
||||||
size="small"
|
|
||||||
InputProps={{
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton>
|
|
||||||
<SearchIcon />
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box sx={{ width: '100px' }}>
|
|
||||||
{/* <IconButton onClick={handleMenuOpen}> */}
|
|
||||||
{/* <MoreVertIcon /> */}
|
|
||||||
{/* </IconButton> */}
|
|
||||||
{/* <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}> */}
|
|
||||||
{/* <MenuItem onClick={handleMenuClose}>Action 1</MenuItem> */}
|
|
||||||
{/* <MenuItem onClick={handleMenuClose}>Action 2</MenuItem> */}
|
|
||||||
{/* </Menu> */}
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel id="demo-simple-select-label">Action</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="demo-simple-select-label"
|
|
||||||
id="demo-simple-select"
|
|
||||||
value={age}
|
|
||||||
label="Age"
|
|
||||||
onChange={handleChangeAge}
|
|
||||||
>
|
|
||||||
<MenuItem value={10}>Ten</MenuItem>
|
|
||||||
<MenuItem value={20}>Twenty</MenuItem>
|
|
||||||
<MenuItem value={30}>Thirty</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table size="small">
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell padding="checkbox"><Checkbox /></TableCell>
|
|
||||||
<TableCell>Merchant-id</TableCell>
|
|
||||||
<TableCell>Tx-id</TableCell>
|
|
||||||
<TableCell>User</TableCell>
|
|
||||||
<TableCell>User email</TableCell>
|
|
||||||
<TableCell>KYC Status</TableCell>
|
|
||||||
<TableCell>KYC PSP</TableCell>
|
|
||||||
<TableCell>KYC PSP status</TableCell>
|
|
||||||
<TableCell>KYC ID status</TableCell>
|
|
||||||
<TableCell>KYC address status</TableCell>
|
|
||||||
<TableCell>KYC liveness status</TableCell>
|
|
||||||
<TableCell>KYC age status</TableCell>
|
|
||||||
<TableCell>KYC peps and sanctions</TableCell>
|
|
||||||
<TableCell>Suspected</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{rows.map((row, idx) => (
|
|
||||||
<TableRow key={idx}>
|
|
||||||
<TableCell padding="checkbox">
|
|
||||||
<Checkbox checked={selectedRows.includes(row.userId)}
|
|
||||||
onChange={handleCheckboxChange(row.userId)} /></TableCell>
|
|
||||||
<TableCell>{row.merchantId}</TableCell>
|
|
||||||
<TableCell>{row.txId}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<a href={`/user/${row.userId}`} target="_blank" rel="noopener noreferrer">
|
|
||||||
{row.userId}
|
|
||||||
</a>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{row.userEmail}</TableCell>
|
|
||||||
<TableCell>{row.kycStatus}</TableCell>
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
<TableCell />
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { Box } from "@mui/material"
|
|
||||||
import { GeneralHealthCard } from "../../GeneralHealthCard/GeneralHealthCard"
|
|
||||||
import { TransactionsOverview } from "../../TransactionsOverview/TransactionsOverview"
|
|
||||||
import { TransactionsWaitingApproval } from "../../TransactionsWaitingApproval/TransactionsWaitingApproval"
|
|
||||||
import { FetchReport } from "../../FetchReports/FetchReports"
|
|
||||||
import { Documentation } from "../../Documentation/Documentation"
|
|
||||||
import { AccountIQ } from "../../AccountIQ/AccountIQ"
|
|
||||||
import { WhatsNew } from "../../WhatsNew/WhatsNew"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const DashboardHomePage = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box sx={{ p: 2 }}>
|
|
||||||
<GeneralHealthCard />
|
|
||||||
</Box>
|
|
||||||
<TransactionsOverview />
|
|
||||||
<TransactionsWaitingApproval />
|
|
||||||
<FetchReport />
|
|
||||||
<Documentation />
|
|
||||||
<AccountIQ />
|
|
||||||
<WhatsNew />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import { 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 * as XLSX from 'xlsx';
|
|
||||||
import { saveAs } from 'file-saver';
|
|
||||||
import { DataGrid } from '@mui/x-data-grid';
|
|
||||||
import { columns } from './constants';
|
|
||||||
import { rows } from './mockData';
|
|
||||||
|
|
||||||
const paginationModel = { page: 0, pageSize: 50 };
|
|
||||||
|
|
||||||
export default function TransactionTable() {
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [fileType, setFileType] = useState<'csv' | 'xls' | 'xlsx'>('csv');
|
|
||||||
const [onlyCurrentTable, setOnlyCurrentTable] = useState(false);
|
|
||||||
|
|
||||||
const handleExport = () => {
|
|
||||||
const exportRows = onlyCurrentTable ? rows.slice(0, 5) : rows;
|
|
||||||
const exportData = [
|
|
||||||
columns.map(col => col.headerName),
|
|
||||||
// @ts-expect-error - Dynamic field access from DataGrid columns
|
|
||||||
...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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledPaper>
|
|
||||||
<Stack direction="row" justifyContent="space-between" alignItems="center" p={2}>
|
|
||||||
<TextField
|
|
||||||
label="Search"
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
// value={'searchQuery'}
|
|
||||||
onChange={(e) => console.log(`setSearchQuery(${e.target.value})`)}
|
|
||||||
sx={{ width: 300 }}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<FileUploadIcon />}
|
|
||||||
onClick={() => setOpen(true)}
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<DataGrid
|
|
||||||
rows={rows}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{ pagination: { paginationModel } }}
|
|
||||||
pageSizeOptions={[50 , 100]}
|
|
||||||
sx={{ border: 0 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Export Dialog */}
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)}>
|
|
||||||
<DialogTitle>Export Transactions</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<FormControl fullWidth sx={{ mt: 2 }}>
|
|
||||||
<Select
|
|
||||||
value={fileType}
|
|
||||||
onChange={(e) => setFileType(e.target.value as 'csv' | 'xls' | 'xlsx')}
|
|
||||||
>
|
|
||||||
<MenuItem value="csv">CSV</MenuItem>
|
|
||||||
<MenuItem value="xls">XLS</MenuItem>
|
|
||||||
<MenuItem value="xlsx">XLSX</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={onlyCurrentTable}
|
|
||||||
onChange={(e) => setOnlyCurrentTable(e.target.checked)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Only export the results in the current table"
|
|
||||||
sx={{ mt: 2 }}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
|
||||||
<Button variant="contained" onClick={handleExport}>Export</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</StyledPaper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledPaper = styled(Paper)(() => ({
|
|
||||||
height: '90vh',
|
|
||||||
}));
|
|
||||||
@ -1,80 +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: '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 },
|
|
||||||
];
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,41 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { styled } from '@mui/system';
|
|
||||||
import { ISidebarLink } from '@/interfaces/SidebarLink.interfaces';
|
|
||||||
|
|
||||||
const LinkContainer = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '12px 1px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
color: theme.palette.text.tertiary,
|
|
||||||
textDecoration: 'none',
|
|
||||||
transition: 'background 0.2s ease-in-out',
|
|
||||||
|
|
||||||
'&:hover': {
|
|
||||||
color: 'rgb(255, 255, 255)',
|
|
||||||
background: 'rgba(255, 255, 255, 0.08)',
|
|
||||||
backgroundColor: theme.palette.action.hover,
|
|
||||||
cursor: 'pointer',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const LinkText = styled('span')(({ theme }) => ({
|
|
||||||
color: theme.palette.text.tertiary,
|
|
||||||
marginLeft: '12px',
|
|
||||||
fontWeight: 500,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function SidebarLink({ title, path, icon: Icon }: ISidebarLink) {
|
|
||||||
return (
|
|
||||||
<Link href={path} passHref legacyBehavior>
|
|
||||||
<a style={{ textDecoration: 'none' }}>
|
|
||||||
<LinkContainer>
|
|
||||||
{Icon && <Icon />}
|
|
||||||
<LinkText>{title}</LinkText>
|
|
||||||
</LinkContainer>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import * as React from "react";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import Drawer from "@mui/material/Drawer";
|
|
||||||
import Button from "@mui/material/Button";
|
|
||||||
import List from "@mui/material/List";
|
|
||||||
import Divider from "@mui/material/Divider";
|
|
||||||
import ListItem from "@mui/material/ListItem";
|
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
|
||||||
import InboxIcon from "@mui/icons-material/MoveToInbox";
|
|
||||||
import MailIcon from "@mui/icons-material/Mail";
|
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
|
||||||
|
|
||||||
export default function RightTemporaryDrawer() {
|
|
||||||
const [open, setOpen] = React.useState(false);
|
|
||||||
|
|
||||||
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 = () => (
|
|
||||||
<Box
|
|
||||||
sx={{ width: 400 }}
|
|
||||||
role="presentation"
|
|
||||||
onClick={toggleDrawer(false)}
|
|
||||||
onKeyDown={toggleDrawer(false)}
|
|
||||||
>
|
|
||||||
<List>
|
|
||||||
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
|
|
||||||
<ListItem key={text} disablePadding>
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemIcon>
|
|
||||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={text} />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
<Divider />
|
|
||||||
<List>
|
|
||||||
{["All mail", "Trash", "Spam"].map((text, index) => (
|
|
||||||
<ListItem key={text} disablePadding>
|
|
||||||
<ListItemButton>
|
|
||||||
<ListItemIcon>
|
|
||||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={text} />
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
sx={{
|
|
||||||
borderRadius: "8px",
|
|
||||||
textTransform: "none",
|
|
||||||
backgroundColor: "#f5f5f5",
|
|
||||||
color: "#555",
|
|
||||||
padding: "6px 12px",
|
|
||||||
boxShadow: "inset 0 0 0 1px #ddd",
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSize: "16px",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
"& .MuiButton-startIcon": {
|
|
||||||
marginRight: "12px",
|
|
||||||
backgroundColor: "#eee",
|
|
||||||
padding: "8px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "#e0e0e0",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
startIcon={<SearchIcon />}
|
|
||||||
onClick={toggleDrawer(true)}
|
|
||||||
>
|
|
||||||
Advanced Search
|
|
||||||
</Button>
|
|
||||||
{/* <Button onClick={toggleDrawer(true)}>Open Right Drawer</Button> */}
|
|
||||||
<Drawer anchor="right" open={open} onClose={toggleDrawer(false)}>
|
|
||||||
{list()}
|
|
||||||
</Drawer>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
// mocks/transactionData.ts
|
|
||||||
export const transactionDummyData = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
merchandId: 100987998,
|
|
||||||
transactionID: 1049131973,
|
|
||||||
user: 1,
|
|
||||||
created: "2025-06-18 10:10:30",
|
|
||||||
state: "FAILED",
|
|
||||||
statusDescription: "ERR_ABOVE_LIMIT",
|
|
||||||
pspStatusCode: 100501,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
merchandId: 100987998,
|
|
||||||
transactionID: 1049131973,
|
|
||||||
user: 2,
|
|
||||||
created: "2025-06-18 10:10:30",
|
|
||||||
state: "FAILED",
|
|
||||||
statusDescription: "ERR_ABOVE_LIMIT",
|
|
||||||
pspStatusCode: 100501,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
merchandId: 100987998,
|
|
||||||
transactionID: 1049131973,
|
|
||||||
user: 3,
|
|
||||||
created: "2025-06-18 10:10:30",
|
|
||||||
state: "FAILED",
|
|
||||||
statusDescription: "ERR_ABOVE_LIMIT",
|
|
||||||
pspStatusCode: 100501,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function TransactionsPage() {
|
|
||||||
const [userId, setUserId] = useState("");
|
|
||||||
const [state, setState] = useState("");
|
|
||||||
const [statusCode, setStatusCode] = useState("");
|
|
||||||
const [transactions, setTransactions] = useState<any[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const fetchTransactions = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
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());
|
|
||||||
const data = await response.json();
|
|
||||||
setTransactions(data.transactions);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching transactions:", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-4">
|
|
||||||
<h1 className="text-xl font-bold mb-4">Transaction Search</h1>
|
|
||||||
|
|
||||||
<div className="flex gap-4 mb-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm mb-1">User ID</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={userId}
|
|
||||||
onChange={(e) => setUserId(e.target.value)}
|
|
||||||
className="border p-2 rounded text-sm"
|
|
||||||
placeholder="Filter by user ID"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm mb-1">State</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={state}
|
|
||||||
onChange={(e) => setState(e.target.value)}
|
|
||||||
className="border p-2 rounded text-sm"
|
|
||||||
placeholder="Filter by state"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm mb-1">Status Code</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={statusCode}
|
|
||||||
onChange={(e) => setStatusCode(e.target.value)}
|
|
||||||
className="border p-2 rounded text-sm"
|
|
||||||
placeholder="Filter by status code"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-end">
|
|
||||||
<button
|
|
||||||
onClick={fetchTransactions}
|
|
||||||
disabled={loading}
|
|
||||||
className="bg-blue-500 text-white px-4 py-2 rounded text-sm"
|
|
||||||
>
|
|
||||||
{loading ? "Loading..." : "Search"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{transactions.length > 0 ? (
|
|
||||||
<div className="border rounded overflow-hidden">
|
|
||||||
<table className="min-w-full">
|
|
||||||
<thead className="bg-gray-100">
|
|
||||||
<tr>
|
|
||||||
<th className="py-2 px-4 text-left text-sm">ID</th>
|
|
||||||
<th className="py-2 px-4 text-left text-sm">User</th>
|
|
||||||
<th className="py-2 px-4 text-left text-sm">State</th>
|
|
||||||
<th className="py-2 px-4 text-left text-sm">Status Code</th>
|
|
||||||
<th className="py-2 px-4 text-left text-sm">Created</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{transactions.map((tx) => (
|
|
||||||
<tr key={tx.id} className="border-t">
|
|
||||||
<td className="py-2 px-4 text-sm">{tx.id}</td>
|
|
||||||
<td className="py-2 px-4 text-sm">{tx.user}</td>
|
|
||||||
<td className="py-2 px-4 text-sm">{tx.state}</td>
|
|
||||||
<td className="py-2 px-4 text-sm">{tx.pspStatusCode}</td>
|
|
||||||
<td className="py-2 px-4 text-sm">{tx.created}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="text-center py-4 text-sm">
|
|
||||||
{loading ? "Loading transactions..." : "No transactions found"}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mocks/handlers.ts
|
|
||||||
import { http, HttpResponse } from "msw";
|
|
||||||
|
|
||||||
export const handlers = [
|
|
||||||
http.get("https://api.example.com/transactions", ({ request }) => {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
|
|
||||||
// Get query parameters
|
|
||||||
const userId = url.searchParams.get("userId");
|
|
||||||
const state = url.searchParams.get("state");
|
|
||||||
const statusCode = url.searchParams.get("statusCode");
|
|
||||||
|
|
||||||
// Filter transactions based on query parameters
|
|
||||||
let filteredTransactions = [...transactionDummyData];
|
|
||||||
|
|
||||||
if (userId) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.user.toString() === userId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.state.toLowerCase() === state.toLowerCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusCode) {
|
|
||||||
filteredTransactions = filteredTransactions.filter(
|
|
||||||
(tx) => tx.pspStatusCode.toString() === statusCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return HttpResponse.json({
|
|
||||||
transactions: filteredTransactions,
|
|
||||||
count: filteredTransactions.length,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import Users from "@/app/features/pages/Admin/Users/users";
|
import Users from "@/app/features/Pages/Admin/Users/users";
|
||||||
|
|
||||||
export default async function BackOfficeUsersPage() {
|
export default async function BackOfficeUsersPage() {
|
||||||
const baseUrl =
|
const baseUrl =
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// This ensures this component is rendered only on the client side
|
// This ensures this component is rendered only on the client side
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Approve } from "@/app/features/pages/Approve/Approve";
|
import { Approve } from "@/app/features/Pages/Approve/Approve";
|
||||||
|
|
||||||
|
|
||||||
export default function ApprovePage() {
|
export default function ApprovePage() {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { DashboardHomePage } from "../features/pages/DashboardHomePage/DashboardHomePage";
|
import { DashboardHomePage } from "../features/Pages/DashboardHomePage/DashboardHomePage";
|
||||||
|
|
||||||
const DashboardPage = () => {
|
const DashboardPage = () => {
|
||||||
return <DashboardHomePage />;
|
return <DashboardHomePage />;
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
// This ensures this component is rendered only on the client side
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import TransactionTable from "@/app/components/pages/transactions/Transactions";
|
|
||||||
|
|
||||||
export default function TransactionPage() {
|
|
||||||
return (
|
|
||||||
<div style={{ width: "100%" }}>
|
|
||||||
{/* This page will now be rendered on the client-side */}
|
|
||||||
<TransactionTable />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -17,7 +17,7 @@ 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";
|
import { ISearchLabel } from "../DataTable/types";
|
||||||
|
|
||||||
export default function AdvancedSearch({ labels }: { labels: ISearchLabel[] }) {
|
export default function AdvancedSearch({ labels }: { labels: ISearchLabel[] }) {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|||||||
@ -6,20 +6,15 @@ import "./LoginModal.scss"; // Adjust path based on your actual structure
|
|||||||
// Define the props interface for LoginModal
|
// Define the props interface for LoginModal
|
||||||
type LoginModalProps = {
|
type LoginModalProps = {
|
||||||
onLogin: (email: string, password: string) => Promise<boolean>;
|
onLogin: (email: string, password: string) => Promise<boolean>;
|
||||||
authMessage: string;
|
|
||||||
clearAuthMessage: () => void;
|
|
||||||
};
|
};
|
||||||
// LoginModal component
|
// LoginModal component
|
||||||
export default function LoginModal({
|
export default function LoginModal({
|
||||||
onLogin,
|
onLogin,
|
||||||
authMessage,
|
|
||||||
clearAuthMessage,
|
|
||||||
}: LoginModalProps) {
|
}: LoginModalProps) {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
console.log("LoginModal rendered"); // Debugging log to check if the component renders
|
|
||||||
|
|
||||||
// Effect to clear authentication messages when email or password inputs change
|
// Effect to clear authentication messages when email or password inputs change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
interface ISearchLabel {
|
export interface ISearchLabel {
|
||||||
label: string;
|
label: string;
|
||||||
field: string;
|
field: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { FetchReport } from "../../FetchReports/FetchReports";
|
|||||||
import { Documentation } from "../../Documentation/Documentation";
|
import { Documentation } from "../../Documentation/Documentation";
|
||||||
import { AccountIQ } from "../../AccountIQ/AccountIQ";
|
import { AccountIQ } from "../../AccountIQ/AccountIQ";
|
||||||
import { WhatsNew } from "../../WhatsNew/WhatsNew";
|
import { WhatsNew } from "../../WhatsNew/WhatsNew";
|
||||||
import { TransactionsOverView } from "../../TransactionsOverview/TransactionsOverview";
|
import { TransactionsOverView } from "../../TransactionsOverView/TransactionsOverview";
|
||||||
|
|
||||||
export const DashboardHomePage = () => {
|
export const DashboardHomePage = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,131 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import { 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 * as XLSX from "xlsx";
|
|
||||||
import { saveAs } from "file-saver";
|
|
||||||
import { DataGrid } from "@mui/x-data-grid";
|
|
||||||
import { columns } from "./constants";
|
|
||||||
import { rows } from "./mockData";
|
|
||||||
|
|
||||||
const paginationModel = { page: 0, pageSize: 50 };
|
|
||||||
|
|
||||||
export default function TransactionTable() {
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv");
|
|
||||||
const [onlyCurrentTable, setOnlyCurrentTable] = useState(false);
|
|
||||||
|
|
||||||
const handleExport = () => {
|
|
||||||
const exportRows = onlyCurrentTable ? rows.slice(0, 5) : rows;
|
|
||||||
const exportData = [
|
|
||||||
columns.map((col) => col.headerName),
|
|
||||||
// @ts-expect-error - Dynamic field access from DataGrid columns
|
|
||||||
...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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledPaper>
|
|
||||||
<Stack
|
|
||||||
direction="row"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
p={2}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
label="Search"
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
// value={'searchQuery'}
|
|
||||||
onChange={(e) => console.log(`setSearchQuery(${e.target.value})`)}
|
|
||||||
sx={{ width: 300 }}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<FileUploadIcon />}
|
|
||||||
onClick={() => setOpen(true)}
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<DataGrid
|
|
||||||
rows={rows}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{ pagination: { paginationModel } }}
|
|
||||||
pageSizeOptions={[50, 100]}
|
|
||||||
sx={{ border: 0 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Export Dialog */}
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)}>
|
|
||||||
<DialogTitle>Export Transactions</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<FormControl fullWidth sx={{ mt: 2 }}>
|
|
||||||
<Select
|
|
||||||
value={fileType}
|
|
||||||
onChange={(e) =>
|
|
||||||
setFileType(e.target.value as "csv" | "xls" | "xlsx")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MenuItem value="csv">CSV</MenuItem>
|
|
||||||
<MenuItem value="xls">XLS</MenuItem>
|
|
||||||
<MenuItem value="xlsx">XLSX</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={onlyCurrentTable}
|
|
||||||
onChange={(e) => setOnlyCurrentTable(e.target.checked)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Only export the results in the current table"
|
|
||||||
sx={{ mt: 2 }}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
|
||||||
<Button variant="contained" onClick={handleExport}>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</StyledPaper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledPaper = styled(Paper)(() => ({
|
|
||||||
height: "90vh",
|
|
||||||
}));
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useSearchParams, useRouter } from "next/navigation";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
FormControl,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
FormControlLabel,
|
|
||||||
Checkbox,
|
|
||||||
Stack,
|
|
||||||
Paper,
|
|
||||||
TextField,
|
|
||||||
} from "@mui/material";
|
|
||||||
import FileUploadIcon from "@mui/icons-material/FileUpload";
|
|
||||||
import { DataGrid } from "@mui/x-data-grid";
|
|
||||||
import AdvancedSearch from "../../AdvancedSearch/AdvancedSearch";
|
|
||||||
import SearchFilters from "@/app/components/searchFilter/SearchFilters";
|
|
||||||
import { exportData } from "@/app/utils/exportData";
|
|
||||||
import { ITransaction } from "./types";
|
|
||||||
|
|
||||||
const paginationModel = { page: 0, pageSize: 50 };
|
|
||||||
|
|
||||||
interface IDepositProps {
|
|
||||||
res: ITransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
const TransactionsTable = ({ res }: IDepositProps) => {
|
|
||||||
const {filteredTransactions, transactionsColumns, transactionsSearchLabels} = res;
|
|
||||||
const router = useRouter()
|
|
||||||
const searchParams = useSearchParams()
|
|
||||||
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv");
|
|
||||||
const [onlyCurrentTable, setOnlyCurrentTable] = useState(false);
|
|
||||||
|
|
||||||
const filters = Object.fromEntries(searchParams.entries());
|
|
||||||
|
|
||||||
|
|
||||||
const handleClickField = (field: string, value: string) => {
|
|
||||||
const params = new URLSearchParams(searchParams.toString())
|
|
||||||
params.set(field, value)
|
|
||||||
router.push(`?${params.toString()}`)
|
|
||||||
router.refresh()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper>
|
|
||||||
<Stack
|
|
||||||
direction="row"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
p={2}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
label="Search"
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
onChange={(e) => console.log(`setSearchQuery(${e.target.value})`)}
|
|
||||||
sx={{ width: 300 }}
|
|
||||||
/>
|
|
||||||
<AdvancedSearch labels={transactionsSearchLabels} />
|
|
||||||
<SearchFilters filters={filters} />
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
startIcon={<FileUploadIcon />}
|
|
||||||
onClick={() => setOpen(true)}
|
|
||||||
>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<DataGrid
|
|
||||||
rows={filteredTransactions}
|
|
||||||
columns={transactionsColumns}
|
|
||||||
initialState={{ pagination: { paginationModel } }}
|
|
||||||
pageSizeOptions={[50, 100]}
|
|
||||||
sx={{ border: 0, cursor: 'pointer' }}
|
|
||||||
|
|
||||||
onCellClick={(params) => {
|
|
||||||
handleClickField(params.field, params.value as string)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Export Dialog */}
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)}>
|
|
||||||
<DialogTitle>Export Transactions</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<FormControl fullWidth sx={{ mt: 2 }}>
|
|
||||||
<Select
|
|
||||||
value={fileType}
|
|
||||||
onChange={(e) =>
|
|
||||||
setFileType(e.target.value as "csv" | "xls" | "xlsx")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MenuItem value="csv">CSV</MenuItem>
|
|
||||||
<MenuItem value="xls">XLS</MenuItem>
|
|
||||||
<MenuItem value="xlsx">XLSX</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={onlyCurrentTable}
|
|
||||||
onChange={(e) => setOnlyCurrentTable(e.target.checked)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Only export the results in the current table"
|
|
||||||
sx={{ mt: 2 }}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setOpen(false)}>Cancel</Button>
|
|
||||||
<Button variant="contained" onClick={() => exportData(filteredTransactions as unknown as ITransaction[], transactionsColumns, fileType, onlyCurrentTable, setOpen)}>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TransactionsTable
|
|
||||||
@ -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" },
|
|
||||||
]
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
|||||||
interface IDepositTransactionsColumns {
|
|
||||||
field: string;
|
|
||||||
headerName: string;
|
|
||||||
width: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFilteredTransactions {
|
|
||||||
id: number;
|
|
||||||
userId: number;
|
|
||||||
merchandId: number;
|
|
||||||
transactionId: number;
|
|
||||||
depositMethod: string;
|
|
||||||
status: string;
|
|
||||||
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[];
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Paper,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
} from "@mui/material";
|
|
||||||
|
|
||||||
const data1 = [
|
|
||||||
{ state: "Success", count: 120, percentage: "60%", color: "green" },
|
|
||||||
{ state: "Pending", count: 50, percentage: "25%", color: "orange" },
|
|
||||||
{ state: "Failed", count: 20, percentage: "10%", color: "red" },
|
|
||||||
{ state: "Other", count: 10, percentage: "5%", color: "gray" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const TransactionsOverviewTable = () => {
|
|
||||||
return (
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell align="center">State</TableCell>
|
|
||||||
<TableCell align="center">Count</TableCell>
|
|
||||||
<TableCell align="center">Percentage</TableCell>
|
|
||||||
<TableCell align="center">Action</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{data1.map((row, i) => (
|
|
||||||
<TableRow key={row.state + i}>
|
|
||||||
<TableCell align="center">
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
alignItems: "center",
|
|
||||||
mx: "auto", // center the flexbox itself
|
|
||||||
width: "73px", // consistent width for alignment
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
borderRadius: "50%",
|
|
||||||
bgcolor: row.color,
|
|
||||||
mr: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{row.state}
|
|
||||||
</Box>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="center">{row.count}</TableCell>
|
|
||||||
<TableCell align="center">{row.percentage}</TableCell>
|
|
||||||
<TableCell align="center">
|
|
||||||
<Button variant="outlined" size="small">
|
|
||||||
View
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
:root {
|
|
||||||
--background: #ffffff;
|
|
||||||
--foreground: #171717;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--background: #0a0a0a;
|
|
||||||
--foreground: #ededed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
max-width: 100vw;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: var(--foreground);
|
|
||||||
background: var(--background);
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +1,31 @@
|
|||||||
|
// app/login/page.tsx
|
||||||
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, Suspense } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import LoginModal from "../features/Auth/LoginModal"; // Your LoginModal component
|
import LoginModal from "../features/Auth/LoginModal";
|
||||||
|
|
||||||
import "./page.scss"; // Global styles for LoginModal and page
|
|
||||||
import Modal from "../components/Modal/Modal";
|
import Modal from "../components/Modal/Modal";
|
||||||
|
import "./page.scss";
|
||||||
|
|
||||||
export default function LoginPage() {
|
function LoginPageContent() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const redirectPath = searchParams.get("redirect") || "/dashboard";
|
const redirectPath = searchParams.get("redirect") || "/dashboard";
|
||||||
|
|
||||||
const [authMessage, setAuthMessage] = useState("");
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check if already logged in by trying to fetch a protected resource or checking a client-accessible flag
|
|
||||||
// For HTTP-only cookies, you can't directly read the token here.
|
|
||||||
// Instead, you'd rely on a server-side check (e.g., in middleware or a Server Component)
|
|
||||||
// or a simple client-side flag if your backend also sets one (less secure for token itself).
|
|
||||||
// For this example, we'll assume if they land here, they need to log in.
|
|
||||||
// A more robust check might involve a quick API call to /api/auth/status
|
|
||||||
// if the token is in an HTTP-only cookie.
|
|
||||||
const checkAuthStatus = async () => {
|
const checkAuthStatus = async () => {
|
||||||
// In a real app, this might be a call to a /api/auth/status endpoint
|
// Optionally implement
|
||||||
// that checks the HTTP-only cookie on the server and returns a boolean.
|
|
||||||
// For now, we'll rely on the middleware to redirect if unauthenticated.
|
|
||||||
// If the user somehow lands on /login with a valid cookie, the middleware
|
|
||||||
// should have redirected them already.
|
|
||||||
};
|
};
|
||||||
checkAuthStatus();
|
checkAuthStatus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLogin = async (email: string, password: string) => {
|
const handleLogin = async (email: string, password: string) => {
|
||||||
setAuthMessage("Attempting login...");
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/auth/login", {
|
const response = await fetch("/api/auth/login", {
|
||||||
// <--- CALLING YOUR INTERNAL ROUTE HANDLER
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -45,36 +33,21 @@ export default function LoginPage() {
|
|||||||
body: JSON.stringify({ email, password }),
|
body: JSON.stringify({ email, password }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Check if the response status is 2xx
|
|
||||||
// Backend has successfully set the HTTP-only cookie
|
|
||||||
setAuthMessage("Login successful!");
|
|
||||||
setIsLoggedIn(true);
|
setIsLoggedIn(true);
|
||||||
// Redirect to the intended path after successful login
|
|
||||||
router.replace(redirectPath);
|
router.replace(redirectPath);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// Handle login errors (e.g., invalid credentials)
|
|
||||||
setAuthMessage(data.message || "Login failed. Please try again.");
|
|
||||||
setIsLoggedIn(false);
|
setIsLoggedIn(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Login failed:", error);
|
console.error("Login failed:", error);
|
||||||
setAuthMessage("An error occurred during login. Please try again later.");
|
|
||||||
setIsLoggedIn(false);
|
setIsLoggedIn(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return isLoggedIn; // Return the current login status
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearAuthMessage = () => setAuthMessage("");
|
|
||||||
|
|
||||||
// If user is already logged in (e.g., redirected by middleware to dashboard),
|
|
||||||
// this page shouldn't be visible. The middleware should handle the primary redirect.
|
|
||||||
// This `isLoggedIn` state here is more for internal page logic if the user somehow
|
|
||||||
// bypasses middleware or lands on /login with a valid session.
|
|
||||||
// For a robust setup, the middleware is key.
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
<div className="page-container__content">
|
<div className="page-container__content">
|
||||||
@ -84,20 +57,22 @@ export default function LoginPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Always show the modal on the login page */}
|
|
||||||
<Modal
|
<Modal
|
||||||
open={true} // Always open on the login page
|
open={true}
|
||||||
onClose={() => {
|
onClose={() => {}}
|
||||||
/* No direct close for login modal, user must log in */
|
|
||||||
}}
|
|
||||||
title="Login to Backoffice"
|
title="Login to Backoffice"
|
||||||
>
|
>
|
||||||
<LoginModal
|
<LoginModal onLogin={handleLogin} />
|
||||||
onLogin={handleLogin} // Pass the API call function
|
|
||||||
authMessage={authMessage}
|
|
||||||
clearAuthMessage={clearAuthMessage}
|
|
||||||
/>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ Export wrapped in Suspense
|
||||||
|
export default function LoginPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<LoginPageContent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
export function MSWProvider({ children }: { children: React.ReactNode }) {
|
|
||||||
useEffect(() => {
|
|
||||||
if (process.env.NEXT_PUBLIC_API_MOCKING === "enabled") {
|
|
||||||
import("../../mock/browser").then(({ }) => {
|
|
||||||
// worker.start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { ReactNode } from 'react';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { store } from '../redux/store';
|
|
||||||
|
|
||||||
export function Providers({ children }: { children: ReactNode }) {
|
|
||||||
return <Provider store={store}>{children}</Provider>;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
// app/test/page.tsx
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
export default function TestPage() {
|
|
||||||
const [user, setUser] = useState(null);
|
|
||||||
const [loginStatus, setLoginStatus] = useState('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Test GET request
|
|
||||||
fetch('https://api.example.com/user')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => setUser(data));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleLogin = async () => {
|
|
||||||
// Test POST request
|
|
||||||
const response = await fetch('https://api.example.com/login', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
username: 'admin',
|
|
||||||
password: 'password123'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
setLoginStatus(response.ok ? 'Login successful' : `Error: ${result.error}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>MSW Test Page</h1>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>User Data (GET)</h2>
|
|
||||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Login Test (POST)</h2>
|
|
||||||
<button onClick={handleLogin}>Login as Admin</button>
|
|
||||||
<p>{loginStatus}</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
const SearchFilters = dynamic(
|
|
||||||
() => import("../components/searchFilter/SearchFilters"),
|
|
||||||
{ ssr: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
const [filters, setFilters] = useState({
|
|
||||||
user: '42',
|
|
||||||
state: 'FAILED',
|
|
||||||
startDate: '2025-06-28 23:25',
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleDeleteFilter = (key: string) => {
|
|
||||||
setFilters((prev) => ({ ...prev, [key]: null }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearAll = () => {
|
|
||||||
setFilters({ user: '', state: '', startDate: '' });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SearchFilters
|
|
||||||
filters={filters}
|
|
||||||
onDeleteFilter={handleDeleteFilter}
|
|
||||||
onClearAll={handleClearAll}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
export default function ProductsPage() {
|
|
||||||
const [category, setCategory] = useState('');
|
|
||||||
const [sort, setSort] = useState('price');
|
|
||||||
const [limit, setLimit] = useState('10');
|
|
||||||
const [products, setProducts] = useState<any[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const fetchProducts = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
// Construct URL with query parameters
|
|
||||||
const url = new URL('https://api.example.com/products');
|
|
||||||
if (category) url.searchParams.append('category', category);
|
|
||||||
if (sort) url.searchParams.append('sort', sort);
|
|
||||||
if (limit) url.searchParams.append('limit', limit);
|
|
||||||
|
|
||||||
const response = await fetch(url.toString());
|
|
||||||
const data = await response.json();
|
|
||||||
setProducts(data.products);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching products:', error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-4">
|
|
||||||
<h1 className="text-2xl font-bold mb-4">Product Search</h1>
|
|
||||||
|
|
||||||
<div className="flex gap-4 mb-6">
|
|
||||||
<div>
|
|
||||||
<label className="block mb-1">Category</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={category}
|
|
||||||
onChange={(e) => setCategory(e.target.value)}
|
|
||||||
className="border p-2 rounded"
|
|
||||||
placeholder="electronics, clothing, etc."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block mb-1">Sort By</label>
|
|
||||||
<select
|
|
||||||
value={sort}
|
|
||||||
onChange={(e) => setSort(e.target.value)}
|
|
||||||
className="border p-2 rounded"
|
|
||||||
>
|
|
||||||
<option value="price">Price</option>
|
|
||||||
<option value="name">Name</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block mb-1">Items Per Page</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={limit}
|
|
||||||
onChange={(e) => setLimit(e.target.value)}
|
|
||||||
className="border p-2 rounded"
|
|
||||||
min="1"
|
|
||||||
max="100"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={fetchProducts}
|
|
||||||
disabled={loading}
|
|
||||||
className="bg-blue-500 text-white px-4 py-2 rounded self-end"
|
|
||||||
>
|
|
||||||
{loading ? 'Loading...' : 'Search'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{products.length > 0 && (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-semibold mb-2">Results</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
||||||
{products.map((product) => (
|
|
||||||
<div key={product.id} className="border p-4 rounded">
|
|
||||||
<h3 className="font-medium">{product.name}</h3>
|
|
||||||
<p>Category: {product.category}</p>
|
|
||||||
<p>Price: ${product.price}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import HomeIcon from '@mui/icons-material/Home';
|
|
||||||
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
|
||||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
|
||||||
import VerifiedUserIcon from '@mui/icons-material/VerifiedUser';
|
|
||||||
import PeopleIcon from '@mui/icons-material/People';
|
|
||||||
import BarChartIcon from '@mui/icons-material/BarChart';
|
|
||||||
import GavelIcon from '@mui/icons-material/Gavel';
|
|
||||||
import HubIcon from '@mui/icons-material/Hub';
|
|
||||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
|
||||||
import InsightsIcon from '@mui/icons-material/Insights';
|
|
||||||
import DescriptionIcon from '@mui/icons-material/Description';
|
|
||||||
import SupportAgentIcon from '@mui/icons-material/SupportAgent';
|
|
||||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
|
||||||
import { ISidebarLink } from '@/interfaces/SidebarLink.interfaces';
|
|
||||||
|
|
||||||
|
|
||||||
export const SIDEBAR_LINKS: ISidebarLink[] = [
|
|
||||||
{ title: 'Home', path: '/dashboard', icon: HomeIcon },
|
|
||||||
{ title: 'Transaction', path: '/dashboard/transactions', icon: AccountBalanceWalletIcon },
|
|
||||||
{ title: 'Approve', path: '/dashboard/approve', icon: CheckCircleIcon },
|
|
||||||
{ title: 'Investigate', path: '/dashboard/investigate', icon: SearchIcon },
|
|
||||||
{ title: 'KYC', path: '/kyc', icon: VerifiedUserIcon },
|
|
||||||
{ title: 'User Accounts', path: '/user-accounts', icon: PeopleIcon },
|
|
||||||
{ title: 'Analytics', path: '/analytics', icon: BarChartIcon },
|
|
||||||
{ title: 'Rules', path: '/rules', icon: GavelIcon },
|
|
||||||
{ title: 'Rules Hub', path: '/rules-hub', icon: HubIcon },
|
|
||||||
{ title: 'Admin', path: '/admin', icon: AdminPanelSettingsIcon },
|
|
||||||
{ title: 'Account IQ', path: '/account-iq', icon: InsightsIcon },
|
|
||||||
{ title: 'Documentation', path: '/documentation', icon: DescriptionIcon },
|
|
||||||
{ title: 'Support', path: '/support', icon: SupportAgentIcon },
|
|
||||||
{ title: 'System Status', path: '/system-status', icon: WarningAmberIcon },
|
|
||||||
];
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import { ElementType } from "react";
|
|
||||||
|
|
||||||
export interface ISidebarLink {
|
|
||||||
title: string;
|
|
||||||
path: string;
|
|
||||||
icon?: ElementType;
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user