689 lines
16 KiB
TypeScript
689 lines
16 KiB
TypeScript
import * as React from "react";
|
|
import { styled } from "@mui/material/styles";
|
|
import Table from "@mui/material/Table";
|
|
import TableBody from "@mui/material/TableBody";
|
|
import TableCell, { tableCellClasses } from "@mui/material/TableCell";
|
|
import TableContainer from "@mui/material/TableContainer";
|
|
import TableHead from "@mui/material/TableHead";
|
|
import TableRow from "@mui/material/TableRow";
|
|
import Paper, { paperClasses } from "@mui/material/Paper";
|
|
import { adminList, deleteAdmin } from "../../redux/slices/adminSlice.ts";
|
|
import { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice.ts";
|
|
|
|
import { deleteManager, managerList } from "../../redux/slices/managerSlice.ts";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import {
|
|
Box,
|
|
Button,
|
|
InputAdornment,
|
|
Menu,
|
|
IconButton,
|
|
Pagination,
|
|
TextField,
|
|
Typography,
|
|
} from "@mui/material";
|
|
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
|
import DeleteModal from "../Modals/DeleteModal/index.tsx";
|
|
import { AppDispatch, RootState } from "../../redux/store/store.ts";
|
|
import ViewModal from "../Modals/ViewModal/index.tsx";
|
|
import VehicleViewModal from "../Modals/VehicleViewModal/index.tsx";
|
|
|
|
import SearchIcon from "@mui/icons-material/Search";
|
|
import TuneIcon from "@mui/icons-material/Tune";
|
|
import { CustomIconButton } from "../AddUserModal/styled.css";
|
|
import ManagerViewModal from "../Modals/ViewManagerModal/index.tsx";
|
|
import UserViewModal from "../Modals/UserViewModal/index.tsx";
|
|
import { deleteUser, userList } from "../../redux/slices/userSlice.ts";
|
|
import { deleteStation, stationList } from "../../redux/slices/stationSlice.ts";
|
|
import StationViewModal from "../Modals/StationViewModal/index.tsx";
|
|
import {
|
|
deleteSlot,
|
|
fetchAvailableSlots,
|
|
} from "../../redux/slices/slotSlice.ts";
|
|
import { bookingList, deleteBooking } from "../../redux/slices/bookSlice.ts";
|
|
// Styled components for customization
|
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
|
[`&.${tableCellClasses.head}`]: {
|
|
backgroundColor: "#454545", // Changed to #272727 for the header
|
|
color: theme.palette.common.white,
|
|
borderBottom: "none", // Remove any border at the bottom of the header
|
|
},
|
|
[`&.${tableCellClasses.body}`]: {
|
|
fontSize: 14,
|
|
borderBottom: "1px solid #454545", // Adding border to body cells
|
|
},
|
|
}));
|
|
|
|
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
|
"&:nth-of-type(odd)": {
|
|
backgroundColor: theme.palette.action.hover,
|
|
},
|
|
"& td, th": {
|
|
borderColor: "#454545", // Applying border color to both td and th
|
|
borderWidth: "1px", // Set border width to ensure it appears
|
|
},
|
|
}));
|
|
|
|
export interface Column {
|
|
id: string;
|
|
label: string;
|
|
align?: "left" | "center" | "right";
|
|
}
|
|
|
|
interface Row {
|
|
[key: string]: any;
|
|
}
|
|
|
|
interface CustomTableProps {
|
|
columns: Column[];
|
|
rows: Row[];
|
|
setDeleteModal: Function;
|
|
setRowData: Function;
|
|
setModalOpen: Function;
|
|
viewModal: boolean;
|
|
setViewModal: Function;
|
|
deleteModal: boolean;
|
|
handleStatusToggle?: (id: string, currentStatus: number) => void;
|
|
tableType: string; // Adding tableType prop to change header text dynamically
|
|
handleClickOpen?: () => void;
|
|
}
|
|
|
|
const CustomTable: React.FC<CustomTableProps> = ({
|
|
columns,
|
|
rows,
|
|
setDeleteModal,
|
|
deleteModal,
|
|
viewModal,
|
|
setRowData,
|
|
setViewModal,
|
|
setModalOpen,
|
|
handleStatusToggle,
|
|
tableType,
|
|
handleClickOpen,
|
|
}) => {
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
|
const [selectedRow, setSelectedRow] = React.useState<Row | null>(null);
|
|
const [searchQuery, setSearchQuery] = React.useState("");
|
|
const [currentPage, setCurrentPage] = React.useState(1);
|
|
const usersPerPage = 10;
|
|
const { user } = useSelector((state: RootState) => state?.profileReducer);
|
|
const open = Boolean(anchorEl);
|
|
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
|
|
setAnchorEl(event.currentTarget);
|
|
setSelectedRow(row);
|
|
setRowData(row);
|
|
};
|
|
|
|
const handleClose = () => {
|
|
setAnchorEl(null);
|
|
};
|
|
|
|
const isImage = (value: any) => {
|
|
if (typeof value === "string") {
|
|
return value.startsWith("http") || value.startsWith("data:image"); // Check for URL or base64 image
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const handleDeleteButton = (id: string | undefined) => {
|
|
if (!id) {
|
|
console.error("ID not found", id);
|
|
return;
|
|
}
|
|
|
|
switch (tableType) {
|
|
case "admin":
|
|
dispatch(deleteAdmin(id || ""));
|
|
break;
|
|
case "vehicle":
|
|
dispatch(deleteVehicle(id || ""));
|
|
break;
|
|
case "manager":
|
|
dispatch(deleteManager(id || ""));
|
|
break;
|
|
case "user":
|
|
dispatch(deleteUser(id || ""));
|
|
break;
|
|
case "station":
|
|
dispatch(deleteStation(id || ""));
|
|
break;
|
|
case "slots":
|
|
dispatch(deleteSlot(id || ""));
|
|
break;
|
|
case "booking":
|
|
dispatch(deleteBooking(id || ""));
|
|
break;
|
|
default:
|
|
console.error("Unknown table type:", tableType);
|
|
return;
|
|
}
|
|
setDeleteModal(false); // Close the modal only after deletion
|
|
handleClose();
|
|
};
|
|
|
|
const handleViewButton = (id: string | undefined) => {
|
|
if (!id) console.error("ID not found", id);
|
|
switch (tableType) {
|
|
case "admin":
|
|
dispatch(adminList());
|
|
break;
|
|
case "vehicle":
|
|
dispatch(vehicleList());
|
|
break;
|
|
case "manager":
|
|
dispatch(managerList());
|
|
break;
|
|
case "user":
|
|
dispatch(userList());
|
|
break;
|
|
case "booking":
|
|
dispatch(bookingList());
|
|
break;
|
|
case "slots":
|
|
dispatch(fetchAvailableSlots());
|
|
break;
|
|
case "station":
|
|
dispatch(stationList());
|
|
break;
|
|
default:
|
|
console.error("Unknown table type:", tableType);
|
|
return;
|
|
}
|
|
|
|
setViewModal(false);
|
|
};
|
|
|
|
const handleToggleStatus = () => {
|
|
if (selectedRow) {
|
|
// Toggle the opposite of current status
|
|
const newStatus = selectedRow.statusValue === 1 ? 0 : 1;
|
|
handleStatusToggle?.(selectedRow.id, newStatus);
|
|
}
|
|
handleClose();
|
|
};
|
|
|
|
const filteredRows = rows.filter(
|
|
(row) =>
|
|
(row.name &&
|
|
row.name.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
|
row.registeredAddress.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
false
|
|
);
|
|
|
|
const indexOfLastRow = currentPage * usersPerPage;
|
|
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
|
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
|
|
|
|
const handlePageChange = (
|
|
_event: React.ChangeEvent<unknown>,
|
|
value: number
|
|
) => {
|
|
setCurrentPage(value);
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
width: "calc(100% - 48px)",
|
|
margin: "0 auto",
|
|
padding: "24px",
|
|
backgroundColor: "#1C1C1C",
|
|
borderRadius: "12px",
|
|
}}
|
|
>
|
|
<Typography
|
|
sx={{
|
|
color: "#FFFFFF",
|
|
fontWeight: 500,
|
|
fontSize: "18px",
|
|
fontFamily: "Gilroy",
|
|
}}
|
|
>
|
|
{/* Dynamic title based on the page type */}
|
|
{(() => {
|
|
switch (tableType) {
|
|
case "admin":
|
|
return "Admin";
|
|
case "role":
|
|
return "Roles";
|
|
case "user":
|
|
return "Users";
|
|
case "manager":
|
|
return "Managers";
|
|
case "vehicle":
|
|
return "Vehicles";
|
|
case "station":
|
|
return "Charging Station";
|
|
case "external-station":
|
|
return "Charging Station";
|
|
case "booking":
|
|
return "Booking";
|
|
case "slots":
|
|
return "Slot";
|
|
default:
|
|
return "List";
|
|
}
|
|
})()}
|
|
</Typography>
|
|
|
|
{/* Search & Buttons Section */}
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
|
|
marginTop: "16px",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
<TextField
|
|
variant="outlined"
|
|
placeholder="Search"
|
|
sx={{
|
|
width: "422px",
|
|
borderRadius: "12px",
|
|
input: { color: "#FFFFFF" },
|
|
backgroundColor: "#272727",
|
|
"& .MuiOutlinedInput-root": {
|
|
borderRadius: "12px",
|
|
width: "422px",
|
|
height: "44px",
|
|
borderWidth: "1px",
|
|
padding: "14px 12px 14px 12px",
|
|
gap: "16px",
|
|
"& fieldset": { borderColor: "#FFFFFF" },
|
|
"&:hover fieldset": { borderColor: "#FFFFFF" },
|
|
"&.Mui-focused fieldset": {
|
|
borderColor: "#52ACDF",
|
|
},
|
|
},
|
|
"& .MuiInputBase-input::placeholder": {
|
|
color: "#D9D8D8",
|
|
opacity: 1,
|
|
},
|
|
}}
|
|
slotProps={{
|
|
input: {
|
|
startAdornment: (
|
|
<InputAdornment position="start">
|
|
<SearchIcon sx={{ color: "#52ACDF" }} />
|
|
</InputAdornment>
|
|
),
|
|
},
|
|
}}
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
justifyContent: "flex-end",
|
|
width: "100%",
|
|
}}
|
|
>
|
|
{!(
|
|
user?.userType === "user" &&
|
|
(tableType === "slots" )) && (
|
|
<Button
|
|
sx={{
|
|
backgroundColor: "#52ACDF",
|
|
color: "white",
|
|
borderRadius: "8px",
|
|
width: "184px",
|
|
marginRight: "16px",
|
|
"&:hover": { backgroundColor: "#439BC1" },
|
|
}}
|
|
onClick={() => handleClickOpen()}
|
|
>
|
|
Add{" "}
|
|
{(() => {
|
|
switch (tableType) {
|
|
case "admin":
|
|
return "Admin";
|
|
case "role":
|
|
return "Role";
|
|
case "user":
|
|
return "User";
|
|
case "manager":
|
|
return "Manager";
|
|
case "vehicle":
|
|
return "Vehicle";
|
|
case "station":
|
|
return "Charging Station";
|
|
case "booking":
|
|
return "Booking";
|
|
case "slots":
|
|
return "Slot";
|
|
case "external-station":
|
|
return "Location";
|
|
default:
|
|
return "Item";
|
|
}
|
|
})()}
|
|
</Button>
|
|
)}
|
|
</Box>
|
|
|
|
<IconButton
|
|
sx={{
|
|
width: "44px",
|
|
height: "44px",
|
|
borderRadius: "8px",
|
|
backgroundColor: "#1C1C1C",
|
|
color: "#52ACDF",
|
|
"&:hover": { backgroundColor: "#333333" },
|
|
"*:where([data-mui-color-scheme='dark']) &": {
|
|
backgroundColor: "#1C1C1C",
|
|
},
|
|
}}
|
|
>
|
|
<TuneIcon />
|
|
</IconButton>
|
|
</Box>
|
|
{/* Table Section */}
|
|
<TableContainer
|
|
component={Paper}
|
|
sx={{
|
|
marginTop: "24px",
|
|
backgroundColor: "#1C1C1C",
|
|
borderRadius: "12px",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
<Table>
|
|
<TableHead
|
|
sx={{
|
|
backgroundColor: "#272727",
|
|
borderBottom: "none",
|
|
".css-1ex4ubw-MuiTableCell-root.MuiTableCell-head ":
|
|
{
|
|
backgroundColor: "#272727",
|
|
},
|
|
}}
|
|
>
|
|
{" "}
|
|
<TableRow>
|
|
{columns.map((column) => (
|
|
<StyledTableCell
|
|
key={column.id}
|
|
sx={{
|
|
color: "#FFFFFF",
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{column.label}
|
|
</StyledTableCell>
|
|
))}
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody
|
|
sx={{
|
|
".MuiTableCell-root": {
|
|
backgroundColor: "#1C1C1C",
|
|
},
|
|
}}
|
|
>
|
|
{currentRows.map((row, rowIndex) => (
|
|
<StyledTableRow key={rowIndex}>
|
|
{columns.map((column) => (
|
|
<StyledTableCell
|
|
key={column.id}
|
|
sx={{
|
|
color: "#FFFFFF",
|
|
backgroundColor: "#272727",
|
|
}}
|
|
>
|
|
{isImage(row[column.id]) ? (
|
|
<img
|
|
src={row[column.id]}
|
|
alt="Row "
|
|
style={{
|
|
width: "50px",
|
|
height: "50px",
|
|
objectFit: "cover",
|
|
}}
|
|
/>
|
|
) : column.id !== "action" ? (
|
|
row[column.id]
|
|
) : (
|
|
<CustomIconButton
|
|
onClick={(e) => {
|
|
handleClick(e, row);
|
|
setRowData(row); // Store the selected row
|
|
}}
|
|
sx={{
|
|
padding: 0,
|
|
minWidth: 0,
|
|
width: "auto",
|
|
height: "auto",
|
|
color: "#FFFFFF",
|
|
}}
|
|
>
|
|
<MoreHorizRoundedIcon
|
|
sx={{ fontSize: "24px" }}
|
|
/>
|
|
</CustomIconButton>
|
|
)}
|
|
</StyledTableCell>
|
|
))}
|
|
</StyledTableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
{/* Pagination */}
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
justifyContent: "flex-end",
|
|
alignItems: "center",
|
|
marginTop: "16px",
|
|
}}
|
|
>
|
|
<Typography
|
|
sx={{ color: "white", fontSize: "16px", fontWeight: 500 }}
|
|
>
|
|
Page Number :
|
|
</Typography>
|
|
<Pagination
|
|
count={Math.ceil(filteredRows.length / usersPerPage)}
|
|
page={currentPage}
|
|
onChange={handlePageChange}
|
|
siblingCount={0}
|
|
boundaryCount={0}
|
|
sx={{
|
|
"& .MuiPaginationItem-root": {
|
|
color: "white",
|
|
borderRadius: "0px",
|
|
},
|
|
"& .MuiPaginationItem-page.Mui-selected": {
|
|
backgroundColor: "transparent",
|
|
fontWeight: "bold",
|
|
color: "#FFFFFF",
|
|
},
|
|
}}
|
|
/>
|
|
</Box>
|
|
{/* Menu Actions */}
|
|
{open && (
|
|
<Menu
|
|
anchorEl={anchorEl}
|
|
id="menu"
|
|
open={open}
|
|
onClose={handleClose}
|
|
transformOrigin={{ horizontal: "right", vertical: "top" }}
|
|
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
|
|
sx={{
|
|
[`& .${paperClasses.root}`]: {
|
|
padding: 0,
|
|
},
|
|
"& .MuiList-root": {
|
|
background: "#272727",
|
|
},
|
|
}}
|
|
>
|
|
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
<Button
|
|
variant="text"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setViewModal(true);
|
|
}}
|
|
color="primary"
|
|
sx={{
|
|
justifyContent: "flex-start",
|
|
py: 0,
|
|
fontWeight: "bold",
|
|
color: "#52ACDF",
|
|
}}
|
|
>
|
|
View
|
|
</Button>
|
|
|
|
{viewModal && tableType === "admin" && (
|
|
<ViewModal
|
|
handleView={() =>
|
|
handleViewButton(selectedRow?.id)
|
|
}
|
|
open={viewModal}
|
|
setViewModal={setViewModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
{viewModal && tableType === "manager" && (
|
|
<ManagerViewModal
|
|
handleView={() =>
|
|
handleViewButton(selectedRow?.id)
|
|
}
|
|
open={viewModal}
|
|
setViewModal={setViewModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
|
|
{viewModal && tableType === "vehicle" && (
|
|
<VehicleViewModal
|
|
handleView={() =>
|
|
handleViewButton(selectedRow?.id)
|
|
}
|
|
open={viewModal}
|
|
setViewModal={setViewModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
{viewModal && tableType === "station" && (
|
|
<StationViewModal
|
|
handleView={() =>
|
|
handleViewButton(selectedRow?.id)
|
|
}
|
|
open={viewModal}
|
|
setViewModal={setViewModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
{viewModal && tableType === "user" && (
|
|
<UserViewModal
|
|
handleView={() =>
|
|
handleViewButton(selectedRow?.id)
|
|
}
|
|
open={viewModal}
|
|
setViewModal={setViewModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
|
|
{/* Edit Button */}
|
|
<Button
|
|
variant="text"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setModalOpen(true);
|
|
if (selectedRow) {
|
|
setModalOpen(true); // Only open if a row is selected
|
|
setRowData(selectedRow);
|
|
}
|
|
}}
|
|
color="primary"
|
|
sx={{
|
|
justifyContent: "flex-start",
|
|
py: 0,
|
|
textTransform: "capitalize",
|
|
}}
|
|
>
|
|
Edit
|
|
</Button>
|
|
|
|
{tableType === "role" && (
|
|
<Button
|
|
variant="text"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleToggleStatus();
|
|
}}
|
|
color="secondary"
|
|
sx={{
|
|
justifyContent: "flex-start",
|
|
py: 0,
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{selectedRow?.statusValue === 1
|
|
? "Deactivate"
|
|
: "Activate"}
|
|
</Button>
|
|
)}
|
|
|
|
{tableType === "station" && (
|
|
<Button
|
|
variant="text"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleToggleStatus();
|
|
}}
|
|
color="secondary"
|
|
sx={{
|
|
justifyContent: "flex-start",
|
|
py: 0,
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
{selectedRow?.statusValue === 1
|
|
? "Not Available"
|
|
: "Available"}
|
|
</Button>
|
|
)}
|
|
|
|
{/* Delete Button */}
|
|
<Button
|
|
variant="text"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setDeleteModal(true);
|
|
}}
|
|
color="error"
|
|
sx={{
|
|
justifyContent: "flex-start",
|
|
py: 0,
|
|
fontWeight: "bold",
|
|
}}
|
|
>
|
|
Delete
|
|
</Button>
|
|
</div>
|
|
</Menu>
|
|
)}
|
|
{/* Modals */}
|
|
{deleteModal && (
|
|
<DeleteModal
|
|
handleDelete={() => handleDeleteButton(selectedRow?.id)}
|
|
open={deleteModal}
|
|
setDeleteModal={setDeleteModal}
|
|
id={selectedRow?.id}
|
|
/>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default CustomTable;
|