Refactored styled to use hybrid Scss and MUI for more dynamic styling

This commit is contained in:
Mitchell Magro 2025-06-30 09:29:58 +02:00
parent 69fbd6d5e1
commit f595509104
44 changed files with 5943 additions and 490 deletions

View File

@ -0,0 +1,23 @@
/* PageLinks.scss */
.link-container {
display: flex;
align-items: center;
padding: 12px 1px;
border-radius: 4px;
color: var(--text-tertiary);
text-decoration: none;
transition: background 0.2s ease-in-out;
&:hover {
color: #fff;
background-color: var(--hover-color);
cursor: pointer;
}
}
.link-text {
color: var(--text-tertiary);
margin-left: 12px;
font-weight: 500;
}

View File

@ -0,0 +1,25 @@
"use client";
import Link from "next/link";
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
import clsx from "clsx"; // Utility to merge class names
import "./PageLinks.scss";
interface IPageLinksProps extends ISidebarLink {
isShowIcon?: boolean;
}
export default function PageLinks({
title,
path,
icon: Icon,
}: IPageLinksProps) {
return (
<Link href={path} passHref legacyBehavior>
<a className={clsx("link-container")}>
{Icon && <Icon />}
<span className="link-text">{title}</span>
</a>
</Link>
);
}

View File

@ -1,7 +1,7 @@
// This ensures this component is rendered only on the client side
'use client';
"use client";
import { Approve } from '@/app/components/Pages/Approve/Approve';
import { Approve } from "@/app/features/Pages/Approve/Approve";
export default function ApprovePage() {
return (

View File

@ -1,15 +1,14 @@
// This ensures this component is rendered only on the client side
// 'use client';
import Typography from '@mui/material/Typography';
import React from 'react';
import Typography from "@mui/material/Typography";
import React from "react";
export default function KycPage() {
return (
<div>
<Typography variant="h4" gutterBottom>
<div>
<Typography variant="h4" gutterBottom>
KYC Overview
</Typography>
</div>
);
}
}

View File

@ -1,16 +1,18 @@
'use client';
"use client";
import React from 'react';
import { LayoutWrapper } from '../components/dashboard/layout/layoutWrapper';
import { MainContent } from '../components/dashboard/layout/mainContent';
import SideBar from '../components/dashboard/sidebar/Sidebar';
import Header from '../components/dashboard/header/Header';
import React from "react";
import { LayoutWrapper } from "../features/dashboard/layout/layoutWrapper";
import { MainContent } from "../features/dashboard/layout/mainContent";
import SideBar from "../features/dashboard/sidebar/Sidebar";
import Header from "../features/dashboard/header/Header";
const DashboardLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const DashboardLayout: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
return (
<LayoutWrapper>
<SideBar />
<div style={{ flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
<div style={{ flexGrow: 1, display: "flex", flexDirection: "column" }}>
<MainContent>
<Header />
{children}

20
app/dashboard/loading.tsx Normal file
View File

@ -0,0 +1,20 @@
// app/dashboard/loading.tsx
"use client";
import CircularProgress from "@mui/material/CircularProgress";
import { styled } from "@mui/system";
const LoaderWrapper = styled("div")({
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
});
export default function Loading() {
return (
<LoaderWrapper>
<CircularProgress />
</LoaderWrapper>
);
}

View File

@ -1,11 +1,9 @@
'use client';
"use client";
import { DashboardHomePage } from "../components/Pages/DashboardHomePage/DashboardHomePage";
import { DashboardHomePage } from "../features/Pages/DashboardHomePage/DashboardHomePage";
const DashboardPage = () => {
return (
<DashboardHomePage />
);
return <DashboardHomePage />;
};
export default DashboardPage;

View File

@ -1,13 +1,13 @@
// This ensures this component is rendered only on the client side
'use client';
"use client";
import TransactionTable from '@/app/components/Pages/Transactions/Transactions';
import TransactionTable from "@/app/features/Pages/transactions/Transactions";
export default function TransactionPage() {
return (
<div style={{ width: '100%' }}>
<div style={{ width: "100%" }}>
{/* This page will now be rendered on the client-side */}
<TransactionTable />
</div>
);
}
}

View File

@ -0,0 +1,23 @@
import { styled } from "@mui/material";
import { SectionCard } from "../SectionCard/SectionCard";
const AccountIQIcon = styled("div")(() => ({
fontWeight: "bold",
color: "#4ecdc4",
marginTop: "4px",
}));
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" },
]}
/>
);
};

View File

@ -0,0 +1,84 @@
import { useState } from "react";
import { Box, Typography, Paper, Popover } from "@mui/material";
import { DateRange, Range, DateRangeProps } from "react-date-range";
import { format } from "date-fns";
import "react-date-range/dist/styles.css";
import "react-date-range/dist/theme/default.css";
export const DateRangePicker = () => {
const [range, setRange] = useState<Range[]>([
{
startDate: new Date(),
endDate: new Date(),
key: "selection"
}
]);
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const handleSelect: DateRangeProps['onChange'] = (ranges) => {
if (ranges.selection) {
setRange([ranges.selection]);
if (ranges.selection.endDate !== ranges.selection.startDate) {
setAnchorEl(null);
}
}
};
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? 'date-range-popover' : undefined;
return (
<Box>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<Paper>
<DateRange
editableDateInputs={true}
onChange={handleSelect}
moveRangeOnFirstSelection={false}
ranges={range}
/>
</Paper>
</Popover>
<Box>
<Typography
onClick={handleClick}
sx={{
fontSize: '0.875rem',
cursor: 'pointer',
p: 1,
borderRadius: 1,
'&:hover': {
backgroundColor: 'action.hover',
}
}}
>
{format(range[0].startDate ?? new Date(), "PPP")} - {format(range[0].endDate ?? new Date(), "PPP")}
</Typography>
</Box>
</Box>
);
}

View File

@ -0,0 +1,18 @@
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" sx={{ height: "auto" }} />}
items={[
{ title: "Provider Integration Overview" },
{ title: "APIs Introduction" },
{ title: "Documentation Overview" },
{ title: "How-Tos" },
]}
/>
);
};

View File

@ -0,0 +1,173 @@
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";
import { DateRangePicker } from "../DateRangePicker/DateRangePicker";
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: "23px",
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">
<DateRangePicker />
</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>
);
};

View File

@ -0,0 +1,72 @@
import { Box, Card, CardContent, Typography, IconButton } 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 { DateRangePicker } from "../DateRangePicker/DateRangePicker";
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>
<Box sx={{ display: "flex", justifyContent: "space-between", mb: 2 }}>
<Typography variant="h5" fontWeight="bold">
General Health
</Typography>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<CalendarTodayIcon fontSize="small" />
<Typography variant="body2">
<DateRangePicker />
</Typography>
<IconButton size="small">
<MoreVertIcon fontSize="small" />
</IconButton>
</Box>
</Box>
<Box sx={{ display: "flex", justifyContent: "space-around", mt: 2 }}>
{stats.map((item, i) => (
<StatItem key={item.label + i} {...item} />
))}
</Box>
</CardContent>
</Card>
);
};

View File

@ -0,0 +1,162 @@
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>
);
}

View File

@ -0,0 +1,24 @@
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 />
<FetchReport />
<TransactionsWaitingApproval />
<Documentation />
<AccountIQ />
<WhatsNew />
</>
)
}

View File

@ -0,0 +1,131 @@
"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",
}));

View File

@ -0,0 +1,80 @@
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

View File

@ -0,0 +1,69 @@
/* 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 >
);
}

View File

@ -0,0 +1,50 @@
import {
CardContent,
Typography,
Divider,
List,
ListItem,
ListItemText,
Paper,
Box,
IconButton,
} from "@mui/material";
import MoreVertIcon from "@mui/icons-material/MoreVert";
import { ISectionCardProps } from "./types";
export const SectionCard = ({ title, icon, items }: ISectionCardProps) => (
<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", gap: 1 }}>
{icon}
<Typography variant="h6" 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={item.title + index} disableGutters>
<ListItemText
primary={item.title}
secondary={item.date}
primaryTypographyProps={{ fontSize: 14 }}
secondaryTypographyProps={{ fontSize: 12 }}
/>
</ListItem>
))}
</List>
</CardContent>
</Paper>
);

View File

@ -0,0 +1,12 @@
import { ReactNode } from "react";
export interface ISectionItem {
title: string;
date?: string;
}
export interface ISectionCardProps {
title: string;
icon: ReactNode;
items: ISectionItem[];
}

View File

@ -0,0 +1,70 @@
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>
);
};

View File

@ -0,0 +1,68 @@
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: "23px",
margin: 2,
display: "flex",
flexDirection: "column",
}}
>
{/* Title and All Transactions Button */}
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
px: 1,
}}
>
<Typography variant="h5" fontWeight="bold">
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>
);
};

View File

@ -0,0 +1,195 @@
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",
},
{
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",
},
{
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="h5" 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}
sx={{
maxHeight: 400, // Set desired height
overflow: "auto",
}}
>
<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, i) => (
<TableRow key={tx.id + i}>
<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>
);
};

View File

@ -0,0 +1,27 @@
import { SectionCard } from "../SectionCard/SectionCard";
import WifiIcon from "@mui/icons-material/Wifi";
export const WhatsNew = () => {
return (
<SectionCard
title="Whats New"
icon={<WifiIcon fontSize="small" sx={{ height: "auto" }} />}
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" },
]}
/>
);
};

View File

@ -0,0 +1,17 @@
.header-toolbar {
display: flex;
align-items: center;
.left-group {
width: 100px;
display: flex;
align-items: center;
gap: 12px; // optional spacing between menu and dropdown
}
.right-group {
margin-left: auto; // pushes it to the far right
display: flex;
align-items: center;
}
}

View File

@ -0,0 +1,46 @@
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/DropDown";
import AccountMenu from "./accountMenu/AccountMenu";
import "./Header.scss";
const Header = () => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
// Handle menu open
const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
// Handle menu close
const handleMenuClose = () => {
setAnchorEl(null);
};
const handleChange = (e: any) => {};
return (
<AppBar
position="sticky"
color="transparent"
elevation={0}
sx={{ borderBottom: "1px solid #22242626" }}
>
<Toolbar className="header-toolbar">
<div className="left-group">
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Dropdown onChange={handleChange} />
</div>
<div className="right-group">
<AccountMenu />
</div>
</Toolbar>
</AppBar>
);
};
export default Header;

View File

@ -0,0 +1,64 @@
"use client";
import React from "react";
import {
Menu,
MenuItem,
IconButton,
ListItemIcon,
Typography,
} from "@mui/material";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import SettingsIcon from "@mui/icons-material/Settings";
import LogoutIcon from "@mui/icons-material/Logout";
export default function AccountMenu() {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
<IconButton onClick={handleClick} color="inherit">
<AccountCircleIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
>
<MenuItem>
<ListItemIcon>
<AccountCircleIcon fontSize="small" />
</ListItemIcon>
<Typography variant="inherit">Account</Typography>
</MenuItem>
<MenuItem>
<ListItemIcon>
<SettingsIcon fontSize="small" />
</ListItemIcon>
<Typography variant="inherit">Settings</Typography>
</MenuItem>
<MenuItem>
<ListItemIcon>
<LogoutIcon fontSize="small" />
</ListItemIcon>
<Typography variant="inherit">Sign out</Typography>
</MenuItem>
</Menu>
</>
);
}

View File

@ -0,0 +1,8 @@
.dropdown-container {
.link-container {
color: var(--text-secondary);
.link-text {
color: var(--text-primary);
}
}
}

View File

@ -0,0 +1,52 @@
import React from "react";
import {
FormControl,
InputLabel,
Select,
MenuItem,
SelectChangeEvent,
} from "@mui/material";
import { SIDEBAR_LINKS } from "@/app/features/dashboard/sidebar/SidebarLink.constants";
import { ISidebarLink } from "@/app/features/dashboard/sidebar/SidebarLink.interfaces";
import PageLinks from "../../../../components/PageLinks/PageLinks";
import "./DropDown.scss";
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={{ minWidth: 200 }}>
<InputLabel id="sidebar-dropdown-label">Navigate To</InputLabel>
<Select
labelId="sidebar-dropdown-label"
value={value}
onChange={handleChange}
label="Navigate To"
MenuProps={{
PaperProps: {
style: {
maxHeight: 200,
},
},
}}
>
<em className="em">Select a page</em>
<MenuItem value="" disabled></MenuItem>
<div className="dropdown-container">
{SIDEBAR_LINKS.map((link: ISidebarLink) => (
<PageLinks key={link.path} title={link.title} path={link.path} />
))}
</div>
</Select>
</FormControl>
);
}

View File

@ -0,0 +1,7 @@
import { styled } from "@mui/system";
export const LayoutWrapper = styled("div")({
display: "flex",
width: "100%",
height: "100vh",
});

View File

@ -0,0 +1,8 @@
import { styled } from '@mui/system';
export const MainContent = styled('div')(({ theme }) => ({
marginLeft: '240px',
padding: theme.spacing(3),
minHeight: '100vh',
width: 'calc(100% - 240px)',
}));

View File

@ -0,0 +1,30 @@
"use client";
import React from "react";
import DashboardIcon from "@mui/icons-material/Dashboard";
import { SIDEBAR_LINKS } from "@/app/features/dashboard/sidebar/SidebarLink.constants";
import PageLinks from "../../../components/PageLinks/PageLinks";
import "./sideBar.scss";
const SideBar = () => {
return (
<aside className="sidebar-container">
<div className="sidebar-header">
<span>
Betrise cashir{" "}
<DashboardIcon fontSize="small" className="sidebar-icon-spacing" />
</span>
</div>
{SIDEBAR_LINKS.map((link) => (
<PageLinks
key={link.path}
title={link.title}
path={link.path}
icon={link.icon}
/>
))}
</aside>
);
};
export default SideBar;

View File

@ -0,0 +1,32 @@
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 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 { ISidebarLink } from "@/app/features/dashboard/sidebar/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 },
];

View File

@ -0,0 +1,7 @@
import { ElementType } from "react";
export interface ISidebarLink {
title: string;
path: string;
icon?: ElementType;
}

View File

@ -0,0 +1,27 @@
.sidebar-container {
position: fixed;
top: 0;
left: 0;
width: 240px;
height: 100vh;
background-color: var(--background-primary);
color: white;
display: flex;
flex-direction: column;
padding: 16px;
z-index: 1100;
border-right: 1px solid #333;
}
.sidebar-header {
font-size: 20px;
font-weight: 600;
margin-bottom: 24px;
display: flex;
align-items: center;
color: white;
}
.sidebar-icon-spacing {
margin-left: 8px;
}

View File

@ -1,12 +1,17 @@
import ThemeRegistry from '@/config/ThemeRegistry';
import type { Metadata } from 'next';
import ThemeRegistry from "@/config/ThemeRegistry";
import type { Metadata } from "next";
import "../styles/globals.scss";
export const metadata: Metadata = {
title: 'Your App',
description: 'Generated by Next.js',
title: "Your App",
description: "Generated by Next.js",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>

11
app/utils/defineStyles.ts Normal file
View File

@ -0,0 +1,11 @@
// utils/defineStyles.ts
import type { SxProps, Theme } from "@mui/material/styles";
/**
* Helper to define style objects with full key/type inference as SxProps<Theme>
*/
export function defineStyles<T extends Record<string, SxProps<Theme>>>(
styles: T
): T {
return styles;
}

View File

@ -1,10 +1,13 @@
"use client";
'use client';
import { ThemeProvider, CssBaseline } from "@mui/material";
import theme from "./theme";
import { ThemeProvider, CssBaseline } from '@mui/material';
import theme from './theme';
export default function ThemeRegistry({ children }: { children: React.ReactNode }) {
export default function ThemeRegistry({
children,
}: {
children: React.ReactNode;
}) {
return (
<ThemeProvider theme={theme}>
<CssBaseline />

1460
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,13 +12,19 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^7.1.1",
"@mui/material": "^7.1.1",
"@mui/material": "^7.1.2",
"@mui/x-data-grid": "^8.5.2",
"@mui/x-date-pickers": "^8.5.3",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"dayjs": "^1.11.13",
"file-saver": "^2.0.5",
"next": "15.3.3",
"react": "^19.0.0",
"react-date-range": "^2.0.1",
"react-dom": "^19.0.0",
"recharts": "^2.15.3",
"sass": "^1.89.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
@ -26,6 +32,7 @@
"@types/file-saver": "^2.0.7",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-date-range": "^1.4.10",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.3",

52
styles/globals.scss Normal file
View File

@ -0,0 +1,52 @@
@use "./variables" as *;
:root {
--background: #0a0a0a;
--foreground: #ededed;
--primary-color: #{$primary-color};
--secondary-color: #{$secondary-color};
--background-primary: #{$background-primary};
--text-primary: #{$text-primary};
--text-secondary: #{$text-secondary};
--text-tertiary: #{$text-tertiary};
--hover-color: #{$hover-color};
--font-family-base: #{$font-family-base};
}
@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;
}
}

10
styles/variables.scss Normal file
View File

@ -0,0 +1,10 @@
// These Variable match thee MUI theme variables. For now they have to be updated manually. But can be done automatically also
$primary-color: #1976d2;
$secondary-color: #d32f2f;
$background-primary: rgb(69, 190, 171);
$text-primary: #000000;
$text-secondary: #555555;
$text-tertiary: #ffffff;
$hover-color: rgba(0, 0, 0, 0.08);
$font-family-base: "Roboto", "Helvetica", "Arial", sans-serif;

1056
yarn.lock

File diff suppressed because it is too large Load Diff