2025-11-17 12:15:20 +01:00

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);