203 lines
6.0 KiB
TypeScript
203 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useMemo } from "react";
|
|
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
|
import { Box, Paper, IconButton, Alert } from "@mui/material";
|
|
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
|
import StatusChangeDialog from "./StatusChangeDialog";
|
|
import DataTableHeader from "./DataTableHeader";
|
|
import { TABLE_COLUMNS } from "./constants";
|
|
import Spinner from "@/app/components/Spinner/Spinner";
|
|
import { useSelector } from "react-redux";
|
|
import { selectStatus, selectError } from "@/app/redux/auth/selectors";
|
|
|
|
interface IDataTableProps<TRow extends { id: number }> {
|
|
rows: TRow[];
|
|
extraColumns?: string[];
|
|
}
|
|
|
|
const DataTable = <TRow extends { id: number }>({
|
|
rows,
|
|
extraColumns,
|
|
}: IDataTableProps<TRow>) => {
|
|
const [modalOpen, setModalOpen] = useState(false);
|
|
const [selectedRowId, setSelectedRowId] = useState<number | null>(null);
|
|
const [newStatus, setNewStatus] = useState<string>("");
|
|
const [reason, setReason] = useState<string>("");
|
|
const [showExtraColumns, setShowExtraColumns] = useState(false);
|
|
|
|
const status = useSelector(selectStatus);
|
|
const errorMessage = useSelector(selectError);
|
|
|
|
// Open status modal
|
|
const handleStatusChange = (id: number, status: string) => {
|
|
setSelectedRowId(id);
|
|
setNewStatus(status);
|
|
setModalOpen(true);
|
|
};
|
|
|
|
const handleStatusSave = () => {
|
|
setModalOpen(false);
|
|
setReason("");
|
|
// rows update should happen in parent component
|
|
};
|
|
|
|
// Columns filtered by extraColumns toggle
|
|
const visibleColumns = useMemo(() => {
|
|
if (!extraColumns || extraColumns.length === 0) return TABLE_COLUMNS;
|
|
return showExtraColumns
|
|
? TABLE_COLUMNS
|
|
: TABLE_COLUMNS.filter(col => !extraColumns.includes(col.field));
|
|
}, [extraColumns, showExtraColumns]);
|
|
|
|
// Columns with custom renderers
|
|
const enhancedColumns = useMemo<GridColDef[]>(() => {
|
|
return visibleColumns.map(col => {
|
|
if (col.field === "status") {
|
|
return {
|
|
...col,
|
|
renderCell: (params: GridRenderCellParams) => {
|
|
const value = params.value?.toLowerCase();
|
|
let bgColor = "#e0e0e0";
|
|
let textColor = "#000";
|
|
switch (value) {
|
|
case "completed":
|
|
bgColor = "#d0f0c0";
|
|
textColor = "#1b5e20";
|
|
break;
|
|
case "pending":
|
|
bgColor = "#fff4cc";
|
|
textColor = "#9e7700";
|
|
break;
|
|
case "inprogress":
|
|
bgColor = "#cce5ff";
|
|
textColor = "#004085";
|
|
break;
|
|
case "error":
|
|
bgColor = "#ffcdd2";
|
|
textColor = "#c62828";
|
|
break;
|
|
}
|
|
return (
|
|
<Box
|
|
sx={{
|
|
backgroundColor: bgColor,
|
|
color: textColor,
|
|
px: 1.5,
|
|
py: 0.5,
|
|
borderRadius: 1,
|
|
fontWeight: 500,
|
|
textTransform: "capitalize",
|
|
display: "inline-block",
|
|
width: "100%",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
{params.value}
|
|
</Box>
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
if (col.field === "userId") {
|
|
return {
|
|
...col,
|
|
headerAlign: "center",
|
|
align: "center",
|
|
renderCell: (params: GridRenderCellParams) => (
|
|
<Box
|
|
sx={{
|
|
display: "grid",
|
|
gridTemplateColumns: "1fr auto",
|
|
alignItems: "center",
|
|
width: "100%",
|
|
px: 1,
|
|
}}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<Box
|
|
sx={{
|
|
fontWeight: 500,
|
|
fontSize: "0.875rem",
|
|
color: "text.primary",
|
|
}}
|
|
>
|
|
{params.value}
|
|
</Box>
|
|
<IconButton
|
|
href={`/users/${params.value}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
size="small"
|
|
sx={{ p: 0.5, ml: 1 }}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<OpenInNewIcon fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
),
|
|
};
|
|
}
|
|
|
|
return col;
|
|
});
|
|
}, [visibleColumns]);
|
|
|
|
return (
|
|
<>
|
|
{status === "loading" && <Spinner size="small" color="#fff" />}
|
|
{status === "failed" && (
|
|
<Alert severity="error">
|
|
{errorMessage || "Failed to load transactions."}
|
|
</Alert>
|
|
)}
|
|
<Paper sx={{ width: "100%", overflowX: "hidden" }}>
|
|
<DataTableHeader
|
|
extraColumns={extraColumns}
|
|
showExtraColumns={showExtraColumns}
|
|
onToggleExtraColumns={() => setShowExtraColumns(prev => !prev)}
|
|
onOpenExport={() => {}}
|
|
/>
|
|
|
|
<Box sx={{ width: "100%", overflowX: "auto" }}>
|
|
<Box sx={{ minWidth: 1200 }}>
|
|
<DataGrid
|
|
rows={rows}
|
|
columns={enhancedColumns}
|
|
pageSizeOptions={[10, 25, 50, 100]}
|
|
sx={{
|
|
border: 0,
|
|
cursor: "pointer",
|
|
"& .MuiDataGrid-cell": {
|
|
py: 1,
|
|
textAlign: "center",
|
|
justifyContent: "center",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
},
|
|
"& .MuiDataGrid-columnHeader": {
|
|
textAlign: "center",
|
|
justifyContent: "center",
|
|
},
|
|
}}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
|
|
<StatusChangeDialog
|
|
open={modalOpen}
|
|
newStatus={newStatus}
|
|
reason={reason}
|
|
setReason={setReason}
|
|
handleClose={() => setModalOpen(false)}
|
|
handleSave={handleStatusSave}
|
|
/>
|
|
</Paper>
|
|
</>
|
|
);
|
|
};
|
|
|
|
// Memoize to avoid unnecessary re-renders
|
|
export default React.memo(DataTable);
|