Merge pull request 'Vehicle & Managers API integeration' (#21) from frontend/apiIntegration into develop
Reviewed-on: DigiMantra/digiev_frontend#21
This commit is contained in:
commit
7793c4b031
|
@ -1,17 +1,20 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Typography,
|
||||||
DialogActions,
|
Modal,
|
||||||
DialogContent,
|
InputAdornment,
|
||||||
DialogTitle,
|
|
||||||
IconButton,
|
|
||||||
TextField,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import Visibility from "@mui/icons-material/Visibility";
|
||||||
|
import VisibilityOff from "@mui/icons-material/VisibilityOff";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
|
||||||
//By Jaanvi : Edit Model :: 11-feb-25
|
//By Jaanvi : Edit Model :: 11-feb-25
|
||||||
interface AddEditCategoryModalProps {
|
interface AddEditCategoryModalProps {
|
||||||
|
@ -44,6 +47,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
editRow,
|
editRow,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
@ -59,7 +63,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
password: "",
|
password: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (data: FormData) => {
|
const onSubmit = (data: FormData) => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
handleUpdate(
|
handleUpdate(
|
||||||
|
@ -77,6 +81,10 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
setValue("name", editRow.name);
|
setValue("name", editRow.name);
|
||||||
|
@ -88,210 +96,327 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||||
}
|
}
|
||||||
}, [editRow, setValue, reset]);
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
const [showPassword, setShowPassword] = React.useState(false);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
maxWidth="md"
|
aria-labelledby="add-edit-category-modal"
|
||||||
fullWidth
|
|
||||||
PaperProps={{
|
|
||||||
component: "form",
|
|
||||||
onSubmit: handleSubmit(onSubmit),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<DialogTitle
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
position: "absolute",
|
||||||
alignItems: "center",
|
top: "50%",
|
||||||
justifyContent: "space-between",
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 600,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{editRow ? "Edit Admin" : "Add Admin"}
|
{/* Header */}
|
||||||
<Box
|
<Box
|
||||||
onClick={handleClose}
|
|
||||||
sx={{
|
sx={{
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{editRow ? "Edit Admin" : "Add Admin"}
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
{/* Horizontal Line */}
|
||||||
<Controller
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
name="name"
|
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
required: "Admin Name is required",
|
|
||||||
minLength: {
|
|
||||||
value: 3,
|
|
||||||
message: "Minimum 3 characters required",
|
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 30,
|
|
||||||
message: "Maximum 30 characters allowed",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...field}
|
|
||||||
autoFocus
|
|
||||||
required
|
|
||||||
margin="dense"
|
|
||||||
label="Admin Name"
|
|
||||||
type="text"
|
|
||||||
fullWidth
|
|
||||||
variant="standard"
|
|
||||||
error={!!errors.name}
|
|
||||||
helperText={errors.name?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
{/* Form */}
|
||||||
name="email"
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
control={control}
|
{/* Input Fields */}
|
||||||
rules={{
|
<Box
|
||||||
required: "Email is required",
|
sx={{
|
||||||
pattern: {
|
display: "flex",
|
||||||
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
flexDirection: "column",
|
||||||
message: "Invalid email address",
|
gap: 2,
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...field}
|
|
||||||
required
|
|
||||||
margin="dense"
|
|
||||||
label="Email"
|
|
||||||
type="email"
|
|
||||||
fullWidth
|
|
||||||
variant="standard"
|
|
||||||
error={!!errors.email}
|
|
||||||
helperText={errors.email?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{!editRow && (
|
|
||||||
<Controller
|
|
||||||
name="password"
|
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
required: "password is required",
|
|
||||||
}}
|
}}
|
||||||
render={({ field }) => (
|
>
|
||||||
<>
|
{/* First Row - Two Inputs */}
|
||||||
<Box sx={{position:"relative" }}>
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
<TextField
|
<Box
|
||||||
{...field}
|
sx={{
|
||||||
required
|
display: "flex",
|
||||||
margin="dense"
|
flexDirection: "column",
|
||||||
label="Password"
|
width: "50%",
|
||||||
type={showPassword ? "text" : "password"}
|
}}
|
||||||
id="password"
|
>
|
||||||
autoComplete="current-password"
|
<Typography variant="body2" fontWeight={500}>
|
||||||
autoFocus
|
Admin Name
|
||||||
fullWidth
|
</Typography>
|
||||||
variant="standard"
|
<Controller
|
||||||
error={!!errors.password}
|
name="name"
|
||||||
helperText={errors.password?.message}
|
control={control}
|
||||||
/>
|
rules={{
|
||||||
<IconButton
|
required: "Admin Name is required",
|
||||||
sx={{
|
minLength: {
|
||||||
position: "absolute",
|
value: 3,
|
||||||
top: "60%",
|
message:
|
||||||
right: "10px",
|
"Minimum 3 characters required",
|
||||||
background: "none",
|
},
|
||||||
borderColor: "transparent",
|
maxLength: {
|
||||||
transform: "translateY(-50%)",
|
value: 30,
|
||||||
"&:hover": {
|
message:
|
||||||
backgroundColor: "transparent",
|
"Maximum 30 characters allowed",
|
||||||
borderColor: "transparent",
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
render={({ field }) => (
|
||||||
setShowPassword((prev) => !prev)
|
<CustomTextField
|
||||||
}
|
{...field}
|
||||||
>
|
required
|
||||||
{showPassword ? (
|
placeholder="Enter Admin Name"
|
||||||
<VisibilityOff />
|
fullWidth
|
||||||
) : (
|
size="small"
|
||||||
<Visibility />
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Email is required",
|
||||||
|
pattern: {
|
||||||
|
value: /\S+@\S+\.\S+/,
|
||||||
|
message:
|
||||||
|
"Please enter a valid email address.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Email"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.email
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Two Inputs */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{!editRow && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight={500}
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Password is required",
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message:
|
||||||
|
"Password must be at least 6 characters long.",
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
|
message:
|
||||||
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Password"
|
||||||
|
type={
|
||||||
|
showPassword
|
||||||
|
? "text"
|
||||||
|
: "password"
|
||||||
|
}
|
||||||
|
id="password"
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.password
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
size="small"
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<CustomIconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={
|
||||||
|
togglePasswordVisibility
|
||||||
|
}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<VisibilityOff />
|
||||||
|
) : (
|
||||||
|
<Visibility />
|
||||||
|
)}
|
||||||
|
</CustomIconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={
|
||||||
|
errors.password?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
)}
|
||||||
)}
|
<Box
|
||||||
/>
|
sx={{
|
||||||
)}
|
display: "flex",
|
||||||
<Controller
|
flexDirection: "column",
|
||||||
name="phone"
|
width: "50%",
|
||||||
control={control}
|
}}
|
||||||
rules={{
|
>
|
||||||
required: "Phone number is required",
|
<Typography variant="body2" fontWeight={500}>
|
||||||
pattern: {
|
Phone Number
|
||||||
value: /^[0-9]*$/,
|
</Typography>
|
||||||
message: "Only numbers are allowed",
|
<Controller
|
||||||
},
|
name="phone"
|
||||||
minLength: {
|
control={control}
|
||||||
value: 6,
|
rules={{
|
||||||
message: "Phone number must be exactly 6 digits",
|
required: "Phone number is required",
|
||||||
},
|
pattern: {
|
||||||
maxLength: {
|
value: /^[0-9]*$/,
|
||||||
value: 14,
|
message: "Only numbers are allowed",
|
||||||
message: "Phone number must be exactly 14 digits",
|
},
|
||||||
},
|
minLength: {
|
||||||
}}
|
value: 6,
|
||||||
render={({ field }) => (
|
message:
|
||||||
<TextField
|
"Phone number must be at least 6 digits",
|
||||||
{...field}
|
},
|
||||||
required
|
maxLength: {
|
||||||
margin="dense"
|
value: 14,
|
||||||
label="Phone Number"
|
message:
|
||||||
type="tel"
|
"Phone number must be at most 14 digits",
|
||||||
fullWidth
|
},
|
||||||
variant="standard"
|
}}
|
||||||
error={!!errors.phone}
|
render={({ field }) => (
|
||||||
helperText={errors.phone?.message}
|
<CustomTextField
|
||||||
/>
|
{...field}
|
||||||
)}
|
required
|
||||||
/>
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Controller
|
{/* Third Row - One Input */}
|
||||||
name="registeredAddress"
|
<Box
|
||||||
control={control}
|
sx={{
|
||||||
rules={{
|
display: "flex",
|
||||||
required: "Address is required",
|
flexDirection: "column",
|
||||||
maxLength: {
|
width: "50%",
|
||||||
value: 100,
|
}}
|
||||||
message: "Address cannot exceed 100 characters",
|
>
|
||||||
},
|
<Typography variant="body2" fontWeight={500}>
|
||||||
}}
|
Address
|
||||||
render={({ field }) => (
|
</Typography>
|
||||||
<TextField
|
<Controller
|
||||||
{...field}
|
name="registeredAddress"
|
||||||
required
|
control={control}
|
||||||
margin="dense"
|
rules={{
|
||||||
label="Address"
|
required: "Address is required",
|
||||||
type="text"
|
maxLength: {
|
||||||
fullWidth
|
value: 100,
|
||||||
variant="standard"
|
message:
|
||||||
error={!!errors.registeredAddress}
|
"Address cannot exceed 100 characters",
|
||||||
helperText={errors.registeredAddress?.message}
|
},
|
||||||
/>
|
}}
|
||||||
)}
|
render={({ field }) => (
|
||||||
/>
|
<CustomTextField
|
||||||
</DialogContent>
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Address"
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<DialogActions>
|
{/* Submit Button */}
|
||||||
<Button onClick={handleClose}>Cancel</Button>
|
<Box
|
||||||
<Button type="submit">{editRow ? "Update" : "Create"}</Button>
|
sx={{
|
||||||
</DialogActions>
|
display: "flex",
|
||||||
</Dialog>
|
justifyContent: "flex-end",
|
||||||
|
width: "100%",
|
||||||
|
mt: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{editRow ? "Update Admin" : "Add Admin"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
321
src/components/AddManagerModal/index.tsx
Normal file
321
src/components/AddManagerModal/index.tsx
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { Box, Button, Typography, Modal, IconButton, InputAdornment } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { addManager } from "../../redux/slices/managerSlice";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AddManagerModal({ open, handleClose }: Props) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm();
|
||||||
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
|
|
||||||
|
const onSubmit = async (data: any) => {
|
||||||
|
const managerData = {
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
phone: data.phone,
|
||||||
|
registeredAddress: data.registeredAddress,
|
||||||
|
roleId: data.role,
|
||||||
|
password: data.password,
|
||||||
|
roleName: "Manager", // Add a role name (you can replace this with a dynamic value if needed)
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dispatch the addManager action
|
||||||
|
await dispatch(addManager(managerData)).unwrap();
|
||||||
|
handleClose(); // Close modal after successful addition
|
||||||
|
reset(); // Reset form fields
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding manager:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle password visibility
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="add-manager-modal"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
Add Manager
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
{/* First Row - Manager Name */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Manager Name */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Manager Name
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={
|
||||||
|
errors.name ? errors.name.message : ""
|
||||||
|
}
|
||||||
|
{...register("name", {
|
||||||
|
required: "Manager Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message:
|
||||||
|
"Manager Name must be at least 2 characters long",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Second Row - Email, Password */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Email */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Email"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={
|
||||||
|
errors.email ? errors.email.message : ""
|
||||||
|
}
|
||||||
|
{...register("email", {
|
||||||
|
required: "Email is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||||
|
message: "Invalid email address",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Password
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Password is required",
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message:
|
||||||
|
"Password must be at least 6 characters long.",
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
|
message:
|
||||||
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Password"
|
||||||
|
type={
|
||||||
|
showPassword
|
||||||
|
? "text"
|
||||||
|
: "password"
|
||||||
|
}
|
||||||
|
id="password"
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.password
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
size="small"
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<CustomIconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={
|
||||||
|
togglePasswordVisibility
|
||||||
|
}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<VisibilityOff />
|
||||||
|
) : (
|
||||||
|
<Visibility />
|
||||||
|
)}
|
||||||
|
</CustomIconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={
|
||||||
|
errors.password?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Third Row - Phone Number, Registered Address */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
{/* Phone Number */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Phone Number
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={
|
||||||
|
errors.phone ? errors.phone.message : ""
|
||||||
|
}
|
||||||
|
{...register("phone", {
|
||||||
|
required: "Phone Number is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[0-9]{10}$/,
|
||||||
|
message:
|
||||||
|
"Phone number must be 10 digits",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Registered Address */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "50%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Registered Address
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Registered Address"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress
|
||||||
|
? errors.registeredAddress.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("registeredAddress", {
|
||||||
|
required: "Registered Address is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
mt: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Manager
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,17 +1,27 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Typography,
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
TextField,
|
TextField,
|
||||||
|
Modal,
|
||||||
|
IconButton,
|
||||||
|
InputAdornment,
|
||||||
|
styled,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import Visibility from "@mui/icons-material/Visibility";
|
||||||
|
import VisibilityOff from "@mui/icons-material/VisibilityOff";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { CustomIconButton, CustomTextField } from "./styled.css.tsx";
|
||||||
|
|
||||||
//By Jaanvi : Edit Model :: 11-feb-25
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
interface AddUserModalProps {
|
interface AddUserModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
|
@ -20,52 +30,55 @@ interface AddUserModalProps {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
email: string,
|
email: string,
|
||||||
|
password: string,
|
||||||
phone: string,
|
phone: string,
|
||||||
password: string
|
|
||||||
) => void;
|
) => void;
|
||||||
editRow: any;
|
editRow: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
const AddUserModal: React.FC<AddUserModalProps> = ({
|
const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
open,
|
open,
|
||||||
handleClose,
|
handleClose,
|
||||||
handleCreate,
|
handleCreate,
|
||||||
|
handleUpdate,
|
||||||
editRow,
|
editRow,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
|
||||||
reset,
|
reset,
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
|
password: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
password: "",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (editRow) {
|
||||||
|
// setValue("name", editRow.name);
|
||||||
|
// setValue("email", editRow.email);
|
||||||
|
// setValue("password", editRow.password);
|
||||||
|
// setValue("phone", editRow.phone);
|
||||||
|
// } else {
|
||||||
|
// reset();
|
||||||
|
// }
|
||||||
|
// }, [editRow, setValue, reset]);
|
||||||
|
|
||||||
const onSubmit = (data: FormData) => {
|
const onSubmit = (data: FormData) => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
|
|
||||||
handleUpdate(
|
handleUpdate(
|
||||||
editRow.id,
|
editRow.id,
|
||||||
data.name,
|
data.name,
|
||||||
data.email,
|
data.email,
|
||||||
data.phone,
|
data.password,
|
||||||
data.password
|
data.phone
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
handleCreate(data);
|
handleCreate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,156 +86,287 @@ const AddUserModal: React.FC<AddUserModalProps> = ({
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setShowPassword((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
maxWidth="md"
|
aria-labelledby="add-user-modal"
|
||||||
fullWidth
|
|
||||||
PaperProps={{
|
|
||||||
component: "form",
|
|
||||||
onSubmit: handleSubmit(onSubmit),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<DialogTitle
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
position: "absolute",
|
||||||
alignItems: "center",
|
top: "50%",
|
||||||
justifyContent: "space-between",
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 600, // Adjusted the width for a wider modal
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{editRow ? "Edit User" : "Add User"}
|
{/* Header */}
|
||||||
<Box
|
<Box
|
||||||
onClick={handleClose}
|
|
||||||
sx={{
|
sx={{
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
{editRow ? "Edit User" : "Add User"}
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
{/* Horizontal Line */}
|
||||||
<Controller
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
name="name"
|
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
required: "Admin Name is required",
|
|
||||||
minLength: {
|
|
||||||
value: 3,
|
|
||||||
message: "Minimum 3 characters required",
|
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 30,
|
|
||||||
message: "Maximum 30 characters allowed",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...field}
|
|
||||||
autoFocus
|
|
||||||
required
|
|
||||||
margin="dense"
|
|
||||||
label="User Name"
|
|
||||||
type="text"
|
|
||||||
fullWidth
|
|
||||||
variant="standard"
|
|
||||||
error={!!errors.name}
|
|
||||||
helperText={errors.name?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
{/* Form */}
|
||||||
name="email"
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
control={control}
|
{/* Input Fields */}
|
||||||
rules={{
|
<Box
|
||||||
required: "Email is required",
|
sx={{
|
||||||
pattern: {
|
display: "flex",
|
||||||
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
flexDirection: "column",
|
||||||
message: "Invalid email address",
|
gap: 2,
|
||||||
},
|
}}
|
||||||
}}
|
>
|
||||||
render={({ field }) => (
|
{/* First Row - Two Inputs */}
|
||||||
<TextField
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
{...field}
|
<Box
|
||||||
required
|
sx={{
|
||||||
margin="dense"
|
display: "flex",
|
||||||
label="Email"
|
flexDirection: "column",
|
||||||
type="email"
|
width: "100%",
|
||||||
fullWidth
|
}}
|
||||||
variant="standard"
|
>
|
||||||
error={!!errors.email}
|
<Typography variant="body2" fontWeight={500}>
|
||||||
helperText={errors.email?.message}
|
User Name
|
||||||
/>
|
</Typography>
|
||||||
)}
|
<Controller
|
||||||
/>
|
name="name"
|
||||||
<Controller
|
control={control}
|
||||||
name="password"
|
rules={{
|
||||||
control={control}
|
required: "User Name is required",
|
||||||
rules={{
|
minLength: {
|
||||||
required: "password is required",
|
value: 3,
|
||||||
}}
|
message:
|
||||||
render={({ field }) => (
|
"Minimum 3 characters required",
|
||||||
<TextField
|
},
|
||||||
{...field}
|
maxLength: {
|
||||||
required
|
value: 30,
|
||||||
margin="dense"
|
message:
|
||||||
label="Password"
|
"Maximum 30 characters allowed",
|
||||||
type="password"
|
},
|
||||||
fullWidth
|
}}
|
||||||
variant="standard"
|
render={({ field }) => (
|
||||||
error={!!errors.password}
|
<CustomTextField
|
||||||
helperText={errors.password?.message}
|
{...field}
|
||||||
/>
|
required
|
||||||
)}
|
placeholder="Enter User Name"
|
||||||
/>
|
fullWidth
|
||||||
<Controller
|
size="small"
|
||||||
name="phone"
|
error={!!errors.name}
|
||||||
control={control}
|
helperText={errors.name?.message}
|
||||||
rules={{
|
/>
|
||||||
required: "Phone number is required",
|
)}
|
||||||
pattern: {
|
/>
|
||||||
value: /^[0-9]*$/,
|
</Box>
|
||||||
message: "Only numbers are allowed",
|
|
||||||
},
|
|
||||||
minLength: {
|
|
||||||
value: 6,
|
|
||||||
message: "Phone number must be exactly 6 digits",
|
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 14,
|
|
||||||
message: "Phone number must be exactly 14 digits",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...field}
|
|
||||||
required
|
|
||||||
margin="dense"
|
|
||||||
label="Phone Number"
|
|
||||||
type="tel"
|
|
||||||
fullWidth
|
|
||||||
variant="standard"
|
|
||||||
error={!!errors.phone}
|
|
||||||
helperText={errors.phone?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
<Box
|
||||||
<Button onClick={handleClose}>Cancel</Button>
|
sx={{
|
||||||
<Button type="submit">Create</Button>
|
display: "flex",
|
||||||
</DialogActions>
|
flexDirection: "column",
|
||||||
</Dialog>
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Email is required",
|
||||||
|
pattern: {
|
||||||
|
value: /\S+@\S+\.\S+/,
|
||||||
|
message:
|
||||||
|
"Please enter a valid email address.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Email"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.email
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</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}>
|
||||||
|
Password
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="password"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Password is required",
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message:
|
||||||
|
"Password must be at least 6 characters long.",
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||||
|
message:
|
||||||
|
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Password"
|
||||||
|
type={
|
||||||
|
showPassword
|
||||||
|
? "text"
|
||||||
|
: "password"
|
||||||
|
}
|
||||||
|
id="password"
|
||||||
|
fullWidth
|
||||||
|
color={
|
||||||
|
errors.password
|
||||||
|
? "error"
|
||||||
|
: "primary"
|
||||||
|
}
|
||||||
|
size="small"
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<CustomIconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={
|
||||||
|
togglePasswordVisibility
|
||||||
|
}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<VisibilityOff />
|
||||||
|
) : (
|
||||||
|
<Visibility />
|
||||||
|
)}
|
||||||
|
</CustomIconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
error={!!errors.password}
|
||||||
|
helperText={
|
||||||
|
errors.password?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Phone Number
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Phone number is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[0-9]*$/,
|
||||||
|
message: "Only numbers are allowed",
|
||||||
|
},
|
||||||
|
minLength: {
|
||||||
|
value: 6,
|
||||||
|
message:
|
||||||
|
"Phone number must be at least 6 digits",
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
value: 14,
|
||||||
|
message:
|
||||||
|
"Phone number must be at most 14 digits",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
required
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
width: "100%",
|
||||||
|
mt: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{editRow ? "Update User" : "Add User"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddUserModal;
|
export default AddUserModal;
|
||||||
function handleUpdate(id: any, name: string, email: string, phone: string, password: string) {
|
|
||||||
throw new Error("Function not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
26
src/components/AddUserModel/styled.css.tsx
Normal file
26
src/components/AddUserModel/styled.css.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { styled } from "@mui/material/styles";
|
||||||
|
import { IconButton, TextField } from "@mui/material";
|
||||||
|
export const CustomIconButton = styled(IconButton)({
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#272727",
|
||||||
|
},
|
||||||
|
"*:where([data-mui-color-scheme='dark']) &": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
border: "none",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Custom TextField with different placeholder color
|
||||||
|
export const CustomTextField = styled(TextField)({
|
||||||
|
"& .MuiInputBase-input::placeholder": {
|
||||||
|
color: "#D9D8D8",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-root.Mui-focused .MuiInputBase-input::placeholder": {
|
||||||
|
color: "darkgray",
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
252
src/components/AddVehicleModal/index.tsx
Normal file
252
src/components/AddVehicleModal/index.tsx
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
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 "../AddUserModel/styled.css.tsx";
|
||||||
|
export default function AddVehicleModal({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleAddVehicle,
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm();
|
||||||
|
|
||||||
|
const onSubmit = (data: any) => {
|
||||||
|
handleAddVehicle(data); // Add vehicle to table
|
||||||
|
handleClose(); // Close modal after adding
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="add-vehicle-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 Vehicle
|
||||||
|
</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}>
|
||||||
|
Vehicle Name
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Vehicle Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={
|
||||||
|
errors.name ? errors.name.message : ""
|
||||||
|
}
|
||||||
|
{...register("name", {
|
||||||
|
required: "Vehicle Name is required",
|
||||||
|
minLength: {
|
||||||
|
value: 2,
|
||||||
|
message:
|
||||||
|
"Vehicle Name must be at least 2 characters long",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Company
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Company Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.company}
|
||||||
|
helperText={
|
||||||
|
errors.company
|
||||||
|
? errors.company.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("company", {
|
||||||
|
required: "Company is required",
|
||||||
|
minLength: {
|
||||||
|
value: 5,
|
||||||
|
message:
|
||||||
|
"Company must be at least 5 characters long",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</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}>
|
||||||
|
Model Name
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Model Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.modelName}
|
||||||
|
helperText={
|
||||||
|
errors.modelName
|
||||||
|
? errors.modelName.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("modelName", {
|
||||||
|
required: "Model Name is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Charge Type
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Charge Type"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.chargeType}
|
||||||
|
helperText={
|
||||||
|
errors.chargeType
|
||||||
|
? errors.chargeType.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("chargeType", {
|
||||||
|
required: "Charge Type is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Third Row - Image URL */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Image URL
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Image URL"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.imageUrl}
|
||||||
|
helperText={
|
||||||
|
errors.imageUrl
|
||||||
|
? errors.imageUrl.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("imageUrl", {
|
||||||
|
required: "Image URL is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^(https?:\/\/)?((([a-zA-Z\d]([a-zA-Z\d-]*[a-zA-Z\d])*)\.)+[a-zA-Z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-zA-Z\d%_.~+]*)*(\?[;&a-zA-Z\d%_.~+=-]*)?(#[-a-zA-Z\d_]*)?$/,
|
||||||
|
message: "Please enter a valid URL",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</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 Vehicle
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -9,14 +9,16 @@ import TableHead from "@mui/material/TableHead";
|
||||||
import TableRow from "@mui/material/TableRow";
|
import TableRow from "@mui/material/TableRow";
|
||||||
import Paper, { paperClasses } from "@mui/material/Paper";
|
import Paper, { paperClasses } from "@mui/material/Paper";
|
||||||
import { adminList, deleteAdmin } from "../../redux/slices/adminSlice";
|
import { adminList, deleteAdmin } from "../../redux/slices/adminSlice";
|
||||||
import { deleteManager } from "../../redux/slices/managerSlice";
|
import { vehicleList, deleteVehicle } from "../../redux/slices/VehicleSlice";
|
||||||
|
|
||||||
|
import { deleteManager, managerList } from "../../redux/slices/managerSlice";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
Menu,
|
Menu,
|
||||||
|
IconButton,
|
||||||
Pagination,
|
Pagination,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
|
@ -25,9 +27,14 @@ 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";
|
||||||
|
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 {
|
||||||
|
CustomIconButton,
|
||||||
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
import ManagerViewModal from "../Modals/ViewManagerModal";
|
||||||
// Styled components for customization
|
// Styled components for customization
|
||||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
[`&.${tableCellClasses.head}`]: {
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
@ -73,7 +80,7 @@ interface CustomTableProps {
|
||||||
handleStatusToggle: (id: string, currentStatus: number) => void;
|
handleStatusToggle: (id: string, currentStatus: number) => void;
|
||||||
tableType: string; // Adding tableType prop to change header text dynamically
|
tableType: string; // Adding tableType prop to change header text dynamically
|
||||||
handleClickOpen: () => void;
|
handleClickOpen: () => void;
|
||||||
handleDeleteButton: (id: string | number | undefined) => void;
|
//handleDeleteButton: (id: string | number | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomTable: React.FC<CustomTableProps> = ({
|
const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
|
@ -97,6 +104,8 @@ 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) => {
|
||||||
|
@ -107,18 +116,13 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
const handleViewButton = (id: string | undefined) => {
|
// const handleViewButton = (id: string | undefined) => {
|
||||||
if (!id) {
|
// if (!id) {
|
||||||
console.error("ID not found for viewing.");
|
// console.error("ID not found for viewing.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
setViewModal(true);
|
// setViewModal(true);
|
||||||
};
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
@ -132,16 +136,58 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteButton = (id: string | undefined) => {
|
|
||||||
if (!id) {
|
|
||||||
console.error("ID not found for viewing.");
|
const handleDeleteButton = (id: string | undefined) => {
|
||||||
return;
|
if (!id) console.error("ID not found", id);
|
||||||
|
|
||||||
|
switch (tableType) {
|
||||||
|
case "admin":
|
||||||
|
dispatch(deleteAdmin(id || ""));
|
||||||
|
break;
|
||||||
|
case "vehicle":
|
||||||
|
dispatch(deleteVehicle(id || ""));
|
||||||
|
break;
|
||||||
|
case "manager":
|
||||||
|
dispatch(deleteManager(id || ""));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("Unknown table type:", tableType);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
setViewModal(true);
|
setDeleteModal(false); // Close the modal only after deletion
|
||||||
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewButton = (id: string | undefined) => {
|
||||||
|
if (!id) console.error("ID not found", id);
|
||||||
|
switch (tableType) {
|
||||||
|
case "admin":
|
||||||
|
dispatch(adminList());
|
||||||
|
break;
|
||||||
|
case "vehicle":
|
||||||
|
dispatch(vehicleList());
|
||||||
|
break;
|
||||||
|
case "manager":
|
||||||
|
dispatch(managerList());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("Unknown table type:", tableType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(adminList());
|
||||||
|
setViewModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToggleStatus = () => {
|
||||||
|
if (selectedRow) {
|
||||||
|
// Toggle the opposite of current status
|
||||||
|
const newStatus = selectedRow.statusValue === 1 ? 0 : 1;
|
||||||
|
handleStatusToggle(selectedRow.id, newStatus);
|
||||||
|
}
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
const filteredRows = rows.filter(
|
const filteredRows = rows.filter(
|
||||||
(row) =>
|
(row) =>
|
||||||
(row.name &&
|
(row.name &&
|
||||||
|
@ -217,11 +263,15 @@ const filteredRows = rows.filter(
|
||||||
borderColor: "#52ACDF",
|
borderColor: "#52ACDF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"& .MuiInputBase-input::placeholder": {
|
||||||
|
color: "#D9D8D8",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
<SearchIcon sx={{ color: "#272727" }} />
|
<SearchIcon sx={{ color: "#52ACDF" }} />
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
@ -252,8 +302,8 @@ const filteredRows = rows.filter(
|
||||||
? "Role"
|
? "Role"
|
||||||
: tableType === "user"
|
: tableType === "user"
|
||||||
? "User"
|
? "User"
|
||||||
: tableType === "managers"
|
: tableType === "manager"
|
||||||
? "Managers"
|
? "Manager"
|
||||||
: tableType === "vehicle"
|
: tableType === "vehicle"
|
||||||
? "Vehicle"
|
? "Vehicle"
|
||||||
: "Item"}
|
: "Item"}
|
||||||
|
@ -330,7 +380,7 @@ const filteredRows = rows.filter(
|
||||||
) : column.id !== "action" ? (
|
) : column.id !== "action" ? (
|
||||||
row[column.id]
|
row[column.id]
|
||||||
) : (
|
) : (
|
||||||
<IconButton
|
<CustomIconButton
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
handleClick(e, row);
|
handleClick(e, row);
|
||||||
setRowData(row); // Store the selected row
|
setRowData(row); // Store the selected row
|
||||||
|
@ -340,20 +390,13 @@ const filteredRows = rows.filter(
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
width: "auto",
|
width: "auto",
|
||||||
height: "auto",
|
height: "auto",
|
||||||
backgroundColor:
|
|
||||||
"transparent",
|
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
border: "none",
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor:
|
|
||||||
"transparent",
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MoreHorizRoundedIcon
|
<MoreHorizRoundedIcon
|
||||||
sx={{ fontSize: "24px" }}
|
sx={{ fontSize: "24px" }}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</CustomIconButton>
|
||||||
)}
|
)}
|
||||||
</StyledTableCell>
|
</StyledTableCell>
|
||||||
))}
|
))}
|
||||||
|
@ -411,6 +454,9 @@ const filteredRows = rows.filter(
|
||||||
[`& .${paperClasses.root}`]: {
|
[`& .${paperClasses.root}`]: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
},
|
},
|
||||||
|
"& .MuiList-root": {
|
||||||
|
background: "#272727", // Remove any divider under menu items
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -424,7 +470,7 @@ const filteredRows = rows.filter(
|
||||||
variant="text"
|
variant="text"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// setSelectedRow(row);
|
// setSelectedRow(row);
|
||||||
setViewModal(true);
|
setViewModal(true);
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -437,16 +483,36 @@ const filteredRows = rows.filter(
|
||||||
>
|
>
|
||||||
View
|
View
|
||||||
</Button>
|
</Button>
|
||||||
{viewModal && (
|
{viewModal && tableType === "admin" && (
|
||||||
<ViewModal
|
<ViewModal
|
||||||
handleView={() => handleViewButton(selectedRow?.id)}
|
handleView={() =>
|
||||||
open={viewModal}
|
handleViewButton(selectedRow?.id)
|
||||||
setViewModal={setViewModal}
|
}
|
||||||
id={selectedRow?.id}
|
open={viewModal}
|
||||||
tableType={tableType}
|
setViewModal={setViewModal}
|
||||||
/>
|
id={selectedRow?.id}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
{viewModal && tableType === "vehicle" && (
|
||||||
|
<VehicleViewModal
|
||||||
|
handleView={() =>
|
||||||
|
handleViewButton(selectedRow?.id)
|
||||||
|
}
|
||||||
|
open={viewModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
id={selectedRow?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{viewModal && tableType === "manager" && (
|
||||||
|
<ManagerViewModal
|
||||||
|
handleView={() =>
|
||||||
|
handleViewButton(selectedRow?.id)
|
||||||
|
}
|
||||||
|
open={viewModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
id={selectedRow?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -485,14 +551,12 @@ const filteredRows = rows.filter(
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDeleteModal(true);
|
setDeleteModal(true);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
color="error"
|
color="error"
|
||||||
sx={{
|
sx={{
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
py: 0,
|
py: 0,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color: "red",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
@ -503,18 +567,16 @@ const filteredRows = rows.filter(
|
||||||
|
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
{deleteModal && (
|
{deleteModal && (
|
||||||
<DeleteModal
|
<DeleteModal
|
||||||
handleDelete={() => handleDeleteButton(selectedRow?.id)}
|
handleDelete={() => handleDeleteButton(selectedRow?.id)}
|
||||||
open={deleteModal}
|
open={deleteModal}
|
||||||
setDeleteModal={setDeleteModal}
|
setDeleteModal={setDeleteModal}
|
||||||
id={selectedRow?.id}
|
id={selectedRow?.id}
|
||||||
tableType={tableType}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomTable;
|
export default CustomTable;
|
||||||
|
|
||||||
|
|
595
src/components/EditManagerModal/index.tsx
Normal file
595
src/components/EditManagerModal/index.tsx
Normal file
|
@ -0,0 +1,595 @@
|
||||||
|
// import React, { useEffect } from "react";
|
||||||
|
// import {
|
||||||
|
// Box,
|
||||||
|
// Button,
|
||||||
|
// Typography,
|
||||||
|
// TextField,
|
||||||
|
// Modal,
|
||||||
|
// IconButton,
|
||||||
|
// } from "@mui/material";
|
||||||
|
// import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
// import { useForm, Controller } from "react-hook-form";
|
||||||
|
// import {
|
||||||
|
// CustomIconButton,
|
||||||
|
// CustomTextField,
|
||||||
|
// } from "../AddUserModel/styled.css.tsx";
|
||||||
|
|
||||||
|
// interface EditManagerModalProps {
|
||||||
|
// open: boolean;
|
||||||
|
// handleClose: () => void;
|
||||||
|
// handleUpdate: (
|
||||||
|
// id: number,
|
||||||
|
// name: string,
|
||||||
|
// email: string,
|
||||||
|
// stationName: string,
|
||||||
|
// registeredAddress: string,
|
||||||
|
// phone: string
|
||||||
|
// ) => void;
|
||||||
|
// editRow: any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface FormData {
|
||||||
|
// name: string;
|
||||||
|
// email: string;
|
||||||
|
// stationName: string;
|
||||||
|
// registeredAddress: string;
|
||||||
|
// phone: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
// open,
|
||||||
|
// handleClose,
|
||||||
|
// handleUpdate,
|
||||||
|
// editRow,
|
||||||
|
// }) => {
|
||||||
|
// const {
|
||||||
|
// control,
|
||||||
|
// handleSubmit,
|
||||||
|
// formState: { errors },
|
||||||
|
// setValue,
|
||||||
|
// reset,
|
||||||
|
// } = useForm<FormData>({
|
||||||
|
// defaultValues: {
|
||||||
|
// name: "",
|
||||||
|
// stationName: "",
|
||||||
|
// email: "",
|
||||||
|
// registeredAddress: "",
|
||||||
|
// phone: "",
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Populate form fields when `editRow` changes
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (editRow) {
|
||||||
|
// setValue("name", editRow.name || "");
|
||||||
|
// setValue("stationName", editRow.stationName || "");
|
||||||
|
// setValue("email", editRow.email || "");
|
||||||
|
// setValue("registeredAddress", editRow.registeredAddress || "");
|
||||||
|
// setValue("phone", editRow.phone || "");
|
||||||
|
// } else {
|
||||||
|
// reset();
|
||||||
|
// }
|
||||||
|
// }, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
// const onSubmit = (data: FormData) => {
|
||||||
|
// if (editRow) {
|
||||||
|
// handleUpdate({
|
||||||
|
// id: editRow.id,
|
||||||
|
// name: data.name,
|
||||||
|
// stationName: data.stationName,
|
||||||
|
// email: data.email,
|
||||||
|
// registeredAddress: data.registeredAddress,
|
||||||
|
// phone: data.phone,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// }
|
||||||
|
// handleClose();
|
||||||
|
// reset();
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <Modal
|
||||||
|
// open={open}
|
||||||
|
// onClose={handleClose}
|
||||||
|
// aria-labelledby="edit-manager-modal"
|
||||||
|
// >
|
||||||
|
// <Box
|
||||||
|
// component="form"
|
||||||
|
// onSubmit={handleSubmit(onSubmit)}
|
||||||
|
// sx={{
|
||||||
|
// position: "absolute",
|
||||||
|
// top: "50%",
|
||||||
|
// left: "50%",
|
||||||
|
// transform: "translate(-50%, -50%)",
|
||||||
|
// width: 400,
|
||||||
|
// bgcolor: "background.paper",
|
||||||
|
// boxShadow: 24,
|
||||||
|
// p: 3,
|
||||||
|
// borderRadius: 2,
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {/* Header */}
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// justifyContent: "space-between",
|
||||||
|
// alignItems: "center",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography variant="h6" fontWeight={600}>
|
||||||
|
// {editRow ? "Edit Manager" : "Add Manager"}
|
||||||
|
// </Typography>
|
||||||
|
// <IconButton onClick={handleClose}>
|
||||||
|
// <CloseIcon />
|
||||||
|
// </IconButton>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Horizontal Line */}
|
||||||
|
// <Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
// {/* Input Fields */}
|
||||||
|
// <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
|
// {/* First Row - Two Inputs */}
|
||||||
|
// <Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography
|
||||||
|
// variant="body2"
|
||||||
|
// fontWeight={500}
|
||||||
|
// mb={0.5}
|
||||||
|
// >
|
||||||
|
// Manager Name
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="name"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Manager Name is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Manager Name"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.name}
|
||||||
|
// helperText={errors.name?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography
|
||||||
|
// variant="body2"
|
||||||
|
// fontWeight={500}
|
||||||
|
// mb={0.5}
|
||||||
|
// >
|
||||||
|
// Station Name
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="stationName"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Station Name is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Station Name"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.stationName}
|
||||||
|
// helperText={errors.stationName?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Second Row - Two Inputs */}
|
||||||
|
// <Box sx={{ display: "flex", gap: 2 }}>
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography
|
||||||
|
// variant="body2"
|
||||||
|
// fontWeight={500}
|
||||||
|
// mb={0.5}
|
||||||
|
// >
|
||||||
|
// Registered Address
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="registeredAddress"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Registered Address is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Registered Address"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.registeredAddress}
|
||||||
|
// helperText={errors.registeredAddress?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography
|
||||||
|
// variant="body2"
|
||||||
|
// fontWeight={500}
|
||||||
|
// mb={0.5}
|
||||||
|
// >
|
||||||
|
// Email
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="email"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Email is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Email"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.email}
|
||||||
|
// helperText={errors.email?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Third Row - Phone Number */}
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: "flex",
|
||||||
|
// flexDirection: "column",
|
||||||
|
// width: "100%",
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
// Phone Number
|
||||||
|
// </Typography>
|
||||||
|
// <Controller
|
||||||
|
// name="phone"
|
||||||
|
// control={control}
|
||||||
|
// rules={{ required: "Phone number is required" }}
|
||||||
|
// render={({ field }) => (
|
||||||
|
// <TextField
|
||||||
|
// {...field}
|
||||||
|
// fullWidth
|
||||||
|
// placeholder="Enter Phone Number"
|
||||||
|
// size="small"
|
||||||
|
// error={!!errors.phone}
|
||||||
|
// helperText={errors.phone?.message}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// />
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
|
||||||
|
// {/* Submit Button */}
|
||||||
|
// <Box
|
||||||
|
// sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
|
||||||
|
// >
|
||||||
|
// <Button
|
||||||
|
// type="submit"
|
||||||
|
// sx={{
|
||||||
|
// backgroundColor: "#52ACDF",
|
||||||
|
// color: "white",
|
||||||
|
// borderRadius: "8px",
|
||||||
|
// width: "117px",
|
||||||
|
// "&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {editRow ? "Update" : "Create"}
|
||||||
|
// </Button>
|
||||||
|
// </Box>
|
||||||
|
// </Box>
|
||||||
|
// </Modal>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
// export default EditManagerModal;
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Modal,
|
||||||
|
CircularProgress,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { updateManager } from "../../redux/slices/managerSlice"; // Import the updateManager action
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx"; // Custom styled components
|
||||||
|
|
||||||
|
interface EditManagerModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
editRow: any; // Manager data including id
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditManagerModal: React.FC<EditManagerModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useDispatch(); // Use dispatch to send Redux actions
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
registeredAddress: "",
|
||||||
|
phone: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
// Set values if editRow is provided
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("name", editRow.name);
|
||||||
|
setValue("email", editRow.email);
|
||||||
|
setValue("registeredAddress", editRow.registeredAddress);
|
||||||
|
setValue("phone", editRow.phone);
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
setLoading(true); // Start loading state
|
||||||
|
|
||||||
|
// Dispatch the updateManager action from Redux
|
||||||
|
try {
|
||||||
|
await dispatch(
|
||||||
|
updateManager({
|
||||||
|
id: editRow.id, // Manager ID
|
||||||
|
managerData: {
|
||||||
|
name: data.name,
|
||||||
|
email: data.email,
|
||||||
|
registeredAddress: data.registeredAddress,
|
||||||
|
phone: data.phone,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).unwrap(); // Ensure that it throws an error if the update fails
|
||||||
|
|
||||||
|
handleClose(); // Close modal on success
|
||||||
|
reset(); // Reset form fields after submit
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
// You can handle the error here or show a toast
|
||||||
|
} finally {
|
||||||
|
setLoading(false); // Stop loading state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="edit-manager-modal"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
component="form"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
Edit Manager
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Horizontal Line */}
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
{/* Input Fields */}
|
||||||
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
|
{/* Manager Name */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Manager Name
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Manager Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Manager Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.name}
|
||||||
|
helperText={errors.name?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Registered Address */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Registered Address
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="registeredAddress"
|
||||||
|
control={control}
|
||||||
|
rules={{
|
||||||
|
required: "Registered Address is required",
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Registered Address"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.registeredAddress}
|
||||||
|
helperText={
|
||||||
|
errors.registeredAddress?.message
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Email */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Email
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="email"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Email is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Email"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.email}
|
||||||
|
helperText={errors.email?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Phone Number */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Phone Number
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Phone number is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Phone Number"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.phone}
|
||||||
|
helperText={errors.phone?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
disabled={loading} // Disable the button during loading state
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<CircularProgress size={24} color="inherit" />
|
||||||
|
) : (
|
||||||
|
"Update Manager"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditManagerModal;
|
310
src/components/EditVehicleModal/index.tsx
Normal file
310
src/components/EditVehicleModal/index.tsx
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
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 { updateVehicle } from "../../redux/slices/VehicleSlice";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddUserModel/styled.css.tsx";
|
||||||
|
interface EditVehicleModalProps {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
handleUpdate: (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
email: string,
|
||||||
|
phone: string,
|
||||||
|
registeredAddress: string,
|
||||||
|
imageUrl: string
|
||||||
|
) => void;
|
||||||
|
editRow: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
company: string;
|
||||||
|
modelName: string;
|
||||||
|
chargeType: string;
|
||||||
|
imageUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
handleUpdate,
|
||||||
|
editRow,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
} = useForm<FormData>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
company: "",
|
||||||
|
modelName: "",
|
||||||
|
chargeType: "",
|
||||||
|
imageUrl: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set values if editRow is provided
|
||||||
|
useEffect(() => {
|
||||||
|
if (editRow) {
|
||||||
|
setValue("name", editRow.name);
|
||||||
|
setValue("company", editRow.company);
|
||||||
|
setValue("modelName", editRow.modelName);
|
||||||
|
setValue("chargeType", editRow.chargeType);
|
||||||
|
setValue("imageUrl", editRow.imageUrl);
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
const onSubmit = (data: FormData) => {
|
||||||
|
if (editRow) {
|
||||||
|
handleUpdate(
|
||||||
|
editRow.id,
|
||||||
|
data.name,
|
||||||
|
data.company,
|
||||||
|
data.modelName,
|
||||||
|
data.chargeType,
|
||||||
|
data.imageUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleClose(); // Close the modal
|
||||||
|
reset(); // Reset the form fields
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="edit-vehicle-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 Vehicle
|
||||||
|
</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}
|
||||||
|
>
|
||||||
|
Vehicle Name
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Vehicle Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Vehicle 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}
|
||||||
|
>
|
||||||
|
Company
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="company"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Company is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Company Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.company}
|
||||||
|
helperText={errors.company?.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}
|
||||||
|
>
|
||||||
|
Model Name
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="modelName"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Model Name is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Model Name"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.modelName}
|
||||||
|
helperText={errors.modelName?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight={500}
|
||||||
|
mb={0.5}
|
||||||
|
>
|
||||||
|
Charge Type
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="chargeType"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Charge Type is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Charge Type"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.chargeType}
|
||||||
|
helperText={errors.chargeType?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Third Row - Image URL Input */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500} mb={0.5}>
|
||||||
|
Image URL
|
||||||
|
</Typography>
|
||||||
|
<Controller
|
||||||
|
name="imageUrl"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: "Image URL is required" }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<CustomTextField
|
||||||
|
{...field}
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Image URL"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.imageUrl}
|
||||||
|
helperText={errors.imageUrl?.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" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update Vehicle
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditVehicleModal;
|
|
@ -49,6 +49,11 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
icon: <ManageAccountsOutlinedIcon />,
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
url: "/panel/manager-list", // Placeholder for now
|
url: "/panel/manager-list", // Placeholder for now
|
||||||
},
|
},
|
||||||
|
userRole === "admin" && {
|
||||||
|
text: "Vehicles",
|
||||||
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
|
url: "/panel/vehicles", // Placeholder for now
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
||||||
|
|
|
@ -1,108 +1,98 @@
|
||||||
import { Box, Modal, Typography, Divider, Button } from "@mui/material";
|
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||||
import { useEffect, useState } from "react";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import { RootState, AppDispatch } from "../../../redux/store/store";
|
|
||||||
import { deleteManager, fetchManagerList } from "../../../redux/slices/managerSlice";
|
|
||||||
|
|
||||||
type User = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
email?: string;
|
|
||||||
phone: string;
|
|
||||||
registeredAddress?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setDeleteModal: (value: boolean) => void;
|
setDeleteModal: Function;
|
||||||
id?: number;
|
handleDelete: (id: string | undefined) => void;
|
||||||
tableType?: "admin" | "manager";
|
id?: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
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: 400,
|
width: 330,
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
borderRadius: 2,
|
borderRadius: 1.5,
|
||||||
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
boxShadow: 24,
|
||||||
p: 4,
|
p: 3,
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 2,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DeleteModal({ open, setDeleteModal, id, tableType }: Props) {
|
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" };
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
|
||||||
const adminList = useSelector((state: RootState) => state.adminReducer.admins) || [];
|
|
||||||
const managerList = useSelector((state: RootState) => state.managerReducer.managers) || [];
|
|
||||||
|
|
||||||
const [selectedItem, setSelectedItem] = useState<User | null>(null);
|
export default function DeleteModal({
|
||||||
const [loading, setLoading] = useState(false);
|
open,
|
||||||
|
setDeleteModal,
|
||||||
|
handleDelete,
|
||||||
|
id,
|
||||||
|
}: Props) {
|
||||||
|
// console.log("DeleteModal opened with ID:", id)
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
if (!id || !tableType) return;
|
<Modal
|
||||||
|
open={open}
|
||||||
const dataList: User[] = tableType === "admin" ? adminList : managerList;
|
aria-labelledby="modal-modal-title"
|
||||||
const item = dataList.find((user) => user.id === id);
|
aria-describedby="modal-modal-description"
|
||||||
setSelectedItem(item || null);
|
>
|
||||||
}, [id, tableType, adminList, managerList]);
|
<Box sx={style}>
|
||||||
|
<Typography
|
||||||
|
id="modal-modal-title"
|
||||||
|
variant="h6"
|
||||||
|
component="h2"
|
||||||
|
align="center"
|
||||||
const handleDelete = async () => {
|
sx={{ flexGrow: 1 }} // This ensures the title takes up available space
|
||||||
if (!id || !tableType) {
|
>
|
||||||
return;
|
Delete Record
|
||||||
}
|
</Typography>
|
||||||
|
<Box
|
||||||
setLoading(true);
|
onClick={() => setDeleteModal(false)}
|
||||||
try {
|
sx={{
|
||||||
let deleteResult;
|
cursor: "pointer",
|
||||||
if (tableType === "managers") {
|
display: "flex",
|
||||||
deleteResult = await dispatch(deleteManager(id)).unwrap();
|
alignItems: "center",
|
||||||
}
|
justifyContent: "flex-end", // Aligns the close icon to the right
|
||||||
|
marginTop: -3.5,
|
||||||
if (deleteResult) {
|
}}
|
||||||
await dispatch(fetchManagerList()); // Refresh list only if deletion is successful
|
>
|
||||||
}
|
<CloseIcon />
|
||||||
} catch (error) {
|
</Box>
|
||||||
console.error("❌ Error while deleting:", error);
|
<Typography
|
||||||
} finally {
|
id="modal-modal-description"
|
||||||
setLoading(false);
|
sx={{ mt: 2 }}
|
||||||
setDeleteModal(false);
|
align="center"
|
||||||
}
|
>
|
||||||
};
|
Are you sure you want to delete this record?
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
return (
|
sx={{
|
||||||
<Modal open={open} onClose={() => setDeleteModal(false)} aria-labelledby="modal-title">
|
display: "flex",
|
||||||
<Box sx={style}>
|
justifyContent: "space-between",
|
||||||
<Typography variant="h5" fontWeight="bold" sx={{ width: "100%", textAlign: "center" }}>
|
mt: 4,
|
||||||
Delete {tableType ? tableType.charAt(0).toUpperCase() + tableType.slice(1) : "User"}
|
gap: 2,
|
||||||
</Typography>
|
}}
|
||||||
|
>
|
||||||
<Divider sx={{ width: "100%" }} />
|
<Button
|
||||||
|
variant="contained"
|
||||||
{selectedItem ? (
|
color="error"
|
||||||
<Typography align="center">
|
type="button"
|
||||||
Are you sure you want to delete <b>{selectedItem.name}</b>?
|
sx={btnStyle}
|
||||||
</Typography>
|
onClick={() => setDeleteModal(false)}
|
||||||
) : (
|
>
|
||||||
<Typography align="center">No {tableType} found with this ID</Typography>
|
Cancel
|
||||||
)}
|
</Button>
|
||||||
|
<Button
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
variant="contained"
|
||||||
<Button variant="contained" color="error" onClick={handleDelete} disabled={loading}>
|
type="button"
|
||||||
{loading ? "Deleting..." : "Delete"}
|
color="primary"
|
||||||
</Button>
|
sx={btnStyle}
|
||||||
<Button variant="outlined" onClick={() => setDeleteModal(false)}>Cancel</Button>
|
onClick={() => handleDelete(id || "")}
|
||||||
</Box>
|
>
|
||||||
</Box>
|
Delete
|
||||||
</Modal>
|
</Button>
|
||||||
);
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
133
src/components/Modals/VehicleViewModal/index.tsx
Normal file
133
src/components/Modals/VehicleViewModal/index.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Box, Modal, Typography, Divider, Grid } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../redux/reducers";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
setViewModal: Function;
|
||||||
|
handleView: (id: string | undefined) => void;
|
||||||
|
id?: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
||||||
|
p: 4,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function VehicleViewModal({ open, setViewModal, id }: Props) {
|
||||||
|
const { vehicles } = useSelector(
|
||||||
|
(state: RootState) => state.vehicleReducer
|
||||||
|
);
|
||||||
|
const [selectedVehicle, setSelectedVehicle] = useState<any>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
const vehicle = vehicles.find((vehicle) => vehicle.id === id);
|
||||||
|
setSelectedVehicle(vehicle || null);
|
||||||
|
}
|
||||||
|
}, [id, vehicles]);
|
||||||
|
|
||||||
|
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" }}>
|
||||||
|
{selectedVehicle?.name || "Vehicle"}'s Details
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
onClick={() => setViewModal(false)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ width: "100%" }} />
|
||||||
|
|
||||||
|
{selectedVehicle ? (
|
||||||
|
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Name:</strong>{" "}
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedVehicle.name}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Company:</strong>{" "}
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedVehicle.company}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Model Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedVehicle.modelName}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Charge Type:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedVehicle.chargeType}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Image URL:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedVehicle.imageUrl ?? "N/A"}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
) : (
|
||||||
|
<Typography align="center">
|
||||||
|
No vehicle found with this ID
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
133
src/components/Modals/ViewManagerModal/index.tsx
Normal file
133
src/components/Modals/ViewManagerModal/index.tsx
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Box, Modal, Typography, Divider, Grid } from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../redux/reducers";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
setViewModal: Function;
|
||||||
|
handleView: (id: string | undefined) => void;
|
||||||
|
id?: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
||||||
|
p: 4,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ManagerViewModal({ open, setViewModal, id }: Props) {
|
||||||
|
const { managers } = useSelector(
|
||||||
|
(state: RootState) => state.managerReducer
|
||||||
|
);
|
||||||
|
const [selectedManager, setSelectedManager] = useState<any>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
const manager = managers.find((manager) => manager.id === id);
|
||||||
|
setSelectedManager(manager || null);
|
||||||
|
}
|
||||||
|
}, [id, managers]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
aria-labelledby="modal-title"
|
||||||
|
aria-describedby="modal-description"
|
||||||
|
>
|
||||||
|
<Box sx={style}>
|
||||||
|
<Typography
|
||||||
|
id="modal-title"
|
||||||
|
variant="h5"
|
||||||
|
fontWeight="bold"
|
||||||
|
sx={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ flex: 1, textAlign: "center" }}>
|
||||||
|
{selectedManager?.name || "Manager"}'s Details
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
onClick={() => setViewModal(false)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Divider sx={{ width: "100%" }} />
|
||||||
|
|
||||||
|
{selectedManager ? (
|
||||||
|
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.name}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Email:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.email}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Phone:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.phone}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Registered Address:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.registeredAddress}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Station Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedManager.stationName}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
) : (
|
||||||
|
<Typography align="center">
|
||||||
|
No manager found with this ID
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,95 +1,123 @@
|
||||||
import { Box, Modal, Typography, Divider } from "@mui/material";
|
import { Box, Button, Modal, Typography, Divider, Grid } from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { RootState } from "../../../redux/store/store";
|
import { RootState } from "../../../redux/store/store";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
type User = {
|
import { useEffect, useState } from "react";
|
||||||
id: number;
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
name: string;
|
|
||||||
email?: string;
|
|
||||||
phone: string;
|
|
||||||
registeredAddress?: string;
|
|
||||||
action?: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setViewModal: (value: boolean) => void;
|
setViewModal: Function;
|
||||||
id?: number;
|
id?: string;
|
||||||
tableType?: "admin" | "manager";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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: 400,
|
width: 400,
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.15)",
|
||||||
p: 4,
|
p: 4,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 2,
|
gap: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ViewModal({ open, setViewModal, id, tableType }: Props) {
|
export default function ViewModal({ open, setViewModal, id }: Props) {
|
||||||
const adminList = useSelector((state: RootState) => state.adminReducer.admins) || [];
|
const { admins } = useSelector((state: RootState) => state.adminReducer);
|
||||||
const managerList = useSelector((state: RootState) => state.managerReducer.managers) || [];
|
const [selectedAdmin, setSelectedAdmin] = useState<any>(null);
|
||||||
|
|
||||||
const [selectedItem, setSelectedItem] = useState<User | null>(null);
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
const admin = admins.find((admin) => admin.id === id);
|
||||||
|
setSelectedAdmin(admin);
|
||||||
|
}
|
||||||
|
}, [id, admins]);
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
if (!id || !tableType) 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" }}>
|
||||||
|
{selectedAdmin?.name || "Admin"}'s Details
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
onClick={() => setViewModal(false)}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
const dataList: User[] = tableType === "admin" ? adminList : managerList;
|
<Divider sx={{ width: "100%" }} />
|
||||||
const item = dataList.find((user) => user.id === id);
|
|
||||||
|
|
||||||
if (item) {
|
{selectedAdmin ? (
|
||||||
setSelectedItem(item); // ✅ Updating selected item properly
|
<Grid container spacing={2} sx={{ width: "80%" }}>
|
||||||
}
|
<Grid item xs={6}>
|
||||||
}, [id, tableType, adminList, managerList]);
|
<Typography variant="body1">
|
||||||
|
<strong>Name:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.name}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Phone:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.phone}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Email:</strong>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.email}
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<strong>Address:</strong>
|
||||||
|
|
||||||
const formattedType = tableType ? tableType.charAt(0).toUpperCase() + tableType.slice(1) : "User";
|
<Typography variant="body2">
|
||||||
|
{selectedAdmin.registeredAddress ?? "N/A"}
|
||||||
return (
|
</Typography>
|
||||||
<Modal open={open} onClose={() => setViewModal(false)} aria-labelledby="modal-title">
|
</Typography>
|
||||||
<Box sx={style}>
|
</Grid>
|
||||||
<Typography variant="h5" fontWeight="bold" sx={{ width: "100%" }}>
|
</Grid>
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
) : (
|
||||||
<Box sx={{ flex: 1, textAlign: "center" }}>
|
<Typography align="center">
|
||||||
{selectedItem?.name ? `${selectedItem.name}'s Details` : `${formattedType} Details`}
|
No admin found with this ID
|
||||||
</Box>
|
</Typography>
|
||||||
<Box onClick={() => setViewModal(false)} sx={{ cursor: "pointer", display: "flex" }}>
|
)}
|
||||||
<CloseIcon />
|
</Box>
|
||||||
</Box>
|
</Modal>
|
||||||
</Box>
|
);
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Divider sx={{ width: "100%" }} />
|
|
||||||
|
|
||||||
{selectedItem ? (
|
|
||||||
<Box sx={{ width: "80%", textAlign: "left", display: "flex", flexDirection: "column", gap: 1.5 }}>
|
|
||||||
<Typography variant="body1">
|
|
||||||
<b>Name:</b> {selectedItem.name || "N/A"}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1">
|
|
||||||
<b>Email:</b> {selectedItem.email || "N/A"}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1">
|
|
||||||
<b>Phone:</b> {selectedItem.phone || "N/A"}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body1">
|
|
||||||
<b>Address:</b> {selectedItem.registeredAddress || "N/A"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Typography align="center">No {formattedType} found with this ID</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ExpandMoreIcon onClick={handleClick} />
|
<ExpandMoreIcon onClick={handleClick} sx={{ cursor: "pointer" }} />
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
id="top-menu"
|
id="top-menu"
|
||||||
|
@ -39,6 +39,10 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
||||||
overflow: "visible",
|
overflow: "visible",
|
||||||
filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
|
filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
|
||||||
mt: 1.5,
|
mt: 1.5,
|
||||||
|
|
||||||
|
"& .MuiList-root": {
|
||||||
|
background: "#272727", // Remove any divider under menu items
|
||||||
|
},
|
||||||
"& .MuiMenuItem-root": {
|
"& .MuiMenuItem-root": {
|
||||||
borderBottom: "none", // Remove any divider under menu items
|
borderBottom: "none", // Remove any divider under menu items
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
// import { useHistory } from "react-router-dom";
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL: process.env.REACT_APP_BACKEND_URL,
|
baseURL: process.env.REACT_APP_BACKEND_URL,
|
||||||
});
|
});
|
||||||
|
@ -11,5 +11,18 @@ http.interceptors.request.use((config) => {
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
http.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response && error.response.status === 401) {
|
||||||
|
|
||||||
|
window.location.href = "/login";
|
||||||
|
|
||||||
|
// const history = useHistory();
|
||||||
|
// history.push("/login");
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default http;
|
export default http;
|
||||||
|
|
|
@ -11,9 +11,8 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
Box,
|
Box,
|
||||||
Grid,
|
Grid,
|
||||||
FormControlLabel,
|
|
||||||
Button,
|
Button,
|
||||||
TextField,
|
FormControlLabel,
|
||||||
Snackbar,
|
Snackbar,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useNavigate } from "react-router-dom"; // Import useNavigate
|
import { useNavigate } from "react-router-dom"; // Import useNavigate
|
||||||
|
@ -21,6 +20,9 @@ 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 {
|
||||||
|
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;
|
||||||
|
@ -155,7 +157,7 @@ const AddEditRolePage: React.FC = () => {
|
||||||
{/* Role Name Input */}
|
{/* Role Name Input */}
|
||||||
<Box sx={{ mb: 2 }}>
|
<Box sx={{ mb: 2 }}>
|
||||||
<label>Role Name</label>
|
<label>Role Name</label>
|
||||||
<TextField
|
<CustomTextField
|
||||||
placeholder="Enter role name"
|
placeholder="Enter role name"
|
||||||
value={roleName}
|
value={roleName}
|
||||||
onChange={handleRoleNameChange}
|
onChange={handleRoleNameChange}
|
||||||
|
@ -256,10 +258,15 @@ const AddEditRolePage: React.FC = () => {
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<Box sx={{ mt: 2, display: "flex", justifyContent: "flex-end" }}>
|
<Box sx={{ mt: 2, display: "flex", justifyContent: "flex-end" }}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{loading ? "Saving..." : "Save Role"}
|
{loading ? "Saving..." : "Save Role"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import { Box, Button, Typography, Modal, IconButton, TextField } from "@mui/material";
|
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
|
||||||
import { useDispatch } from "react-redux";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
import { addManager } from "../../redux/slices/managerSlice";
|
|
||||||
|
|
||||||
interface AddManagerModalProps {
|
|
||||||
open: boolean;
|
|
||||||
handleClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddManagerModal: React.FC<AddManagerModalProps> = ({ open, handleClose }) => {
|
|
||||||
// State for input fields
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [stationLocation, setStationLocation] = useState("");
|
|
||||||
const [phone, setPhone] = useState("");
|
|
||||||
const [email, setEmail] = useState("");
|
|
||||||
const [password, setPassword] = useState("");
|
|
||||||
|
|
||||||
const roleId = 5; // Fixed role ID
|
|
||||||
const roleName = "Peon"; // Required role name
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
// Function to validate form inputs
|
|
||||||
const validateInputs = () => {
|
|
||||||
if (!name || !stationLocation || !phone || !email || !password) {
|
|
||||||
toast.error("All fields are required.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const phoneRegex = /^[0-9]{6,14}$/;
|
|
||||||
if (!phoneRegex.test(phone)) {
|
|
||||||
toast.error("Phone number must be between 6 to 14 digits.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
||||||
if (!emailRegex.test(email)) {
|
|
||||||
toast.error("Enter a valid email address.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 6) {
|
|
||||||
toast.error("Password must be at least 6 characters long.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle form submission
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
if (!validateInputs()) return;
|
|
||||||
|
|
||||||
const managerData = {
|
|
||||||
name,
|
|
||||||
registeredAddress: stationLocation,
|
|
||||||
phone,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
roleId,
|
|
||||||
roleName, // ✅ Ensure roleName is correctly included
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await dispatch(addManager(managerData)).unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ✅ Ensure response contains expected data
|
|
||||||
if (!response || !response.id) {
|
|
||||||
throw new Error("Invalid response from server. ID is missing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Show success message from API if available, fallback if not
|
|
||||||
toast.success(response.message || "Manager added successfully!");
|
|
||||||
|
|
||||||
resetForm();
|
|
||||||
handleClose();
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("API Error:", error); // ✅ Log error for debugging
|
|
||||||
|
|
||||||
// ✅ Handle both API errors and unexpected errors
|
|
||||||
toast.error(
|
|
||||||
error?.response?.data?.message || error.message || "Failed to add manager"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to reset form fields
|
|
||||||
const resetForm = () => {
|
|
||||||
setName("");
|
|
||||||
setStationLocation("");
|
|
||||||
setPhone("");
|
|
||||||
setEmail("");
|
|
||||||
setPassword("");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal open={open} onClose={handleClose} aria-labelledby="add-manager-modal">
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
width: 400,
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
boxShadow: 24,
|
|
||||||
p: 3,
|
|
||||||
borderRadius: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Modal Header */}
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
||||||
<Typography variant="h6" fontWeight={600}>
|
|
||||||
Add Manager
|
|
||||||
</Typography>
|
|
||||||
<IconButton onClick={handleClose}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
|
||||||
|
|
||||||
{/* Input Fields */}
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
||||||
<TextField label="Manager Name" fullWidth size="small" value={name} onChange={(e) => setName(e.target.value)} />
|
|
||||||
<TextField label="Station Location" fullWidth size="small" value={stationLocation} onChange={(e) => setStationLocation(e.target.value)} />
|
|
||||||
<TextField label="Phone Number" fullWidth size="small" value={phone} onChange={(e) => setPhone(e.target.value)} />
|
|
||||||
<TextField label="Email" fullWidth size="small" value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
||||||
<TextField label="Password" type="password" fullWidth size="small" value={password} onChange={(e) => setPassword(e.target.value)} />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Submit Button */}
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}>
|
|
||||||
<Button variant="contained" onClick={handleSubmit}>
|
|
||||||
Add Manager
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddManagerModal;
|
|
|
@ -14,44 +14,49 @@ interface ForgotPasswordProps {
|
||||||
|
|
||||||
export default function ForgotPassword({ open, handleClose }: ForgotPasswordProps) {
|
export default function ForgotPassword({ open, handleClose }: ForgotPasswordProps) {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
component: 'form',
|
component: "form",
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handleClose();
|
handleClose();
|
||||||
},
|
},
|
||||||
sx: { backgroundImage: 'none' },
|
sx: { backgroundImage: "none" },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle>Reset password</DialogTitle>
|
<DialogTitle>Reset password</DialogTitle>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '100%' }}
|
sx={{
|
||||||
>
|
display: "flex",
|
||||||
<DialogContentText>
|
flexDirection: "column",
|
||||||
Enter your account's email address, and we'll send you a link to
|
gap: 2,
|
||||||
reset your password.
|
width: "100%",
|
||||||
</DialogContentText>
|
}}
|
||||||
<OutlinedInput
|
>
|
||||||
autoFocus
|
<DialogContentText>
|
||||||
required
|
Enter your account's email address, and we'll send
|
||||||
margin="dense"
|
you a link to reset your password.
|
||||||
id="email"
|
</DialogContentText>
|
||||||
name="email"
|
<OutlinedInput
|
||||||
label="Email address"
|
autoFocus
|
||||||
placeholder="Email address"
|
required
|
||||||
type="email"
|
margin="dense"
|
||||||
fullWidth
|
id="email"
|
||||||
/>
|
name="email"
|
||||||
</DialogContent>
|
label="Email address"
|
||||||
<DialogActions sx={{ pb: 3, px: 3 }}>
|
placeholder="Email address"
|
||||||
<Button onClick={handleClose}>Cancel</Button>
|
type="email"
|
||||||
<Button variant="contained" type="submit">
|
fullWidth
|
||||||
Continue
|
/>
|
||||||
</Button>
|
</DialogContent>
|
||||||
</DialogActions>
|
<DialogActions sx={{ pb: 3, px: 3 }}>
|
||||||
</Dialog>
|
<Button onClick={handleClose}>Cancel</Button>
|
||||||
|
<Button variant="contained" type="submit">
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,15 +50,15 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
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}>
|
||||||
|
|
|
@ -1,191 +0,0 @@
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Typography,
|
|
||||||
TextField,
|
|
||||||
Modal,
|
|
||||||
IconButton,
|
|
||||||
} from "@mui/material";
|
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
|
||||||
import { useForm, Controller } from "react-hook-form";
|
|
||||||
|
|
||||||
interface EditModalProps {
|
|
||||||
open: boolean;
|
|
||||||
handleClose: () => void;
|
|
||||||
handleCreate: (data: FormData) => void;
|
|
||||||
handleUpdate: (id: string, data: FormData) => void;
|
|
||||||
editRow: any | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
managerName: string;
|
|
||||||
stationName: string;
|
|
||||||
stationLocation: string;
|
|
||||||
phoneNumber: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EditModal: React.FC<EditModalProps> = ({
|
|
||||||
open,
|
|
||||||
handleClose,
|
|
||||||
handleCreate,
|
|
||||||
handleUpdate,
|
|
||||||
editRow,
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
formState: { errors },
|
|
||||||
setValue,
|
|
||||||
reset,
|
|
||||||
} = useForm<FormData>({
|
|
||||||
defaultValues: {
|
|
||||||
managerName: "",
|
|
||||||
stationName: "",
|
|
||||||
stationLocation: "",
|
|
||||||
phoneNumber: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Populate form fields when `editRow` changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (editRow) {
|
|
||||||
setValue("managerName", editRow.name || "");
|
|
||||||
setValue("stationName", editRow.stationName || "");
|
|
||||||
setValue("stationLocation", editRow.registeredAddress || "");
|
|
||||||
setValue("phoneNumber", editRow.phone || "");
|
|
||||||
} else {
|
|
||||||
reset({ // ✅ Ensure default values are reset when adding a new manager
|
|
||||||
managerName: "",
|
|
||||||
stationName: "",
|
|
||||||
stationLocation: "",
|
|
||||||
phoneNumber: "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [editRow, setValue, reset]);
|
|
||||||
|
|
||||||
|
|
||||||
const onSubmit = (data: FormData) => {
|
|
||||||
if (editRow) {
|
|
||||||
handleUpdate({
|
|
||||||
id: editRow.id,
|
|
||||||
managerName: data.managerName,
|
|
||||||
stationLocation: data.stationLocation,
|
|
||||||
phoneNumber: data.phoneNumber
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
handleCreate(data);
|
|
||||||
}
|
|
||||||
handleClose();
|
|
||||||
reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal open={open} onClose={handleClose} aria-labelledby="edit-manager-modal">
|
|
||||||
<Box
|
|
||||||
component="form"
|
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
width: 400,
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
boxShadow: 24,
|
|
||||||
p: 3,
|
|
||||||
borderRadius: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Header */}
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
||||||
<Typography variant="h6" fontWeight={600}>
|
|
||||||
{editRow ? "Edit Manager" : "Add Manager"}
|
|
||||||
</Typography>
|
|
||||||
<IconButton onClick={handleClose}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Horizontal Line */}
|
|
||||||
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
|
||||||
|
|
||||||
{/* Input Fields */}
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
|
||||||
{/* Manager Name */}
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>Manager Name</Typography>
|
|
||||||
<Controller
|
|
||||||
name="managerName"
|
|
||||||
control={control}
|
|
||||||
rules={{ required: "Manager Name is required" }}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField {...field} fullWidth placeholder="Enter Manager Name" size="small" error={!!errors.managerName} helperText={errors.managerName?.message} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Station Name */}
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
|
||||||
{/* Station Location */}
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>Station Location</Typography>
|
|
||||||
<Controller
|
|
||||||
name="stationLocation"
|
|
||||||
control={control}
|
|
||||||
rules={{ required: "Station Location is required" }}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField {...field} fullWidth placeholder="Enter Station Location" size="small" error={!!errors.stationLocation} helperText={errors.stationLocation?.message} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Phone Number */}
|
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
||||||
<Typography variant="body2" fontWeight={500} mb={0.5}>Phone Number</Typography>
|
|
||||||
<Controller
|
|
||||||
name="phoneNumber"
|
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
required: "Phone number is required",
|
|
||||||
pattern: {
|
|
||||||
value: /^[0-9]+$/,
|
|
||||||
message: "Only numbers are allowed",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField {...field} fullWidth placeholder="Enter Phone Number" size="small" error={!!errors.phoneNumber} helperText={errors.phoneNumber?.message} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Submit Button */}
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 3 }}>
|
|
||||||
<Button variant="contained" type="submit">
|
|
||||||
{editRow ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditModal;
|
|
|
@ -1,240 +1,157 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Box, Button, Typography, TextField, InputAdornment, IconButton } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
|
IconButton,
|
||||||
|
} from "@mui/material";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
import EqualizerIcon from "@mui/icons-material/Tune";
|
import EqualizerIcon from "@mui/icons-material/Tune";
|
||||||
import CustomTable, { Column } from "../../components/CustomTable";
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
import AddManagerModal from "../../pages/AddManagerModal";
|
import AddManagerModal from "../../components/AddManagerModal";
|
||||||
import EditUserModal from "../EditUserModal";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { fetchManagerList, addManager,updateManager,deleteManager } from "../../redux/slices/managerSlice";
|
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||||
|
import {
|
||||||
|
managerList,
|
||||||
|
addManager,
|
||||||
|
updateManager,
|
||||||
|
deleteManager,
|
||||||
|
} from "../../redux/slices/managerSlice";
|
||||||
import { RootState, AppDispatch } from "../../redux/store/store";
|
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import EditManagerModal from "../../components/EditManagerModal";
|
||||||
|
|
||||||
export default function ManagerList() {
|
export default function ManagerList() {
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
const { managers, isLoading } = useSelector((state: RootState) => state.managerReducer);
|
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
|
||||||
|
const [editRow, setEditRow] = useState<any>(null);
|
||||||
|
const { reset } = useForm();
|
||||||
|
|
||||||
const [search, setSearch] = useState("");
|
const [deleteModal, setDeleteModal] = useState<boolean>(false);
|
||||||
const [addButtonModal, setAddButtonModal] = useState(false);
|
const [viewModal, setViewModal] = useState<boolean>(false);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [rowData, setRowData] = useState<any | null>(null);
|
||||||
const [rowData, setRowData] = useState(null);
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [viewModal, setViewModal] = useState(false);
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const managers = useSelector(
|
||||||
|
(state: RootState) => state.managerReducer.managers
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchManagerList()); // Fetch data when component mounts
|
dispatch(managerList());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setRowData(null); // Reset row data when opening for new admin
|
||||||
|
setAddModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
// Function to handle adding a manager
|
const handleCloseModal = () => {
|
||||||
const handleAddManager = async (newManager: {
|
setAddModalOpen(false);
|
||||||
name: string;
|
setEditModalOpen(false);
|
||||||
registeredAddress: string;
|
setRowData(null);
|
||||||
phone: string;
|
reset();
|
||||||
email: string;
|
};
|
||||||
password: string;
|
|
||||||
}) => {
|
const handleAddManager = async (data: {
|
||||||
await dispatch(addManager(newManager));
|
name: string;
|
||||||
dispatch(fetchManagerList()); // Refresh list after adding
|
email: string;
|
||||||
handleCloseModal(); // Close modal after adding
|
phone: string;
|
||||||
};
|
registeredAddress: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await dispatch(addManager(data)); // Dispatch action to add manager
|
||||||
|
await dispatch(managerList()); // Fetch the updated list
|
||||||
|
handleCloseModal(); // Close the modal
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding manager", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = async (
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
email: string,
|
||||||
|
phone: string,
|
||||||
|
registeredAddress: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Creating the managerData object to match the expected payload structure
|
||||||
|
const managerData = {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
registeredAddress,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dispatching the updateManager action with the correct payload structure
|
||||||
|
await dispatch(updateManager({ id, managerData }));
|
||||||
|
await dispatch(managerList()); // Refresh the manager list after updating
|
||||||
|
handleCloseModal(); // Close the modal after updating
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Update failed", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Remove 'stationName' from columns
|
||||||
|
const categoryColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "name", label: "Name" },
|
||||||
|
{ id: "email", label: "Email" },
|
||||||
|
{ id: "phone", label: "Phone" },
|
||||||
|
{ id: "registeredAddress", label: "Station Location" },
|
||||||
|
|
||||||
// Function to handle updating a
|
{ id: "action", label: "Action", align: "center" },
|
||||||
const handleUpdateManager = async (updatedManager) => {
|
];
|
||||||
if (!updatedManager || typeof updatedManager !== "object") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rowData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const managerId = rowData.id;
|
|
||||||
|
|
||||||
if (!updatedManager.managerName || !updatedManager.stationLocation || !updatedManager.phoneNumber) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await dispatch(updateManager({
|
|
||||||
id: managerId,
|
|
||||||
name: updatedManager.managerName,
|
|
||||||
registeredAddress: updatedManager.stationLocation,
|
|
||||||
phone: updatedManager.phoneNumber,
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (response?.payload?.statusCode === 200) {
|
|
||||||
await dispatch(fetchManagerList()); // ✅ Refresh list after update
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCloseModal();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Update failed:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Function to handle deleting a manager
|
|
||||||
|
|
||||||
|
// Update rows to remove 'stationName'
|
||||||
|
const categoryRows = managers?.map(
|
||||||
|
(
|
||||||
|
manager: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) => ({
|
||||||
|
id: manager?.id,
|
||||||
|
srno: index + 1,
|
||||||
|
name: manager?.name,
|
||||||
|
email: manager?.email,
|
||||||
|
phone: manager.phone ?? "NA",
|
||||||
|
registeredAddress: manager?.registeredAddress ?? "NA",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const handleDeleteManager = async () => {
|
return (
|
||||||
if (!rowData?.id) {
|
<>
|
||||||
console.error("❌ No manager ID found for deletion!");
|
<CustomTable
|
||||||
return;
|
columns={categoryColumns}
|
||||||
}
|
rows={categoryRows}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
try {
|
deleteModal={deleteModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
const response = await dispatch(deleteManager(rowData.id));
|
viewModal={viewModal}
|
||||||
|
setRowData={setRowData}
|
||||||
if (response?.payload) {
|
setModalOpen={() => setEditModalOpen(true)}
|
||||||
|
tableType="manager"
|
||||||
dispatch(fetchManagerList()); // Refresh list after deletion
|
handleClickOpen={handleClickOpen}
|
||||||
} else {
|
/>
|
||||||
console.error("❌ Deletion failed!", response);
|
<AddManagerModal
|
||||||
}
|
open={addModalOpen}
|
||||||
} catch (error) {
|
handleClose={handleCloseModal}
|
||||||
console.error("❌ Error deleting manager:", error);
|
handleAddManager={handleAddManager}
|
||||||
}
|
/>
|
||||||
|
<EditManagerModal
|
||||||
setDeleteModal(false); // Close delete modal
|
open={editModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
handleCloseModal();
|
handleUpdate={handleUpdate}
|
||||||
};
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to handle search input change
|
|
||||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setSearch(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Open the Add Manager Modal
|
|
||||||
const handleClickOpen = () => {
|
|
||||||
setRowData(null);
|
|
||||||
setAddButtonModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Close all modals
|
|
||||||
const handleCloseModal = () => {
|
|
||||||
setAddButtonModal(false);
|
|
||||||
setModalOpen(false);
|
|
||||||
setRowData(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Table columns definition
|
|
||||||
const managerColumns: Column[] = [
|
|
||||||
{ id: "name", label: "Manager Name" },
|
|
||||||
{ id: "registeredAddress", label: "Station Location" },
|
|
||||||
{ id: "phone", label: "Phone Number" },
|
|
||||||
{ id: "action", label: "Action", align: "center" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Filtered manager list based on search input
|
|
||||||
const filteredManagers = managers.filter((manager) =>
|
|
||||||
Object.values(manager).some((value) =>
|
|
||||||
typeof value === "string" && value.toLowerCase().includes(search.toLowerCase())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Header Section */}
|
|
||||||
<Box sx={{ width: "100%", display: "flex", flexDirection: "column", gap: 2, mt: 2 }}>
|
|
||||||
<Typography component="h2" variant="h6" sx={{ fontWeight: 600 }}>
|
|
||||||
Managers
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
||||||
<TextField
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
placeholder="Search"
|
|
||||||
value={search}
|
|
||||||
onChange={handleSearchChange}
|
|
||||||
InputProps={{
|
|
||||||
startAdornment: (
|
|
||||||
<InputAdornment position="start">
|
|
||||||
<SearchIcon />
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
|
||||||
<Button variant="contained" size="medium" onClick={handleClickOpen}>
|
|
||||||
Add Manager
|
|
||||||
</Button>
|
|
||||||
<IconButton>
|
|
||||||
<EqualizerIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Table Section */}
|
|
||||||
{isLoading ? (
|
|
||||||
<Typography>Loading managers...</Typography>
|
|
||||||
) : (
|
|
||||||
<CustomTable
|
|
||||||
columns={managerColumns}
|
|
||||||
rows={filteredManagers.map((manager) => ({
|
|
||||||
id: manager.id,
|
|
||||||
name: manager.name,
|
|
||||||
registeredAddress: manager.registeredAddress,
|
|
||||||
phone: manager.phone,
|
|
||||||
action: (
|
|
||||||
<Box sx={{ display: "flex", gap: 1 }}>
|
|
||||||
<Button onClick={() => { setRowData(manager); setModalOpen(true); }}>Edit</Button>
|
|
||||||
<Button onClick={() => { setRowData(manager); setViewModal(true); }}>View</Button>
|
|
||||||
<Button color="error" onClick={() => { setRowData(manager); setDeleteModal(true); }}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
),
|
|
||||||
|
|
||||||
}))}
|
|
||||||
tableType="managers"
|
|
||||||
setRowData={setRowData}
|
|
||||||
setModalOpen={setModalOpen}
|
|
||||||
setViewModal={setViewModal}
|
|
||||||
viewModal={viewModal}
|
|
||||||
setDeleteModal={setDeleteModal}
|
|
||||||
deleteModal={deleteModal}
|
|
||||||
handleDeleteButton={handleDeleteManager}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Modals */}
|
|
||||||
<EditUserModal
|
|
||||||
open={modalOpen}
|
|
||||||
handleClose={handleCloseModal}
|
|
||||||
editRow={rowData}
|
|
||||||
handleUpdate={handleUpdateManager} // Pass function
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<AddManagerModal
|
|
||||||
open={addButtonModal}
|
|
||||||
handleClose={handleCloseModal}
|
|
||||||
handleAddManager={(data) =>
|
|
||||||
handleAddManager({
|
|
||||||
name: data.managerName,
|
|
||||||
registeredAddress: data.stationLocation,
|
|
||||||
phone: data.phoneNumber,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,55 +12,18 @@ import {
|
||||||
updateVehicle,
|
updateVehicle,
|
||||||
vehicleList,
|
vehicleList,
|
||||||
} from "../../redux/slices/VehicleSlice";
|
} from "../../redux/slices/VehicleSlice";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import AddVehicleModal from "../../components/AddVehicleModal";
|
||||||
const categoryRows = [
|
import EditVehicleModal from "../../components/EditVehicleModal";
|
||||||
{
|
|
||||||
srno: 1,
|
|
||||||
id: 1, // Using a number for 'id'
|
|
||||||
name: "Tesla Model S",
|
|
||||||
brand: "Tesla",
|
|
||||||
imageUrl:
|
|
||||||
"https://example.com/https://imgd-ct.aeplcdn.com/1056x660/n/cw/ec/93821/model-s-exterior-front-view.jpeg?q=80.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
srno: 2,
|
|
||||||
id: 2,
|
|
||||||
name: "BMW X5",
|
|
||||||
brand: "BMW",
|
|
||||||
imageUrl: "https://example.com/bmw_x5.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
srno: 3,
|
|
||||||
id: 3,
|
|
||||||
name: "Audi A6",
|
|
||||||
brand: "Audi",
|
|
||||||
imageUrl: "https://example.com/audi_a6.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
srno: 4,
|
|
||||||
id: 4,
|
|
||||||
name: "Mercedes-Benz S-Class",
|
|
||||||
brand: "Mercedes-Benz",
|
|
||||||
imageUrl: "https://example.com/mercedes_s_class.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
srno: 5,
|
|
||||||
id: 5,
|
|
||||||
name: "Ford Mustang",
|
|
||||||
brand: "Ford",
|
|
||||||
imageUrl: "https://example.com/ford_mustang.jpg",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
export default function VehicleList() {
|
export default function VehicleList() {
|
||||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
|
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
|
||||||
const [editRow, setEditRow] = useState<any>(null);
|
const [editRow, setEditRow] = useState<any>(null);
|
||||||
const { reset } = useForm();
|
const { reset } = useForm();
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
|
const [deleteModal, setDeleteModal] = useState<boolean>(false);
|
||||||
const [viewModal, setViewModal] = React.useState<boolean>(false);
|
const [viewModal, setViewModal] = useState<boolean>(false);
|
||||||
const [rowData, setRowData] = React.useState<any | null>(null);
|
const [rowData, setRowData] = useState<any | null>(null);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const vehicles = useSelector(
|
const vehicles = useSelector(
|
||||||
|
@ -73,32 +36,38 @@ export default function VehicleList() {
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setRowData(null); // Reset row data when opening for new admin
|
setRowData(null); // Reset row data when opening for new admin
|
||||||
setModalOpen(true);
|
setAddModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setModalOpen(false);
|
setAddModalOpen(false);
|
||||||
|
setEditModalOpen(false);
|
||||||
setRowData(null);
|
setRowData(null);
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreate = async (data: {
|
const handleAddVehicle = async (data: {
|
||||||
name: string;
|
vehicleName: string;
|
||||||
brand: string;
|
company: string;
|
||||||
|
modelName: string;
|
||||||
|
chargeType: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
await dispatch(addVehicle(data));
|
await dispatch(addVehicle(data)); // Dispatch action to add vehicle
|
||||||
await dispatch(vehicleList());
|
await dispatch(vehicleList()); // Fetch the updated list
|
||||||
handleCloseModal();
|
handleCloseModal(); // Close the modal
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Creation failed", error);
|
console.error("Error adding vehicle", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdate = async (
|
const handleUpdate = async (
|
||||||
id: number,
|
id: number,
|
||||||
name: string,
|
name: string,
|
||||||
brand: string,
|
company: string,
|
||||||
|
modelName: string,
|
||||||
|
chargeType: string,
|
||||||
imageUrl: string
|
imageUrl: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
@ -106,53 +75,70 @@ export default function VehicleList() {
|
||||||
updateVehicle({
|
updateVehicle({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
brand,
|
company,
|
||||||
|
modelName,
|
||||||
|
chargeType,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await dispatch(vehicleList());
|
await dispatch(vehicleList());
|
||||||
|
handleCloseModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Update failed", error);
|
console.error("Update failed", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoryColumns: Column[] = [
|
const categoryColumns: Column[] = [
|
||||||
{ id: "srno", label: "Sr No" },
|
{ id: "srno", label: "Sr No" },
|
||||||
{ id: "name", label: "Vehicle Name" },
|
{ id: "name", label: "Vehicle Name" },
|
||||||
{ id: "brand", label: "Brand" },
|
{ id: "company", label: "Company" },
|
||||||
|
{ id: "modelName", label: "Model Name" },
|
||||||
|
{ id: "chargeType", label: "Charge Type" },
|
||||||
{ id: "imageUrl", label: "Image" },
|
{ id: "imageUrl", label: "Image" },
|
||||||
{ id: "action", label: "Action", align: "center" },
|
{ id: "action", label: "Action", align: "center" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredVehicles = vehicles?.filter(
|
const filteredVehicles = vehicles?.filter(
|
||||||
(vehicle) =>
|
(vehicle) =>
|
||||||
vehicle.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
vehicle.brand.toLowerCase().includes(searchTerm.toLowerCase())
|
vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
vehicle.modelName
|
||||||
|
?.toLowerCase()
|
||||||
|
.includes(searchTerm.toLowerCase()) ||
|
||||||
|
vehicle.chargeType
|
||||||
|
?.toLowerCase()
|
||||||
|
.includes(searchTerm.toLowerCase()) ||
|
||||||
|
vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
// const categoryRows = filteredVehicles?.length
|
const categoryRows = filteredVehicles?.length
|
||||||
// ? filteredVehicles?.map(
|
? filteredVehicles?.map(
|
||||||
// (
|
(
|
||||||
// vehicle: {
|
vehicle: {
|
||||||
// id: number;
|
id: number;
|
||||||
// name: string;
|
name: string;
|
||||||
// brand: string;
|
company: string;
|
||||||
// imageUrl: string;
|
modelName: string;
|
||||||
// },
|
chargeType: string;
|
||||||
// index: number
|
imageUrl: string;
|
||||||
// ) => ({
|
},
|
||||||
// id: vehicle?.id,
|
index: number
|
||||||
// srno: index + 1,
|
) => ({
|
||||||
// name: vehicle?.name,
|
id: vehicle?.id,
|
||||||
// brand: vehicle?.brand,
|
srno: index + 1,
|
||||||
// imageUrl: vehicle?.imageUrl,
|
name: vehicle?.name,
|
||||||
// })
|
company: vehicle?.company,
|
||||||
// )
|
modelName: vehicle?.modelName,
|
||||||
// : [];
|
chargeType: vehicle?.chargeType,
|
||||||
|
imageUrl: vehicle?.imageUrl,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
|
|
||||||
<CustomTable
|
<CustomTable
|
||||||
columns={categoryColumns}
|
columns={categoryColumns}
|
||||||
rows={categoryRows}
|
rows={categoryRows}
|
||||||
|
@ -161,20 +147,22 @@ export default function VehicleList() {
|
||||||
setViewModal={setViewModal}
|
setViewModal={setViewModal}
|
||||||
viewModal={viewModal}
|
viewModal={viewModal}
|
||||||
setRowData={setRowData}
|
setRowData={setRowData}
|
||||||
setModalOpen={setModalOpen}
|
setModalOpen={() => setEditModalOpen(true)}
|
||||||
tableType="vehicle"
|
tableType="vehicle"
|
||||||
handleClickOpen={handleClickOpen}
|
handleClickOpen={handleClickOpen}
|
||||||
/>
|
/>
|
||||||
{/* <AddEditCategoryModal
|
<AddVehicleModal
|
||||||
open={modalOpen}
|
open={addModalOpen}
|
||||||
handleClose={handleCloseModal}
|
handleClose={handleCloseModal}
|
||||||
editRow={rowData}
|
handleAddVehicle={handleAddVehicle}
|
||||||
/>
|
/>
|
||||||
<DeleteModal
|
<EditVehicleModal
|
||||||
open={deleteModal}
|
open={editModalOpen}
|
||||||
setDeleteModal={setDeleteModal}
|
handleClose={handleCloseModal}
|
||||||
handleDelete={handleDelete}
|
handleUpdate={handleUpdate}
|
||||||
/> */}
|
editRow={rowData}
|
||||||
|
/>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,15 @@ import http from "../../lib/https";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
interface Vehicle {
|
interface Vehicle {
|
||||||
id:number;
|
id: number;
|
||||||
name:string;
|
name: string;
|
||||||
brand:string;
|
company: string;
|
||||||
imageUrl:string;
|
modelName: string;
|
||||||
|
chargeType: string;
|
||||||
|
imageUrl: string;
|
||||||
}
|
}
|
||||||
interface VehicleState {
|
interface VehicleState {
|
||||||
vehicles:Vehicle[];
|
vehicles: Vehicle[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
}
|
}
|
||||||
|
@ -20,39 +21,42 @@ const initialState: VehicleState = {
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const vehicleList = createAsyncThunk<Vehicle, void, { rejectValue: string }>(
|
export const vehicleList = createAsyncThunk<
|
||||||
"fetchVehicles",
|
Vehicle,
|
||||||
async (_, { rejectWithValue }) => {
|
void,
|
||||||
try {
|
{ rejectValue: string }
|
||||||
const token = localStorage?.getItem("authToken");
|
>("fetchVehicles", async (_, { rejectWithValue }) => {
|
||||||
if (!token) throw new Error("No token found");
|
try {
|
||||||
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
const response = await http.get("/");
|
const response = await http.get("/get-vehicles");
|
||||||
|
|
||||||
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) {
|
||||||
toast.error("Error Fetching Profile" + error);
|
toast.error("Error Fetching Profile" + error);
|
||||||
return rejectWithValue(
|
return rejectWithValue(
|
||||||
error?.response?.data?.message || "An error occurred"
|
error?.response?.data?.message || "An error occurred"
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
//Add Vehicle
|
//Add Vehicle
|
||||||
export const addVehicle = createAsyncThunk<
|
export const addVehicle = createAsyncThunk<
|
||||||
Vehicle,
|
Vehicle,
|
||||||
{
|
{
|
||||||
name: string;
|
name: string;
|
||||||
brand: string;
|
company: string;
|
||||||
|
modelName: string;
|
||||||
|
chargeType: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
},
|
},
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("/AddVehicle", async (data, { rejectWithValue }) => {
|
>("/AddVehicle", async (data, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.post("/", data);
|
const response = await http.post("create-vehicle", data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return rejectWithValue(
|
return rejectWithValue(
|
||||||
|
@ -61,13 +65,15 @@ export const addVehicle = createAsyncThunk<
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Update Vehicle details
|
// Update Vehicle details
|
||||||
export const updateVehicle = createAsyncThunk(
|
export const updateVehicle = createAsyncThunk(
|
||||||
"updateVehicle",
|
"updateVehicle",
|
||||||
async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => {
|
async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const response = await http.put(`/${id}`, vehicleData);
|
const response = await http.patch(
|
||||||
|
`${id}/update-vehicle`,
|
||||||
|
vehicleData
|
||||||
|
);
|
||||||
toast.success("Vehicle Deatils updated successfully");
|
toast.success("Vehicle Deatils updated successfully");
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -78,6 +84,23 @@ export const updateVehicle = createAsyncThunk(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
export const deleteVehicle = createAsyncThunk<
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("deleteVehicle", async (id, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.delete(`/${id}/delete-vehicle`);
|
||||||
|
toast.success(response.data?.message);
|
||||||
|
return id;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error deleting the vehicle" + error);
|
||||||
|
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
const vehicleSlice = createSlice({
|
const vehicleSlice = createSlice({
|
||||||
name: "vehicle",
|
name: "vehicle",
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -125,6 +148,18 @@ const vehicleSlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(updateVehicle.rejected, (state) => {
|
.addCase(updateVehicle.rejected, (state) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
})
|
||||||
|
.addCase(deleteVehicle.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(deleteVehicle.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.vehicles = state.vehicles.filter(
|
||||||
|
(vehicle) => String(vehicle.id) !== String(action.payload)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.addCase(deleteVehicle.rejected, (state) => {
|
||||||
|
state.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -90,11 +90,8 @@ const initialState: AuthState = {
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
// error: null,
|
// error: null,
|
||||||
//Eknoor singh
|
|
||||||
//date:- 12-Feb-2025
|
|
||||||
//initial state of token set to null
|
|
||||||
token: null,
|
token: null,
|
||||||
role: null, // New field for role
|
role: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const authSlice = createSlice({
|
const authSlice = createSlice({
|
||||||
|
@ -144,14 +141,7 @@ const authSlice = createSlice({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// created by Jaanvi and Eknoor
|
|
||||||
//AdminList
|
|
||||||
|
|
||||||
//Eknoor singh
|
|
||||||
//date:- 12-Feb-2025
|
|
||||||
//Reducers for fetching profiles has been implemente
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// export const { logout } = authSlice.actions;
|
|
||||||
export default authSlice.reducer;
|
export default authSlice.reducer;
|
||||||
|
|
|
@ -2,188 +2,179 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import http from "../../lib/https";
|
import http from "../../lib/https";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
// Define TypeScript types
|
// Define the Manager interface based on the payload
|
||||||
|
|
||||||
interface Manager {
|
interface Manager {
|
||||||
id: string;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
phone?: string;
|
phone: string;
|
||||||
role: string;
|
registeredAddress: string;
|
||||||
|
roleId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ManagerState {
|
interface ManagerState {
|
||||||
managers: Manager[];
|
managers: Manager[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
const initialState: ManagerState = {
|
const initialState: ManagerState = {
|
||||||
managers: [],
|
managers: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch Manager List
|
// Fetch Manager List (Async Thunk)
|
||||||
export const fetchManagerList = createAsyncThunk<Manager[], void, { rejectValue: string }>(
|
export const managerList = createAsyncThunk<
|
||||||
"fetchManagers",
|
Manager[],
|
||||||
async (_, { rejectWithValue }) => {
|
void,
|
||||||
try {
|
{ rejectValue: string }
|
||||||
const response = await http.get("manager-list");
|
>("fetchManagers", async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
if (!response.data?.data) throw new Error("Invalid API response");
|
const response = await http.get("manager-list");
|
||||||
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Managers: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return response.data.data;
|
// Create Manager (Async Thunk)
|
||||||
} catch (error: any) {
|
export const addManager = createAsyncThunk<
|
||||||
toast.error("Error Fetching Managers: " + error);
|
Manager,
|
||||||
return rejectWithValue(
|
Manager,
|
||||||
error?.response?.data?.message || "An error occurred"
|
{ rejectValue: string }
|
||||||
);
|
>("addManager", async (data, { rejectWithValue }) => {
|
||||||
}
|
try {
|
||||||
}
|
const response = await http.post("create-manager", data);
|
||||||
);
|
toast.success("Manager created successfully");
|
||||||
|
return response.data?.data;
|
||||||
// Create Manager
|
} catch (error: any) {
|
||||||
export const addManager = createAsyncThunk(
|
toast.error("Error creating manager: " + error.message);
|
||||||
"addManager",
|
return rejectWithValue(
|
||||||
async (data, { rejectWithValue }) => {
|
error.response?.data?.message || "An error occurred"
|
||||||
try {
|
);
|
||||||
|
}
|
||||||
const response = await http.post("create-manager", data);
|
});
|
||||||
toast.success("Manager created successfully");
|
|
||||||
|
|
||||||
// ✅ Ensure the response contains the expected data
|
|
||||||
if (!response.data || !response.data.data || !response.data.data.id) {
|
|
||||||
console.error("❌ ERROR: Missing manager ID in response", response.data);
|
|
||||||
throw new Error("Invalid API response: Missing manager ID");
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data.data;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("❌ API Error:", error?.response?.data || error);
|
|
||||||
return rejectWithValue(error?.response?.data?.message || "An error occurred");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// Update Manager (Async Thunk)
|
||||||
export const updateManager = createAsyncThunk<
|
export const updateManager = createAsyncThunk<
|
||||||
Manager,
|
Manager,
|
||||||
{ id: string; name?: string; email?: string; phone?: string; role?: string; registeredAddress?: string },
|
{ id: number; managerData: Manager },
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>(
|
>("updateManager", async ({ id, managerData }, { rejectWithValue }) => {
|
||||||
"updateManager",
|
if (!id) {
|
||||||
async ({ id, ...managerData }, { rejectWithValue }) => {
|
return rejectWithValue("Manager ID is required.");
|
||||||
try {
|
}
|
||||||
|
try {
|
||||||
|
const response = await http.put(`/${id}/update-manager`, managerData);
|
||||||
|
toast.success("Manager updated successfully");
|
||||||
|
return response?.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error updating manager: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const response = await http.put(`${id}/update-manager`, managerData);
|
// Delete Manager (Async Thunk)
|
||||||
|
|
||||||
|
|
||||||
toast.success("Manager updated successfully!");
|
|
||||||
return response.data.data; // ✅ Extracting correct response data
|
|
||||||
} catch (error: any) {
|
|
||||||
|
|
||||||
toast.error("Error updating manager: " + error);
|
|
||||||
return rejectWithValue(
|
|
||||||
error.response?.data?.message || "An error occurred"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Delete Manager
|
|
||||||
export const deleteManager = createAsyncThunk<
|
export const deleteManager = createAsyncThunk<
|
||||||
string,
|
string,
|
||||||
string,
|
string,
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>(
|
>("deleteManager", async (id, { rejectWithValue }) => {
|
||||||
"deleteManager",
|
try {
|
||||||
async (id, { rejectWithValue }) => {
|
await http.delete(`/${id}/delete-manager`);
|
||||||
try {
|
toast.success("Manager deleted successfully!");
|
||||||
await http.delete(`/${id}/delete-manager`);
|
return id;
|
||||||
toast.success("Manager deleted successfully!");
|
} catch (error: any) {
|
||||||
return id; // Return the ID of the deleted manager
|
toast.error("Error deleting manager: " + error.message);
|
||||||
} catch (error: any) {
|
return rejectWithValue(
|
||||||
toast.error("Error deleting manager: " + error);
|
error.response?.data?.message || "An error occurred"
|
||||||
return rejectWithValue(
|
);
|
||||||
error.response?.data?.message || "An error occurred"
|
}
|
||||||
);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create Slice
|
// Create Slice
|
||||||
const managerSlice = createSlice({
|
const managerSlice = createSlice({
|
||||||
name: "fetchManagers",
|
name: "maanger",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {},
|
reducers: {},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder
|
builder
|
||||||
// Fetch Managers
|
// Fetch Managers
|
||||||
.addCase(fetchManagerList.pending, (state) => {
|
.addCase(managerList.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(
|
.addCase(
|
||||||
fetchManagerList.fulfilled,
|
managerList.fulfilled,
|
||||||
(state, action: PayloadAction<Manager[]>) => {
|
(state, action: PayloadAction<Manager[]>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.managers = action.payload;
|
state.managers = action.payload;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(fetchManagerList.rejected, (state, action) => {
|
.addCase(managerList.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to fetch managers";
|
state.error = action.payload || "Failed to fetch managers";
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add Manager
|
// Add Manager
|
||||||
.addCase(addManager.pending, (state) => {
|
.addCase(addManager.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
.addCase(addManager.fulfilled, (state, action: PayloadAction<Manager>) => {
|
.addCase(
|
||||||
state.loading = false;
|
addManager.fulfilled,
|
||||||
state.managers.push(action.payload);
|
(state, action: PayloadAction<Manager>) => {
|
||||||
})
|
state.loading = false;
|
||||||
.addCase(addManager.rejected, (state, action) => {
|
state.managers.push(action.payload);
|
||||||
state.loading = false;
|
}
|
||||||
})
|
)
|
||||||
|
.addCase(addManager.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to add manager";
|
||||||
|
})
|
||||||
|
|
||||||
// Update Manager
|
// Update Manager
|
||||||
.addCase(updateManager.pending, (state) => {
|
.addCase(updateManager.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
.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(m => m.id === updatedManager.id);
|
// const index = state.managers.findIndex(
|
||||||
if (index !== -1) {
|
// (manager) => manager.id === updatedManager.id
|
||||||
state.managers[index] = { ...state.managers[index], ...updatedManager }; // 🔥 Merge updated fields
|
// );
|
||||||
}
|
// if (index !== -1) {
|
||||||
})
|
// state.managers[index] = updatedManager; // Update the manager in the state
|
||||||
|
// }
|
||||||
.addCase(updateManager.rejected, (state) => {
|
})
|
||||||
state.loading = false;
|
.addCase(updateManager.rejected, (state, action) => {
|
||||||
})
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to update manager";
|
||||||
|
})
|
||||||
|
|
||||||
// Delete Manager
|
// Delete Manager
|
||||||
// Delete Manager
|
.addCase(deleteManager.pending, (state) => {
|
||||||
.addCase(deleteManager.pending, (state) => {
|
state.loading = true;
|
||||||
state.loading = true;
|
})
|
||||||
})
|
.addCase(deleteManager.fulfilled, (state, action) => {
|
||||||
.addCase(deleteManager.fulfilled, (state, action) => {
|
state.loading = false;
|
||||||
state.loading = false;
|
state.managers = state.managers.filter(
|
||||||
state.managers = state.managers.filter(manager => manager.id !== action.payload);
|
(manager) => manager.id !== action.payload
|
||||||
})
|
);
|
||||||
.addCase(deleteManager.rejected, (state) => {
|
})
|
||||||
state.loading = false;
|
.addCase(deleteManager.rejected, (state, action) => {
|
||||||
});
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to delete manager";
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default managerSlice.reducer;
|
export default managerSlice.reducer;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export const dataDisplayCustomizations = {
|
||||||
padding: '8px',
|
padding: '8px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
|
||||||
gap: 0,
|
gap: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -385,7 +385,7 @@ export const inputsCustomizations = {
|
||||||
color: (theme.vars || theme).palette.text.primary,
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
backgroundColor: (theme.vars || theme).palette.background.default,
|
backgroundColor: "#272727",
|
||||||
transition: 'border 120ms ease-in',
|
transition: 'border 120ms ease-in',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
borderColor: gray[400],
|
borderColor: gray[400],
|
||||||
|
|
|
@ -1,445 +1,459 @@
|
||||||
import * as React from 'react';
|
import * as React from "react";
|
||||||
import { alpha, Theme, Components } from '@mui/material/styles';
|
import { alpha, Theme, Components } from "@mui/material/styles";
|
||||||
import { outlinedInputClasses } from '@mui/material/OutlinedInput';
|
import { outlinedInputClasses } from "@mui/material/OutlinedInput";
|
||||||
import { svgIconClasses } from '@mui/material/SvgIcon';
|
import { svgIconClasses } from "@mui/material/SvgIcon";
|
||||||
import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup';
|
import { toggleButtonGroupClasses } from "@mui/material/ToggleButtonGroup";
|
||||||
import { toggleButtonClasses } from '@mui/material/ToggleButton';
|
import { toggleButtonClasses } from "@mui/material/ToggleButton";
|
||||||
import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded';
|
import CheckBoxOutlineBlankRoundedIcon from "@mui/icons-material/CheckBoxOutlineBlankRounded";
|
||||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
import CheckRoundedIcon from "@mui/icons-material/CheckRounded";
|
||||||
import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
|
import RemoveRoundedIcon from "@mui/icons-material/RemoveRounded";
|
||||||
import { gray, brand } from '../themePrimitives';
|
import { gray, brand } from "../themePrimitives";
|
||||||
|
|
||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
export const inputsCustomizations: Components<Theme> = {
|
export const inputsCustomizations: Components<Theme> = {
|
||||||
MuiButtonBase: {
|
MuiButtonBase: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
disableTouchRipple: true,
|
disableTouchRipple: true,
|
||||||
disableRipple: true,
|
disableRipple: true,
|
||||||
},
|
},
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: ({ theme }) => ({
|
root: ({ theme }) => ({
|
||||||
boxSizing: 'border-box',
|
boxSizing: "border-box",
|
||||||
transition: 'all 100ms ease-in',
|
transition: "all 100ms ease-in",
|
||||||
'&:focus-visible': {
|
"&:focus-visible": {
|
||||||
outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`,
|
outline: `3px solid ${alpha(
|
||||||
outlineOffset: '2px',
|
theme.palette.primary.main,
|
||||||
},
|
0.5
|
||||||
}),
|
)}`,
|
||||||
},
|
outlineOffset: "2px",
|
||||||
},
|
},
|
||||||
MuiButton: {
|
}),
|
||||||
styleOverrides: {
|
},
|
||||||
root: ({ theme }) => ({
|
},
|
||||||
boxShadow: 'none',
|
MuiButton: {
|
||||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
styleOverrides: {
|
||||||
textTransform: 'none',
|
root: ({ theme }) => ({
|
||||||
variants: [
|
boxShadow: "none",
|
||||||
{
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
props: {
|
textTransform: "none",
|
||||||
size: 'small',
|
variants: [
|
||||||
},
|
{
|
||||||
style: {
|
props: {
|
||||||
height: '2.25rem',
|
size: "small",
|
||||||
padding: '8px 12px',
|
},
|
||||||
},
|
style: {
|
||||||
},
|
height: "2.25rem",
|
||||||
{
|
padding: "8px 12px",
|
||||||
props: {
|
},
|
||||||
size: 'medium',
|
},
|
||||||
},
|
{
|
||||||
style: {
|
props: {
|
||||||
height: '2.5rem', // 40px
|
size: "medium",
|
||||||
},
|
},
|
||||||
},
|
style: {
|
||||||
{
|
height: "2.5rem", // 40px
|
||||||
props: {
|
},
|
||||||
color: 'primary',
|
},
|
||||||
variant: 'contained',
|
{
|
||||||
},
|
props: {
|
||||||
style: {
|
color: "primary",
|
||||||
color: 'white',
|
variant: "contained",
|
||||||
backgroundColor: gray[900],
|
},
|
||||||
backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`,
|
style: {
|
||||||
boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`,
|
color: "white",
|
||||||
border: `1px solid ${gray[700]}`,
|
backgroundColor: gray[900],
|
||||||
'&:hover': {
|
backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`,
|
||||||
backgroundImage: 'none',
|
boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`,
|
||||||
backgroundColor: gray[700],
|
border: `1px solid ${gray[700]}`,
|
||||||
boxShadow: 'none',
|
"&:hover": {
|
||||||
},
|
backgroundImage: "none",
|
||||||
'&:active': {
|
backgroundColor: gray[700],
|
||||||
backgroundColor: gray[800],
|
boxShadow: "none",
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
"&:active": {
|
||||||
color: 'black',
|
backgroundColor: gray[800],
|
||||||
backgroundColor: gray[50],
|
},
|
||||||
backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`,
|
...theme.applyStyles("dark", {
|
||||||
boxShadow: 'inset 0 -1px 0 hsl(220, 30%, 80%)',
|
color: "black",
|
||||||
border: `1px solid ${gray[50]}`,
|
backgroundColor: gray[50],
|
||||||
'&:hover': {
|
backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`,
|
||||||
backgroundImage: 'none',
|
boxShadow: "inset 0 -1px 0 hsl(220, 30%, 80%)",
|
||||||
backgroundColor: gray[300],
|
border: `1px solid ${gray[50]}`,
|
||||||
boxShadow: 'none',
|
"&:hover": {
|
||||||
},
|
backgroundImage: "none",
|
||||||
'&:active': {
|
backgroundColor: gray[300],
|
||||||
backgroundColor: gray[400],
|
boxShadow: "none",
|
||||||
},
|
},
|
||||||
}),
|
"&:active": {
|
||||||
},
|
backgroundColor: gray[400],
|
||||||
},
|
},
|
||||||
{
|
}),
|
||||||
props: {
|
},
|
||||||
color: 'secondary',
|
},
|
||||||
variant: 'contained',
|
{
|
||||||
},
|
props: {
|
||||||
style: {
|
color: "secondary",
|
||||||
color: 'white',
|
variant: "contained",
|
||||||
backgroundColor: brand[300],
|
},
|
||||||
backgroundImage: `linear-gradient(to bottom, ${alpha(brand[400], 0.8)}, ${brand[500]})`,
|
style: {
|
||||||
boxShadow: `inset 0 2px 0 ${alpha(brand[200], 0.2)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`,
|
color: "white",
|
||||||
border: `1px solid ${brand[500]}`,
|
backgroundColor: brand[300],
|
||||||
'&:hover': {
|
backgroundImage: `linear-gradient(to bottom, ${alpha(
|
||||||
backgroundColor: brand[700],
|
brand[400],
|
||||||
boxShadow: 'none',
|
0.8
|
||||||
},
|
)}, ${brand[500]})`,
|
||||||
'&:active': {
|
boxShadow: `inset 0 2px 0 ${alpha(
|
||||||
backgroundColor: brand[700],
|
brand[200],
|
||||||
backgroundImage: 'none',
|
0.2
|
||||||
},
|
)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`,
|
||||||
},
|
border: `1px solid ${brand[500]}`,
|
||||||
},
|
"&:hover": {
|
||||||
{
|
backgroundColor: brand[700],
|
||||||
props: {
|
boxShadow: "none",
|
||||||
variant: 'outlined',
|
},
|
||||||
},
|
"&:active": {
|
||||||
style: {
|
backgroundColor: brand[700],
|
||||||
color: (theme.vars || theme).palette.text.primary,
|
backgroundImage: "none",
|
||||||
border: '1px solid',
|
},
|
||||||
borderColor: gray[200],
|
},
|
||||||
backgroundColor: alpha(gray[50], 0.3),
|
},
|
||||||
'&:hover': {
|
{
|
||||||
backgroundColor: gray[100],
|
props: {
|
||||||
borderColor: gray[300],
|
variant: "outlined",
|
||||||
},
|
},
|
||||||
'&:active': {
|
style: {
|
||||||
backgroundColor: gray[200],
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
},
|
border: "1px solid",
|
||||||
...theme.applyStyles('dark', {
|
borderColor: gray[200],
|
||||||
backgroundColor: gray[800],
|
backgroundColor: alpha(gray[50], 0.3),
|
||||||
borderColor: gray[700],
|
"&:hover": {
|
||||||
|
backgroundColor: gray[100],
|
||||||
|
borderColor: gray[300],
|
||||||
|
},
|
||||||
|
"&:active": {
|
||||||
|
backgroundColor: gray[200],
|
||||||
|
},
|
||||||
|
...theme.applyStyles("dark", {
|
||||||
|
backgroundColor: gray[800],
|
||||||
|
borderColor: gray[700],
|
||||||
|
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: gray[900],
|
backgroundColor: gray[900],
|
||||||
borderColor: gray[600],
|
borderColor: gray[600],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: gray[900],
|
backgroundColor: gray[900],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
color: 'secondary',
|
color: "secondary",
|
||||||
variant: 'outlined',
|
variant: "outlined",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
color: brand[700],
|
color: brand[700],
|
||||||
border: '1px solid',
|
border: "1px solid",
|
||||||
borderColor: brand[200],
|
borderColor: brand[200],
|
||||||
backgroundColor: brand[50],
|
backgroundColor: brand[50],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: brand[100],
|
backgroundColor: brand[100],
|
||||||
borderColor: brand[400],
|
borderColor: brand[400],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: alpha(brand[200], 0.7),
|
backgroundColor: alpha(brand[200], 0.7),
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
color: brand[50],
|
color: brand[50],
|
||||||
border: '1px solid',
|
border: "1px solid",
|
||||||
borderColor: brand[900],
|
borderColor: brand[900],
|
||||||
backgroundColor: alpha(brand[900], 0.3),
|
backgroundColor: alpha(brand[900], 0.3),
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
borderColor: brand[700],
|
borderColor: brand[700],
|
||||||
backgroundColor: alpha(brand[900], 0.6),
|
backgroundColor: alpha(brand[900], 0.6),
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: alpha(brand[900], 0.5),
|
backgroundColor: alpha(brand[900], 0.5),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
variant: 'text',
|
variant: "text",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
color: gray[600],
|
color: gray[600],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: gray[100],
|
backgroundColor: gray[100],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: gray[200],
|
backgroundColor: gray[200],
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
color: gray[50],
|
color: gray[50],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: gray[700],
|
backgroundColor: gray[700],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: alpha(gray[700], 0.7),
|
backgroundColor: alpha(gray[700], 0.7),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
color: 'secondary',
|
color: "secondary",
|
||||||
variant: 'text',
|
variant: "text",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
color: brand[700],
|
color: brand[700],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: alpha(brand[100], 0.5),
|
backgroundColor: alpha(brand[100], 0.5),
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: alpha(brand[200], 0.7),
|
backgroundColor: alpha(brand[200], 0.7),
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
color: brand[100],
|
color: brand[100],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: alpha(brand[900], 0.5),
|
backgroundColor: alpha(brand[900], 0.5),
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: alpha(brand[900], 0.3),
|
backgroundColor: alpha(brand[900], 0.3),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiIconButton: {
|
MuiIconButton: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: ({ theme }) => ({
|
root: ({ theme }) => ({
|
||||||
boxShadow: 'none',
|
boxShadow: "none",
|
||||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
textTransform: 'none',
|
textTransform: "none",
|
||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
color: (theme.vars || theme).palette.text.primary,
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
border: '1px solid ',
|
border: "1px solid ",
|
||||||
borderColor: gray[200],
|
borderColor: gray[200],
|
||||||
backgroundColor: alpha(gray[50], 0.3),
|
backgroundColor: alpha(gray[50], 0.3),
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: gray[100],
|
backgroundColor: gray[100],
|
||||||
borderColor: gray[300],
|
borderColor: gray[300],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: gray[200],
|
backgroundColor: gray[200],
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
backgroundColor: gray[800],
|
backgroundColor: gray[800],
|
||||||
borderColor: gray[700],
|
borderColor: gray[700],
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: gray[900],
|
backgroundColor: gray[900],
|
||||||
borderColor: gray[600],
|
borderColor: gray[600],
|
||||||
},
|
},
|
||||||
'&:active': {
|
"&:active": {
|
||||||
backgroundColor: gray[900],
|
backgroundColor: gray[900],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
size: 'small',
|
size: "small",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
width: '2.25rem',
|
width: "2.25rem",
|
||||||
height: '2.25rem',
|
height: "2.25rem",
|
||||||
padding: '0.25rem',
|
padding: "0.25rem",
|
||||||
[`& .${svgIconClasses.root}`]: { fontSize: '1rem' },
|
[`& .${svgIconClasses.root}`]: { fontSize: "1rem" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
size: 'medium',
|
size: "medium",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
width: '2.5rem',
|
width: "2.5rem",
|
||||||
height: '2.5rem',
|
height: "2.5rem",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiToggleButtonGroup: {
|
MuiToggleButtonGroup: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: ({ theme }) => ({
|
root: ({ theme }) => ({
|
||||||
borderRadius: '10px',
|
borderRadius: "10px",
|
||||||
boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`,
|
boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`,
|
||||||
[`& .${toggleButtonGroupClasses.selected}`]: {
|
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||||
color: brand[500],
|
color: brand[500],
|
||||||
},
|
},
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
[`& .${toggleButtonGroupClasses.selected}`]: {
|
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||||
color: '#fff',
|
color: "#fff",
|
||||||
},
|
},
|
||||||
boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`,
|
boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiToggleButton: {
|
MuiToggleButton: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: ({ theme }) => ({
|
root: ({ theme }) => ({
|
||||||
padding: '12px 16px',
|
padding: "12px 16px",
|
||||||
textTransform: 'none',
|
textTransform: "none",
|
||||||
borderRadius: '10px',
|
borderRadius: "10px",
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
...theme.applyStyles('dark', {
|
...theme.applyStyles("dark", {
|
||||||
color: gray[400],
|
color: gray[400],
|
||||||
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)',
|
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.5)",
|
||||||
[`&.${toggleButtonClasses.selected}`]: {
|
[`&.${toggleButtonClasses.selected}`]: {
|
||||||
color: brand[300],
|
color: brand[300],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiCheckbox: {
|
MuiCheckbox: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
disableRipple: true,
|
disableRipple: true,
|
||||||
icon: (
|
icon: (
|
||||||
<CheckBoxOutlineBlankRoundedIcon sx={{ color: 'hsla(210, 0%, 0%, 0.0)' }} />
|
<CheckBoxOutlineBlankRoundedIcon
|
||||||
),
|
sx={{ color: "hsla(210, 0%, 0%, 0.0)" }}
|
||||||
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
|
/>
|
||||||
indeterminateIcon: <RemoveRoundedIcon sx={{ height: 14, width: 14 }} />,
|
),
|
||||||
},
|
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
|
||||||
styleOverrides: {
|
indeterminateIcon: (
|
||||||
root: ({ theme }) => ({
|
<RemoveRoundedIcon sx={{ height: 14, width: 14 }} />
|
||||||
margin: 10,
|
),
|
||||||
height: 16,
|
},
|
||||||
width: 16,
|
styleOverrides: {
|
||||||
borderRadius: 5,
|
root: ({ theme }) => ({
|
||||||
border: '1px solid ',
|
margin: 10,
|
||||||
borderColor: alpha(gray[300], 0.8),
|
height: 16,
|
||||||
boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset',
|
width: 16,
|
||||||
backgroundColor: alpha(gray[100], 0.4),
|
borderRadius: 5,
|
||||||
transition: 'border-color, background-color, 120ms ease-in',
|
border: "1px solid ",
|
||||||
'&:hover': {
|
borderColor: alpha(gray[300], 0.8),
|
||||||
borderColor: brand[300],
|
boxShadow: "0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset",
|
||||||
},
|
backgroundColor: alpha(gray[100], 0.4),
|
||||||
'&.Mui-focusVisible': {
|
transition: "border-color, background-color, 120ms ease-in",
|
||||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
"&:hover": {
|
||||||
outlineOffset: '2px',
|
borderColor: brand[300],
|
||||||
borderColor: brand[400],
|
},
|
||||||
},
|
"&.Mui-focusVisible": {
|
||||||
'&.Mui-checked': {
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
color: 'white',
|
outlineOffset: "2px",
|
||||||
backgroundColor: brand[500],
|
borderColor: brand[400],
|
||||||
borderColor: brand[500],
|
},
|
||||||
boxShadow: `none`,
|
"&.Mui-checked": {
|
||||||
'&:hover': {
|
color: "white",
|
||||||
backgroundColor: brand[600],
|
backgroundColor: brand[500],
|
||||||
},
|
borderColor: brand[500],
|
||||||
},
|
boxShadow: `none`,
|
||||||
...theme.applyStyles('dark', {
|
"&:hover": {
|
||||||
borderColor: alpha(gray[700], 0.8),
|
backgroundColor: brand[600],
|
||||||
boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset',
|
},
|
||||||
backgroundColor: alpha(gray[900], 0.8),
|
},
|
||||||
'&:hover': {
|
...theme.applyStyles("dark", {
|
||||||
borderColor: brand[300],
|
borderColor: alpha(gray[700], 0.8),
|
||||||
},
|
boxShadow: "0 0 0 1.5px hsl(210, 0%, 0%) inset",
|
||||||
'&.Mui-focusVisible': {
|
backgroundColor: alpha(gray[900], 0.8),
|
||||||
borderColor: brand[400],
|
"&:hover": {
|
||||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
borderColor: brand[300],
|
||||||
outlineOffset: '2px',
|
},
|
||||||
},
|
"&.Mui-focusVisible": {
|
||||||
}),
|
borderColor: brand[400],
|
||||||
}),
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
},
|
outlineOffset: "2px",
|
||||||
},
|
},
|
||||||
MuiInputBase: {
|
}),
|
||||||
styleOverrides: {
|
}),
|
||||||
root: {
|
},
|
||||||
border: 'none',
|
},
|
||||||
},
|
MuiInputBase: {
|
||||||
input: {
|
styleOverrides: {
|
||||||
'&::placeholder': {
|
root: {
|
||||||
opacity: 0.7,
|
border: "none",
|
||||||
color: gray[500],
|
},
|
||||||
},
|
input: {
|
||||||
},
|
"&::placeholder": {
|
||||||
},
|
opacity: 0.7,
|
||||||
},
|
color: gray[500],
|
||||||
MuiOutlinedInput: {
|
},
|
||||||
styleOverrides: {
|
},
|
||||||
input: {
|
},
|
||||||
padding: 0,
|
},
|
||||||
},
|
MuiOutlinedInput: {
|
||||||
root: ({ theme }) => ({
|
styleOverrides: {
|
||||||
padding: '8px 12px',
|
input: {
|
||||||
color: (theme.vars || theme).palette.text.primary,
|
padding: 0,
|
||||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
},
|
||||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
root: ({ theme }) => ({
|
||||||
backgroundColor: (theme.vars || theme).palette.background.default,
|
padding: "8px 12px",
|
||||||
transition: 'border 120ms ease-in',
|
color: (theme.vars || theme).palette.text.primary,
|
||||||
'&:hover': {
|
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||||
borderColor: gray[400],
|
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||||
},
|
backgroundColor: (theme.vars || theme).palette.background
|
||||||
[`&.${outlinedInputClasses.focused}`]: {
|
.default,
|
||||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
transition: "border 120ms ease-in",
|
||||||
borderColor: brand[400],
|
"&:hover": {
|
||||||
},
|
borderColor: gray[400],
|
||||||
...theme.applyStyles('dark', {
|
},
|
||||||
'&:hover': {
|
[`&.${outlinedInputClasses.focused}`]: {
|
||||||
borderColor: gray[500],
|
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||||
},
|
borderColor: brand[400],
|
||||||
}),
|
},
|
||||||
variants: [
|
...theme.applyStyles("dark", {
|
||||||
{
|
"&:hover": {
|
||||||
props: {
|
borderColor: gray[500],
|
||||||
size: 'small',
|
},
|
||||||
},
|
}),
|
||||||
style: {
|
variants: [
|
||||||
height: '2.25rem',
|
{
|
||||||
},
|
props: {
|
||||||
},
|
size: "small",
|
||||||
{
|
},
|
||||||
props: {
|
style: {
|
||||||
size: 'medium',
|
height: "2.25rem",
|
||||||
},
|
},
|
||||||
style: {
|
},
|
||||||
height: '2.5rem',
|
{
|
||||||
},
|
props: {
|
||||||
},
|
size: "medium",
|
||||||
],
|
},
|
||||||
}),
|
style: {
|
||||||
notchedOutline: {
|
height: "2.5rem",
|
||||||
border: 'none',
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
},
|
}),
|
||||||
MuiInputAdornment: {
|
notchedOutline: {
|
||||||
styleOverrides: {
|
border: "none",
|
||||||
root: ({ theme }) => ({
|
},
|
||||||
color: (theme.vars || theme).palette.grey[500],
|
},
|
||||||
...theme.applyStyles('dark', {
|
},
|
||||||
color: (theme.vars || theme).palette.grey[400],
|
MuiInputAdornment: {
|
||||||
}),
|
styleOverrides: {
|
||||||
}),
|
root: ({ theme }) => ({
|
||||||
},
|
color: (theme.vars || theme).palette.grey[500],
|
||||||
},
|
...theme.applyStyles("dark", {
|
||||||
MuiFormLabel: {
|
color: (theme.vars || theme).palette.grey[400],
|
||||||
styleOverrides: {
|
}),
|
||||||
root: ({ theme }) => ({
|
}),
|
||||||
typography: theme.typography.caption,
|
},
|
||||||
marginBottom: 8,
|
},
|
||||||
}),
|
MuiFormLabel: {
|
||||||
},
|
styleOverrides: {
|
||||||
},
|
root: ({ theme }) => ({
|
||||||
|
typography: theme.typography.caption,
|
||||||
|
marginBottom: 8,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue