dev-jaanvi #1
|
@ -13,10 +13,6 @@ import { RootState } from "../../redux/store/store";
|
||||||
import DashboardOutlinedIcon from "@mui/icons-material/DashboardOutlined";
|
import DashboardOutlinedIcon from "@mui/icons-material/DashboardOutlined";
|
||||||
import ManageAccountsOutlinedIcon from "@mui/icons-material/ManageAccountsOutlined";
|
import ManageAccountsOutlinedIcon from "@mui/icons-material/ManageAccountsOutlined";
|
||||||
|
|
||||||
//Eknoor singh and Jaanvi
|
|
||||||
//date:- 12-Feb-2025
|
|
||||||
//Made a different variable for super admin to access all the details.
|
|
||||||
|
|
||||||
type PropType = {
|
type PropType = {
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
};
|
};
|
||||||
|
@ -26,6 +22,7 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
const userRole = useSelector(
|
const userRole = useSelector(
|
||||||
(state: RootState) => state.profileReducer.user?.userType
|
(state: RootState) => state.profileReducer.user?.userType
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseMenuItems = [
|
const baseMenuItems = [
|
||||||
{
|
{
|
||||||
text: "Dashboard",
|
text: "Dashboard",
|
||||||
|
@ -37,19 +34,25 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
icon: <AnalyticsRoundedIcon />,
|
icon: <AnalyticsRoundedIcon />,
|
||||||
url: "/panel/admin-list",
|
url: "/panel/admin-list",
|
||||||
},
|
},
|
||||||
userRole === "admin" && {
|
|
||||||
text: "Users",
|
|
||||||
icon: <AnalyticsRoundedIcon />,
|
|
||||||
url: "/panel/user-list",
|
|
||||||
},
|
|
||||||
userRole === "superadmin" && {
|
userRole === "superadmin" && {
|
||||||
text: "Roles",
|
text: "Roles",
|
||||||
icon: <AnalyticsRoundedIcon />,
|
icon: <AnalyticsRoundedIcon />,
|
||||||
url: "/panel/role-list",
|
url: "/panel/role-list",
|
||||||
},
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Users",
|
||||||
|
icon: <AnalyticsRoundedIcon />,
|
||||||
|
url: "/panel/user-list",
|
||||||
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Managers",
|
||||||
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
|
url: "/panel/manager-list", // Placeholder for now
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack sx={{ flexGrow: 1, p: 1, justifyContent: "space-between" }}>
|
<Stack sx={{ flexGrow: 1, p: 1, justifyContent: "space-between" }}>
|
||||||
<List dense>
|
<List dense>
|
||||||
|
@ -59,7 +62,6 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
disablePadding
|
disablePadding
|
||||||
sx={{ display: "block", py: 1 }}
|
sx={{ display: "block", py: 1 }}
|
||||||
>
|
>
|
||||||
{/* Wrap ListItemButton with Link to enable routing */}
|
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
component={Link}
|
component={Link}
|
||||||
to={item.url}
|
to={item.url}
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
import { Box, Button, Modal, Typography } from "@mui/material";
|
|
||||||
import { AppDispatch, RootState } from "../../../redux/store/store";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
open: boolean;
|
|
||||||
setViewModal: Function;
|
|
||||||
handleView: (id: string | undefined) => void;
|
|
||||||
id?: string | undefined;
|
|
||||||
};
|
|
||||||
;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
position: "absolute",
|
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
width: 330,
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
borderRadius: 1.5,
|
|
||||||
boxShadow: 24,
|
|
||||||
p: 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize", alignItems: "center" };
|
|
||||||
|
|
||||||
export default function ViewModal({
|
|
||||||
open,
|
|
||||||
setViewModal,
|
|
||||||
id, // Selected user's ID
|
|
||||||
}: Props) {
|
|
||||||
const { admins } = useSelector((state: RootState) => state.adminReducer);
|
|
||||||
const [selectedAdmin, setSelectedAdmin] = useState<any>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (id) {
|
|
||||||
const admin = admins.find((admin) => admin.id === id);
|
|
||||||
setSelectedAdmin(admin || null);
|
|
||||||
}
|
|
||||||
}, [id, admins]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={open}
|
|
||||||
aria-labelledby="modal-modal-title"
|
|
||||||
aria-describedby="modal-modal-description"
|
|
||||||
>
|
|
||||||
<Box sx={style}>
|
|
||||||
<Typography
|
|
||||||
id="modal-modal-title"
|
|
||||||
variant="h6"
|
|
||||||
component="h2"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
Details of {selectedAdmin?.name}
|
|
||||||
</Typography>
|
|
||||||
{selectedAdmin ? (
|
|
||||||
<>
|
|
||||||
<Typography align="center">Name: {selectedAdmin?.name}</Typography>
|
|
||||||
<Typography align="center">Email: {selectedAdmin?.email}</Typography>
|
|
||||||
<Typography align="center">Phone: {selectedAdmin?.phone}</Typography>
|
|
||||||
<Typography align="center">Address: {selectedAdmin?.registeredAddress}</Typography>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Typography align="center">No admin found with this ID</Typography>
|
|
||||||
)}
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", mt: 4, gap: 2 }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
type="button"
|
|
||||||
sx={btnStyle}
|
|
||||||
onClick={() => setViewModal(false)}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
126
src/pages/AddManagerModal/index.tsx
Normal file
126
src/pages/AddManagerModal/index.tsx
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
Modal,
|
||||||
|
IconButton,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
|
||||||
|
export default function AddManagerModal({ open, handleClose, handleAddManager }) {
|
||||||
|
// State for input fields
|
||||||
|
const [managerName, setManagerName] = useState("");
|
||||||
|
const [stationName, setStationName] = useState("");
|
||||||
|
const [stationLocation, setStationLocation] = useState("");
|
||||||
|
const [phoneNumber, setPhoneNumber] = useState("");
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (!managerName || !stationName || !stationLocation || !phoneNumber) {
|
||||||
|
alert("Please fill all fields");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newManager = {
|
||||||
|
managerName,
|
||||||
|
stationName,
|
||||||
|
stationLocation,
|
||||||
|
phoneNumber,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleAddManager(newManager); // Add manager to table
|
||||||
|
handleClose(); // Close modal after adding
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={open} onClose={handleClose} aria-labelledby="add-manager-modal">
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
Add Managers
|
||||||
|
</Typography>
|
||||||
|
<IconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Input Fields */}
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
|
{/* First Row - Two Inputs */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>Manager Name</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Name"
|
||||||
|
size="small"
|
||||||
|
value={managerName}
|
||||||
|
onChange={(e) => setManagerName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>Station Name</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Name"
|
||||||
|
size="small"
|
||||||
|
value={stationName}
|
||||||
|
onChange={(e) => setStationName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Two Inputs */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>Station Location</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Location"
|
||||||
|
size="small"
|
||||||
|
value={stationLocation}
|
||||||
|
onChange={(e) => setStationLocation(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>Phone Number</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
value={phoneNumber}
|
||||||
|
onChange={(e) => setPhoneNumber(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}>
|
||||||
|
<Button variant="contained" onClick={handleSubmit}>
|
||||||
|
Add Manager
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
175
src/pages/EditUserModal/index.tsx
Normal file
175
src/pages/EditUserModal/index.tsx
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
Modal,
|
||||||
|
IconButton,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
|
||||||
|
interface EditModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
handleCreate: (data: FormData) => void;
|
||||||
|
handleUpdate: (id: string, data: FormData) => void;
|
||||||
|
editRow: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
managerName: string;
|
||||||
|
stationName: string;
|
||||||
|
stationLocation: string;
|
||||||
|
phoneNumber: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditModal: React.FC<EditModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleCreate,
|
||||||
|
handleUpdate,
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
managerName: "",
|
||||||
|
stationName: "",
|
||||||
|
stationLocation: "",
|
||||||
|
phoneNumber: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("managerName", editRow.managerName);
|
||||||
|
setValue("stationName", editRow.stationName);
|
||||||
|
setValue("stationLocation", editRow.stationLocation);
|
||||||
|
setValue("phoneNumber", editRow.phoneNumber);
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
handleUpdate(editRow.id, data);
|
||||||
|
} else {
|
||||||
|
handleCreate(data);
|
||||||
|
}
|
||||||
|
handleClose();
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={open} onClose={handleClose} aria-labelledby="edit-manager-modal">
|
||||||
|
<Box
|
||||||
|
component="form"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{editRow ? "Edit Manager" : "Add Manager"}
|
||||||
|
</Typography>
|
||||||
|
<IconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Input Fields */}
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
|
{/* First Row - Two Inputs */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>Manager Name</Typography>
|
||||||
|
<Controller
|
||||||
|
name="managerName"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Manager Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...field} fullWidth placeholder="Enter Manager Name" size="small" error={!!errors.managerName} helperText={errors.managerName?.message} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>Station Name</Typography>
|
||||||
|
<Controller
|
||||||
|
name="stationName"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Station Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...field} fullWidth placeholder="Enter Station Name" size="small" error={!!errors.stationName} helperText={errors.stationName?.message} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Two Inputs */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>Station Location</Typography>
|
||||||
|
<Controller
|
||||||
|
name="stationLocation"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Station Location is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...field} fullWidth placeholder="Enter Station Location" size="small" error={!!errors.stationLocation} helperText={errors.stationLocation?.message} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>Phone Number</Typography>
|
||||||
|
<Controller
|
||||||
|
name="phoneNumber"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Phone number is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[0-9]+$/,
|
||||||
|
message: "Only numbers are allowed",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...field} fullWidth placeholder="Enter Phone Number" size="small" error={!!errors.phoneNumber} helperText={errors.phoneNumber?.message} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}>
|
||||||
|
<Button variant="contained" type="submit">
|
||||||
|
{editRow ? "Update" : "Create"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditModal;
|
131
src/pages/ManagerList/index.tsx
Normal file
131
src/pages/ManagerList/index.tsx
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { Box, Button, Typography, TextField, InputAdornment, IconButton } from "@mui/material";
|
||||||
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
|
import EqualizerIcon from "@mui/icons-material/Tune";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
|
import AddManagerModal from "../../pages/AddManagerModal";
|
||||||
|
import EditUserModal from "../EditUserModal";
|
||||||
|
|
||||||
|
export default function ManagerList() {
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const [addButtonModal, setAddButtonModal] = useState(false);
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const [rowData, setRowData] = useState(null);
|
||||||
|
const [viewModal, setViewModal] = React.useState<boolean>(false);
|
||||||
|
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
|
const [managers, setManagers] = useState([
|
||||||
|
{ id: 1, srno: 1, managerName: "John Doe", stationName: "Station A", stationLocation: "Location X", phoneNumber: "123-456-7890" },
|
||||||
|
{ id: 2, srno: 2, managerName: "Jane Smith", stationName: "Station B", stationLocation: "Location Y", phoneNumber: "987-654-3210" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleSearchChange = (event) => {
|
||||||
|
setSearch(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setRowData(null);
|
||||||
|
setAddButtonModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setAddButtonModal(false);
|
||||||
|
setModalOpen(false);
|
||||||
|
setRowData(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = (id, updatedData) => {
|
||||||
|
setManagers((prevManagers) =>
|
||||||
|
prevManagers.map((manager) => (manager.id === id ? { ...manager, ...updatedData } : manager))
|
||||||
|
);
|
||||||
|
handleCloseModal();
|
||||||
|
};
|
||||||
|
const handleAddManager = (newManager) => {
|
||||||
|
setManagers((prevManagers) => {
|
||||||
|
const newId = prevManagers.length > 0 ? prevManagers[prevManagers.length - 1].id + 1 : 1;
|
||||||
|
return [...prevManagers, { ...newManager, id: newId, srno: newId }];
|
||||||
|
});
|
||||||
|
setAddButtonModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const managerColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "managerName", label: "Manager Name" },
|
||||||
|
{ id: "stationName", label: "Station Name" },
|
||||||
|
{ id: "stationLocation", label: "Station Location" },
|
||||||
|
{ id: "phoneNumber", label: "Phone Number" },
|
||||||
|
{ id: "action", label: "Action", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredManagers = managers.filter((manager) =>
|
||||||
|
Object.values(manager).some((value) =>
|
||||||
|
typeof value === "string" && value.toLowerCase().includes(search.toLowerCase())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box sx={{ width: "100%", display: "flex", flexDirection: "column", gap: 2, mt: 2 }}>
|
||||||
|
<Typography component="h2" variant="h6" sx={{ fontWeight: 600 }}>
|
||||||
|
Managers
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
placeholder="Search"
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||||
|
<Button variant="contained" size="medium" onClick={handleClickOpen}>
|
||||||
|
Add Manager
|
||||||
|
</Button>
|
||||||
|
<IconButton>
|
||||||
|
<EqualizerIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<CustomTable
|
||||||
|
columns={managerColumns}
|
||||||
|
rows={filteredManagers}
|
||||||
|
setRowData={setRowData}
|
||||||
|
setModalOpen={setModalOpen}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
viewModal={viewModal}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
|
deleteModal={deleteModal}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditUserModal
|
||||||
|
open={modalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleUpdate={handleUpdate}
|
||||||
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AddManagerModal
|
||||||
|
open={addButtonModal}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
initialData={rowData}
|
||||||
|
handleAddManager={handleAddManager}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
103
src/redux/slices/managerSlice.ts
Normal file
103
src/redux/slices/managerSlice.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
// Interfaces
|
||||||
|
interface Manager {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
phone: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ManagerState {
|
||||||
|
managers: Manager[];
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy API function placeholder
|
||||||
|
const dummyApiCall = async (response: any, delay = 500) =>
|
||||||
|
new Promise((resolve) => setTimeout(() => resolve(response), delay));
|
||||||
|
|
||||||
|
// Fetch Manager List
|
||||||
|
export const fetchManagerList = createAsyncThunk<Manager[], void, { rejectValue: string }>(
|
||||||
|
"fetchManagerList",
|
||||||
|
async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response: any = await dummyApiCall({ data: [{ id: "1", name: "John Doe", email: "john@example.com", role: "Manager", phone: "1234567890", registeredAddress: "Somewhere" }] });
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error fetching manager list: " + error);
|
||||||
|
return rejectWithValue("An error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete Manager
|
||||||
|
export const deleteManager = createAsyncThunk<string, string, { rejectValue: string }>(
|
||||||
|
"deleteManager",
|
||||||
|
async (id, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
await dummyApiCall(null);initialState
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add Manager
|
||||||
|
export const addManager = createAsyncThunk<
|
||||||
|
Manager,
|
||||||
|
{ name: string; email: string; phone: string; registeredAddress: string },
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("addManager", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response: any = await dummyApiCall({ id: "2", ...data });
|
||||||
|
return response;
|
||||||
|
} catch (error: any) {
|
||||||
|
return rejectWithValue("An error occurred");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialState: ManagerState = {
|
||||||
|
managers: [],
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const managerSlice = createSlice({
|
||||||
|
name: "manager",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(fetchManagerList.pending, (state) => {
|
||||||
|
state.isLoading = true;
|
||||||
|
})
|
||||||
|
.addCase(fetchManagerList.fulfilled, (state, action: PayloadAction<Manager[]>) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.managers = action.payload;
|
||||||
|
})
|
||||||
|
.addCase(fetchManagerList.rejected, (state) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
})
|
||||||
|
.addCase(deleteManager.pending, (state) => {
|
||||||
|
state.isLoading = true;
|
||||||
|
})
|
||||||
|
.addCase(deleteManager.fulfilled, (state, action) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.managers = state.managers.filter((manager) => manager.id !== action.payload);
|
||||||
|
})
|
||||||
|
.addCase(deleteManager.rejected, (state) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
})
|
||||||
|
.addCase(addManager.pending, (state) => {
|
||||||
|
state.isLoading = true;
|
||||||
|
})
|
||||||
|
.addCase(addManager.fulfilled, (state, action) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.managers.push(action.payload);
|
||||||
|
})
|
||||||
|
.addCase(addManager.rejected, (state) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default managerSlice.reducer;
|
|
@ -15,6 +15,8 @@ const ProfilePage = lazy(() => import("./pages/ProfilePage"));
|
||||||
const NotFoundPage = lazy(() => import("./pages/NotFound"));
|
const NotFoundPage = lazy(() => import("./pages/NotFound"));
|
||||||
const UserList = lazy(() => import("./pages/UserList"));
|
const UserList = lazy(() => import("./pages/UserList"));
|
||||||
const PermissionsTable = lazy(() => import("./pages/PermissionTable"));
|
const PermissionsTable = lazy(() => import("./pages/PermissionTable"));
|
||||||
|
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
||||||
|
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
caps: string[];
|
caps: string[];
|
||||||
|
@ -86,6 +88,18 @@ export default function AppRouter() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="manager-list"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute
|
||||||
|
caps={[]}
|
||||||
|
component={<ManagerList />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="role-list"
|
path="role-list"
|
||||||
element={
|
element={
|
||||||
|
|
|
@ -55,5 +55,6 @@ export default function AppTheme(props: AppThemeProps) {
|
||||||
<ThemeProvider theme={theme} disableTransitionOnChange>
|
<ThemeProvider theme={theme} disableTransitionOnChange>
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue