195 lines
5.8 KiB
TypeScript
195 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
|
import { DataGrid, GridPaginationModel } 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,
|
|
selectPagination,
|
|
selectPaginationModel,
|
|
} from "@/app/redux/advanedSearch/selectors";
|
|
import { makeSelectEnhancedColumns } from "./re-selectors";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { DataRowBase } from "./types";
|
|
import { setPagination } from "@/app/redux/advanedSearch/advancedSearchSlice";
|
|
import { AppDispatch } from "@/app/redux/store";
|
|
|
|
interface DataTableProps<TRow extends DataRowBase> {
|
|
rows: TRow[];
|
|
extraColumns?: string[];
|
|
enableStatusActions?: boolean;
|
|
totalRows?: number;
|
|
}
|
|
|
|
const DataTable = <TRow extends DataRowBase>({
|
|
rows: localRows,
|
|
extraColumns,
|
|
enableStatusActions = false,
|
|
totalRows: totalRows,
|
|
}: DataTableProps<TRow>) => {
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const [showExtraColumns, setShowExtraColumns] = useState(false);
|
|
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);
|
|
const pagination = useSelector(selectPagination);
|
|
const paginationModel = useSelector(selectPaginationModel);
|
|
|
|
const handlePaginationModelChange = useCallback(
|
|
(model: GridPaginationModel) => {
|
|
console.log("model", model);
|
|
const nextPage = model.page + 1;
|
|
const nextLimit = model.pageSize;
|
|
|
|
if (nextPage !== pagination.page || nextLimit !== pagination.limit) {
|
|
dispatch(setPagination({ page: nextPage, limit: nextLimit }));
|
|
}
|
|
},
|
|
[dispatch, pagination.page, pagination.limit]
|
|
);
|
|
|
|
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"
|
|
);
|
|
}
|
|
|
|
setModalOpen(false);
|
|
setReason("");
|
|
setPendingStatus("");
|
|
setStatusUpdateError(null);
|
|
setSelectedRowId(null);
|
|
} catch (err) {
|
|
setStatusUpdateError(
|
|
err instanceof Error ? err.message : "Failed to update transaction"
|
|
);
|
|
} finally {
|
|
setIsUpdatingStatus(false);
|
|
}
|
|
};
|
|
|
|
const selectEnhancedColumns = useMemo(makeSelectEnhancedColumns, []);
|
|
|
|
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: "85vw" }}>
|
|
<Box sx={{ minWidth: 1200 }}>
|
|
<DataGrid
|
|
rows={localRows}
|
|
columns={enhancedColumns}
|
|
paginationModel={paginationModel}
|
|
onPaginationModelChange={handlePaginationModelChange}
|
|
paginationMode={totalRows ? "server" : "client"}
|
|
rowCount={totalRows}
|
|
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);
|