Implemented changes as per feedback

This commit is contained in:
Eknoor Singh 2025-02-17 16:03:16 +05:30
parent c869245bbf
commit a751cb1f4f
17 changed files with 330 additions and 21059 deletions

26
package-lock.json generated
View file

@ -18,6 +18,7 @@
"@mui/x-tree-view": "^7.23.2", "@mui/x-tree-view": "^7.23.2",
"@react-spring/web": "^9.7.5", "@react-spring/web": "^9.7.5",
"@reduxjs/toolkit": "^2.5.0", "@reduxjs/toolkit": "^2.5.0",
"@types/babel__core": "^7.20.5",
"AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs", "AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs",
"add": "^2.0.6", "add": "^2.0.6",
"AppBar": "file:@mui/material/AppBar", "AppBar": "file:@mui/material/AppBar",
@ -39,8 +40,8 @@
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.1.1", "react-router-dom": "^7.1.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-toastify": "^11.0.2",
"RichTreeView": "file:@mui/x-tree-view/RichTreeView", "RichTreeView": "file:@mui/x-tree-view/RichTreeView",
"sonner": "^1.7.4",
"Stack": "file:@mui/material/Stack", "Stack": "file:@mui/material/Stack",
"styles": "file:@mui/material/styles", "styles": "file:@mui/material/styles",
"Tabs": "file:@mui/material/Tabs", "Tabs": "file:@mui/material/Tabs",
@ -15291,19 +15292,6 @@
} }
} }
}, },
"node_modules/react-toastify": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.3.tgz",
"integrity": "sha512-cbPtHJPfc0sGqVwozBwaTrTu1ogB9+BLLjd4dDXd863qYLj7DGrQ2sg5RAChjFUB4yc3w8iXOtWcJqPK/6xqRQ==",
"license": "MIT",
"dependencies": {
"clsx": "^2.1.1"
},
"peerDependencies": {
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
}
},
"node_modules/react-transition-group": { "node_modules/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -16347,6 +16335,16 @@
"websocket-driver": "^0.7.4" "websocket-driver": "^0.7.4"
} }
}, },
"node_modules/sonner": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz",
"integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==",
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"node_modules/source-list-map": { "node_modules/source-list-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",

View file

@ -13,6 +13,7 @@
"@mui/x-tree-view": "^7.23.2", "@mui/x-tree-view": "^7.23.2",
"@react-spring/web": "^9.7.5", "@react-spring/web": "^9.7.5",
"@reduxjs/toolkit": "^2.5.0", "@reduxjs/toolkit": "^2.5.0",
"@types/babel__core": "^7.20.5",
"AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs", "AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs",
"add": "^2.0.6", "add": "^2.0.6",
"AppBar": "file:@mui/material/AppBar", "AppBar": "file:@mui/material/AppBar",
@ -34,8 +35,8 @@
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.1.1", "react-router-dom": "^7.1.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-toastify": "^11.0.2",
"RichTreeView": "file:@mui/x-tree-view/RichTreeView", "RichTreeView": "file:@mui/x-tree-view/RichTreeView",
"sonner": "^1.7.4",
"Stack": "file:@mui/material/Stack", "Stack": "file:@mui/material/Stack",
"styles": "file:@mui/material/styles", "styles": "file:@mui/material/styles",
"Tabs": "file:@mui/material/Tabs", "Tabs": "file:@mui/material/Tabs",

File diff suppressed because it is too large Load diff

View file

@ -74,27 +74,6 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
> >
<DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle> <DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle>
<DialogContent> <DialogContent>
<Controller
name="category"
control={control}
rules={{
required: "Category Name is required",
}}
render={({ field }) => (
<TextField
{...field}
autoFocus
required
margin="dense"
label="Add Category Name"
type="text"
fullWidth
variant="standard"
error={!!errors.category}
helperText={errors.category?.message}
/>
)}
/>
<Controller <Controller
name="name" name="name"
control={control} control={control}
@ -120,19 +99,17 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
<Controller <Controller
name="role" name="role"
control={control} control={control}
rules={{
required: "Role is required",
}}
render={({ field }) => ( render={({ field }) => (
<TextField <TextField
{...field} {...field}
margin="dense" margin="dense"
label="Role" label="Role"
type="text"
fullWidth fullWidth
variant="standard" variant="standard"
error={!!errors.role} error={!!errors.role}
helperText={errors.role?.message} helperText={errors.role?.message}
disabled
/> />
)} )}
/> />

View file

@ -44,7 +44,7 @@ type PropType = {
export default function MenuContent({ hidden }: PropType) { export default function MenuContent({ hidden }: PropType) {
const location = useLocation(); const location = useLocation();
const userRole = useSelector( const userRole = useSelector(
(state: RootState) => state.profile.user?.role (state: RootState) => state.profileReducer.user?.role
); );
const mainListItems = [ const mainListItems = [

View file

@ -12,6 +12,7 @@ import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded";
import MenuButton from "../MenuButton"; import MenuButton from "../MenuButton";
import { Avatar } from "@mui/material"; import { Avatar } from "@mui/material";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
const MenuItem = styled(MuiMenuItem)({ const MenuItem = styled(MuiMenuItem)({
margin: "2px 0", margin: "2px 0",
@ -40,6 +41,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
const handlelogout = () => { const handlelogout = () => {
localStorage.clear(); localStorage.clear();
navigate("/auth/login"); navigate("/auth/login");
toast.success("Logged out successfully");
}; };
return ( return (
<React.Fragment> <React.Fragment>
@ -97,7 +99,12 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
{/* //Eknoor singh and jaanvi {/* //Eknoor singh and jaanvi
//date:- 13-Feb-2025 //date:- 13-Feb-2025
//Implemented logout functionality which was static previously */} //Implemented logout functionality which was static previously */}
<ListItemText onClick={handlelogout}>Logout</ListItemText> <ListItemText
className="toast-button"
onClick={handlelogout}
>
Logout
</ListItemText>
<ListItemIcon> <ListItemIcon>
<LogoutRoundedIcon fontSize="small" /> <LogoutRoundedIcon fontSize="small" />
</ListItemIcon> </ListItemIcon>

View file

@ -34,7 +34,7 @@ export default function SideMenu() {
//Dispatch is called with user from Authstate Interface //Dispatch is called with user from Authstate Interface
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { user } = useSelector((state: RootState) => state?.profile); const { user } = useSelector((state: RootState) => state?.profileReducer);
useEffect(() => { useEffect(() => {
dispatch(fetchAdminProfile()); dispatch(fetchAdminProfile());

View file

@ -1,26 +1,25 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import './index.css'; import "./index.css";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from "./reportWebVitals";
import App from './App'; import App from "./App";
import { Provider } from 'react-redux'; import { Provider } from "react-redux";
import store from './redux/store/store.ts'; import store from "./redux/store/store.ts";
import { Slide, ToastContainer } from 'react-toastify'; import { Toaster } from "sonner";
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<Provider store={store}> <Provider store={store}>
<App /> <App />
<ToastContainer <Toaster
autoClose={2000} position="top-right"
hideProgressBar richColors
theme="dark" closeButton
transition={Slide} duration={6000}
toastStyle={{ border: '1px solid dimgray' }} />
/> </Provider>
</Provider> </React.StrictMode>
</React.StrictMode>
); );
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function

View file

@ -17,7 +17,7 @@ export default function AdminList() {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
// Fetching admin data from the Redux store // Fetching admin data from the Redux store
const admins = useSelector((state: RootState) => state.admin.admins); const admins = useSelector((state: RootState) => state.adminReducer.admins);
// Dispatching the API call when the component mounts // Dispatching the API call when the component mounts
useEffect(() => { useEffect(() => {

View file

@ -1,228 +1,247 @@
import * as React from 'react'; import * as React from "react";
import Box from '@mui/material/Box'; import Box from "@mui/material/Box";
import Button from '@mui/material/Button'; import Button from "@mui/material/Button";
import Checkbox from '@mui/material/Checkbox'; import Checkbox from "@mui/material/Checkbox";
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from "@mui/material/CssBaseline";
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel from "@mui/material/FormControlLabel";
import Divider from '@mui/material/Divider'; import Divider from "@mui/material/Divider";
import FormLabel from '@mui/material/FormLabel'; import FormLabel from "@mui/material/FormLabel";
import FormControl from '@mui/material/FormControl'; import FormControl from "@mui/material/FormControl";
import Link from '@mui/material/Link'; import Link from "@mui/material/Link";
import TextField from '@mui/material/TextField'; import TextField from "@mui/material/TextField";
import Typography from '@mui/material/Typography'; import Typography from "@mui/material/Typography";
import Stack from '@mui/material/Stack'; import Stack from "@mui/material/Stack";
import MuiCard from '@mui/material/Card'; import MuiCard from "@mui/material/Card";
import { styled } from '@mui/material/styles'; import { styled } from "@mui/material/styles";
import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { useForm, Controller, SubmitHandler } from "react-hook-form";
import { useDispatch } from 'react-redux'; import { useDispatch } from "react-redux";
import { loginUser } from '../../../redux/slices/authSlice.ts'; import { loginUser } from "../../../redux/slices/authSlice.ts";
import ColorModeSelect from '../../../shared-theme/ColorModeSelect.tsx'; import ColorModeSelect from "../../../shared-theme/ColorModeSelect.tsx";
import AppTheme from '../../../shared-theme/AppTheme.tsx'; import AppTheme from "../../../shared-theme/AppTheme.tsx";
import ForgotPassword from './ForgotPassword.tsx'; import ForgotPassword from "./ForgotPassword.tsx";
import { toast } from 'react-toastify'; import { toast } from "sonner";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
const Card = styled(MuiCard)(({ theme }) => ({ const Card = styled(MuiCard)(({ theme }) => ({
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
alignSelf: 'center', alignSelf: "center",
width: '100%', width: "100%",
padding: theme.spacing(4), padding: theme.spacing(4),
gap: theme.spacing(2), gap: theme.spacing(2),
margin: 'auto', margin: "auto",
[theme.breakpoints.up('sm')]: { [theme.breakpoints.up("sm")]: {
maxWidth: '450px', maxWidth: "450px",
}, },
boxShadow: boxShadow:
'hsla(220, 30%, 5%, 0.05) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.05) 0px 15px 35px -5px', "hsla(220, 30%, 5%, 0.05) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.05) 0px 15px 35px -5px",
...theme.applyStyles('dark', { ...theme.applyStyles("dark", {
boxShadow: boxShadow:
'hsla(220, 30%, 5%, 0.5) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.08) 0px 15px 35px -5px', "hsla(220, 30%, 5%, 0.5) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.08) 0px 15px 35px -5px",
}), }),
})); }));
const SignInContainer = styled(Stack)(({ theme }) => ({ const SignInContainer = styled(Stack)(({ theme }) => ({
height: 'calc((1 - var(--template-frame-height, 0)) * 100dvh)', height: "calc((1 - var(--template-frame-height, 0)) * 100dvh)",
minHeight: '100%', minHeight: "100%",
padding: theme.spacing(2), padding: theme.spacing(2),
[theme.breakpoints.up('sm')]: { [theme.breakpoints.up("sm")]: {
padding: theme.spacing(4), padding: theme.spacing(4),
}, },
'&::before': { "&::before": {
content: '""', content: '""',
display: 'block', display: "block",
position: 'absolute', position: "absolute",
zIndex: -1, zIndex: -1,
inset: 0, inset: 0,
backgroundImage: backgroundImage:
'radial-gradient(ellipse at 50% 50%, hsl(210, 100%, 97%), hsl(0, 0%, 100%))', "radial-gradient(ellipse at 50% 50%, hsl(210, 100%, 97%), hsl(0, 0%, 100%))",
backgroundRepeat: 'no-repeat', backgroundRepeat: "no-repeat",
...theme.applyStyles('dark', { ...theme.applyStyles("dark", {
backgroundImage: backgroundImage:
'radial-gradient(at 50% 50%, hsla(210, 100%, 16%, 0.5), hsl(220, 30%, 5%))', "radial-gradient(at 50% 50%, hsla(210, 100%, 16%, 0.5), hsl(220, 30%, 5%))",
}), }),
}, },
})); }));
interface ILoginForm { interface ILoginForm {
email: string; email: string;
password: string; password: string;
} }
export default function Login(props: { disableCustomTheme?: boolean }) { export default function Login(props: { disableCustomTheme?: boolean }) {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const { const {
control, control,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
setError, setError,
} = useForm<ILoginForm>(); } = useForm<ILoginForm>();
const dispatch = useDispatch(); const dispatch = useDispatch();
const router = useNavigate(); const router = useNavigate();
const handleClickOpen = () => { const handleClickOpen = () => {
setOpen(true); setOpen(true);
}; };
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
}; };
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) { } catch (error: any) {
console.log('Login failed:', error); console.log("Login failed:", error);
toast.error('Login failed: ' + error); toast.error("Login failed: " + error);
} }
}; };
return ( return (
<AppTheme {...props}> <AppTheme {...props}>
<CssBaseline enableColorScheme /> <CssBaseline enableColorScheme />
<SignInContainer direction="column" justifyContent="space-between"> <SignInContainer direction="column" justifyContent="space-between">
<ColorModeSelect <ColorModeSelect
sx={{ position: 'fixed', top: '1rem', right: '1rem' }} sx={{ position: "fixed", top: "1rem", right: "1rem" }}
/> />
<Card variant="outlined"> <Card variant="outlined">
{/* <SitemarkIcon /> */} {/* <SitemarkIcon /> */}
Digi-EV Digi-EV
<Typography <Typography
component="h1" component="h1"
variant="h4" variant="h4"
sx={{ width: '100%', fontSize: 'clamp(2rem, 10vw, 2.15rem)' }} sx={{
> width: "100%",
Sign in fontSize: "clamp(2rem, 10vw, 2.15rem)",
</Typography> }}
<Box >
component="form" Sign in
onSubmit={handleSubmit(onSubmit)} </Typography>
noValidate <Box
sx={{ component="form"
display: 'flex', onSubmit={handleSubmit(onSubmit)}
flexDirection: 'column', noValidate
width: '100%', sx={{
gap: 2, display: "flex",
}} flexDirection: "column",
> width: "100%",
<FormControl> gap: 2,
<FormLabel htmlFor="email">Email</FormLabel> }}
<Controller >
name="email" <FormControl>
control={control} <FormLabel htmlFor="email">Email</FormLabel>
defaultValue="" <Controller
rules={{ name="email"
required: 'Email is required', control={control}
pattern: { defaultValue=""
value: /\S+@\S+\.\S+/, rules={{
message: 'Please enter a valid email address.', required: "Email is required",
}, pattern: {
}} value: /\S+@\S+\.\S+/,
render={({ field }) => ( message:
<TextField "Please enter a valid email address.",
{...field} },
error={!!errors.email} }}
helperText={errors.email?.message} render={({ field }) => (
id="email" <TextField
type="email" {...field}
placeholder="your@email.com" error={!!errors.email}
autoComplete="email" helperText={errors.email?.message}
autoFocus id="email"
required type="email"
fullWidth placeholder="your@email.com"
variant="outlined" autoComplete="email"
color={errors.email ? 'error' : 'primary'} autoFocus
/> required
)} fullWidth
/> variant="outlined"
</FormControl> color={
errors.email ? "error" : "primary"
}
/>
)}
/>
</FormControl>
<FormControl> <FormControl>
<FormLabel htmlFor="password">Password</FormLabel> <FormLabel htmlFor="password">Password</FormLabel>
<Controller <Controller
name="password" name="password"
control={control} control={control}
defaultValue="" defaultValue=""
rules={{ rules={{
required: 'Password is required', required: "Password is required",
minLength: { minLength: {
value: 6, value: 6,
message: 'Password must be at least 6 characters long.', message:
}, "Password must be at least 6 characters long.",
}} },
render={({ field }) => ( }}
<TextField render={({ field }) => (
{...field} <TextField
error={!!errors.password} {...field}
helperText={errors.password?.message} error={!!errors.password}
name="password" helperText={errors.password?.message}
placeholder="••••••" name="password"
type="password" placeholder="••••••"
id="password" type="password"
autoComplete="current-password" id="password"
autoFocus autoComplete="current-password"
required autoFocus
fullWidth required
variant="outlined" fullWidth
color={errors.password ? 'error' : 'primary'} variant="outlined"
/> color={
)} errors.password
/> ? "error"
</FormControl> : "primary"
}
/>
)}
/>
</FormControl>
<FormControlLabel <FormControlLabel
control={<Checkbox value="remember" color="primary" />} control={
label="Remember me" <Checkbox value="remember" color="primary" />
/> }
<ForgotPassword open={open} handleClose={handleClose} /> label="Remember me"
<Button type="submit" fullWidth variant="contained"> />
Sign in <ForgotPassword open={open} handleClose={handleClose} />
</Button> <Button type="submit" fullWidth variant="contained">
<Link Sign in
component="button" </Button>
type="button" <Link
onClick={handleClickOpen} component="button"
variant="body2" type="button"
sx={{ alignSelf: 'center' }} onClick={handleClickOpen}
> variant="body2"
Forgot your password? sx={{ alignSelf: "center" }}
</Link> >
</Box> Forgot your password?
<Divider>or</Divider> </Link>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> </Box>
<Typography sx={{ textAlign: 'center' }}> <Divider>or</Divider>
Don&apos;t have an account?{' '} <Box
<Link sx={{
href="/auth/signup" display: "flex",
variant="body2" flexDirection: "column",
sx={{ alignSelf: 'center' }} gap: 2,
> }}
Sign up >
</Link> <Typography sx={{ textAlign: "center" }}>
</Typography> Don&apos;t have an account?{" "}
</Box> <Link
</Card> href="/auth/signup"
</SignInContainer> variant="body2"
</AppTheme> sx={{ alignSelf: "center" }}
); >
Sign up
</Link>
</Typography>
</Box>
</Card>
</SignInContainer>
</AppTheme>
);
} }

View file

@ -20,7 +20,7 @@ import ColorModeSelect from "../../../shared-theme/ColorModeSelect.tsx";
import MuiPhoneNumber from "mui-phone-number"; import MuiPhoneNumber from "mui-phone-number";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { registerUser } from "../../../redux/slices/authSlice.ts"; import { registerUser } from "../../../redux/slices/authSlice.ts";
import { toast } from "react-toastify"; import { toast } from "sonner";
import { InputLabel, MenuItem, Select } from "@mui/material"; import { InputLabel, MenuItem, Select } from "@mui/material";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -98,8 +98,8 @@ export default function SignUp(props: { disableCustomTheme?: boolean }) {
const roleOptions = [ const roleOptions = [
{ value: "admin", label: "Admin" }, { value: "admin", label: "Admin" },
{ value: "user", label: "User" } { value: "user", label: "User" },
]; ];
// Enhanced email validation regex // Enhanced email validation regex
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
@ -133,8 +133,8 @@ export default function SignUp(props: { disableCustomTheme?: boolean }) {
}; };
await dispatch(registerUser(payload)).unwrap(); await dispatch(registerUser(payload)).unwrap();
toast.success("Registration successful!");
navigate("/auth/login"); navigate("/auth/login");
toast.success("Registration successful!");
} catch (error: any) { } catch (error: any) {
toast.error(error?.message || "Registration failed"); toast.error(error?.message || "Registration failed");
console.error("Registration error:", error); console.error("Registration error:", error);

View file

@ -23,8 +23,8 @@ const ProfilePage = () => {
//date:- 12-Feb-2025 //date:- 12-Feb-2025
//Dispatch is called and user, isLoading, and error from Authstate Interface //Dispatch is called and user, isLoading, and error from Authstate Interface
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { user, isLoading, error } = useSelector( const { user, isLoading } = useSelector(
(state: RootState) => state?.profile (state: RootState) => state?.profileReducer
); );
useEffect(() => { useEffect(() => {
@ -46,16 +46,6 @@ const ProfilePage = () => {
); );
} }
if (error) {
return (
<Container>
<Typography variant="h5" color="error" gutterBottom>
<h2>An error occurred while loading profile</h2>
</Typography>
</Container>
);
}
return ( return (
<Container sx={{ py: 4 }}> <Container sx={{ py: 4 }}>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>

View file

@ -1,6 +1,6 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https"; import http from "../../lib/https";
import { toast } from "react-toastify"; import { toast } from "sonner";
// Interfaces // Interfaces
interface User { interface User {
@ -25,7 +25,7 @@ interface AuthState {
admins: Admin[]; admins: Admin[];
isAuthenticated: boolean; isAuthenticated: boolean;
isLoading: boolean; isLoading: boolean;
error: string | null; // error: string | null;
token: string | null; token: string | null;
} }
@ -39,6 +39,8 @@ export const adminList = createAsyncThunk<
const response = await http.get("auth/admin-list"); const response = await http.get("auth/admin-list");
return response?.data?.data; return response?.data?.data;
} catch (error: any) { } catch (error: any) {
toast.error("Error fetching users list" + error);
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
@ -56,6 +58,8 @@ export const deleteAdmin = createAsyncThunk<
toast.success(response.data?.message); toast.success(response.data?.message);
return id; return id;
} catch (error: any) { } catch (error: any) {
toast.error("Error deleting the user" + error);
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
@ -64,7 +68,7 @@ export const deleteAdmin = createAsyncThunk<
// Update Admin // Update Admin
export const updateAdmin = createAsyncThunk( export const updateAdmin = createAsyncThunk(
"UpdateAdmin", "updateAdmin",
async ( async (
{ id, name, role }: { id: any; name: string; role: string }, { id, name, role }: { id: any; name: string; role: string },
{ rejectWithValue } { rejectWithValue }
@ -77,6 +81,8 @@ export const updateAdmin = createAsyncThunk(
toast.success("Admin updated successfully"); toast.success("Admin updated successfully");
return response?.data; return response?.data;
} catch (error: any) { } catch (error: any) {
toast.error("Error updating the user" + error);
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
@ -89,20 +95,19 @@ const initialState: AuthState = {
admins: [], admins: [],
isAuthenticated: false, isAuthenticated: false,
isLoading: false, isLoading: false,
error: null, // error: null,
token: null, token: null,
}; };
const adminSlice = createSlice({ const adminSlice = createSlice({
name: "admin", name: "admin",
initialState, initialState,
reducers: { reducers: {},
},
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
.addCase(adminList.pending, (state) => { .addCase(adminList.pending, (state) => {
state.isLoading = true; state.isLoading = true;
state.error = null; // state.error = null;
}) })
.addCase( .addCase(
adminList.fulfilled, adminList.fulfilled,
@ -113,9 +118,9 @@ const adminSlice = createSlice({
) )
.addCase( .addCase(
adminList.rejected, adminList.rejected,
(state, action: PayloadAction<string | undefined>) => { (state) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload || "An error occurred"; // state.error = action.payload || "An error occurred";
} }
) )
.addCase(deleteAdmin.pending, (state) => { .addCase(deleteAdmin.pending, (state) => {
@ -127,13 +132,11 @@ const adminSlice = createSlice({
(admin) => String(admin.id) !== String(action.payload) (admin) => String(admin.id) !== String(action.payload)
); );
}) })
.addCase(deleteAdmin.rejected, (state, action) => { .addCase(deleteAdmin.rejected, (state) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload || "Failed to delete admin";
}) })
.addCase(updateAdmin.pending, (state) => { .addCase(updateAdmin.pending, (state) => {
state.isLoading = true; state.isLoading = true;
state.error = null;
}) })
.addCase(updateAdmin.fulfilled, (state, action) => { .addCase(updateAdmin.fulfilled, (state, action) => {
const updatedAdmin = action.payload; const updatedAdmin = action.payload;
@ -142,12 +145,8 @@ const adminSlice = createSlice({
); );
state.isLoading = false; state.isLoading = false;
}) })
.addCase(updateAdmin.rejected, (state, action) => { .addCase(updateAdmin.rejected, (state) => {
state.isLoading = false; state.isLoading = false;
state.error =
typeof action.payload === "string"
? action.payload
: "Something went wrong while updating Admin!";
}); });
}, },
}); });

View file

@ -1,6 +1,6 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https"; import http from "../../lib/https";
import { toast } from "react-toastify"; import { toast } from "sonner";
// Define types for state // Define types for state
//Eknoor singh //Eknoor singh
@ -9,12 +9,6 @@ import { toast } from "react-toastify";
interface User { interface User {
data: any; data: any;
token: string | null; token: string | null;
map(
arg0: (
admin: { name: any; role: any; email: any; phone: any },
index: number
) => { srno: number; name: any; role: any; email: any; phone: any }
): unknown;
id: string; id: string;
name: string; name: string;
email: string; email: string;
@ -35,8 +29,9 @@ interface AuthState {
admins: Admin[]; admins: Admin[];
isAuthenticated: boolean; isAuthenticated: boolean;
isLoading: boolean; isLoading: boolean;
error: object | string | null; // error: object | string | null;
token: string | null; token: string | null;
role: string | null;
} }
// Async thunk for login // Async thunk for login
@ -47,6 +42,9 @@ export const loginUser = createAsyncThunk<
>("LoginUser", async ({ email, password }, { rejectWithValue }) => { >("LoginUser", async ({ email, password }, { rejectWithValue }) => {
try { try {
// this is endpoint not action name // this is endpoint not action name
//use below commented endpoint if using deployed backend........
// const response = await http.post("admin/login", {
const response = await http.post("auth/login", { const response = await http.post("auth/login", {
email, email,
password, password,
@ -55,6 +53,8 @@ export const loginUser = createAsyncThunk<
toast.success(response.data?.message); toast.success(response.data?.message);
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
toast.error("Error logging in the user" + error);
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
@ -88,24 +88,24 @@ const initialState: AuthState = {
admins: [], admins: [],
isAuthenticated: false, isAuthenticated: false,
isLoading: false, isLoading: false,
error: null, // error: null,
//Eknoor singh //Eknoor singh
//date:- 12-Feb-2025 //date:- 12-Feb-2025
//initial state of token set to null //initial state of token set to null
token: null, token: null,
role: null, // New field for role
}; };
const authSlice = createSlice({ const authSlice = createSlice({
name: "auth", name: "auth",
initialState, initialState,
reducers: { reducers: {},
},
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
// Login // Login
.addCase(loginUser.pending, (state) => { .addCase(loginUser.pending, (state) => {
state.isLoading = true; state.isLoading = true;
state.error = null; // state.error = null;
}) })
.addCase(loginUser.fulfilled, (state, action) => { .addCase(loginUser.fulfilled, (state, action) => {
state.isLoading = false; state.isLoading = false;
@ -116,15 +116,16 @@ const authSlice = createSlice({
.addCase( .addCase(
loginUser.rejected, loginUser.rejected,
(state, action: PayloadAction<string | undefined>) => { // (state, action: PayloadAction<string | undefined>) => {
(state) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload || "An error occurred"; // state.error = action.payload || "An error occurred";
} }
) )
// Register // Register
.addCase(registerUser.pending, (state) => { .addCase(registerUser.pending, (state) => {
state.isLoading = true; state.isLoading = true;
state.error = null; // state.error = null;
}) })
.addCase( .addCase(
registerUser.fulfilled, registerUser.fulfilled,
@ -138,7 +139,7 @@ const authSlice = createSlice({
registerUser.rejected, registerUser.rejected,
(state, action: PayloadAction<string | undefined>) => { (state, action: PayloadAction<string | undefined>) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload || "An error occurred"; // state.error = action.payload || "An error occurred";
} }
); );

View file

@ -1,5 +1,6 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import http from "../../lib/https"; import http from "../../lib/https";
import { toast } from "sonner";
interface User { interface User {
token: string | null; token: string | null;
@ -14,7 +15,6 @@ interface AuthState {
user: User | null; user: User | null;
isAuthenticated: boolean; isAuthenticated: boolean;
isLoading: boolean; isLoading: boolean;
error: string | null;
} }
export const fetchAdminProfile = createAsyncThunk< export const fetchAdminProfile = createAsyncThunk<
@ -26,15 +26,16 @@ export const fetchAdminProfile = createAsyncThunk<
const token = localStorage?.getItem("authToken"); const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found"); if (!token) throw new Error("No token found");
const response = await http.get("/auth/profile", { const response = await http.get("/auth/profile");
headers: { Authorization: `Bearer ${token}` },
});
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) {
return rejectWithValue(error?.response?.data?.message || "An error occurred"); toast.error("Error Fetching Profile" + error);
return rejectWithValue(
error?.response?.data?.message || "An error occurred"
);
} }
}); });
@ -42,28 +43,24 @@ const initialState: AuthState = {
user: null, user: null,
isAuthenticated: false, isAuthenticated: false,
isLoading: false, isLoading: false,
error: null,
}; };
const profileSlice = createSlice({ const profileSlice = createSlice({
name: "profile", name: "profile",
initialState, initialState,
reducers: { reducers: {},
},
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
.addCase(fetchAdminProfile.pending, (state) => { .addCase(fetchAdminProfile.pending, (state) => {
state.isLoading = true; state.isLoading = true;
state.error = null;
}) })
.addCase(fetchAdminProfile.fulfilled, (state, action) => { .addCase(fetchAdminProfile.fulfilled, (state, action) => {
state.isLoading = false; state.isLoading = false;
state.user = action.payload; state.user = action.payload;
state.isAuthenticated = true; state.isAuthenticated = true;
}) })
.addCase(fetchAdminProfile.rejected, (state, action) => { .addCase(fetchAdminProfile.rejected, (state) => {
state.isLoading = false; state.isLoading = false;
state.error = action.payload || "Failed to fetch admin profile";
}); });
}, },
}); });

View file

@ -1,17 +1,9 @@
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from "@reduxjs/toolkit";
import authReducer from '../slices/authSlice.ts' import rootReducer from "../reducers.ts";
import adminReducer from "../slices/adminSlice.ts"
import profileReducer from "../slices/profileSlice.ts"
const store = configureStore({ const store = configureStore({
reducer: { reducer: rootReducer,
auth: authReducer,
admin: adminReducer,
profile: profileReducer
},
}); });
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch; export type AppDispatch = typeof store.dispatch;
export default store; export default store;

View file

@ -38,7 +38,7 @@ const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ caps, component }) => {
// Super Admin Route Component // Super Admin Route Component
const SuperAdminRoute: React.FC<SuperAdminRouteProps> = ({ children }) => { const SuperAdminRoute: React.FC<SuperAdminRouteProps> = ({ children }) => {
const userRole = useSelector( const userRole = useSelector(
(state: RootState) => state.profile.user?.role (state: RootState) => state.profileReducer.user?.role
); );
if (userRole !== "superadmin") { if (userRole !== "superadmin") {