dev-jaanvi #1
1
package-lock.json
generated
1
package-lock.json
generated
|
@ -15041,6 +15041,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden; /* Prevents scrolling */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.App {
|
.App {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { BrowserRouter as Router } from "react-router-dom";
|
import { BrowserRouter as Router } from "react-router-dom";
|
||||||
import AppRouter from "./router";
|
import AppRouter from "./router";
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
|
321
src/components/AddManagerModal/index.tsx
Normal file
321
src/components/AddManagerModal/index.tsx
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { Box, Button, Typography, Modal, IconButton, InputAdornment } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { addManager } from "../../redux/slices/managerSlice";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm();
|
||||||
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
|
|
||||||
|
const onSubmit = async (data: any) => {
|
||||||
|
const managerData = {
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
phone: data.phone,
|
||||||
|
registeredAddress: data.registeredAddress,
|
||||||
|
roleId: data.role,
|
||||||
|
password: data.password,
|
||||||
|
roleName: "Manager", // Add a role name (you can replace this with a dynamic value if needed)
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dispatch the addManager action
|
||||||
|
await dispatch(addManager(managerData)).unwrap();
|
||||||
|
handleClose(); // Close modal after successful addition
|
||||||
|
reset(); // Reset form fields
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding manager:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle password visibility
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 Manager
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
{/* First Row - Manager Name */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Manager Name */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Manager Name
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={
|
||||||
|
errors.name ? errors.name.message : ""
|
||||||
|
}
|
||||||
|
{...register("name", {
|
||||||
|
required: "Manager Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message:
|
||||||
|
"Manager Name must be at least 2 characters long",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Email, Password */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Email */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Email"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={
|
||||||
|
errors.email ? errors.email.message : ""
|
||||||
|
}
|
||||||
|
{...register("email", {
|
||||||
|
required: "Email is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||||
|
message: "Invalid email address",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Password
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Password is required",
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message:
|
||||||
|
"Password must be at least 6 characters long.",
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
|
message:
|
||||||
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Password"
|
||||||
|
type={
|
||||||
|
showPassword
|
||||||
|
? "text"
|
||||||
|
: "password"
|
||||||
|
}
|
||||||
|
id="password"
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.password
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
size="small"
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<CustomIconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={
|
||||||
|
togglePasswordVisibility
|
||||||
|
}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<VisibilityOff />
|
||||||
|
) : (
|
||||||
|
<Visibility />
|
||||||
|
)}
|
||||||
|
</CustomIconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={
|
||||||
|
errors.password?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Third Row - Phone Number, Registered Address */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Phone Number */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Phone Number
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={
|
||||||
|
errors.phone ? errors.phone.message : ""
|
||||||
|
}
|
||||||
|
{...register("phone", {
|
||||||
|
required: "Phone Number is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[0-9]{10}$/,
|
||||||
|
message:
|
||||||
|
"Phone number must be 10 digits",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Registered Address */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Registered Address
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Registered Address"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress
|
||||||
|
? errors.registeredAddress.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("registeredAddress", {
|
||||||
|
required: "Registered Address is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
mt: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Manager
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -369,7 +369,4 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddUserModal;
|
export default AddUserModal;
|
||||||
function setValue(arg0: string, name: any) {
|
|
||||||
throw new Error("Function not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Paper, { paperClasses } from "@mui/material/Paper";
|
||||||
import { adminList, deleteAdmin } from "../../redux/slices/adminSlice";
|
import { adminList, deleteAdmin } from "../../redux/slices/adminSlice";
|
||||||
import { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice";
|
import { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice";
|
||||||
|
|
||||||
|
import { deleteManager, managerList } from "../../redux/slices/managerSlice";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
@ -33,6 +34,7 @@ import TuneIcon from "@mui/icons-material/Tune";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
} from "../AddUserModel/styled.css.tsx";
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
import ManagerViewModal from "../Modals/ViewManagerModal";
|
||||||
// Styled components for customization
|
// Styled components for customization
|
||||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
[`&.${tableCellClasses.head}`]: {
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
@ -78,6 +80,7 @@ interface CustomTableProps {
|
||||||
handleStatusToggle: (id: string, currentStatus: number) => void;
|
handleStatusToggle: (id: string, currentStatus: number) => void;
|
||||||
tableType: string; // Adding tableType prop to change header text dynamically
|
tableType: string; // Adding tableType prop to change header text dynamically
|
||||||
handleClickOpen: () => void;
|
handleClickOpen: () => void;
|
||||||
|
//handleDeleteButton: (id: string | number | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomTable: React.FC<CustomTableProps> = ({
|
const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
|
@ -106,10 +109,21 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
|
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
|
||||||
|
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
setSelectedRow(row); // Ensure the row data is set
|
setSelectedRow(row);
|
||||||
setRowData(row);
|
setRowData(row);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
// const handleViewButton = (id: string | undefined) => {
|
||||||
|
// if (!id) {
|
||||||
|
// console.error("ID not found for viewing.");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// setViewModal(true);
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
|
@ -181,7 +195,6 @@ const filteredRows = rows.filter(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const indexOfLastRow = currentPage * usersPerPage;
|
const indexOfLastRow = currentPage * usersPerPage;
|
||||||
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
||||||
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
|
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
|
||||||
|
@ -457,6 +470,7 @@ const filteredRows = rows.filter(
|
||||||
variant="text"
|
variant="text"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
// setSelectedRow(row);
|
||||||
setViewModal(true);
|
setViewModal(true);
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -489,6 +503,16 @@ const filteredRows = rows.filter(
|
||||||
id={selectedRow?.id}
|
id={selectedRow?.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{viewModal && tableType === "manager" && (
|
||||||
|
<ManagerViewModal
|
||||||
|
handleView={() =>
|
||||||
|
handleViewButton(selectedRow?.id)
|
||||||
|
}
|
||||||
|
open={viewModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
id={selectedRow?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -533,7 +557,6 @@ const filteredRows = rows.filter(
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
py: 0,
|
py: 0,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
|
595
src/components/EditManagerModal/index.tsx
Normal file
595
src/components/EditManagerModal/index.tsx
Normal file
|
@ -0,0 +1,595 @@
|
||||||
|
// 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";
|
||||||
|
// import {
|
||||||
|
// CustomIconButton,
|
||||||
|
// CustomTextField,
|
||||||
|
// } from "../AddUserModel/styled.css.tsx";
|
||||||
|
|
||||||
|
// interface EditManagerModalProps {
|
||||||
|
// open: boolean;
|
||||||
|
// handleClose: () => void;
|
||||||
|
// handleUpdate: (
|
||||||
|
// id: number,
|
||||||
|
// name: string,
|
||||||
|
// email: string,
|
||||||
|
// stationName: string,
|
||||||
|
// registeredAddress: string,
|
||||||
|
// phone: string
|
||||||
|
// ) => void;
|
||||||
|
// editRow: any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface FormData {
|
||||||
|
// name: string;
|
||||||
|
// email: string;
|
||||||
|
// stationName: string;
|
||||||
|
// registeredAddress: string;
|
||||||
|
// phone: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
// open,
|
||||||
|
// handleClose,
|
||||||
|
// handleUpdate,
|
||||||
|
// editRow,
|
||||||
|
// }) => {
|
||||||
|
// const {
|
||||||
|
// control,
|
||||||
|
// handleSubmit,
|
||||||
|
// formState: { errors },
|
||||||
|
// setValue,
|
||||||
|
// reset,
|
||||||
|
// } = useForm<FormData>({
|
||||||
|
// defaultValues: {
|
||||||
|
// name: "",
|
||||||
|
// stationName: "",
|
||||||
|
// email: "",
|
||||||
|
// registeredAddress: "",
|
||||||
|
// phone: "",
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Populate form fields when `editRow` changes
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (editRow) {
|
||||||
|
// setValue("name", editRow.name || "");
|
||||||
|
// setValue("stationName", editRow.stationName || "");
|
||||||
|
// setValue("email", editRow.email || "");
|
||||||
|
// setValue("registeredAddress", editRow.registeredAddress || "");
|
||||||
|
// setValue("phone", editRow.phone || "");
|
||||||
|
// } else {
|
||||||
|
// reset();
|
||||||
|
// }
|
||||||
|
// }, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
// const onSubmit = (data: FormData) => {
|
||||||
|
// if (editRow) {
|
||||||
|
// handleUpdate({
|
||||||
|
// id: editRow.id,
|
||||||
|
// name: data.name,
|
||||||
|
// stationName: data.stationName,
|
||||||
|
// email: data.email,
|
||||||
|
// registeredAddress: data.registeredAddress,
|
||||||
|
// phone: data.phone,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// }
|
||||||
|
// 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="name"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Manager Name is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Manager Name"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.name}
|
||||||
|
// helperText={errors.name?.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}
|
||||||
|
// >
|
||||||
|
// Registered Address
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="registeredAddress"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Registered Address is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Registered Address"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.registeredAddress}
|
||||||
|
// helperText={errors.registeredAddress?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography
|
||||||
|
// variant="body2"
|
||||||
|
// fontWeight={500}
|
||||||
|
// mb={0.5}
|
||||||
|
// >
|
||||||
|
// Email
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="email"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Email is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Email"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.email}
|
||||||
|
// helperText={errors.email?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Third Row - Phone Number */}
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
// Phone Number
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="phone"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Phone number is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Phone Number"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.phone}
|
||||||
|
// helperText={errors.phone?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Submit Button */}
|
||||||
|
// <Box
|
||||||
|
// sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
|
||||||
|
// >
|
||||||
|
// <Button
|
||||||
|
// type="submit"
|
||||||
|
// sx={{
|
||||||
|
// backgroundColor: "#52ACDF",
|
||||||
|
// color: "white",
|
||||||
|
// borderRadius: "8px",
|
||||||
|
// width: "117px",
|
||||||
|
// "&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {editRow ? "Update" : "Create"}
|
||||||
|
// </Button>
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
// </Modal>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// export default EditManagerModal;
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Modal,
|
||||||
|
CircularProgress,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { updateManager } from "../../redux/slices/managerSlice"; // Import the updateManager action
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx"; // Custom styled components
|
||||||
|
|
||||||
|
interface EditManagerModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
editRow: any; // Manager data including id
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useDispatch(); // Use dispatch to send Redux actions
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
registeredAddress: "",
|
||||||
|
phone: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
// Set values if editRow is provided
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("name", editRow.name);
|
||||||
|
setValue("email", editRow.email);
|
||||||
|
setValue("registeredAddress", editRow.registeredAddress);
|
||||||
|
setValue("phone", editRow.phone);
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
setLoading(true); // Start loading state
|
||||||
|
|
||||||
|
// Dispatch the updateManager action from Redux
|
||||||
|
try {
|
||||||
|
await dispatch(
|
||||||
|
updateManager({
|
||||||
|
id: editRow.id, // Manager ID
|
||||||
|
managerData: {
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
registeredAddress: data.registeredAddress,
|
||||||
|
phone: data.phone,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).unwrap(); // Ensure that it throws an error if the update fails
|
||||||
|
|
||||||
|
handleClose(); // Close modal on success
|
||||||
|
reset(); // Reset form fields after submit
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
// You can handle the error here or show a toast
|
||||||
|
} finally {
|
||||||
|
setLoading(false); // Stop loading state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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}>
|
||||||
|
Edit Manager
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Input Fields */}
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
|
{/* Manager Name */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Manager Name
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Manager Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Registered Address */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Registered Address
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="registeredAddress"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Registered Address is required",
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Registered Address"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Email */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Email is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Email"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Phone Number */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Phone Number
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Phone number is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
disabled={loading} // Disable the button during loading state
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<CircularProgress size={24} color="inherit" />
|
||||||
|
) : (
|
||||||
|
"Update Manager"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditManagerModal;
|
|
@ -21,7 +21,7 @@ interface EditVehicleModalProps {
|
||||||
email: string,
|
email: string,
|
||||||
phone: string,
|
phone: string,
|
||||||
registeredAddress: string,
|
registeredAddress: string,
|
||||||
password: string
|
imageUrl: string
|
||||||
) => void;
|
) => void;
|
||||||
editRow: any;
|
editRow: any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { CircularProgress, Box, Typography } from '@mui/material';
|
||||||
|
|
||||||
// import styled from './Loading.style';
|
|
||||||
|
|
||||||
function LoadingComponent() {
|
function LoadingComponent() {
|
||||||
// const { isdarktheme } = useSelector((state) => ({
|
return (
|
||||||
// isdarktheme: state?.authReducer?.isDarkTheme,
|
<Box
|
||||||
// }));
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
return (<h1>Loading</h1>);
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress size={50} color="primary" />
|
||||||
|
<Typography variant="h6" sx={{ marginTop: 2 }}>
|
||||||
|
Loading...
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoadingComponent;
|
export default LoadingComponent;
|
|
@ -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,24 +34,30 @@ 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" && {
|
userRole === "admin" && {
|
||||||
text: "Vehicles",
|
text: "Users",
|
||||||
icon: <AnalyticsRoundedIcon />,
|
icon: <AnalyticsRoundedIcon />,
|
||||||
url: "/panel/vehicle-list",
|
url: "/panel/user-list",
|
||||||
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Managers",
|
||||||
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
|
url: "/panel/manager-list", // Placeholder for now
|
||||||
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Vehicles",
|
||||||
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
|
url: "/panel/vehicles", // 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>
|
||||||
|
@ -64,7 +67,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}
|
||||||
|
|
|
@ -53,7 +53,7 @@ export default function DeleteModal({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "flex-end", // Aligns the close icon to the right
|
justifyContent: "flex-end", // Aligns the close icon to the right
|
||||||
marginTop: -3.5
|
marginTop: -3.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
|
|
133
src/components/Modals/ViewManagerModal/index.tsx
Normal file
133
src/components/Modals/ViewManagerModal/index.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Box, Modal, Typography, Divider, Grid } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../redux/reducers";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
setViewModal: Function;
|
||||||
|
handleView: (id: string | undefined) => void;
|
||||||
|
id?: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
||||||
|
p: 4,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ManagerViewModal({ open, setViewModal, id }: Props) {
|
||||||
|
const { managers } = useSelector(
|
||||||
|
(state: RootState) => state.managerReducer
|
||||||
|
);
|
||||||
|
const [selectedManager, setSelectedManager] = useState<any>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
const manager = managers.find((manager) => manager.id === id);
|
||||||
|
setSelectedManager(manager || null);
|
||||||
|
}
|
||||||
|
}, [id, managers]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
aria-labelledby="modal-title"
|
||||||
|
aria-describedby="modal-description"
|
||||||
|
>
|
||||||
|
<Box sx={style}>
|
||||||
|
<Typography
|
||||||
|
id="modal-title"
|
||||||
|
variant="h5"
|
||||||
|
fontWeight="bold"
|
||||||
|
sx={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ flex: 1, textAlign: "center" }}>
|
||||||
|
{selectedManager?.name || "Manager"}'s Details
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
onClick={() => setViewModal(false)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ width: "100%" }} />
|
||||||
|
|
||||||
|
{selectedManager ? (
|
||||||
|
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.name}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Email:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.email}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Phone:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.phone}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Registered Address:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.registeredAddress}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Station Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.stationName}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
) : (
|
||||||
|
<Typography align="center">
|
||||||
|
No manager found with this ID
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -89,20 +89,26 @@ export default function ViewModal({ open, setViewModal, id }: Props) {
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<strong>Phone:</strong>
|
<strong>Phone:</strong>
|
||||||
<Typography variant="body2">{selectedAdmin.phone}</Typography>
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.phone}
|
||||||
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<strong>Email:</strong>
|
<strong>Email:</strong>
|
||||||
<Typography variant="body2">{selectedAdmin.email}</Typography>
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.email}
|
||||||
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<strong>Address:</strong>
|
<strong>Address:</strong>
|
||||||
|
|
||||||
<Typography variant="body2">{selectedAdmin.registeredAddress ?? "N/A"}</Typography>
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.registeredAddress ?? "N/A"}
|
||||||
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
Grid,
|
Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
|
InputAdornment
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useForm, Controller, SubmitHandler } from "react-hook-form";
|
import { useForm, Controller, SubmitHandler } from "react-hook-form";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
|
@ -29,7 +30,9 @@ interface ILoginForm {
|
||||||
|
|
||||||
export default function Login(props: { disableCustomTheme?: boolean }) {
|
export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const [isClicked, setIsClicked] = React.useState(false);
|
||||||
const [showPassword, setShowPassword] = React.useState(false);
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
@ -69,7 +72,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
md={7}
|
md={7}
|
||||||
sx={{
|
sx={{
|
||||||
background: `url('/mainPageLogo.png') center/cover no-repeat`,
|
background: `url('/mainPageLogo.png') center/cover no-repeat`,
|
||||||
height: { xs: "0%", sm: "0%", md: "100%" },
|
// height: { xs: "0%", sm: "50%", md: "100%" },
|
||||||
backgroundSize: "cover",
|
backgroundSize: "cover",
|
||||||
display: { xs: "none", md: "block" }, // Hide the image on xs and sm screens
|
display: { xs: "none", md: "block" }, // Hide the image on xs and sm screens
|
||||||
}}
|
}}
|
||||||
|
@ -87,9 +90,11 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
||||||
height: "100%",
|
|
||||||
|
height: "auto", // ✅ Allows the height to adjust dynamically
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -105,13 +110,8 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
Welcome Back!
|
Welcome Back!
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Card
|
<Card variant="outlined" sx={{ width: { xs: "100%", sm: "300px",lg:"408px" }, padding: "24px", borderRadius: "12px", backgroundColor: "#1E1E1E", border: "1px solid #4B5255" }}>
|
||||||
variant="outlined"
|
|
||||||
sx={{
|
|
||||||
maxWidth: "400px",
|
|
||||||
width: { xs: "80%", sm: "80%", md: "100%" },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
<Box
|
||||||
component="form"
|
component="form"
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
@ -122,38 +122,24 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography component="h1" variant="h4" sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "24px" }}>Login</Typography>
|
||||||
component="h1"
|
|
||||||
variant="h4"
|
|
||||||
sx={{
|
|
||||||
textAlign: "center",
|
|
||||||
color: "white",
|
|
||||||
fontSize: { xs: "1.5rem", sm: "2rem" },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Login
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
<Typography
|
||||||
component="h6"
|
component="h6"
|
||||||
variant="subtitle2"
|
variant="subtitle2"
|
||||||
sx={{
|
sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "16px" }}
|
||||||
textAlign: "center",
|
|
||||||
color: "white",
|
|
||||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Log in with your email and password
|
Log in with your email and password
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<FormControl sx={{ width: "100%" }}>
|
|
||||||
|
{/* -------------------------------- Email Field ----------------- */}
|
||||||
|
<FormControl sx={{ width: "100%" }}>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
htmlFor="email"
|
htmlFor="email"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: {
|
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||||
xs: "0.9rem",
|
|
||||||
sm: "1rem",
|
|
||||||
},
|
|
||||||
color: "white",
|
color: "white",
|
||||||
|
fontFamily: "Gilroy, sans-serif", // ✅ Apply Gilroy font
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Email
|
Email
|
||||||
|
@ -166,17 +152,14 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
required: "Email is required",
|
required: "Email is required",
|
||||||
pattern: {
|
pattern: {
|
||||||
value: /\S+@\S+\.\S+/,
|
value: /\S+@\S+\.\S+/,
|
||||||
message:
|
message: "Please enter a valid email address.",
|
||||||
"Please enter a valid email address.",
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...field}
|
{...field}
|
||||||
error={!!errors.email}
|
error={!!errors.email}
|
||||||
helperText={
|
helperText={errors.email?.message}
|
||||||
errors.email?.message
|
|
||||||
}
|
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
|
@ -185,33 +168,48 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
required
|
required
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color={
|
color={errors.email ? "error" : "primary"}
|
||||||
errors.email
|
InputProps={{
|
||||||
? "error"
|
sx: {
|
||||||
: "primary"
|
height: "50px",
|
||||||
}
|
alignItems: "center",
|
||||||
sx={{
|
backgroundColor: "#1E1F1F",
|
||||||
input: {
|
fontFamily: "Gilroy, sans-serif",
|
||||||
fontSize: {
|
|
||||||
xs: "0.9rem",
|
|
||||||
sm: "1rem",
|
|
||||||
},
|
},
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
backgroundColor: "#1E1F1F",
|
||||||
|
borderRadius: "4px",
|
||||||
|
"& fieldset": { borderColor: "#4b5255" },
|
||||||
|
"&:hover fieldset": { borderColor: "#4b5255" },
|
||||||
|
"&.Mui-focused fieldset": { borderColor: "#4b5255" },
|
||||||
|
},
|
||||||
|
"& input": {
|
||||||
|
color: "white",
|
||||||
|
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input::placeholder": {
|
||||||
|
color: "white",
|
||||||
|
opacity: 1,
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl sx={{ width: "100%" }}>
|
|
||||||
|
{/* -------------------------------- Password Field ----------------- */}
|
||||||
|
<FormControl sx={{ width: "100%" }}>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
htmlFor="password"
|
htmlFor="password"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: {
|
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||||
xs: "0.9rem",
|
|
||||||
sm: "1rem",
|
|
||||||
},
|
|
||||||
color: "white",
|
color: "white",
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Password
|
Password
|
||||||
|
@ -224,80 +222,78 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
required: "Password is required",
|
required: "Password is required",
|
||||||
minLength: {
|
minLength: {
|
||||||
value: 6,
|
value: 6,
|
||||||
message:
|
message: "Password must be at least 6 characters long.",
|
||||||
"Password must be at least 6 characters long.",
|
|
||||||
},
|
},
|
||||||
pattern: {
|
pattern: {
|
||||||
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
value:
|
||||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
message:
|
message:
|
||||||
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Box sx={{ position: "relative" }}>
|
|
||||||
<TextField
|
<TextField
|
||||||
{...field}
|
{...field}
|
||||||
error={!!errors.password}
|
error={!!errors.password}
|
||||||
helperText={
|
helperText={errors.password?.message}
|
||||||
errors.password?.message
|
|
||||||
}
|
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type={
|
type={showPassword ? "text" : "password"}
|
||||||
showPassword
|
|
||||||
? "text"
|
|
||||||
: "password"
|
|
||||||
}
|
|
||||||
id="password"
|
id="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
autoFocus
|
|
||||||
required
|
required
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color={
|
color={errors.password ? "error" : "primary"}
|
||||||
errors.password
|
InputProps={{
|
||||||
? "error"
|
sx: {
|
||||||
: "primary"
|
height: "50px",
|
||||||
}
|
fontFamily: "Gilroy, sans-serif", // Apply Gilroy font
|
||||||
sx={{
|
},
|
||||||
paddingRight: "40px",
|
|
||||||
height: "40px",
|
endAdornment: (
|
||||||
marginBottom: "8px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
onClick={() => setShowPassword((prev) => !prev)}
|
||||||
|
edge="end"
|
||||||
sx={{
|
sx={{
|
||||||
position: "absolute",
|
color: "white",
|
||||||
top: "50%",
|
padding: 0,
|
||||||
right: "10px",
|
margin: 0,
|
||||||
transform:
|
backgroundColor: "transparent",
|
||||||
"translateY(-50%)",
|
border: "none",
|
||||||
background: "none",
|
boxShadow: "none",
|
||||||
borderColor:
|
"&:hover": { backgroundColor: "transparent" },
|
||||||
"transparent",
|
"&:focus": { outline: "none", border: "none" },
|
||||||
"&:hover": {
|
}}
|
||||||
backgroundColor:
|
>
|
||||||
"transparent",
|
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||||
borderColor:
|
</IconButton>
|
||||||
"transparent",
|
),
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
backgroundColor: "#1E1F1F",
|
||||||
|
borderRadius: "4px",
|
||||||
|
"& fieldset": { borderColor: "#4b5255" },
|
||||||
|
"&:hover fieldset": { borderColor: "#4b5255" },
|
||||||
|
"&.Mui-focused fieldset": { borderColor: "#4b5255" },
|
||||||
|
},
|
||||||
|
"& input": {
|
||||||
|
color: "white",
|
||||||
|
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input::placeholder": {
|
||||||
|
color: "white",
|
||||||
|
opacity: 1,
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
/>
|
||||||
setShowPassword(
|
|
||||||
(prev) => !prev
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{showPassword ? (
|
|
||||||
<VisibilityOff />
|
|
||||||
) : (
|
|
||||||
<Visibility />
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -312,23 +308,46 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value="remember"
|
value="remember"
|
||||||
color="primary"
|
sx={{
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
|
border: "2px solid #4b5255",
|
||||||
|
borderRadius: "4px",
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
"&.Mui-checked": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
borderColor: "#4b5255",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
}
|
}
|
||||||
label="Remember me"
|
label="Remember me"
|
||||||
/>
|
/>
|
||||||
<Link
|
|
||||||
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClickOpen}
|
onClick={handleClickOpen}
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{
|
sx={{
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
color: "#01579b",
|
color: "#01579b",
|
||||||
|
textDecoration: "none", // ✅ Removes underline
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<ForgotPassword
|
<ForgotPassword
|
||||||
open={open}
|
open={open}
|
||||||
|
@ -340,6 +359,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
disabled={!isValid}
|
disabled={!isValid}
|
||||||
sx={{
|
sx={{
|
||||||
color: "white",
|
color: "white",
|
||||||
|
fontFamily: "Gilroy, sans-serif",
|
||||||
backgroundColor: "#52ACDF",
|
backgroundColor: "#52ACDF",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: "#52ACDF",
|
backgroundColor: "#52ACDF",
|
||||||
|
|
157
src/pages/ManagerList/index.tsx
Normal file
157
src/pages/ManagerList/index.tsx
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import React, { useEffect, 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 "../../components/AddManagerModal";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||||
|
import {
|
||||||
|
managerList,
|
||||||
|
addManager,
|
||||||
|
updateManager,
|
||||||
|
deleteManager,
|
||||||
|
} from "../../redux/slices/managerSlice";
|
||||||
|
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import EditManagerModal from "../../components/EditManagerModal";
|
||||||
|
|
||||||
|
export default function ManagerList() {
|
||||||
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
|
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
|
||||||
|
const [editRow, setEditRow] = useState<any>(null);
|
||||||
|
const { reset } = useForm();
|
||||||
|
|
||||||
|
const [deleteModal, setDeleteModal] = useState<boolean>(false);
|
||||||
|
const [viewModal, setViewModal] = useState<boolean>(false);
|
||||||
|
const [rowData, setRowData] = useState<any | null>(null);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
const managers = useSelector(
|
||||||
|
(state: RootState) => state.managerReducer.managers
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(managerList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setRowData(null); // Reset row data when opening for new admin
|
||||||
|
setAddModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setAddModalOpen(false);
|
||||||
|
setEditModalOpen(false);
|
||||||
|
setRowData(null);
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddManager = async (data: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await dispatch(addManager(data)); // Dispatch action to add manager
|
||||||
|
await dispatch(managerList()); // Fetch the updated list
|
||||||
|
handleCloseModal(); // Close the modal
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding manager", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = async (
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
email: string,
|
||||||
|
phone: string,
|
||||||
|
registeredAddress: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Creating the managerData object to match the expected payload structure
|
||||||
|
const managerData = {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
registeredAddress,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dispatching the updateManager action with the correct payload structure
|
||||||
|
await dispatch(updateManager({ id, managerData }));
|
||||||
|
await dispatch(managerList()); // Refresh the manager list after updating
|
||||||
|
handleCloseModal(); // Close the modal after updating
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Update failed", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Remove 'stationName' from columns
|
||||||
|
const categoryColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "name", label: "Name" },
|
||||||
|
{ id: "email", label: "Email" },
|
||||||
|
{ id: "phone", label: "Phone" },
|
||||||
|
{ id: "registeredAddress", label: "Station Location" },
|
||||||
|
|
||||||
|
{ id: "action", label: "Action", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Update rows to remove 'stationName'
|
||||||
|
const categoryRows = managers?.map(
|
||||||
|
(
|
||||||
|
manager: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) => ({
|
||||||
|
id: manager?.id,
|
||||||
|
srno: index + 1,
|
||||||
|
name: manager?.name,
|
||||||
|
email: manager?.email,
|
||||||
|
phone: manager.phone ?? "NA",
|
||||||
|
registeredAddress: manager?.registeredAddress ?? "NA",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CustomTable
|
||||||
|
columns={categoryColumns}
|
||||||
|
rows={categoryRows}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
|
deleteModal={deleteModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
viewModal={viewModal}
|
||||||
|
setRowData={setRowData}
|
||||||
|
setModalOpen={() => setEditModalOpen(true)}
|
||||||
|
tableType="manager"
|
||||||
|
handleClickOpen={handleClickOpen}
|
||||||
|
/>
|
||||||
|
<AddManagerModal
|
||||||
|
open={addModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleAddManager={handleAddManager}
|
||||||
|
/>
|
||||||
|
<EditManagerModal
|
||||||
|
open={editModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleUpdate={handleUpdate}
|
||||||
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import profileReducer from "./slices/profileSlice";
|
||||||
import userReducer from "./slices/userSlice.ts";
|
import userReducer from "./slices/userSlice.ts";
|
||||||
import roleReducer from "./slices/roleSlice.ts";
|
import roleReducer from "./slices/roleSlice.ts";
|
||||||
import vehicleReducer from "./slices/VehicleSlice.ts";
|
import vehicleReducer from "./slices/VehicleSlice.ts";
|
||||||
|
import managerReducer from "../redux/slices/managerSlice.ts";
|
||||||
|
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
|
@ -14,7 +15,9 @@ const rootReducer = combineReducers({
|
||||||
profileReducer,
|
profileReducer,
|
||||||
userReducer,
|
userReducer,
|
||||||
roleReducer,
|
roleReducer,
|
||||||
vehicleReducer
|
vehicleReducer,
|
||||||
|
managerReducer,
|
||||||
|
// Add other reducers here...
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof rootReducer>;
|
export type RootState = ReturnType<typeof rootReducer>;
|
||||||
|
|
180
src/redux/slices/managerSlice.ts
Normal file
180
src/redux/slices/managerSlice.ts
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
// Define the Manager interface based on the payload
|
||||||
|
|
||||||
|
interface Manager {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
roleId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ManagerState {
|
||||||
|
managers: Manager[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
const initialState: ManagerState = {
|
||||||
|
managers: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch Manager List (Async Thunk)
|
||||||
|
export const managerList = createAsyncThunk<
|
||||||
|
Manager[],
|
||||||
|
void,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("fetchManagers", async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.get("manager-list");
|
||||||
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Managers: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create Manager (Async Thunk)
|
||||||
|
export const addManager = createAsyncThunk<
|
||||||
|
Manager,
|
||||||
|
Manager,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("addManager", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.post("create-manager", data);
|
||||||
|
toast.success("Manager created successfully");
|
||||||
|
return response.data?.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error creating manager: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Manager (Async Thunk)
|
||||||
|
export const updateManager = createAsyncThunk<
|
||||||
|
Manager,
|
||||||
|
{ id: number; managerData: Manager },
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("updateManager", async ({ id, managerData }, { rejectWithValue }) => {
|
||||||
|
if (!id) {
|
||||||
|
return rejectWithValue("Manager ID is required.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await http.put(`/${id}/update-manager`, managerData);
|
||||||
|
toast.success("Manager updated successfully");
|
||||||
|
return response?.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error updating manager: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete Manager (Async Thunk)
|
||||||
|
export const deleteManager = createAsyncThunk<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("deleteManager", async (id, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
await http.delete(`/${id}/delete-manager`);
|
||||||
|
toast.success("Manager deleted successfully!");
|
||||||
|
return id;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error deleting manager: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create Slice
|
||||||
|
const managerSlice = createSlice({
|
||||||
|
name: "maanger",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
// Fetch Managers
|
||||||
|
.addCase(managerList.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
managerList.fulfilled,
|
||||||
|
(state, action: PayloadAction<Manager[]>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.managers = action.payload;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(managerList.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to fetch managers";
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add Manager
|
||||||
|
.addCase(addManager.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
addManager.fulfilled,
|
||||||
|
(state, action: PayloadAction<Manager>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.managers.push(action.payload);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(addManager.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to add manager";
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update Manager
|
||||||
|
.addCase(updateManager.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(updateManager.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
// const updatedManager = action.payload;
|
||||||
|
// const index = state.managers.findIndex(
|
||||||
|
// (manager) => manager.id === updatedManager.id
|
||||||
|
// );
|
||||||
|
// if (index !== -1) {
|
||||||
|
// state.managers[index] = updatedManager; // Update the manager in the state
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
.addCase(updateManager.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to update manager";
|
||||||
|
})
|
||||||
|
|
||||||
|
// Delete Manager
|
||||||
|
.addCase(deleteManager.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(deleteManager.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.managers = state.managers.filter(
|
||||||
|
(manager) => manager.id !== action.payload
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.addCase(deleteManager.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to delete manager";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default managerSlice.reducer;
|
|
@ -16,6 +16,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[];
|
||||||
|
@ -87,6 +89,18 @@ export default function AppRouter() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="manager-list"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute
|
||||||
|
caps={[]}
|
||||||
|
component={<ManagerList />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="role-list"
|
path="role-list"
|
||||||
element={
|
element={
|
||||||
|
|
Loading…
Reference in a new issue