some bug fixes and add viewModal for user
This commit is contained in:
parent
90f44b5183
commit
1e922f1ac5
|
@ -102,7 +102,12 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="add-edit-category-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -174,6 +179,11 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
message:
|
||||
"Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"Admin Name must only contain letters and spaces",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
|
@ -205,9 +215,9 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
rules={{
|
||||
required: "Email is required",
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||
message:
|
||||
"Please enter a valid email address.",
|
||||
"Please enter a valid email address (e.g., example@domain.com).",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Box, Button, Typography, Modal, IconButton, InputAdornment } from "@mui/material";
|
||||
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 { addManager, managerList } from "../../redux/slices/managerSlice";
|
||||
import {
|
||||
CustomIconButton,
|
||||
CustomTextField,
|
||||
} from "../AddUserModel/styled.css.tsx";
|
||||
import React from "react";
|
||||
import React, { useState, useRef } from "react";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
export default function AddManagerModal({ open, handleClose }: Props) {
|
||||
export default function AddManagerModal({
|
||||
open,
|
||||
handleClose,
|
||||
handleAddManager,
|
||||
}) {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
control,
|
||||
|
@ -25,22 +30,24 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
|||
formState: { errors },
|
||||
reset,
|
||||
} = useForm();
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
|
||||
// Handle form submission
|
||||
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)
|
||||
password: data.password,
|
||||
roleName: "Manager", // You can replace this with dynamic role if needed
|
||||
};
|
||||
|
||||
try {
|
||||
// Dispatch the addManager action
|
||||
await dispatch(addManager(managerData)).unwrap();
|
||||
await dispatch(addManager(managerData));
|
||||
dispatch(managerList());
|
||||
handleClose(); // Close modal after successful addition
|
||||
reset(); // Reset form fields
|
||||
} catch (error) {
|
||||
|
@ -48,15 +55,21 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
|||
}
|
||||
};
|
||||
|
||||
// Toggle password visibility
|
||||
const togglePasswordVisibility = () => {
|
||||
|
||||
const togglePasswordVisibility = (e: React.MouseEvent) => {
|
||||
e.preventDefault(); // Prevent focus loss
|
||||
setShowPassword((prev) => !prev);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="add-manager-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -93,49 +106,42 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
|||
|
||||
{/* 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>
|
||||
{/* Manager Name */}
|
||||
<Box
|
||||
sx={{ display: "flex", flexDirection: "column", mb: 2 }}
|
||||
>
|
||||
<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: 3,
|
||||
message: "Minimum 3 characters required",
|
||||
},
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message: "Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"Manager Name must only contain letters and spaces",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Second Row - Email, Password */}
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
{/* Email and Password */}
|
||||
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||
{/* Email */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body2" fontWeight={500}>
|
||||
Email
|
||||
</Typography>
|
||||
|
@ -158,91 +164,73 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
|||
</Box>
|
||||
|
||||
{/* Password */}
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<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.",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
required
|
||||
placeholder="Enter Password"
|
||||
type={
|
||||
showPassword ? "text" : "password"
|
||||
}
|
||||
fullWidth
|
||||
color={
|
||||
errors.password
|
||||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
size="small"
|
||||
// onMouseDown={togglePasswordVisibility}
|
||||
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>
|
||||
|
||||
{/* Third Row - Phone Number, Registered Address */}
|
||||
<Box sx={{ display: "flex", gap: 2 }}>
|
||||
{/* Phone Number */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
}}
|
||||
>
|
||||
{/* Phone and Registered Address */}
|
||||
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||
{/* Phone */}
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body2" fontWeight={500}>
|
||||
Phone Number
|
||||
</Typography>
|
||||
|
@ -266,13 +254,7 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
|||
</Box>
|
||||
|
||||
{/* Registered Address */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body2" fontWeight={500}>
|
||||
Registered Address
|
||||
</Typography>
|
||||
|
|
|
@ -93,7 +93,12 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="add-user-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -165,6 +170,11 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
|||
message:
|
||||
"Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"User Name must only contain letters and spaces",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
|
|
|
@ -28,7 +28,12 @@ export default function AddVehicleModal({
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="add-vehicle-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -96,9 +101,19 @@ export default function AddVehicleModal({
|
|||
{...register("name", {
|
||||
required: "Vehicle Name is required",
|
||||
minLength: {
|
||||
value: 2,
|
||||
value: 3,
|
||||
message:
|
||||
"Vehicle Name must be at least 2 characters long",
|
||||
"Minimum 3 characters required",
|
||||
},
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message:
|
||||
"Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"Vehicle Name must only contain letters and spaces",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
@ -232,7 +247,6 @@ export default function AddVehicleModal({
|
|||
}}
|
||||
>
|
||||
<Button
|
||||
|
||||
type="submit"
|
||||
sx={{
|
||||
backgroundColor: "#52ACDF",
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import * as React from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import Table from "@mui/material/Table";
|
||||
|
@ -18,12 +17,12 @@ import {
|
|||
Button,
|
||||
InputAdornment,
|
||||
Menu,
|
||||
IconButton,
|
||||
IconButton,
|
||||
Pagination,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
||||
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
||||
import DeleteModal from "../Modals/DeleteModal";
|
||||
import { AppDispatch } from "../../redux/store/store";
|
||||
import ViewModal from "../Modals/ViewModal";
|
||||
|
@ -31,10 +30,10 @@ import VehicleViewModal from "../Modals/VehicleViewModal";
|
|||
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import TuneIcon from "@mui/icons-material/Tune";
|
||||
import {
|
||||
CustomIconButton,
|
||||
} from "../AddUserModel/styled.css.tsx";
|
||||
import { CustomIconButton } from "../AddUserModel/styled.css.tsx";
|
||||
import ManagerViewModal from "../Modals/ViewManagerModal";
|
||||
import UserViewModal from "../Modals/UserViewModal/index.tsx";
|
||||
import { deleteUser, userList } from "../../redux/slices/userSlice.ts";
|
||||
// Styled components for customization
|
||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||
[`&.${tableCellClasses.head}`]: {
|
||||
|
@ -95,7 +94,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
handleStatusToggle,
|
||||
tableType,
|
||||
handleClickOpen,
|
||||
|
||||
}) => {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
@ -104,17 +102,12 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
const [currentPage, setCurrentPage] = React.useState(1);
|
||||
const usersPerPage = 10;
|
||||
|
||||
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
|
||||
|
||||
setAnchorEl(event.currentTarget);
|
||||
setSelectedRow(row);
|
||||
setRowData(row);
|
||||
|
||||
|
||||
};
|
||||
// const handleViewButton = (id: string | undefined) => {
|
||||
// if (!id) {
|
||||
|
@ -123,7 +116,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
// }
|
||||
// setViewModal(true);
|
||||
// };
|
||||
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
|
@ -136,8 +128,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
return false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleDeleteButton = (id: string | undefined) => {
|
||||
if (!id) console.error("ID not found", id);
|
||||
|
||||
|
@ -151,6 +141,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
case "manager":
|
||||
dispatch(deleteManager(id || ""));
|
||||
break;
|
||||
case "user":
|
||||
dispatch(deleteUser(id || ""));
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown table type:", tableType);
|
||||
return;
|
||||
|
@ -158,24 +151,27 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
setDeleteModal(false); // Close the modal only after deletion
|
||||
handleClose();
|
||||
};
|
||||
|
||||
|
||||
const handleViewButton = (id: string | undefined) => {
|
||||
if (!id) console.error("ID not found", id);
|
||||
switch (tableType) {
|
||||
case "admin":
|
||||
dispatch(adminList());
|
||||
break;
|
||||
case "vehicle":
|
||||
dispatch(vehicleList());
|
||||
break;
|
||||
case "manager":
|
||||
dispatch(managerList());
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown table type:", tableType);
|
||||
return;
|
||||
}
|
||||
dispatch(adminList());
|
||||
// switch (tableType) {
|
||||
// case "admin":
|
||||
// dispatch(adminList());
|
||||
// break;
|
||||
// case "vehicle":
|
||||
// dispatch(vehicleList());
|
||||
// break;
|
||||
// case "manager":
|
||||
// dispatch(managerList());
|
||||
// break;
|
||||
// case "user":
|
||||
// dispatch(userList());
|
||||
// break;
|
||||
// default:
|
||||
// console.error("Unknown table type:", tableType);
|
||||
// return;
|
||||
// }
|
||||
|
||||
setViewModal(false);
|
||||
};
|
||||
|
||||
|
@ -188,12 +184,12 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
handleClose();
|
||||
};
|
||||
|
||||
const filteredRows = rows.filter(
|
||||
(row) =>
|
||||
(row.name &&
|
||||
row.name.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
||||
false
|
||||
);
|
||||
const filteredRows = rows.filter(
|
||||
(row) =>
|
||||
(row.name &&
|
||||
row.name.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
||||
false
|
||||
);
|
||||
|
||||
const indexOfLastRow = currentPage * usersPerPage;
|
||||
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
||||
|
@ -513,6 +509,16 @@ const filteredRows = rows.filter(
|
|||
id={selectedRow?.id}
|
||||
/>
|
||||
)}
|
||||
{viewModal && tableType === "user" && (
|
||||
<UserViewModal
|
||||
handleView={() =>
|
||||
handleViewButton(selectedRow?.id)
|
||||
}
|
||||
open={viewModal}
|
||||
setViewModal={setViewModal}
|
||||
id={selectedRow?.id}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="text"
|
||||
|
@ -579,4 +585,3 @@ const filteredRows = rows.filter(
|
|||
};
|
||||
|
||||
export default CustomTable;
|
||||
|
||||
|
|
|
@ -1,317 +1,3 @@
|
|||
// 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,
|
||||
|
@ -323,7 +9,7 @@ import {
|
|||
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 { managerList, updateManager } from "../../redux/slices/managerSlice"; // Import the updateManager action
|
||||
import {
|
||||
CustomIconButton,
|
||||
CustomTextField,
|
||||
|
@ -379,9 +65,6 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
if (editRow) {
|
||||
setLoading(true); // Start loading state
|
||||
|
||||
// Dispatch the updateManager action from Redux
|
||||
try {
|
||||
await dispatch(
|
||||
updateManager({
|
||||
|
@ -394,7 +77,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
},
|
||||
})
|
||||
).unwrap(); // Ensure that it throws an error if the update fails
|
||||
|
||||
dispatch(managerList());
|
||||
handleClose(); // Close modal on success
|
||||
reset(); // Reset form fields after submit
|
||||
} catch (error) {
|
||||
|
@ -409,7 +92,12 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="edit-manager-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -446,24 +134,35 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
{/* 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%",
|
||||
}}
|
||||
>
|
||||
{/* Input Fields (2 inputs per row) */}
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 2 }}>
|
||||
{/* Manager Name and Email in one row */}
|
||||
<Box sx={{ flex: "1 1 48%" }}>
|
||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||
Manager Name
|
||||
</Typography>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: "Manager Name is required" }}
|
||||
render={({ field }) => (
|
||||
rules={{
|
||||
required: "Manager Name is required",
|
||||
minLength: {
|
||||
value: 3,
|
||||
message:
|
||||
"Minimum 3 characters required",
|
||||
},
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message:
|
||||
"Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"Manager Name must only contain letters and spaces",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
|
@ -471,19 +170,35 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
size="small"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Registered Address */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: "1 1 48%" }}>
|
||||
<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>
|
||||
|
||||
{/* Registered Address and Phone Number in one row */}
|
||||
<Box sx={{ flex: "1 1 48%" }}>
|
||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||
Registered Address
|
||||
</Typography>
|
||||
|
@ -508,42 +223,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
|||
/>
|
||||
</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%",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: "1 1 48%" }}>
|
||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||
Phone Number
|
||||
</Typography>
|
||||
|
|
|
@ -87,7 +87,12 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClose={(e, reason) => {
|
||||
if (reason === "backdropClick") {
|
||||
return;
|
||||
}
|
||||
handleClose(); // Close modal when clicking cross or cancel
|
||||
}}
|
||||
aria-labelledby="edit-vehicle-modal"
|
||||
>
|
||||
<Box
|
||||
|
@ -145,7 +150,24 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
|||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: "Vehicle Name is required" }}
|
||||
rules={{
|
||||
required: "Vehicle Name is required",
|
||||
minLength: {
|
||||
value: 3,
|
||||
message:
|
||||
"Minimum 3 characters required",
|
||||
},
|
||||
maxLength: {
|
||||
value: 30,
|
||||
message:
|
||||
"Maximum 30 characters allowed",
|
||||
},
|
||||
pattern: {
|
||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
||||
message:
|
||||
"Vehicle Name must only contain letters and spaces",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
|
@ -154,6 +176,7 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
|||
size="small"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -52,7 +52,7 @@ export default function MenuContent({ hidden }: PropType) {
|
|||
userRole === "admin" && {
|
||||
text: "Vehicles",
|
||||
icon: <ManageAccountsOutlinedIcon />,
|
||||
url: "/panel/vehicles", // Placeholder for now
|
||||
url: "/panel/vehicle-list", // Placeholder for now
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -1,81 +1,112 @@
|
|||
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
setLogoutModal: Function;
|
||||
handlelogout: any;
|
||||
open: boolean;
|
||||
setLogoutModal: Function;
|
||||
handlelogout: any;
|
||||
};
|
||||
|
||||
|
||||
const style = {
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 330,
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: 1.5,
|
||||
boxShadow: 24,
|
||||
p: 3,
|
||||
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" };
|
||||
|
||||
|
||||
export default function LogoutModal({
|
||||
open,
|
||||
setLogoutModal,
|
||||
handlelogout,
|
||||
open,
|
||||
setLogoutModal,
|
||||
handlelogout,
|
||||
}: Props) {
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-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" }}>Logout</Box>
|
||||
<Box onClick={() => setLogoutModal(false)} sx={{ cursor: "pointer", display: "flex", alignItems: "center" }}>
|
||||
<CloseIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
</Typography>
|
||||
<Typography
|
||||
id="modal-modal-description"
|
||||
sx={{ mt: 2 }}
|
||||
align="center"
|
||||
>
|
||||
Are you sure you want to Logout?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 4,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
type="button"
|
||||
sx={btnStyle}
|
||||
onClick={() => setLogoutModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="button"
|
||||
color="primary"
|
||||
sx={btnStyle}
|
||||
onClick={() => handlelogout()}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
// Function to prevent closing the modal when clicking outside
|
||||
const handleClose = (event: React.SyntheticEvent) => {
|
||||
// Close only when clicking the close button, not the backdrop
|
||||
setLogoutModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={(e, reason) =>
|
||||
reason !== "backdropClick" && setLogoutModal(false)
|
||||
} // Prevent closing on backdrop click
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
BackdropProps={{
|
||||
onClick: (e) => e.stopPropagation(), // Stop propagation on backdrop click to prevent closing the modal
|
||||
}}
|
||||
>
|
||||
<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" }}>Logout</Box>
|
||||
<Box
|
||||
onClick={() => setLogoutModal(false)}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
</Typography>
|
||||
<Typography
|
||||
id="modal-modal-description"
|
||||
sx={{ mt: 2 }}
|
||||
align="center"
|
||||
>
|
||||
Are you sure you want to Logout?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 4,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
type="button"
|
||||
sx={btnStyle}
|
||||
onClick={() => setLogoutModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="button"
|
||||
color="primary"
|
||||
sx={btnStyle}
|
||||
onClick={() => handlelogout()}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
117
src/components/Modals/UserViewModal/index.tsx
Normal file
117
src/components/Modals/UserViewModal/index.tsx
Normal file
|
@ -0,0 +1,117 @@
|
|||
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?: string | 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 UserViewModal({ open, setViewModal, id }: Props) {
|
||||
const { users } = useSelector(
|
||||
(state: RootState) => state.userReducer // Assuming users are stored in userReducer
|
||||
);
|
||||
const [selectedUser, setSelectedUser] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
const user = users.find((user) => user.id === id);
|
||||
setSelectedUser(user || null);
|
||||
}
|
||||
}, [id, users]);
|
||||
|
||||
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" }}>
|
||||
{selectedUser?.name || "User"}'s Details
|
||||
</Box>
|
||||
<Box
|
||||
onClick={() => setViewModal(false)}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
</Typography>
|
||||
|
||||
<Divider sx={{ width: "100%" }} />
|
||||
|
||||
{selectedUser ? (
|
||||
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body1">
|
||||
<strong>Name:</strong>{" "}
|
||||
<Typography variant="body2">
|
||||
{selectedUser.name}
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body1">
|
||||
<strong>Email:</strong>{" "}
|
||||
<Typography variant="body2">
|
||||
{selectedUser.email}
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body1">
|
||||
<strong>Phone:</strong>{" "}
|
||||
<Typography variant="body2">
|
||||
{selectedUser.phone ?? "N/A"}
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<Typography align="center">
|
||||
No user found with this ID
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -9,9 +9,9 @@ import {
|
|||
TextField,
|
||||
Typography,
|
||||
Grid,
|
||||
IconButton,
|
||||
|
||||
Link,
|
||||
InputAdornment
|
||||
InputAdornment,
|
||||
} from "@mui/material";
|
||||
import { useForm, Controller, SubmitHandler } from "react-hook-form";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
@ -22,7 +22,9 @@ import { toast } from "sonner";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||
import { Card, SignInContainer } from "./styled.css.tsx";
|
||||
|
||||
import {
|
||||
CustomIconButton,
|
||||
} from "../../../components/AddUserModel/styled.css.tsx";
|
||||
interface ILoginForm {
|
||||
email: string;
|
||||
password: string;
|
||||
|
@ -30,9 +32,8 @@ interface ILoginForm {
|
|||
|
||||
export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [isClicked, setIsClicked] = React.useState(false);
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
|
@ -48,17 +49,22 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
|
||||
const togglePasswordVisibility = (e: React.MouseEvent) => {
|
||||
e.preventDefault(); // Prevent focus loss
|
||||
setShowPassword((prev) => !prev);
|
||||
};
|
||||
|
||||
const onSubmit: SubmitHandler<ILoginForm> = async (data: ILoginForm) => {
|
||||
try {
|
||||
const response = await dispatch(loginUser(data)).unwrap();
|
||||
if (response?.data?.token) {
|
||||
router("/panel/dashboard");
|
||||
try {
|
||||
const response = await dispatch(loginUser(data)).unwrap();
|
||||
if (response?.data?.token) {
|
||||
router("/panel/dashboard");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log("Login failed:", error);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log("Login failed:", error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<AppTheme {...props}>
|
||||
|
@ -80,21 +86,19 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
|
||||
{/* Form Section */}
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
md={5}
|
||||
sx={{
|
||||
backgroundColor: "black",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
||||
|
||||
height: "auto", // ✅ Allows the height to adjust dynamically
|
||||
}}
|
||||
>
|
||||
|
||||
item
|
||||
xs={12}
|
||||
md={5}
|
||||
sx={{
|
||||
backgroundColor: "black",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
||||
height: "auto",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
|
@ -110,8 +114,16 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
Welcome Back!
|
||||
</Typography>
|
||||
|
||||
<Card variant="outlined" sx={{ width: { xs: "100%", sm: "300px",lg:"408px" }, padding: "24px", borderRadius: "12px", backgroundColor: "#1E1E1E", border: "1px solid #4B5255" }}>
|
||||
|
||||
<Card
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "300px", lg: "408px" },
|
||||
padding: "24px",
|
||||
borderRadius: "12px",
|
||||
backgroundColor: "#1E1E1E",
|
||||
border: "1px solid #4B5255",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
|
@ -122,178 +134,251 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Typography component="h1" variant="h4" sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "24px" }}>Login</Typography>
|
||||
<Typography
|
||||
component="h6"
|
||||
variant="subtitle2"
|
||||
sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "16px" }}
|
||||
>
|
||||
Log in with your email and password
|
||||
</Typography>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h4"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
color: "white",
|
||||
fontFamily: "Gilroy",
|
||||
fontSize: "24px",
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Typography>
|
||||
<Typography
|
||||
component="h6"
|
||||
variant="subtitle2"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
color: "white",
|
||||
fontFamily: "Gilroy",
|
||||
fontSize: "16px",
|
||||
}}
|
||||
>
|
||||
Log in with your email and password
|
||||
</Typography>
|
||||
|
||||
{/* -------------------------------- Email Field ----------------- */}
|
||||
<FormControl sx={{ width: "100%" }}>
|
||||
<FormLabel
|
||||
htmlFor="email"
|
||||
sx={{
|
||||
fontSize: {
|
||||
xs: "0.9rem",
|
||||
sm: "1rem",
|
||||
},
|
||||
color: "white",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
}}
|
||||
>
|
||||
Email
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
rules={{
|
||||
required: "Email is required",
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message:
|
||||
"Please enter a valid email address.",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
error={!!errors.email}
|
||||
helperText={
|
||||
errors.email?.message
|
||||
}
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
required
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
color={
|
||||
errors.email
|
||||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "50px",
|
||||
alignItems: "center",
|
||||
backgroundColor:
|
||||
"#1E1F1F",
|
||||
fontFamily:
|
||||
"Gilroy, sans-serif",
|
||||
},
|
||||
}}
|
||||
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>
|
||||
|
||||
{/* -------------------------------- Email Field ----------------- */}
|
||||
<FormControl sx={{ width: "100%" }}>
|
||||
<FormLabel
|
||||
htmlFor="email"
|
||||
sx={{
|
||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||
color: "white",
|
||||
fontFamily: "Gilroy, sans-serif", // ✅ Apply Gilroy font
|
||||
}}
|
||||
>
|
||||
Email
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
rules={{
|
||||
required: "Email is required",
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message: "Please enter a valid email address.",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email?.message}
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
required
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
color={errors.email ? "error" : "primary"}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "50px",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#1E1F1F",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
},
|
||||
}}
|
||||
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>
|
||||
|
||||
|
||||
{/* -------------------------------- Password Field ----------------- */}
|
||||
<FormControl sx={{ width: "100%" }}>
|
||||
<FormLabel
|
||||
htmlFor="password"
|
||||
sx={{
|
||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
||||
color: "white",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
}}
|
||||
>
|
||||
Password
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
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 }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
error={!!errors.password}
|
||||
helperText={errors.password?.message}
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
color={errors.password ? "error" : "primary"}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "50px",
|
||||
fontFamily: "Gilroy, sans-serif", // Apply Gilroy font
|
||||
},
|
||||
|
||||
endAdornment: (
|
||||
<IconButton
|
||||
onClick={() => setShowPassword((prev) => !prev)}
|
||||
edge="end"
|
||||
sx={{
|
||||
color: "white",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
boxShadow: "none",
|
||||
"&:hover": { backgroundColor: "transparent" },
|
||||
"&:focus": { outline: "none", border: "none" },
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
),
|
||||
}}
|
||||
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>
|
||||
|
||||
{/* -------------------------------- Password Field ----------------- */}
|
||||
<FormControl sx={{ width: "100%" }}>
|
||||
<FormLabel
|
||||
htmlFor="password"
|
||||
sx={{
|
||||
fontSize: {
|
||||
xs: "0.9rem",
|
||||
sm: "1rem",
|
||||
},
|
||||
color: "white",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
}}
|
||||
>
|
||||
Password
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
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 }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
error={!!errors.password}
|
||||
helperText={
|
||||
errors.password?.message
|
||||
}
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
type={
|
||||
showPassword
|
||||
? "text"
|
||||
: "password"
|
||||
}
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
color={
|
||||
errors.password
|
||||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
onMouseDown={
|
||||
togglePasswordVisibility
|
||||
}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CustomIconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={
|
||||
togglePasswordVisibility
|
||||
}
|
||||
edge="end"
|
||||
>
|
||||
{showPassword ? (
|
||||
<VisibilityOff />
|
||||
) : (
|
||||
<Visibility />
|
||||
)}
|
||||
</CustomIconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
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>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -304,50 +389,52 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value="remember"
|
||||
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"
|
||||
/>
|
||||
|
||||
<Link
|
||||
component="button"
|
||||
type="button"
|
||||
onClick={handleClickOpen}
|
||||
variant="body2"
|
||||
sx={{
|
||||
alignSelf: "center",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
color: "#01579b",
|
||||
textDecoration: "none", // ✅ Removes underline
|
||||
|
||||
}}
|
||||
>
|
||||
Forgot password?
|
||||
</Link>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
value="remember"
|
||||
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"
|
||||
/>
|
||||
|
||||
<Link
|
||||
component="button"
|
||||
type="button"
|
||||
onClick={handleClickOpen}
|
||||
variant="body2"
|
||||
sx={{
|
||||
alignSelf: "center",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
color: "#01579b",
|
||||
textDecoration: "none", // ✅ Removes underline
|
||||
}}
|
||||
>
|
||||
Forgot password?
|
||||
</Link>
|
||||
</Box>
|
||||
<ForgotPassword
|
||||
open={open}
|
||||
|
@ -359,7 +446,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
disabled={!isValid}
|
||||
sx={{
|
||||
color: "white",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
fontFamily: "Gilroy, sans-serif",
|
||||
backgroundColor: "#52ACDF",
|
||||
"&:hover": {
|
||||
backgroundColor: "#52ACDF",
|
||||
|
|
|
@ -9,10 +9,9 @@ import {
|
|||
datePickersCustomizations,
|
||||
treeViewCustomizations,
|
||||
} from "./theme/customizations";
|
||||
import DashboardLayout from "../../layouts/DashboardLayout";
|
||||
import AppTheme from "../../shared-theme/AppTheme";
|
||||
import MainGrid from "../../components/MainGrid";
|
||||
import AdminList from "../AdminList";
|
||||
|
||||
|
||||
const xThemeComponents = {
|
||||
...chartsCustomizations,
|
||||
|
|
|
@ -1,27 +1,18 @@
|
|||
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 { Box, Button, TextField, Typography } from "@mui/material";
|
||||
import CustomTable, { Column } from "../../components/CustomTable";
|
||||
import AddManagerModal from "../../components/AddManagerModal";
|
||||
import EditManagerModal from "../../components/EditManagerModal";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||
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";
|
||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||
|
||||
export default function ManagerList() {
|
||||
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||
|
@ -43,7 +34,7 @@ export default function ManagerList() {
|
|||
}, [dispatch]);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setRowData(null); // Reset row data when opening for new admin
|
||||
setRowData(null); // Reset row data when opening for new manager
|
||||
setAddModalOpen(true);
|
||||
};
|
||||
|
||||
|
@ -77,56 +68,64 @@ export default function ManagerList() {
|
|||
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
|
||||
await dispatch(
|
||||
updateManager({
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
phone,
|
||||
registeredAddress,
|
||||
})
|
||||
);
|
||||
await dispatch(managerList()); // Refresh the list after update
|
||||
handleCloseModal(); // Close modal after update
|
||||
} 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",
|
||||
})
|
||||
const filteredManagers = managers?.filter(
|
||||
(manager) =>
|
||||
manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
manager.phone?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
manager.registeredAddress
|
||||
?.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const categoryRows = filteredManagers?.length
|
||||
? filteredManagers?.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
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Box, Button, TextField, Typography } from "@mui/material";
|
||||
import AddEditCategoryModal from "../../components/AddEditCategoryModal";
|
||||
import { useForm } from "react-hook-form";
|
||||
import CustomTable, { Column } from "../../components/CustomTable";
|
||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||
import { RootState } from "../../redux/reducers";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch } from "../../redux/store/store";
|
||||
|
|
|
@ -33,6 +33,9 @@ export const managerList = createAsyncThunk<
|
|||
{ rejectValue: string }
|
||||
>("fetchManagers", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const token = localStorage?.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await http.get("manager-list");
|
||||
if (!response.data?.data) throw new Error("Invalid API response");
|
||||
return response.data.data;
|
||||
|
@ -46,7 +49,6 @@ export const managerList = createAsyncThunk<
|
|||
|
||||
// Create Manager (Async Thunk)
|
||||
export const addManager = createAsyncThunk<
|
||||
Manager,
|
||||
Manager,
|
||||
{ rejectValue: string }
|
||||
>("addManager", async (data, { rejectWithValue }) => {
|
||||
|
@ -103,7 +105,7 @@ export const deleteManager = createAsyncThunk<
|
|||
|
||||
// Create Slice
|
||||
const managerSlice = createSlice({
|
||||
name: "maanger",
|
||||
name: "manager",
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
|
@ -147,13 +149,13 @@ const managerSlice = createSlice({
|
|||
})
|
||||
.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
|
||||
// }
|
||||
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;
|
||||
|
@ -166,9 +168,6 @@ const managerSlice = createSlice({
|
|||
})
|
||||
.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;
|
||||
|
|
|
@ -56,7 +56,6 @@ export const createUser = createAsyncThunk<
|
|||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
|
||||
},
|
||||
{ rejectValue: string }
|
||||
>("/CreateUser", async (data, { rejectWithValue }) => {
|
||||
|
@ -70,8 +69,6 @@ export const createUser = createAsyncThunk<
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
export const updateUser = createAsyncThunk(
|
||||
"updateUser",
|
||||
async ({ id, ...userData }: User, { rejectWithValue }) => {
|
||||
|
@ -87,6 +84,24 @@ export const updateUser = createAsyncThunk(
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteUser = createAsyncThunk<
|
||||
string,
|
||||
string,
|
||||
{ rejectValue: string }
|
||||
>("deleteUser", async (id, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await http.delete(`/${id}/delete-user`);
|
||||
toast.success(response.data?.message);
|
||||
return id;
|
||||
} catch (error: any) {
|
||||
toast.error("Error deleting the user" + error);
|
||||
|
||||
return rejectWithValue(
|
||||
error.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
const userSlice = createSlice({
|
||||
name: "fetchUsers",
|
||||
initialState,
|
||||
|
@ -133,6 +148,18 @@ const userSlice = createSlice({
|
|||
})
|
||||
.addCase(updateUser.rejected, (state) => {
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(deleteUser.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(deleteUser.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.users = state.users.filter(
|
||||
(user) => String(user.id) !== String(action.payload)
|
||||
);
|
||||
})
|
||||
.addCase(deleteUser.rejected, (state) => {
|
||||
state.loading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -2,30 +2,31 @@ import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom";
|
|||
import React, { lazy, Suspense } from "react";
|
||||
import LoadingComponent from "./components/Loading";
|
||||
import DashboardLayout from "./layouts/DashboardLayout";
|
||||
import RoleList from "./pages/RoleList";
|
||||
import AddEditRolePage from "./pages/AddEditRolePage";
|
||||
import VehicleList from "./pages/VehicleList";
|
||||
// import RoleList from "./pages/RoleList";
|
||||
// import AddEditRolePage from "./pages/AddEditRolePage";
|
||||
// import VehicleList from "./pages/VehicleList";
|
||||
|
||||
// Page imports
|
||||
const Login = lazy(() => import("./pages/Auth/Login"));
|
||||
const SignUp = lazy(() => import("./pages/Auth/SignUp"));
|
||||
const Dashboard = lazy(() => import("./pages/Dashboard"));
|
||||
const Vehicles = lazy(() => import("./pages/VehicleList"));
|
||||
const VehicleList = lazy(() => import("./pages/VehicleList"));
|
||||
const AdminList = lazy(() => import("./pages/AdminList"));
|
||||
const ProfilePage = lazy(() => import("./pages/ProfilePage"));
|
||||
const NotFoundPage = lazy(() => import("./pages/NotFound"));
|
||||
const UserList = lazy(() => import("./pages/UserList"));
|
||||
const PermissionsTable = lazy(() => import("./pages/PermissionTable"));
|
||||
const AddEditRolePage = lazy(() => import("./pages/AddEditRolePage"));
|
||||
const RoleList = lazy(() => import("./pages/RoleList"));
|
||||
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
||||
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
caps: string[];
|
||||
// caps: string[];
|
||||
component: React.ReactNode;
|
||||
}
|
||||
|
||||
// Protected Route Component
|
||||
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ caps, component }) => {
|
||||
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ component }) => {
|
||||
if (!localStorage.getItem("authToken")) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
@ -42,11 +43,6 @@ export default function AppRouter() {
|
|||
|
||||
{/* Auth Routes */}
|
||||
<Route path="">
|
||||
<Route
|
||||
path=""
|
||||
element={<Navigate to="/login" replace />}
|
||||
index
|
||||
/>
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route path="signup" element={<SignUp />} />
|
||||
</Route>
|
||||
|
@ -57,25 +53,17 @@ export default function AppRouter() {
|
|||
path="dashboard"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<Dashboard />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vehicles"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
component={<Vehicles />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="admin-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<AdminList />}
|
||||
/>
|
||||
}
|
||||
|
@ -84,7 +72,7 @@ export default function AppRouter() {
|
|||
path="user-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<UserList />}
|
||||
/>
|
||||
}
|
||||
|
@ -94,7 +82,7 @@ export default function AppRouter() {
|
|||
path="manager-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<ManagerList />}
|
||||
/>
|
||||
}
|
||||
|
@ -105,7 +93,7 @@ export default function AppRouter() {
|
|||
path="role-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<RoleList />}
|
||||
/>
|
||||
}
|
||||
|
@ -114,7 +102,7 @@ export default function AppRouter() {
|
|||
path="vehicle-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<VehicleList />}
|
||||
/>
|
||||
}
|
||||
|
@ -123,7 +111,7 @@ export default function AppRouter() {
|
|||
path="permissions"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<AddEditRolePage />}
|
||||
/>
|
||||
}
|
||||
|
@ -132,7 +120,7 @@ export default function AppRouter() {
|
|||
path="profile"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
|
||||
component={<ProfilePage />}
|
||||
/>
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue