176 lines
5.0 KiB
TypeScript

"use client";
import React, { useState, useEffect, useCallback } from "react";
import { DataGrid } from "@mui/x-data-grid";
import { Box, Paper, Alert } from "@mui/material";
import DataTableHeader from "./DataTableHeader";
import StatusChangeDialog from "./StatusChangeDialog";
import Spinner from "@/app/components/Spinner/Spinner";
import { selectStatus, selectError } from "@/app/redux/advanedSearch/selectors";
import { selectEnhancedColumns } from "./re-selectors";
import { useSelector } from "react-redux";
import { DataRowBase } from "./types";
interface DataTableProps<TRow extends DataRowBase> {
rows: TRow[];
extraColumns?: string[];
enableStatusActions?: boolean;
}
const DataTable = <TRow extends DataRowBase>({
rows,
extraColumns,
enableStatusActions = false,
}: DataTableProps<TRow>) => {
const [showExtraColumns, setShowExtraColumns] = useState(false);
const [localRows, setLocalRows] = useState(rows);
const [modalOpen, setModalOpen] = useState(false);
const [selectedRowId, setSelectedRowId] = useState<number | null>(null);
const [pendingStatus, setPendingStatus] = useState<string>("");
const [reason, setReason] = useState<string>("");
const [statusUpdateError, setStatusUpdateError] = useState<string | null>(
null
);
const [isUpdatingStatus, setIsUpdatingStatus] = useState(false);
const status = useSelector(selectStatus);
const errorMessage = useSelector(selectError);
useEffect(() => {
setLocalRows(rows);
}, [rows]);
const handleStatusChange = useCallback((rowId: number, newStatus: string) => {
setSelectedRowId(rowId);
setPendingStatus(newStatus);
setModalOpen(true);
}, []);
const handleStatusSave = async () => {
if (!selectedRowId || !pendingStatus) return;
setStatusUpdateError(null);
setIsUpdatingStatus(true);
try {
const payload = {
data: {
status: pendingStatus,
notes: reason.trim(),
},
fields: ["Status", "Notes"],
};
const response = await fetch(
`/api/dashboard/transactions/${selectedRowId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
}
);
const result = await response.json();
if (!response.ok) {
throw new Error(
result?.message || result?.error || "Failed to update transaction"
);
}
setLocalRows(prev =>
prev.map(row =>
row.id === selectedRowId ? { ...row, status: pendingStatus } : row
)
);
setModalOpen(false);
setReason("");
setPendingStatus("");
setStatusUpdateError(null);
setSelectedRowId(null);
} catch (err) {
setStatusUpdateError(
err instanceof Error ? err.message : "Failed to update transaction"
);
} finally {
setIsUpdatingStatus(false);
}
};
// Columns with custom renderers
const enhancedColumns = useSelector(state =>
selectEnhancedColumns(
state,
enableStatusActions,
extraColumns,
showExtraColumns,
localRows,
handleStatusChange
)
);
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={localRows}
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={pendingStatus}
reason={reason}
setReason={setReason}
handleClose={() => {
setModalOpen(false);
setReason("");
setPendingStatus("");
setStatusUpdateError(null);
setSelectedRowId(null);
}}
handleSave={handleStatusSave}
isSubmitting={isUpdatingStatus}
errorMessage={statusUpdateError}
/>
</Paper>
</>
);
};
// Memoize to avoid unnecessary re-renders
export default React.memo(DataTable);