Merge pull request 'frontend/apiIntegration' (#22) from frontend/apiIntegration into develop
Reviewed-on: DigiMantra/digiev_frontend#22
This commit is contained in:
commit
a5b7e77af4
|
@ -14,7 +14,7 @@ import { useForm, Controller } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
|
|
||||||
//By Jaanvi : Edit Model :: 11-feb-25
|
//By Jaanvi : Edit Model :: 11-feb-25
|
||||||
interface AddEditCategoryModalProps {
|
interface AddEditCategoryModalProps {
|
||||||
|
@ -102,7 +102,12 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
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"
|
aria-labelledby="add-edit-category-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -174,6 +179,11 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
message:
|
message:
|
||||||
"Maximum 30 characters allowed",
|
"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 }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
|
@ -205,9 +215,9 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
rules={{
|
rules={{
|
||||||
required: "Email is required",
|
required: "Email is required",
|
||||||
pattern: {
|
pattern: {
|
||||||
value: /\S+@\S+\.\S+/,
|
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||||
message:
|
message:
|
||||||
"Please enter a valid email address.",
|
"Please enter a valid email address (e.g., example@domain.com).",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
|
|
@ -30,7 +30,6 @@ interface AddUserModalProps {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
password: string,
|
|
||||||
phone: string,
|
phone: string,
|
||||||
|
|
||||||
) => void;
|
) => void;
|
||||||
|
@ -50,24 +49,24 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
reset,
|
reset,
|
||||||
|
setValue,
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
password: "",
|
|
||||||
phone: "",
|
phone: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// useEffect(() => {
|
|
||||||
// if (editRow) {
|
useEffect(() => {
|
||||||
// setValue("name", editRow.name);
|
if (editRow) {
|
||||||
// setValue("email", editRow.email);
|
setValue("name", editRow.name);
|
||||||
// setValue("password", editRow.password);
|
setValue("email", editRow.email);
|
||||||
// setValue("phone", editRow.phone);
|
setValue("phone", editRow.phone);
|
||||||
// } else {
|
} else {
|
||||||
// reset();
|
reset();
|
||||||
// }
|
}
|
||||||
// }, [editRow, setValue, reset]);
|
}, [editRow, setValue,reset]);
|
||||||
|
|
||||||
const onSubmit = (data: FormData) => {
|
const onSubmit = (data: FormData) => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
|
@ -75,7 +74,6 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
editRow.id,
|
editRow.id,
|
||||||
data.name,
|
data.name,
|
||||||
data.email,
|
data.email,
|
||||||
data.password,
|
|
||||||
data.phone
|
data.phone
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +91,12 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
aria-labelledby="add-user-modal"
|
aria-labelledby="add-user-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -165,6 +168,11 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
message:
|
message:
|
||||||
"Maximum 30 characters allowed",
|
"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 }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
|
@ -224,7 +232,7 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
|
|
||||||
{/* Second Row - Two Inputs */}
|
{/* Second Row - Two Inputs */}
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
<Box
|
{!editRow && ( <Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
@ -294,7 +302,7 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>)}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -370,3 +378,7 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
|
|
||||||
export default AddUserModal;
|
export default AddUserModal;
|
||||||
|
|
||||||
|
function setValue(arg0: string, name: any) {
|
||||||
|
throw new Error("Function not implemented.");
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
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 CloseIcon from "@mui/icons-material/Close";
|
||||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { addManager } from "../../redux/slices/managerSlice";
|
import { addManager, managerList } from "../../redux/slices/managerSlice";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
import React from "react";
|
import React, { useState, useRef } from "react";
|
||||||
|
|
||||||
type Props = {
|
export default function AddManagerModal({
|
||||||
open: boolean;
|
open,
|
||||||
handleClose: () => void;
|
handleClose,
|
||||||
};
|
handleAddManager,
|
||||||
|
}) {
|
||||||
export default function AddManagerModal({ open, handleClose }: Props) {
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
@ -25,22 +30,23 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
reset,
|
reset,
|
||||||
} = useForm();
|
} = useForm();
|
||||||
const [showPassword, setShowPassword] = React.useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
// Handle form submission
|
||||||
const onSubmit = async (data: any) => {
|
const onSubmit = async (data: any) => {
|
||||||
const managerData = {
|
const managerData = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
phone: data.phone,
|
phone: data.phone,
|
||||||
registeredAddress: data.registeredAddress,
|
password: data.password,
|
||||||
roleId: data.role,
|
stationId: data.stationId, // Send stationId here
|
||||||
password: data.password,
|
|
||||||
roleName: "Manager", // Add a role name (you can replace this with a dynamic value if needed)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Dispatch the addManager action
|
// Dispatch the addManager action
|
||||||
await dispatch(addManager(managerData)).unwrap();
|
await dispatch(addManager(managerData));
|
||||||
|
dispatch(managerList());
|
||||||
handleClose(); // Close modal after successful addition
|
handleClose(); // Close modal after successful addition
|
||||||
reset(); // Reset form fields
|
reset(); // Reset form fields
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -48,15 +54,20 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Toggle password visibility
|
const togglePasswordVisibility = (e: React.MouseEvent) => {
|
||||||
const togglePasswordVisibility = () => {
|
e.preventDefault(); // Prevent focus loss
|
||||||
setShowPassword((prev) => !prev);
|
setShowPassword((prev) => !prev);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
aria-labelledby="add-manager-modal"
|
aria-labelledby="add-manager-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -93,16 +104,9 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
|
|
||||||
{/* Form */}
|
{/* Form */}
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
{/* First Row - Manager Name */}
|
{/* Manager Name */}
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
{/* Manager Name */}
|
<Box sx={{ flex: 1 }}>
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Manager Name
|
Manager Name
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -117,25 +121,47 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
{...register("name", {
|
{...register("name", {
|
||||||
required: "Manager Name is required",
|
required: "Manager Name is required",
|
||||||
minLength: {
|
minLength: {
|
||||||
value: 2,
|
value: 3,
|
||||||
message:
|
message:
|
||||||
"Manager 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:
|
||||||
|
"Manager Name must only contain letters and spaces",
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Station Id
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Id"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.stationId}
|
||||||
|
helperText={
|
||||||
|
errors.stationId
|
||||||
|
? errors.stationId.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("stationId", {
|
||||||
|
required: "Station Id is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* Email and Password */}
|
||||||
{/* Second Row - Email, Password */}
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
|
||||||
{/* Email */}
|
{/* Email */}
|
||||||
<Box
|
<Box sx={{ flex: 1 }}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "50%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Email
|
Email
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -158,91 +184,73 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Password */}
|
{/* Password */}
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Box
|
<Typography variant="body2" fontWeight={500}>
|
||||||
sx={{
|
Password
|
||||||
display: "flex",
|
</Typography>
|
||||||
flexDirection: "column",
|
<Controller
|
||||||
width: "100%",
|
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 }) => (
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<CustomTextField
|
||||||
Password
|
{...field}
|
||||||
</Typography>
|
required
|
||||||
<Controller
|
placeholder="Enter Password"
|
||||||
name="password"
|
type={
|
||||||
control={control}
|
showPassword ? "text" : "password"
|
||||||
rules={{
|
}
|
||||||
required: "Password is required",
|
fullWidth
|
||||||
minLength: {
|
color={
|
||||||
value: 6,
|
errors.password
|
||||||
message:
|
? "error"
|
||||||
"Password must be at least 6 characters long.",
|
: "primary"
|
||||||
},
|
}
|
||||||
pattern: {
|
size="small"
|
||||||
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
// onMouseDown={togglePasswordVisibility}
|
||||||
message:
|
InputProps={{
|
||||||
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
endAdornment: (
|
||||||
},
|
<InputAdornment position="end">
|
||||||
}}
|
<CustomIconButton
|
||||||
render={({ field }) => (
|
aria-label="toggle password visibility"
|
||||||
<CustomTextField
|
onClick={
|
||||||
{...field}
|
togglePasswordVisibility
|
||||||
required
|
}
|
||||||
placeholder="Enter Password"
|
edge="end"
|
||||||
type={
|
>
|
||||||
showPassword
|
{showPassword ? (
|
||||||
? "text"
|
<VisibilityOff />
|
||||||
: "password"
|
) : (
|
||||||
}
|
<Visibility />
|
||||||
id="password"
|
)}
|
||||||
fullWidth
|
</CustomIconButton>
|
||||||
color={
|
</InputAdornment>
|
||||||
errors.password
|
),
|
||||||
? "error"
|
}}
|
||||||
: "primary"
|
error={!!errors.password}
|
||||||
}
|
helperText={errors.password?.message}
|
||||||
size="small"
|
/>
|
||||||
InputProps={{
|
)}
|
||||||
endAdornment: (
|
/>
|
||||||
<InputAdornment position="end">
|
</Box>
|
||||||
<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>
|
</Box>
|
||||||
|
|
||||||
{/* Third Row - Phone Number, Registered Address */}
|
{/* Phone and Registered Address */}
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
{/* Phone Number */}
|
{/* Phone */}
|
||||||
<Box
|
<Box sx={{ flex: 1 }}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "50%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Phone Number
|
Phone Number
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -265,32 +273,6 @@ export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
/>
|
/>
|
||||||
</Box>
|
</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>
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
|
|
205
src/components/AddStationModal/index.tsx
Normal file
205
src/components/AddStationModal/index.tsx
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { Box, Button, Typography, Modal } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddEditUserModel/styled.css.tsx"; // Assuming custom styled components
|
||||||
|
|
||||||
|
export default function AddStationModal({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleAddStation,
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm();
|
||||||
|
|
||||||
|
const onSubmit = (data: any) => {
|
||||||
|
handleAddStation(data); // Add station to the list
|
||||||
|
handleClose(); // Close modal after adding
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
|
aria-labelledby="add-station-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 Charging Station
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
{/* 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}>
|
||||||
|
Station Name
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={
|
||||||
|
errors.name ? errors.name.message : ""
|
||||||
|
}
|
||||||
|
{...register("name", {
|
||||||
|
required: "Station Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 3,
|
||||||
|
message:
|
||||||
|
"Minimum 3 characters required",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 30,
|
||||||
|
message:
|
||||||
|
"Maximum 30 characters allowed",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Station Location
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Charging Station Address"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress
|
||||||
|
? errors.registeredAddress.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("registeredAddress", {
|
||||||
|
required:
|
||||||
|
"Registered Address is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Total Slots */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Total Slots
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Total Slots"
|
||||||
|
size="small"
|
||||||
|
type="number"
|
||||||
|
error={!!errors.totalSlots}
|
||||||
|
helperText={
|
||||||
|
errors.totalSlots
|
||||||
|
? errors.totalSlots.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("totalSlots", {
|
||||||
|
required: "Total Slots are required",
|
||||||
|
min: {
|
||||||
|
value: 1,
|
||||||
|
message:
|
||||||
|
"At least 1 slot is required",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</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 Station
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,7 +6,10 @@ import {
|
||||||
Modal,
|
Modal,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { CustomIconButton, CustomTextField } from "../AddUserModel/styled.css.tsx";
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
export default function AddVehicleModal({
|
export default function AddVehicleModal({
|
||||||
open,
|
open,
|
||||||
handleClose,
|
handleClose,
|
||||||
|
@ -28,7 +31,12 @@ export default function AddVehicleModal({
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
aria-labelledby="add-vehicle-modal"
|
aria-labelledby="add-vehicle-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -96,9 +104,19 @@ export default function AddVehicleModal({
|
||||||
{...register("name", {
|
{...register("name", {
|
||||||
required: "Vehicle Name is required",
|
required: "Vehicle Name is required",
|
||||||
minLength: {
|
minLength: {
|
||||||
value: 2,
|
value: 3,
|
||||||
message:
|
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 +250,6 @@ export default function AddVehicleModal({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
||||||
type="submit"
|
type="submit"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#52ACDF",
|
backgroundColor: "#52ACDF",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { styled } from "@mui/material/styles";
|
import { styled } from "@mui/material/styles";
|
||||||
import Table from "@mui/material/Table";
|
import Table from "@mui/material/Table";
|
||||||
|
@ -18,12 +17,12 @@ import {
|
||||||
Button,
|
Button,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
Menu,
|
Menu,
|
||||||
IconButton,
|
IconButton,
|
||||||
Pagination,
|
Pagination,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
||||||
import DeleteModal from "../Modals/DeleteModal";
|
import DeleteModal from "../Modals/DeleteModal";
|
||||||
import { AppDispatch } from "../../redux/store/store";
|
import { AppDispatch } from "../../redux/store/store";
|
||||||
import ViewModal from "../Modals/ViewModal";
|
import ViewModal from "../Modals/ViewModal";
|
||||||
|
@ -31,10 +30,12 @@ import VehicleViewModal from "../Modals/VehicleViewModal";
|
||||||
|
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
import TuneIcon from "@mui/icons-material/Tune";
|
import TuneIcon from "@mui/icons-material/Tune";
|
||||||
import {
|
import { CustomIconButton } from "../AddEditUserModel/styled.css.tsx";
|
||||||
CustomIconButton,
|
|
||||||
} from "../AddUserModel/styled.css.tsx";
|
|
||||||
import ManagerViewModal from "../Modals/ViewManagerModal";
|
import ManagerViewModal from "../Modals/ViewManagerModal";
|
||||||
|
import UserViewModal from "../Modals/UserViewModal/index.tsx";
|
||||||
|
import { deleteUser, userList } from "../../redux/slices/userSlice.ts";
|
||||||
|
import { deleteStation } from "../../redux/slices/stationSlice.ts";
|
||||||
|
import StationViewModal from "../Modals/StationViewModal/index.tsx";
|
||||||
// Styled components for customization
|
// Styled components for customization
|
||||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
[`&.${tableCellClasses.head}`]: {
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
@ -95,7 +96,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
handleStatusToggle,
|
handleStatusToggle,
|
||||||
tableType,
|
tableType,
|
||||||
handleClickOpen,
|
handleClickOpen,
|
||||||
|
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
@ -104,17 +104,12 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
const [currentPage, setCurrentPage] = React.useState(1);
|
const [currentPage, setCurrentPage] = React.useState(1);
|
||||||
const usersPerPage = 10;
|
const usersPerPage = 10;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
setSelectedRow(row);
|
||||||
setRowData(row);
|
setRowData(row);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
// const handleViewButton = (id: string | undefined) => {
|
// const handleViewButton = (id: string | undefined) => {
|
||||||
// if (!id) {
|
// if (!id) {
|
||||||
|
@ -123,7 +118,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
// }
|
// }
|
||||||
// setViewModal(true);
|
// setViewModal(true);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
|
@ -136,8 +130,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteButton = (id: string | undefined) => {
|
const handleDeleteButton = (id: string | undefined) => {
|
||||||
if (!id) console.error("ID not found", id);
|
if (!id) console.error("ID not found", id);
|
||||||
|
|
||||||
|
@ -151,6 +143,12 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
case "manager":
|
case "manager":
|
||||||
dispatch(deleteManager(id || ""));
|
dispatch(deleteManager(id || ""));
|
||||||
break;
|
break;
|
||||||
|
case "user":
|
||||||
|
dispatch(deleteUser(id || ""));
|
||||||
|
break;
|
||||||
|
case "station":
|
||||||
|
dispatch(deleteStation(id || ""));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unknown table type:", tableType);
|
console.error("Unknown table type:", tableType);
|
||||||
return;
|
return;
|
||||||
|
@ -158,24 +156,27 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
setDeleteModal(false); // Close the modal only after deletion
|
setDeleteModal(false); // Close the modal only after deletion
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewButton = (id: string | undefined) => {
|
const handleViewButton = (id: string | undefined) => {
|
||||||
if (!id) console.error("ID not found", id);
|
if (!id) console.error("ID not found", id);
|
||||||
switch (tableType) {
|
// switch (tableType) {
|
||||||
case "admin":
|
// case "admin":
|
||||||
dispatch(adminList());
|
// dispatch(adminList());
|
||||||
break;
|
// break;
|
||||||
case "vehicle":
|
// case "vehicle":
|
||||||
dispatch(vehicleList());
|
// dispatch(vehicleList());
|
||||||
break;
|
// break;
|
||||||
case "manager":
|
// case "manager":
|
||||||
dispatch(managerList());
|
// dispatch(managerList());
|
||||||
break;
|
// break;
|
||||||
default:
|
// case "user":
|
||||||
console.error("Unknown table type:", tableType);
|
// dispatch(userList());
|
||||||
return;
|
// break;
|
||||||
}
|
// default:
|
||||||
dispatch(adminList());
|
// console.error("Unknown table type:", tableType);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
setViewModal(false);
|
setViewModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -188,12 +189,12 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredRows = rows.filter(
|
const filteredRows = rows.filter(
|
||||||
(row) =>
|
(row) =>
|
||||||
(row.name &&
|
(row.name &&
|
||||||
row.name.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
row.name.toLowerCase().includes(searchQuery.toLowerCase())) ||
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
const indexOfLastRow = currentPage * usersPerPage;
|
const indexOfLastRow = currentPage * usersPerPage;
|
||||||
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
||||||
|
@ -235,6 +236,8 @@ const filteredRows = rows.filter(
|
||||||
? "Managers"
|
? "Managers"
|
||||||
: tableType === "vehicle"
|
: tableType === "vehicle"
|
||||||
? "Vehicles"
|
? "Vehicles"
|
||||||
|
: tableType === "station"
|
||||||
|
? "Charging Station"
|
||||||
: "List"}
|
: "List"}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
@ -306,6 +309,8 @@ const filteredRows = rows.filter(
|
||||||
? "Manager"
|
? "Manager"
|
||||||
: tableType === "vehicle"
|
: tableType === "vehicle"
|
||||||
? "Vehicle"
|
? "Vehicle"
|
||||||
|
: tableType === "station"
|
||||||
|
? "Charging Station"
|
||||||
: "Item"}
|
: "Item"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -513,6 +518,26 @@ const filteredRows = rows.filter(
|
||||||
id={selectedRow?.id}
|
id={selectedRow?.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{viewModal && tableType === "user" && (
|
||||||
|
<UserViewModal
|
||||||
|
handleView={() =>
|
||||||
|
handleViewButton(selectedRow?.id)
|
||||||
|
}
|
||||||
|
open={viewModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
id={selectedRow?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{viewModal && tableType === "station" && (
|
||||||
|
<StationViewModal
|
||||||
|
handleView={() =>
|
||||||
|
handleViewButton(selectedRow?.id)
|
||||||
|
}
|
||||||
|
open={viewModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
id={selectedRow?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -545,6 +570,25 @@ const filteredRows = rows.filter(
|
||||||
: "Activate"}
|
: "Activate"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{tableType === "station" && (
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleToggleStatus();
|
||||||
|
}}
|
||||||
|
color="secondary"
|
||||||
|
sx={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
py: 0,
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedRow?.statusValue === 1
|
||||||
|
? "Not Available"
|
||||||
|
: "Available"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -579,4 +623,3 @@ const filteredRows = rows.filter(
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomTable;
|
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 React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
@ -323,11 +9,11 @@ import {
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
import { useDispatch } from "react-redux";
|
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 {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddUserModel/styled.css.tsx"; // Custom styled components
|
} from "../AddEditUserModel/styled.css.tsx"; // Custom styled components
|
||||||
|
|
||||||
interface EditManagerModalProps {
|
interface EditManagerModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
@ -338,8 +24,8 @@ interface EditManagerModalProps {
|
||||||
interface FormData {
|
interface FormData {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
registeredAddress: string;
|
|
||||||
phone: string;
|
phone: string;
|
||||||
|
stationId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
@ -358,8 +44,8 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
registeredAddress: "",
|
|
||||||
phone: "",
|
phone: "",
|
||||||
|
stationId: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -370,8 +56,8 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
setValue("name", editRow.name);
|
setValue("name", editRow.name);
|
||||||
setValue("email", editRow.email);
|
setValue("email", editRow.email);
|
||||||
setValue("registeredAddress", editRow.registeredAddress);
|
|
||||||
setValue("phone", editRow.phone);
|
setValue("phone", editRow.phone);
|
||||||
|
setValue("stationId", editRow.stationId);
|
||||||
} else {
|
} else {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -379,9 +65,6 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
|
||||||
const onSubmit = async (data: FormData) => {
|
const onSubmit = async (data: FormData) => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
setLoading(true); // Start loading state
|
|
||||||
|
|
||||||
// Dispatch the updateManager action from Redux
|
|
||||||
try {
|
try {
|
||||||
await dispatch(
|
await dispatch(
|
||||||
updateManager({
|
updateManager({
|
||||||
|
@ -389,12 +72,13 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
managerData: {
|
managerData: {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
registeredAddress: data.registeredAddress,
|
|
||||||
phone: data.phone,
|
phone: data.phone,
|
||||||
|
stationId: data.stationId,
|
||||||
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).unwrap(); // Ensure that it throws an error if the update fails
|
).unwrap(); // Ensure that it throws an error if the update fails
|
||||||
|
dispatch(managerList());
|
||||||
handleClose(); // Close modal on success
|
handleClose(); // Close modal on success
|
||||||
reset(); // Reset form fields after submit
|
reset(); // Reset form fields after submit
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -409,7 +93,12 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
aria-labelledby="edit-manager-modal"
|
aria-labelledby="edit-manager-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -446,23 +135,32 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
{/* Horizontal Line */}
|
{/* Horizontal Line */}
|
||||||
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
{/* Input Fields */}
|
{/* Input Fields (Name, Email, and Phone) */}
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 2 }}>
|
||||||
{/* Manager Name */}
|
{/* Manager Name */}
|
||||||
<Box
|
<Box sx={{ flex: "1 1 48%" }}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
Manager Name
|
Manager Name
|
||||||
</Typography>
|
</Typography>
|
||||||
<Controller
|
<Controller
|
||||||
name="name"
|
name="name"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: "Manager Name is required" }}
|
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 }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
{...field}
|
{...field}
|
||||||
|
@ -476,46 +174,8 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
/>
|
/>
|
||||||
</Box>
|
</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 */}
|
{/* Email */}
|
||||||
<Box
|
<Box sx={{ flex: "1 1 48%" }}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
Email
|
Email
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -537,13 +197,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Phone Number */}
|
{/* Phone Number */}
|
||||||
<Box
|
<Box sx={{ flex: "1 1 48%" }}>
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
Phone Number
|
Phone Number
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -563,6 +217,26 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* <Box sx={{ flex: "1 1 48%" }}>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
StationId
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="stationId"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "StationId is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Id"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.stationId}
|
||||||
|
helperText={errors.stationId?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box> */}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
|
@ -591,5 +265,4 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
export default EditManagerModal;
|
||||||
export default EditManagerModal;
|
|
270
src/components/EditStationModal/index.tsx
Normal file
270
src/components/EditStationModal/index.tsx
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { Box, Button, Typography, Modal } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
|
|
||||||
|
interface EditStationModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
handleUpdate: (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
registeredAddress: string,
|
||||||
|
totalSlots: number,
|
||||||
|
status: number
|
||||||
|
) => void;
|
||||||
|
editRow: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
totalSlots: number;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleUpdate,
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
registeredAddress: "",
|
||||||
|
totalSlots: 0,
|
||||||
|
status: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set values if editRow is provided
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("name", editRow.name);
|
||||||
|
setValue("registeredAddress", editRow.registeredAddress);
|
||||||
|
setValue("totalSlots", editRow.totalSlots);
|
||||||
|
setValue("status", editRow.number);
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
handleUpdate(
|
||||||
|
editRow.id,
|
||||||
|
data.name,
|
||||||
|
data.registeredAddress,
|
||||||
|
data.totalSlots,
|
||||||
|
data.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleClose(); // Close the modal
|
||||||
|
reset(); // Reset the form fields
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
|
aria-labelledby="edit-station-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 Charging Station
|
||||||
|
</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 }}>
|
||||||
|
{/* 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}
|
||||||
|
>
|
||||||
|
Station Name
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Station Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 3,
|
||||||
|
message:
|
||||||
|
"Minimum 3 characters required",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 30,
|
||||||
|
message:
|
||||||
|
"Maximum 30 characters allowed",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station 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 Location
|
||||||
|
</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>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Total Slots */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight={500}
|
||||||
|
mb={0.5}
|
||||||
|
>
|
||||||
|
Total Slots
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="totalSlots"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Total Slots are required",
|
||||||
|
min: {
|
||||||
|
value: 1,
|
||||||
|
message: "At least 1 slot is required",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Total Slots"
|
||||||
|
size="small"
|
||||||
|
type="number"
|
||||||
|
error={!!errors.totalSlots}
|
||||||
|
helperText={errors.totalSlots?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</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" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update Charging Station
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditStationModal;
|
|
@ -11,7 +11,7 @@ import { updateVehicle } from "../../redux/slices/VehicleSlice";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
interface EditVehicleModalProps {
|
interface EditVehicleModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
|
@ -87,7 +87,12 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose(); // Close modal when clicking cross or cancel
|
||||||
|
}}
|
||||||
aria-labelledby="edit-vehicle-modal"
|
aria-labelledby="edit-vehicle-modal"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -145,7 +150,24 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
||||||
<Controller
|
<Controller
|
||||||
name="name"
|
name="name"
|
||||||
control={control}
|
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 }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
{...field}
|
{...field}
|
||||||
|
@ -154,6 +176,7 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
||||||
size="small"
|
size="small"
|
||||||
error={!!errors.name}
|
error={!!errors.name}
|
||||||
helperText={errors.name?.message}
|
helperText={errors.name?.message}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -43,17 +43,24 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
text: "Users",
|
text: "Users",
|
||||||
icon: <AnalyticsRoundedIcon />,
|
icon: <AnalyticsRoundedIcon />,
|
||||||
url: "/panel/user-list",
|
url: "/panel/user-list",
|
||||||
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Charging Stations",
|
||||||
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
|
url: "/panel/station-list", // Placeholder for now
|
||||||
},
|
},
|
||||||
userRole === "admin" && {
|
userRole === "admin" && {
|
||||||
text: "Managers",
|
text: "Managers",
|
||||||
icon: <ManageAccountsOutlinedIcon />,
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
url: "/panel/manager-list", // Placeholder for now
|
url: "/panel/manager-list", // Placeholder for now
|
||||||
},
|
},
|
||||||
|
|
||||||
userRole === "admin" && {
|
userRole === "admin" && {
|
||||||
text: "Vehicles",
|
text: "Vehicles",
|
||||||
icon: <ManageAccountsOutlinedIcon />,
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
url: "/panel/vehicles", // Placeholder for now
|
url: "/panel/vehicle-list", // Placeholder for now
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
||||||
|
@ -99,4 +106,4 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
</List>
|
</List>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,81 +1,112 @@
|
||||||
import { Box, Button, Modal, Typography } from "@mui/material";
|
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setLogoutModal: Function;
|
setLogoutModal: Function;
|
||||||
handlelogout: any;
|
handlelogout: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "50%",
|
top: "50%",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%)",
|
||||||
width: 330,
|
width: 330,
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
borderRadius: 1.5,
|
borderRadius: 1.5,
|
||||||
boxShadow: 24,
|
boxShadow: 24,
|
||||||
p: 3,
|
p: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" };
|
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" };
|
||||||
|
|
||||||
export default function LogoutModal({
|
export default function LogoutModal({
|
||||||
open,
|
open,
|
||||||
setLogoutModal,
|
setLogoutModal,
|
||||||
handlelogout,
|
handlelogout,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
// Function to prevent closing the modal when clicking outside
|
||||||
<Modal
|
const handleClose = (event: React.SyntheticEvent) => {
|
||||||
open={open}
|
// Close only when clicking the close button, not the backdrop
|
||||||
aria-labelledby="modal-modal-title"
|
setLogoutModal(false);
|
||||||
aria-describedby="modal-modal-description"
|
};
|
||||||
>
|
|
||||||
<Box sx={style}>
|
return (
|
||||||
<Typography id="modal-title" variant="h5" fontWeight="bold" sx={{ width: "100%" }}>
|
<Modal
|
||||||
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%" }}>
|
open={open}
|
||||||
<Box sx={{ flex: 1, textAlign: "center" }}>Logout</Box>
|
onClose={(e, reason) =>
|
||||||
<Box onClick={() => setLogoutModal(false)} sx={{ cursor: "pointer", display: "flex", alignItems: "center" }}>
|
reason !== "backdropClick" && setLogoutModal(false)
|
||||||
<CloseIcon />
|
} // Prevent closing on backdrop click
|
||||||
</Box>
|
aria-labelledby="modal-modal-title"
|
||||||
</Box>
|
aria-describedby="modal-modal-description"
|
||||||
</Typography>
|
BackdropProps={{
|
||||||
<Typography
|
onClick: (e) => e.stopPropagation(), // Stop propagation on backdrop click to prevent closing the modal
|
||||||
id="modal-modal-description"
|
}}
|
||||||
sx={{ mt: 2 }}
|
>
|
||||||
align="center"
|
<Box sx={style}>
|
||||||
>
|
<Typography
|
||||||
Are you sure you want to Logout?
|
id="modal-title"
|
||||||
</Typography>
|
variant="h5"
|
||||||
<Box
|
fontWeight="bold"
|
||||||
sx={{
|
sx={{ width: "100%" }}
|
||||||
display: "flex",
|
>
|
||||||
justifyContent: "space-between",
|
<Box
|
||||||
mt: 4,
|
sx={{
|
||||||
gap: 2,
|
display: "flex",
|
||||||
}}
|
alignItems: "center",
|
||||||
>
|
justifyContent: "space-between",
|
||||||
<Button
|
width: "100%",
|
||||||
variant="contained"
|
}}
|
||||||
color="error"
|
>
|
||||||
type="button"
|
<Box sx={{ flex: 1, textAlign: "center" }}>Logout</Box>
|
||||||
sx={btnStyle}
|
<Box
|
||||||
onClick={() => setLogoutModal(false)}
|
onClick={() => setLogoutModal(false)}
|
||||||
>
|
sx={{
|
||||||
Cancel
|
cursor: "pointer",
|
||||||
</Button>
|
display: "flex",
|
||||||
<Button
|
alignItems: "center",
|
||||||
variant="contained"
|
}}
|
||||||
type="button"
|
>
|
||||||
color="primary"
|
<CloseIcon />
|
||||||
sx={btnStyle}
|
</Box>
|
||||||
onClick={() => handlelogout()}
|
</Box>
|
||||||
>
|
</Typography>
|
||||||
Logout
|
<Typography
|
||||||
</Button>
|
id="modal-modal-description"
|
||||||
</Box>
|
sx={{ mt: 2 }}
|
||||||
</Box>
|
align="center"
|
||||||
</Modal>
|
>
|
||||||
);
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
127
src/components/Modals/StationViewModal/index.tsx
Normal file
127
src/components/Modals/StationViewModal/index.tsx
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
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 StationViewModal({ open, setViewModal, id }: Props) {
|
||||||
|
const { stations } = useSelector(
|
||||||
|
(state: RootState) => state.stationReducer
|
||||||
|
);
|
||||||
|
const [selectedStation, setSelectedStation] = useState<any>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
const station = stations.find((station) => station.id === id);
|
||||||
|
setSelectedStation(station || null);
|
||||||
|
}
|
||||||
|
}, [id, stations]);
|
||||||
|
|
||||||
|
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" }}>
|
||||||
|
{selectedStation?.name || "Station"}'s Details
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
onClick={() => setViewModal(false)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ width: "100%" }} />
|
||||||
|
|
||||||
|
{selectedStation ? (
|
||||||
|
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Station Name:</strong>{" "}
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedStation.name}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Station Location:</strong>{" "}
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedStation.registeredAddress}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Total Slots:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedStation.totalSlots}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Status:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedStation.status === "available"
|
||||||
|
? "Available"
|
||||||
|
: "Not Available"}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
) : (
|
||||||
|
<Typography align="center">
|
||||||
|
No station found with this ID
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|
||||||
const manager = managers.find((manager) => manager.id === id);
|
const manager = managers.find((manager) => manager.id === id);
|
||||||
setSelectedManager(manager || null);
|
setSelectedManager(manager || null);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,7 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
<strong>Registered Address:</strong>
|
<strong>Station Location:</strong>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{selectedManager.registeredAddress}
|
{selectedManager.registeredAddress}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
@ -107,7 +107,8 @@ export default function ViewModal({ open, setViewModal, id }: Props) {
|
||||||
<strong>Address:</strong>
|
<strong>Address:</strong>
|
||||||
|
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{selectedAdmin.registeredAddress ?? "N/A"}
|
{selectedAdmin?.Admins?.[0]
|
||||||
|
?.registeredAddress ?? "N/A"}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
@ -20,9 +20,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||||
import { createRole } from "../../redux/slices/roleSlice"; // Import the createRole action
|
import { createRole } from "../../redux/slices/roleSlice"; // Import the createRole action
|
||||||
import { AppDispatch, RootState } from "../../redux/store/store"; // Assuming this is the path to your store file
|
import { AppDispatch, RootState } from "../../redux/store/store"; // Assuming this is the path to your store file
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import { CustomTextField } from "../../components/AddEditUserModel/styled.css.tsx";
|
||||||
CustomTextField,
|
|
||||||
} from "../../components/AddUserModel/styled.css.tsx";
|
|
||||||
// Define the data structure for permission
|
// Define the data structure for permission
|
||||||
interface Permission {
|
interface Permission {
|
||||||
module: string;
|
module: string;
|
||||||
|
|
|
@ -69,7 +69,6 @@ export default function AdminList() {
|
||||||
email,
|
email,
|
||||||
phone,
|
phone,
|
||||||
registeredAddress,
|
registeredAddress,
|
||||||
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await dispatch(adminList());
|
await dispatch(adminList());
|
||||||
|
@ -86,40 +85,30 @@ export default function AdminList() {
|
||||||
{ id: "registeredAddress", label: "Address" },
|
{ id: "registeredAddress", label: "Address" },
|
||||||
{ id: "action", label: "Action", align: "center" },
|
{ id: "action", label: "Action", align: "center" },
|
||||||
];
|
];
|
||||||
// const filteredAdmins = admins?.filter(
|
|
||||||
// (admin) =>
|
|
||||||
// admin.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
// admin.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
// admin.phone.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
// admin.registeredAddress
|
|
||||||
// .toLowerCase()
|
|
||||||
// .includes(searchTerm.toLowerCase())
|
|
||||||
// );
|
|
||||||
|
|
||||||
const categoryRows = admins?.map(
|
const categoryRows = admins?.map(
|
||||||
(
|
(
|
||||||
admin: {
|
admin: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
registeredAddress: string;
|
Admins: { registeredAddress: string }[]; // Adjusted to support array of Admins
|
||||||
},
|
},
|
||||||
index: number
|
index: number
|
||||||
) => ({
|
) => ({
|
||||||
id: admin?.id,
|
id: admin?.id,
|
||||||
srno: index + 1,
|
srno: index + 1,
|
||||||
name: admin?.name,
|
name: admin?.name,
|
||||||
email: admin?.email,
|
email: admin?.email,
|
||||||
phone: admin?.phone,
|
phone: admin?.phone,
|
||||||
registeredAddress: admin?.registeredAddress || "NA",
|
registeredAddress: admin?.Admins?.[0]?.registeredAddress || "NA",
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<CustomTable
|
<CustomTable
|
||||||
columns={categoryColumns}
|
columns={categoryColumns}
|
||||||
rows={categoryRows}
|
rows={categoryRows}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
Grid,
|
Grid,
|
||||||
IconButton,
|
|
||||||
Link,
|
Link,
|
||||||
InputAdornment
|
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";
|
||||||
|
@ -22,7 +22,7 @@ import { toast } from "sonner";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
import { Card, SignInContainer } from "./styled.css.tsx";
|
import { Card, SignInContainer } from "./styled.css.tsx";
|
||||||
|
import { CustomIconButton } from "../../../components/AddEditUserModel/styled.css.tsx";
|
||||||
interface ILoginForm {
|
interface ILoginForm {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
@ -30,9 +30,8 @@ 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,
|
||||||
|
@ -48,17 +47,22 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const togglePasswordVisibility = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault(); // Prevent focus loss
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<ILoginForm> = async (data: ILoginForm) => {
|
const onSubmit: SubmitHandler<ILoginForm> = async (data: ILoginForm) => {
|
||||||
try {
|
try {
|
||||||
const response = await dispatch(loginUser(data)).unwrap();
|
const response = await dispatch(loginUser(data)).unwrap();
|
||||||
if (response?.data?.token) {
|
if (response?.data?.token) {
|
||||||
router("/panel/dashboard");
|
router("/panel/dashboard");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("Login failed:", error);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
};
|
||||||
console.log("Login failed:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppTheme {...props}>
|
<AppTheme {...props}>
|
||||||
|
@ -80,21 +84,19 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
|
|
||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
md={5}
|
md={5}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "black",
|
backgroundColor: "black",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
padding: { xs: "2rem", md: "3rem", lg: "3rem" },
|
||||||
|
height: "auto",
|
||||||
height: "auto", // ✅ Allows the height to adjust dynamically
|
}}
|
||||||
}}
|
>
|
||||||
>
|
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -110,8 +112,16 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
Welcome Back!
|
Welcome Back!
|
||||||
</Typography>
|
</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
|
<Box
|
||||||
component="form"
|
component="form"
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
@ -122,178 +132,251 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography component="h1" variant="h4" sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "24px" }}>Login</Typography>
|
<Typography
|
||||||
<Typography
|
component="h1"
|
||||||
component="h6"
|
variant="h4"
|
||||||
variant="subtitle2"
|
sx={{
|
||||||
sx={{ textAlign: "center", color: "white", fontFamily: "Gilroy", fontSize: "16px" }}
|
textAlign: "center",
|
||||||
>
|
color: "white",
|
||||||
Log in with your email and password
|
fontFamily: "Gilroy",
|
||||||
</Typography>
|
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 ----------------- */}
|
{/* -------------------------------- Password Field ----------------- */}
|
||||||
<FormControl sx={{ width: "100%" }}>
|
<FormControl sx={{ width: "100%" }}>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
htmlFor="email"
|
htmlFor="password"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
fontSize: {
|
||||||
color: "white",
|
xs: "0.9rem",
|
||||||
fontFamily: "Gilroy, sans-serif", // ✅ Apply Gilroy font
|
sm: "1rem",
|
||||||
}}
|
},
|
||||||
>
|
color: "white",
|
||||||
Email
|
fontFamily: "Gilroy, sans-serif",
|
||||||
</FormLabel>
|
}}
|
||||||
<Controller
|
>
|
||||||
name="email"
|
Password
|
||||||
control={control}
|
</FormLabel>
|
||||||
defaultValue=""
|
<Controller
|
||||||
rules={{
|
name="password"
|
||||||
required: "Email is required",
|
control={control}
|
||||||
pattern: {
|
defaultValue=""
|
||||||
value: /\S+@\S+\.\S+/,
|
rules={{
|
||||||
message: "Please enter a valid email address.",
|
required: "Password is required",
|
||||||
},
|
minLength: {
|
||||||
}}
|
value: 6,
|
||||||
render={({ field }) => (
|
message:
|
||||||
<TextField
|
"Password must be at least 6 characters long.",
|
||||||
{...field}
|
},
|
||||||
error={!!errors.email}
|
pattern: {
|
||||||
helperText={errors.email?.message}
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
id="email"
|
message:
|
||||||
type="email"
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
placeholder="Email"
|
},
|
||||||
autoComplete="email"
|
}}
|
||||||
autoFocus
|
render={({ field }) => (
|
||||||
required
|
<TextField
|
||||||
fullWidth
|
{...field}
|
||||||
variant="outlined"
|
error={!!errors.password}
|
||||||
color={errors.email ? "error" : "primary"}
|
helperText={
|
||||||
InputProps={{
|
errors.password?.message
|
||||||
sx: {
|
}
|
||||||
height: "50px",
|
name="password"
|
||||||
alignItems: "center",
|
placeholder="Password"
|
||||||
backgroundColor: "#1E1F1F",
|
type={
|
||||||
fontFamily: "Gilroy, sans-serif",
|
showPassword
|
||||||
},
|
? "text"
|
||||||
}}
|
: "password"
|
||||||
sx={{
|
}
|
||||||
"& .MuiOutlinedInput-root": {
|
id="password"
|
||||||
backgroundColor: "#1E1F1F",
|
autoComplete="current-password"
|
||||||
borderRadius: "4px",
|
required
|
||||||
"& fieldset": { borderColor: "#4b5255" },
|
fullWidth
|
||||||
"&:hover fieldset": { borderColor: "#4b5255" },
|
variant="outlined"
|
||||||
"&.Mui-focused fieldset": { borderColor: "#4b5255" },
|
color={
|
||||||
},
|
errors.password
|
||||||
"& input": {
|
? "error"
|
||||||
color: "white",
|
: "primary"
|
||||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
}
|
||||||
fontFamily: "Gilroy, sans-serif",
|
onMouseDown={
|
||||||
},
|
togglePasswordVisibility
|
||||||
"& .MuiInputBase-input::placeholder": {
|
}
|
||||||
color: "white",
|
InputProps={{
|
||||||
opacity: 1,
|
endAdornment: (
|
||||||
fontFamily: "Gilroy, sans-serif",
|
<InputAdornment position="end">
|
||||||
},
|
<CustomIconButton
|
||||||
}}
|
aria-label="toggle password visibility"
|
||||||
/>
|
onClick={
|
||||||
)}
|
togglePasswordVisibility
|
||||||
/>
|
}
|
||||||
</FormControl>
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
{/* -------------------------------- Password Field ----------------- */}
|
<VisibilityOff />
|
||||||
<FormControl sx={{ width: "100%" }}>
|
) : (
|
||||||
<FormLabel
|
<Visibility />
|
||||||
htmlFor="password"
|
)}
|
||||||
sx={{
|
</CustomIconButton>
|
||||||
fontSize: { xs: "0.9rem", sm: "1rem" },
|
</InputAdornment>
|
||||||
color: "white",
|
),
|
||||||
fontFamily: "Gilroy, sans-serif",
|
}}
|
||||||
}}
|
sx={{
|
||||||
>
|
"& .MuiOutlinedInput-root":
|
||||||
Password
|
{
|
||||||
</FormLabel>
|
backgroundColor:
|
||||||
<Controller
|
"#1E1F1F",
|
||||||
name="password"
|
borderRadius: "4px",
|
||||||
control={control}
|
"& fieldset": {
|
||||||
defaultValue=""
|
borderColor:
|
||||||
rules={{
|
"#4b5255",
|
||||||
required: "Password is required",
|
},
|
||||||
minLength: {
|
"&:hover fieldset":
|
||||||
value: 6,
|
{
|
||||||
message: "Password must be at least 6 characters long.",
|
borderColor:
|
||||||
},
|
"#4b5255",
|
||||||
pattern: {
|
},
|
||||||
value:
|
"&.Mui-focused fieldset":
|
||||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
{
|
||||||
message:
|
borderColor:
|
||||||
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
"#4b5255",
|
||||||
},
|
},
|
||||||
}}
|
},
|
||||||
render={({ field }) => (
|
"& input": {
|
||||||
<TextField
|
color: "white",
|
||||||
{...field}
|
fontSize: {
|
||||||
error={!!errors.password}
|
xs: "0.9rem",
|
||||||
helperText={errors.password?.message}
|
sm: "1rem",
|
||||||
name="password"
|
},
|
||||||
placeholder="Password"
|
fontFamily:
|
||||||
type={showPassword ? "text" : "password"}
|
"Gilroy, sans-serif",
|
||||||
id="password"
|
},
|
||||||
autoComplete="current-password"
|
"& .MuiInputBase-input::placeholder":
|
||||||
required
|
{
|
||||||
fullWidth
|
color: "white",
|
||||||
variant="outlined"
|
opacity: 1,
|
||||||
color={errors.password ? "error" : "primary"}
|
fontFamily:
|
||||||
InputProps={{
|
"Gilroy, sans-serif",
|
||||||
sx: {
|
},
|
||||||
height: "50px",
|
}}
|
||||||
fontFamily: "Gilroy, sans-serif", // Apply Gilroy font
|
/>
|
||||||
},
|
)}
|
||||||
|
/>
|
||||||
endAdornment: (
|
</FormControl>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -304,50 +387,52 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value="remember"
|
value="remember"
|
||||||
sx={{
|
sx={{
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
fontFamily: "Gilroy, sans-serif",
|
fontFamily:
|
||||||
border: "2px solid #4b5255",
|
"Gilroy, sans-serif",
|
||||||
borderRadius: "4px",
|
border: "2px solid #4b5255",
|
||||||
backgroundColor: "transparent",
|
borderRadius: "4px",
|
||||||
"&:hover": {
|
backgroundColor:
|
||||||
backgroundColor: "transparent",
|
"transparent",
|
||||||
},
|
"&:hover": {
|
||||||
"&.Mui-checked": {
|
backgroundColor:
|
||||||
backgroundColor: "transparent",
|
"transparent",
|
||||||
borderColor: "#4b5255",
|
},
|
||||||
"&:hover": {
|
"&.Mui-checked": {
|
||||||
backgroundColor: "transparent",
|
backgroundColor:
|
||||||
},
|
"transparent",
|
||||||
},
|
borderColor: "#4b5255",
|
||||||
}}
|
"&:hover": {
|
||||||
/>
|
backgroundColor:
|
||||||
|
"transparent",
|
||||||
}
|
},
|
||||||
label="Remember me"
|
},
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
<Link
|
}
|
||||||
component="button"
|
label="Remember me"
|
||||||
type="button"
|
/>
|
||||||
onClick={handleClickOpen}
|
|
||||||
variant="body2"
|
|
||||||
sx={{
|
|
||||||
alignSelf: "center",
|
|
||||||
fontFamily: "Gilroy, sans-serif",
|
|
||||||
color: "#01579b",
|
|
||||||
textDecoration: "none", // ✅ Removes underline
|
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Forgot password?
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
<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>
|
</Box>
|
||||||
<ForgotPassword
|
<ForgotPassword
|
||||||
open={open}
|
open={open}
|
||||||
|
@ -359,7 +444,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
disabled={!isValid}
|
disabled={!isValid}
|
||||||
sx={{
|
sx={{
|
||||||
color: "white",
|
color: "white",
|
||||||
fontFamily: "Gilroy, sans-serif",
|
fontFamily: "Gilroy, sans-serif",
|
||||||
backgroundColor: "#52ACDF",
|
backgroundColor: "#52ACDF",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: "#52ACDF",
|
backgroundColor: "#52ACDF",
|
||||||
|
|
|
@ -9,10 +9,9 @@ import {
|
||||||
datePickersCustomizations,
|
datePickersCustomizations,
|
||||||
treeViewCustomizations,
|
treeViewCustomizations,
|
||||||
} from "./theme/customizations";
|
} from "./theme/customizations";
|
||||||
import DashboardLayout from "../../layouts/DashboardLayout";
|
|
||||||
import AppTheme from "../../shared-theme/AppTheme";
|
import AppTheme from "../../shared-theme/AppTheme";
|
||||||
import MainGrid from "../../components/MainGrid";
|
import MainGrid from "../../components/MainGrid";
|
||||||
import AdminList from "../AdminList";
|
|
||||||
|
|
||||||
const xThemeComponents = {
|
const xThemeComponents = {
|
||||||
...chartsCustomizations,
|
...chartsCustomizations,
|
||||||
|
|
|
@ -1,27 +1,18 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import { Box, Button, TextField, Typography } from "@mui/material";
|
||||||
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 CustomTable, { Column } from "../../components/CustomTable";
|
||||||
import AddManagerModal from "../../components/AddManagerModal";
|
import AddManagerModal from "../../components/AddManagerModal";
|
||||||
|
import EditManagerModal from "../../components/EditManagerModal";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||||
import {
|
import {
|
||||||
managerList,
|
managerList,
|
||||||
addManager,
|
addManager,
|
||||||
updateManager,
|
updateManager,
|
||||||
deleteManager,
|
deleteManager,
|
||||||
} from "../../redux/slices/managerSlice";
|
} from "../../redux/slices/managerSlice";
|
||||||
import { RootState, AppDispatch } from "../../redux/store/store";
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import EditManagerModal from "../../components/EditManagerModal";
|
|
||||||
|
|
||||||
export default function ManagerList() {
|
export default function ManagerList() {
|
||||||
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
|
@ -38,15 +29,18 @@ export default function ManagerList() {
|
||||||
(state: RootState) => state.managerReducer.managers
|
(state: RootState) => state.managerReducer.managers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Fetch manager list on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(managerList());
|
dispatch(managerList());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
// Open Add Manager Modal
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setRowData(null); // Reset row data when opening for new admin
|
setRowData(null); // Reset row data when opening for new manager
|
||||||
setAddModalOpen(true);
|
setAddModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Close all modals
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setAddModalOpen(false);
|
setAddModalOpen(false);
|
||||||
setEditModalOpen(false);
|
setEditModalOpen(false);
|
||||||
|
@ -54,13 +48,15 @@ export default function ManagerList() {
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle adding a new manager
|
||||||
const handleAddManager = async (data: {
|
const handleAddManager = async (data: {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
registeredAddress: string;
|
stationId: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
// Add manager with stationId
|
||||||
await dispatch(addManager(data)); // Dispatch action to add manager
|
await dispatch(addManager(data)); // Dispatch action to add manager
|
||||||
await dispatch(managerList()); // Fetch the updated list
|
await dispatch(managerList()); // Fetch the updated list
|
||||||
handleCloseModal(); // Close the modal
|
handleCloseModal(); // Close the modal
|
||||||
|
@ -69,66 +65,105 @@ export default function ManagerList() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle updating an existing manager
|
||||||
const handleUpdate = async (
|
const handleUpdate = async (
|
||||||
id: number,
|
id: number,
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
phone: string,
|
phone: string,
|
||||||
registeredAddress: string
|
stationId: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
// Creating the managerData object to match the expected payload structure
|
// Update manager with stationId
|
||||||
const managerData = {
|
await dispatch(
|
||||||
name,
|
updateManager({
|
||||||
email,
|
id,
|
||||||
phone,
|
name,
|
||||||
registeredAddress,
|
email,
|
||||||
};
|
phone,
|
||||||
|
stationId,
|
||||||
// Dispatching the updateManager action with the correct payload structure
|
})
|
||||||
await dispatch(updateManager({ id, managerData }));
|
);
|
||||||
await dispatch(managerList()); // Refresh the manager list after updating
|
await dispatch(managerList()); // Refresh the list after update
|
||||||
handleCloseModal(); // Close the modal after updating
|
handleCloseModal(); // Close modal after update
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Update failed", error);
|
console.error("Update failed", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Columns for the manager table
|
||||||
// Remove 'stationName' from columns
|
|
||||||
const categoryColumns: Column[] = [
|
const categoryColumns: Column[] = [
|
||||||
{ id: "srno", label: "Sr No" },
|
{ id: "srno", label: "Sr No" },
|
||||||
{ id: "name", label: "Name" },
|
{ id: "name", label: "Name" },
|
||||||
{ id: "email", label: "Email" },
|
{ id: "email", label: "Email" },
|
||||||
{ id: "phone", label: "Phone" },
|
{ id: "phone", label: "Phone" },
|
||||||
|
{ id: "stationName", label: "Station Name" },
|
||||||
{ id: "registeredAddress", label: "Station Location" },
|
{ id: "registeredAddress", label: "Station Location" },
|
||||||
|
|
||||||
{ id: "action", label: "Action", align: "center" },
|
{ id: "action", label: "Action", align: "center" },
|
||||||
];
|
];
|
||||||
|
// const categoryColumns: Column[] = [
|
||||||
|
// { id: "srno", label: "Sr No" },
|
||||||
|
// { id: "name", label: "Name" },
|
||||||
|
// { id: "email", label: "Email" },
|
||||||
|
// { id: "phone", label: "Phone" },
|
||||||
|
// { id: "stationName", label: "Station Name" }, // Added station name column
|
||||||
|
// { id: "stationAddress", label: "Station Location" }, // Added station address column
|
||||||
|
// { id: "action", label: "Action", align: "center" },
|
||||||
|
// ];
|
||||||
|
|
||||||
// Update rows to remove 'stationName'
|
|
||||||
const categoryRows = managers?.map(
|
// Filter managers based on search term
|
||||||
(
|
const filteredManagers = managers?.filter(
|
||||||
manager: {
|
(manager) =>
|
||||||
id: number;
|
manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
name: string;
|
manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
email: string;
|
manager.phone?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
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",
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Format rows to display manager details
|
||||||
|
// const categoryRows = filteredManagers?.length
|
||||||
|
// ? filteredManagers?.map(
|
||||||
|
// (
|
||||||
|
// manager: {
|
||||||
|
// id: number;
|
||||||
|
// name: string;
|
||||||
|
// email: string;
|
||||||
|
// phone: string;
|
||||||
|
|
||||||
|
|
||||||
|
// },
|
||||||
|
// index: number
|
||||||
|
// ) => ({
|
||||||
|
// id: manager?.id,
|
||||||
|
// srno: index + 1,
|
||||||
|
// name: manager?.name,
|
||||||
|
// email: manager?.email,
|
||||||
|
// phone: manager.phone ?? "NA",
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// : [];
|
||||||
|
|
||||||
|
const categoryRows = filteredManagers?.length
|
||||||
|
? filteredManagers.map((manager, index) => {
|
||||||
|
const station = manager?.ChargingStation; // Correct access to the ChargingStation data
|
||||||
|
return {
|
||||||
|
id: manager.id,
|
||||||
|
srno: index + 1,
|
||||||
|
name: manager.name,
|
||||||
|
email: manager.email,
|
||||||
|
phone: manager.phone ?? "NA",
|
||||||
|
stationName: station?.name ?? "NA", // Corrected station name
|
||||||
|
registeredAddress: station?.registeredAddress ?? "NA", // Corrected station address
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* Custom Table to show manager list */}
|
||||||
<CustomTable
|
<CustomTable
|
||||||
columns={categoryColumns}
|
columns={categoryColumns}
|
||||||
rows={categoryRows}
|
rows={categoryRows}
|
||||||
|
@ -141,11 +176,13 @@ export default function ManagerList() {
|
||||||
tableType="manager"
|
tableType="manager"
|
||||||
handleClickOpen={handleClickOpen}
|
handleClickOpen={handleClickOpen}
|
||||||
/>
|
/>
|
||||||
|
{/* Add Manager Modal */}
|
||||||
<AddManagerModal
|
<AddManagerModal
|
||||||
open={addModalOpen}
|
open={addModalOpen}
|
||||||
handleClose={handleCloseModal}
|
handleClose={handleCloseModal}
|
||||||
handleAddManager={handleAddManager}
|
handleAddManager={handleAddManager}
|
||||||
/>
|
/>
|
||||||
|
{/* Edit Manager Modal */}
|
||||||
<EditManagerModal
|
<EditManagerModal
|
||||||
open={editModalOpen}
|
open={editModalOpen}
|
||||||
handleClose={handleCloseModal}
|
handleClose={handleCloseModal}
|
||||||
|
|
200
src/pages/StationList/index.tsx
Normal file
200
src/pages/StationList/index.tsx
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
|
import { RootState } from "../../redux/reducers";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { AppDispatch } from "../../redux/store/store";
|
||||||
|
import AddStationModal from "../../components/AddStationModal";
|
||||||
|
import EditStationModal from "../../components/EditStationModal";
|
||||||
|
import {
|
||||||
|
createStation,
|
||||||
|
stationList,
|
||||||
|
toggleStatus,
|
||||||
|
updateStation,
|
||||||
|
} from "../../redux/slices/stationSlice";
|
||||||
|
import { Chip, Switch } from "@mui/material";
|
||||||
|
|
||||||
|
export default function StationList() {
|
||||||
|
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 stations = useSelector(
|
||||||
|
(state: RootState) => state.stationReducer.stations
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(stationList());
|
||||||
|
}, [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 handleAddStation = async (data: {
|
||||||
|
name: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
totalSlots: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await dispatch(createStation(data)); // Dispatch action to add Station
|
||||||
|
await dispatch(stationList()); // Fetch the updated list
|
||||||
|
handleCloseModal(); // Close the modal
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding Station", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = async (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
registeredAddress: string,
|
||||||
|
totalSlots: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await dispatch(
|
||||||
|
updateStation({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
registeredAddress,
|
||||||
|
totalSlots,
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await dispatch(stationList());
|
||||||
|
handleCloseModal();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Update failed", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStatusToggle = async(id: string, newStatus: number) => {
|
||||||
|
await dispatch(toggleStatus({ id, status: newStatus }));
|
||||||
|
|
||||||
|
};
|
||||||
|
// Toggle station status
|
||||||
|
// const handleStatusToggle = async (id: string, currentStatus: number) => {
|
||||||
|
// try {
|
||||||
|
// const newStatus = currentStatus === 1 ? 0 : 1; // Toggle between Active(1) and Inactive(0)
|
||||||
|
// await dispatch(
|
||||||
|
// updateStation({
|
||||||
|
// id,
|
||||||
|
// status: newStatus,
|
||||||
|
// name: stations.name,
|
||||||
|
// registeredAddress: stations.registeredAddress,
|
||||||
|
// totalSlots: stations.totalSlots,
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// await dispatch(stationList());
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error toggling status", error);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
const filterStations = stations?.filter((station) =>
|
||||||
|
station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
const categoryRows = filterStations?.length
|
||||||
|
? filterStations?.map((station: Station, index: number) => ({
|
||||||
|
id: station.id,
|
||||||
|
srno: index + 1,
|
||||||
|
name: station.name,
|
||||||
|
registeredAddress:station.registeredAddress,
|
||||||
|
totalSlots:station.totalSlots,
|
||||||
|
status: (
|
||||||
|
<Chip
|
||||||
|
label={
|
||||||
|
station.status === 1 ? "Available" : "Not Available"
|
||||||
|
}
|
||||||
|
color={station.status === 1 ? "primary" : "default"}
|
||||||
|
variant="outlined"
|
||||||
|
sx={{
|
||||||
|
fontWeight: 600,
|
||||||
|
width: "80px",
|
||||||
|
textAlign: "center",
|
||||||
|
borderRadius: "4px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
statusValue: station.status,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const categoryColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "id", label: "Station ID" },
|
||||||
|
{ id: "name", label: "Station Name" },
|
||||||
|
{ id: "registeredAddress", label: "Station Location" },
|
||||||
|
{ id: "totalSlots", label: "Total Slots" },
|
||||||
|
//{ id: "status", label: "Status" },
|
||||||
|
{ id: "action", label: "Action", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Filter stations based on search term
|
||||||
|
// const filterStations = stations?.filter((station) =>
|
||||||
|
// station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Prepare categoryRows with toggle switch for status
|
||||||
|
// const categoryRows = filterStations?.length
|
||||||
|
// ? filterStations?.map((station: any, index: number) => ({
|
||||||
|
// id: station.id,
|
||||||
|
// srno: index + 1,
|
||||||
|
// name: station.name,
|
||||||
|
// status: (
|
||||||
|
// <Switch
|
||||||
|
// checked={station.status === 1}
|
||||||
|
// onChange={() =>
|
||||||
|
// handleStatusToggle(station.id, station.status)
|
||||||
|
// }
|
||||||
|
// color="primary"
|
||||||
|
// inputProps={{ "aria-label": "station-status-toggle" }}
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
|
// statusValue: station.status,
|
||||||
|
// }))
|
||||||
|
// : [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CustomTable
|
||||||
|
columns={categoryColumns}
|
||||||
|
rows={categoryRows}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
|
deleteModal={deleteModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
viewModal={viewModal}
|
||||||
|
setRowData={setRowData}
|
||||||
|
setModalOpen={() => setEditModalOpen(true)}
|
||||||
|
handleStatusToggle={handleStatusToggle}
|
||||||
|
tableType="station"
|
||||||
|
handleClickOpen={handleClickOpen}
|
||||||
|
/>
|
||||||
|
<AddStationModal
|
||||||
|
open={addModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleAddStation={handleAddStation}
|
||||||
|
/>
|
||||||
|
<EditStationModal
|
||||||
|
open={editModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleUpdate={handleUpdate}
|
||||||
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import { createUser, updateUser, userList } from "../../redux/slices/userSlice";
|
||||||
import { AppDispatch, RootState } from "../../redux/store/store";
|
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||||
import { string } from "prop-types";
|
import { string } from "prop-types";
|
||||||
import { adminList, updateAdmin } from "../../redux/slices/adminSlice";
|
import { adminList, updateAdmin } from "../../redux/slices/adminSlice";
|
||||||
import AddUserModal from "../../components/AddUserModel";
|
import AddUserModal from "../../components/AddEditUserModel";
|
||||||
|
|
||||||
export default function UserList() {
|
export default function UserList() {
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import React, { useEffect, useState } from "react";
|
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 { useForm } from "react-hook-form";
|
||||||
import CustomTable, { Column } from "../../components/CustomTable";
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
|
||||||
import { RootState } from "../../redux/reducers";
|
import { RootState } from "../../redux/reducers";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { AppDispatch } from "../../redux/store/store";
|
import { AppDispatch } from "../../redux/store/store";
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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";
|
import managerReducer from "../redux/slices/managerSlice.ts";
|
||||||
|
import stationReducer from "../redux/slices/stationSlice.ts";
|
||||||
|
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
|
@ -17,6 +18,7 @@ const rootReducer = combineReducers({
|
||||||
roleReducer,
|
roleReducer,
|
||||||
vehicleReducer,
|
vehicleReducer,
|
||||||
managerReducer,
|
managerReducer,
|
||||||
|
stationReducer
|
||||||
// Add other reducers here...
|
// Add other reducers here...
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const updateVehicle = createAsyncThunk(
|
||||||
async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => {
|
async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.patch(
|
const response = await http.patch(
|
||||||
`${id}/update-vehicle`,
|
`/update-vehicle/${id}`,
|
||||||
vehicleData
|
vehicleData
|
||||||
);
|
);
|
||||||
toast.success("Vehicle Deatils updated successfully");
|
toast.success("Vehicle Deatils updated successfully");
|
||||||
|
@ -90,7 +90,7 @@ export const deleteVehicle = createAsyncThunk<
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("deleteVehicle", async (id, { rejectWithValue }) => {
|
>("deleteVehicle", async (id, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.delete(`/${id}/delete-vehicle`);
|
const response = await http.delete(`/delete-vehicle/${id}`);
|
||||||
toast.success(response.data?.message);
|
toast.success(response.data?.message);
|
||||||
return id;
|
return id;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const deleteAdmin = createAsyncThunk<
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("deleteAdmin", async (id, { rejectWithValue }) => {
|
>("deleteAdmin", async (id, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.delete(`/${id}/delete-admin`);
|
const response = await http.delete(`/delete-admin/${id}`);
|
||||||
toast.success(response.data?.message);
|
toast.success(response.data?.message);
|
||||||
return id;
|
return id;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -92,7 +92,7 @@ export const updateAdmin = createAsyncThunk(
|
||||||
"updateAdmin",
|
"updateAdmin",
|
||||||
async ({ id, ...userData }: User, { rejectWithValue }) => {
|
async ({ id, ...userData }: User, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.put(`/${id}/update-admin`, userData);
|
const response = await http.put(`/update-admin/${id}`, userData);
|
||||||
toast.success("Admin updated successfully");
|
toast.success("Admin updated successfully");
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
@ -5,12 +5,12 @@ import { toast } from "sonner";
|
||||||
// Define the Manager interface based on the payload
|
// Define the Manager interface based on the payload
|
||||||
|
|
||||||
interface Manager {
|
interface Manager {
|
||||||
|
Manager: any;
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
registeredAddress: string;
|
stationId: string;
|
||||||
roleId: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ManagerState {
|
interface ManagerState {
|
||||||
|
@ -33,7 +33,10 @@ export const managerList = createAsyncThunk<
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("fetchManagers", async (_, { rejectWithValue }) => {
|
>("fetchManagers", async (_, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.get("manager-list");
|
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");
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -47,7 +50,12 @@ export const managerList = createAsyncThunk<
|
||||||
// Create Manager (Async Thunk)
|
// Create Manager (Async Thunk)
|
||||||
export const addManager = createAsyncThunk<
|
export const addManager = createAsyncThunk<
|
||||||
Manager,
|
Manager,
|
||||||
Manager,
|
{
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
stationId: string;
|
||||||
|
},
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("addManager", async (data, { rejectWithValue }) => {
|
>("addManager", async (data, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
|
@ -72,7 +80,7 @@ export const updateManager = createAsyncThunk<
|
||||||
return rejectWithValue("Manager ID is required.");
|
return rejectWithValue("Manager ID is required.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await http.put(`/${id}/update-manager`, managerData);
|
const response = await http.put(`/update-manager/${id}`, managerData);
|
||||||
toast.success("Manager updated successfully");
|
toast.success("Manager updated successfully");
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -90,7 +98,7 @@ export const deleteManager = createAsyncThunk<
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("deleteManager", async (id, { rejectWithValue }) => {
|
>("deleteManager", async (id, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
await http.delete(`/${id}/delete-manager`);
|
await http.delete(`/delete-manager/${id}`);
|
||||||
toast.success("Manager deleted successfully!");
|
toast.success("Manager deleted successfully!");
|
||||||
return id;
|
return id;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -103,7 +111,7 @@ export const deleteManager = createAsyncThunk<
|
||||||
|
|
||||||
// Create Slice
|
// Create Slice
|
||||||
const managerSlice = createSlice({
|
const managerSlice = createSlice({
|
||||||
name: "maanger",
|
name: "manager",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {},
|
reducers: {},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
|
@ -147,13 +155,13 @@ const managerSlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(updateManager.fulfilled, (state, action) => {
|
.addCase(updateManager.fulfilled, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
// const updatedManager = action.payload;
|
const updatedManager = action.payload;
|
||||||
// const index = state.managers.findIndex(
|
const index = state.managers.findIndex(
|
||||||
// (manager) => manager.id === updatedManager.id
|
(manager) => manager.id === updatedManager.id
|
||||||
// );
|
);
|
||||||
// if (index !== -1) {
|
if (index !== -1) {
|
||||||
// state.managers[index] = updatedManager; // Update the manager in the state
|
state.managers[index] = updatedManager; // Update the manager in the state
|
||||||
// }
|
}
|
||||||
})
|
})
|
||||||
.addCase(updateManager.rejected, (state, action) => {
|
.addCase(updateManager.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
@ -166,9 +174,6 @@ const managerSlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(deleteManager.fulfilled, (state, action) => {
|
.addCase(deleteManager.fulfilled, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.managers = state.managers.filter(
|
|
||||||
(manager) => manager.id !== action.payload
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.addCase(deleteManager.rejected, (state, action) => {
|
.addCase(deleteManager.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
|
246
src/redux/slices/stationSlice.ts
Normal file
246
src/redux/slices/stationSlice.ts
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import axios from "axios";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
// Define TypeScript types
|
||||||
|
interface Station {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
totalSlots: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StationState {
|
||||||
|
stations: Station[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
const initialState: StationState = {
|
||||||
|
stations: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stationList = createAsyncThunk<any, void, { rejectValue: string }>(
|
||||||
|
"fetchStations",
|
||||||
|
async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
|
const response = await http.get("/get-stations");
|
||||||
|
|
||||||
|
if (!response.data) throw new Error("Invalid API response");
|
||||||
|
|
||||||
|
// Return the full response to handle in the reducer
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Stations: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create Station
|
||||||
|
export const createStation = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
totalSlots: string;
|
||||||
|
},
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("Station/createStation", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.post("/create-station", data);
|
||||||
|
toast.success("Station created successfully");
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(
|
||||||
|
"Failed to create Station: " +
|
||||||
|
(error.response?.data?.message || "Unknown error")
|
||||||
|
);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update Station details
|
||||||
|
export const updateStation = createAsyncThunk(
|
||||||
|
"updateStation",
|
||||||
|
async ({ id, ...stationData }: Station, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.patch(
|
||||||
|
`/update-station/${id}`,
|
||||||
|
stationData
|
||||||
|
);
|
||||||
|
toast.success("Station Deatils updated successfully");
|
||||||
|
return response?.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error updating the user: " + error);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export const deleteStation = createAsyncThunk<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("deleteStation", async (id, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
await http.delete(`/delete-station/${id}`);
|
||||||
|
toast.success("Station deleted successfully!");
|
||||||
|
return id;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error deleting the Station" + error);
|
||||||
|
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const toggleStatus = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
{ id: string; status: number },
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("station/toggleStatus", async ({ id, status }, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.patch(`/update-station/${id}`, { status });
|
||||||
|
|
||||||
|
if (response.data.statusCode === 200) {
|
||||||
|
toast.success(
|
||||||
|
response.data.message || "Status updated successfully"
|
||||||
|
);
|
||||||
|
// Return both the response data and the requested status for reliable state updates
|
||||||
|
return {
|
||||||
|
responseData: response.data,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data.message || "Failed to update status");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error(
|
||||||
|
"Error updating status: " + (error.message || "Unknown error")
|
||||||
|
);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message ||
|
||||||
|
error.message ||
|
||||||
|
"An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const stationSlice = createSlice({
|
||||||
|
name: "stations",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(stationList.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
stationList.fulfilled,
|
||||||
|
(state, action: PayloadAction<any>) => {
|
||||||
|
state.loading = false;
|
||||||
|
// Properly extract stations from the response data structure
|
||||||
|
state.stations =
|
||||||
|
action.payload.data?.results ||
|
||||||
|
action.payload.data ||
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(stationList.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to fetch stations";
|
||||||
|
})
|
||||||
|
.addCase(createStation.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
createStation.fulfilled,
|
||||||
|
(state, action: PayloadAction<any>) => {
|
||||||
|
state.loading = false;
|
||||||
|
// Add the newly created station to the state if it exists in the response
|
||||||
|
if (action.payload.data) {
|
||||||
|
state.stations.push(action.payload.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
createStation.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to create station";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(toggleStatus.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
toggleStatus.fulfilled,
|
||||||
|
(state, action: PayloadAction<any>) => {
|
||||||
|
state.loading = false;
|
||||||
|
|
||||||
|
// Get the id and updated status from the action payload
|
||||||
|
const { id, status } = action.payload;
|
||||||
|
|
||||||
|
// Find and update the station with the new status
|
||||||
|
const stationIndex = state.stations.findIndex(
|
||||||
|
(station) => station.id === id
|
||||||
|
);
|
||||||
|
if (stationIndex !== -1) {
|
||||||
|
state.stations[stationIndex] = {
|
||||||
|
...state.stations[stationIndex],
|
||||||
|
status: status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
toggleStatus.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error =
|
||||||
|
action.payload || "Failed to toggle station status";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(updateStation.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(updateStation.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload;
|
||||||
|
})
|
||||||
|
.addCase(updateStation.rejected, (state) => {
|
||||||
|
state.loading = false;
|
||||||
|
})
|
||||||
|
.addCase(deleteStation.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(deleteStation.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.stations = state.stations.filter(
|
||||||
|
(station) => String(station.id) !== String(action.payload)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.addCase(deleteStation.rejected, (state) => {
|
||||||
|
state.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default stationSlice.reducer;
|
|
@ -56,12 +56,11 @@ export const createUser = createAsyncThunk<
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
||||||
},
|
},
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("/CreateUser", async (data, { rejectWithValue }) => {
|
>("/CreateUser", async (data, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.post("create-user", data);
|
const response = await http.post("/create-user", data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return rejectWithValue(
|
return rejectWithValue(
|
||||||
|
@ -70,13 +69,11 @@ export const createUser = createAsyncThunk<
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const updateUser = createAsyncThunk(
|
export const updateUser = createAsyncThunk(
|
||||||
"updateUser",
|
"updateUser",
|
||||||
async ({ id, ...userData }: User, { rejectWithValue }) => {
|
async ({ id, ...userData }: User, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.put(`/${id}/update-user`, userData);
|
const response = await http.put(`/update-user/${id}`, userData);
|
||||||
toast.success("User updated successfully");
|
toast.success("User updated successfully");
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -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(`/delete-user/${id}`);
|
||||||
|
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({
|
const userSlice = createSlice({
|
||||||
name: "fetchUsers",
|
name: "fetchUsers",
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -133,6 +148,18 @@ const userSlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(updateUser.rejected, (state) => {
|
.addCase(updateUser.rejected, (state) => {
|
||||||
state.loading = false;
|
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,32 @@ import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom";
|
||||||
import React, { lazy, Suspense } from "react";
|
import React, { lazy, Suspense } from "react";
|
||||||
import LoadingComponent from "./components/Loading";
|
import LoadingComponent from "./components/Loading";
|
||||||
import DashboardLayout from "./layouts/DashboardLayout";
|
import DashboardLayout from "./layouts/DashboardLayout";
|
||||||
import RoleList from "./pages/RoleList";
|
// import RoleList from "./pages/RoleList";
|
||||||
import AddEditRolePage from "./pages/AddEditRolePage";
|
// import AddEditRolePage from "./pages/AddEditRolePage";
|
||||||
import VehicleList from "./pages/VehicleList";
|
// import VehicleList from "./pages/VehicleList";
|
||||||
|
|
||||||
// Page imports
|
// Page imports
|
||||||
const Login = lazy(() => import("./pages/Auth/Login"));
|
const Login = lazy(() => import("./pages/Auth/Login"));
|
||||||
const SignUp = lazy(() => import("./pages/Auth/SignUp"));
|
const SignUp = lazy(() => import("./pages/Auth/SignUp"));
|
||||||
const Dashboard = lazy(() => import("./pages/Dashboard"));
|
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 AdminList = lazy(() => import("./pages/AdminList"));
|
||||||
const ProfilePage = lazy(() => import("./pages/ProfilePage"));
|
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 AddEditRolePage = lazy(() => import("./pages/AddEditRolePage"));
|
||||||
|
const RoleList = lazy(() => import("./pages/RoleList"));
|
||||||
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
||||||
|
const StationList = lazy(() => import("./pages/StationList"));
|
||||||
|
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
caps: string[];
|
// caps: string[];
|
||||||
component: React.ReactNode;
|
component: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protected Route Component
|
// Protected Route Component
|
||||||
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ caps, component }) => {
|
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ component }) => {
|
||||||
if (!localStorage.getItem("authToken")) {
|
if (!localStorage.getItem("authToken")) {
|
||||||
return <Navigate to="/login" replace />;
|
return <Navigate to="/login" replace />;
|
||||||
}
|
}
|
||||||
|
@ -42,11 +44,6 @@ export default function AppRouter() {
|
||||||
|
|
||||||
{/* Auth Routes */}
|
{/* Auth Routes */}
|
||||||
<Route path="">
|
<Route path="">
|
||||||
<Route
|
|
||||||
path=""
|
|
||||||
element={<Navigate to="/login" replace />}
|
|
||||||
index
|
|
||||||
/>
|
|
||||||
<Route path="login" element={<Login />} />
|
<Route path="login" element={<Login />} />
|
||||||
<Route path="signup" element={<SignUp />} />
|
<Route path="signup" element={<SignUp />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -55,87 +52,44 @@ export default function AppRouter() {
|
||||||
<Route path="/panel" element={<DashboardLayout />}>
|
<Route path="/panel" element={<DashboardLayout />}>
|
||||||
<Route
|
<Route
|
||||||
path="dashboard"
|
path="dashboard"
|
||||||
element={
|
element={<ProtectedRoute component={<Dashboard />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<Dashboard />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="vehicles"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<Vehicles />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="admin-list"
|
path="admin-list"
|
||||||
element={
|
element={<ProtectedRoute component={<AdminList />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<AdminList />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="user-list"
|
path="user-list"
|
||||||
element={
|
element={<ProtectedRoute component={<UserList />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<UserList />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
|
||||||
path="manager-list"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<ManagerList />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="manager-list"
|
||||||
|
element={<ProtectedRoute component={<ManagerList />} />}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="role-list"
|
path="role-list"
|
||||||
element={
|
element={<ProtectedRoute component={<RoleList />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<RoleList />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="vehicle-list"
|
path="vehicle-list"
|
||||||
element={
|
element={<ProtectedRoute component={<VehicleList />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<VehicleList />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="permissions"
|
path="permissions"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute
|
<ProtectedRoute component={<AddEditRolePage />} />
|
||||||
caps={[]}
|
|
||||||
component={<AddEditRolePage />}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="station-list"
|
||||||
|
element={<ProtectedRoute component={<StationList />} />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="profile"
|
path="profile"
|
||||||
element={
|
element={<ProtectedRoute component={<ProfilePage />} />}
|
||||||
<ProtectedRoute
|
|
||||||
caps={[]}
|
|
||||||
component={<ProfilePage />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue