Resoved Conflits
This commit is contained in:
commit
98cde9fb8d
|
@ -73,4 +73,4 @@
|
|||
"@types/react-dom": "^19.0.2",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
}
|
14815
pnpm-lock.yaml
14815
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,160 +1,3 @@
|
|||
// import React, { useEffect } from "react";
|
||||
// import {
|
||||
// Button,
|
||||
// Dialog,
|
||||
// DialogActions,
|
||||
// DialogContent,
|
||||
// DialogTitle,
|
||||
// TextField,
|
||||
// } from "@mui/material";
|
||||
// import { useForm, Controller } from "react-hook-form";
|
||||
// import { useSelector } from "react-redux";
|
||||
// import { RootState } from "../../redux/store/store";
|
||||
|
||||
// //By Jaanvi : Edit Model :: 11-feb-25
|
||||
// interface AddEditCategoryModalProps {
|
||||
// open: boolean;
|
||||
// handleClose: () => void;
|
||||
// editRow: any;
|
||||
// handleUpdate: (id: string, name: string, role: string) => void;
|
||||
// }
|
||||
|
||||
// interface FormData {
|
||||
// role: string;
|
||||
// name: string;
|
||||
// category: string;
|
||||
// }
|
||||
|
||||
// const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||
// open,
|
||||
// handleClose,
|
||||
// editRow,
|
||||
// handleUpdate,
|
||||
// }) => {
|
||||
// const {
|
||||
// control,
|
||||
// handleSubmit,
|
||||
// formState: { errors },
|
||||
// setValue,
|
||||
// reset,
|
||||
// } = useForm<FormData>({
|
||||
// defaultValues: {
|
||||
// category: "",
|
||||
// name: "",
|
||||
// role: "",
|
||||
// },
|
||||
// });
|
||||
// const { isLoading } = useSelector((state: RootState) => state.auth.admin);
|
||||
|
||||
// const onSubmit = (data: FormData) => {
|
||||
// if (editRow) {
|
||||
// handleUpdate(editRow.id, data.name, data.role);
|
||||
// handleClose();
|
||||
// reset();
|
||||
// } else {
|
||||
// console.log(data.category);
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (editRow) {
|
||||
// setValue("category", editRow.name);
|
||||
// setValue("name", editRow.name);
|
||||
// setValue("role", editRow.role);
|
||||
// } else {
|
||||
// reset();
|
||||
// }
|
||||
// }, [editRow, setValue, reset]);
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <Dialog
|
||||
// open={open}
|
||||
// onClose={handleClose}
|
||||
// maxWidth="md"
|
||||
// fullWidth
|
||||
// PaperProps={{
|
||||
// component: "form",
|
||||
// onSubmit: handleSubmit(onSubmit),
|
||||
// }}
|
||||
// >
|
||||
// <DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle>
|
||||
// <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}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
// {/* AdminField */}
|
||||
// <Controller
|
||||
// name="name"
|
||||
// control={control}
|
||||
// rules={{
|
||||
// required: "Admin Name is required",
|
||||
// }}
|
||||
// render={({ field }) => (
|
||||
// <TextField
|
||||
// {...field}
|
||||
// autoFocus
|
||||
// required
|
||||
// margin="dense"
|
||||
// label="Admin Name"
|
||||
// type="text"
|
||||
// fullWidth
|
||||
// variant="standard"
|
||||
// error={!!errors.name}
|
||||
// helperText={errors.name?.message}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
|
||||
// <Controller
|
||||
// name="role"
|
||||
// control={control}
|
||||
// rules={{
|
||||
// required: "Role is required",
|
||||
// }}
|
||||
// render={({ field }) => (
|
||||
// <TextField
|
||||
// {...field}
|
||||
// margin="dense"
|
||||
// label="Role"
|
||||
// type="text"
|
||||
// fullWidth
|
||||
// variant="standard"
|
||||
// error={!!errors.role}
|
||||
// helperText={errors.role?.message}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
// </DialogContent>
|
||||
// <DialogActions>
|
||||
// <Button onClick={handleClose}>Cancel</Button>
|
||||
// <Button type="submit">Save</Button>
|
||||
// </DialogActions>
|
||||
// </Dialog>
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default AddEditCategoryModal;
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
Button,
|
||||
|
@ -170,14 +13,14 @@ import { useForm, Controller } from "react-hook-form";
|
|||
interface AddEditCategoryModalProps {
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
editRow: any;
|
||||
handleUpdate: (id: string, name: string, role: string) => void;
|
||||
editRow: any;
|
||||
}
|
||||
|
||||
interface FormData {
|
||||
category: string;
|
||||
role: string;
|
||||
name: string;
|
||||
//category: string;
|
||||
}
|
||||
|
||||
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
||||
|
@ -194,7 +37,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
reset,
|
||||
} = useForm<FormData>({
|
||||
defaultValues: {
|
||||
//category: "",
|
||||
category: "",
|
||||
name: "",
|
||||
role: "",
|
||||
},
|
||||
|
@ -204,16 +47,13 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
if (editRow) {
|
||||
handleUpdate(editRow.id, data.name, data.role);
|
||||
}
|
||||
// else {
|
||||
// console.log(data.category);
|
||||
// }
|
||||
handleClose();
|
||||
reset();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (editRow) {
|
||||
// setValue("category", editRow.name);
|
||||
setValue("category", editRow.name);
|
||||
setValue("name", editRow.name);
|
||||
setValue("role", editRow.role);
|
||||
} else {
|
||||
|
@ -235,7 +75,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
>
|
||||
<DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle>
|
||||
<DialogContent>
|
||||
{/* <Controller
|
||||
<Controller
|
||||
name="category"
|
||||
control={control}
|
||||
rules={{
|
||||
|
@ -255,8 +95,7 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
|
|||
helperText={errors.category?.message}
|
||||
/>
|
||||
)}
|
||||
/> */}
|
||||
{/* AdminField */}
|
||||
/>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
|
|
|
@ -45,7 +45,7 @@ const AddEditTagsModal: React.FC<AddEditTagsModalProps> = ({ open, handleClose,e
|
|||
onSubmit: handleSubmit(onSubmit),
|
||||
}}
|
||||
>
|
||||
<DialogTitle>{editRow ? "Editsefwefwe" : 'Add'} Tag</DialogTitle>
|
||||
<DialogTitle>{editRow ? "Edit" : 'Add'} Tag</DialogTitle>
|
||||
<DialogContent>
|
||||
<Controller
|
||||
name="tag"
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded"
|
||||
import DeleteModal from "../Modals/DeleteModal/DeleteModal"
|
||||
import { AppDispatch } from "../../redux/store/store"
|
||||
|
||||
|
||||
// Styled components for customization
|
||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||
[`&.${tableCellClasses.head}`]: {
|
||||
|
@ -31,7 +31,7 @@ const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
|||
fontSize: 14,
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
||||
"&:nth-of-type(odd)": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
|
@ -40,7 +40,7 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
|||
border: 0,
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
export interface Column {
|
||||
id: string
|
||||
label: string
|
||||
|
@ -73,31 +73,31 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
|
||||
const [selectedRow, setSelectedRow] = React.useState<Row | null>(null)
|
||||
const open = Boolean(anchorEl)
|
||||
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
setSelectedRow(row) // Ensure the row data is set
|
||||
}
|
||||
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
|
||||
const isImage = (value: any) => {
|
||||
if (typeof value === "string") {
|
||||
return value.startsWith("http") || value.startsWith("data:image") // Check for URL or base64 image
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
const handleDeleteButton = (id: string | undefined) => {
|
||||
if (!id) console.error("ID not found", id)
|
||||
|
||||
|
||||
dispatch(deleteAdmin(id || ""))
|
||||
setDeleteModal(false) // Close the modal only after deletion
|
||||
handleClose()
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 700 }} aria-label="customized table">
|
||||
|
@ -189,7 +189,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
>
|
||||
Edit
|
||||
</Button>
|
||||
|
||||
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
|
@ -221,5 +221,5 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
</TableContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomTable
|
||||
|
||||
export default CustomTable
|
||||
|
|
|
@ -1,84 +1,84 @@
|
|||
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||
import { Box, Button, Modal, Typography } from "@mui/material"
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
setDeleteModal: Function;
|
||||
handleDelete: (id: string | undefined) => void;
|
||||
id?: string | undefined;
|
||||
};
|
||||
open: boolean
|
||||
setDeleteModal: Function
|
||||
handleDelete: (id: string | undefined) => void
|
||||
id?: string | undefined
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 330,
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: 1.5,
|
||||
boxShadow: 24,
|
||||
p: 3,
|
||||
};
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 330,
|
||||
bgcolor: "background.paper",
|
||||
borderRadius: 1.5,
|
||||
boxShadow: 24,
|
||||
p: 3,
|
||||
}
|
||||
|
||||
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" };
|
||||
const btnStyle = { py: 1, px: 5, width: "50%", textTransform: "capitalize" }
|
||||
|
||||
export default function DeleteModal({
|
||||
open,
|
||||
setDeleteModal,
|
||||
handleDelete,
|
||||
id,
|
||||
open,
|
||||
setDeleteModal,
|
||||
handleDelete,
|
||||
id,
|
||||
}: Props) {
|
||||
// console.log("DeleteModal opened with ID:", id)
|
||||
// console.log("DeleteModal opened with ID:", id)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<Typography
|
||||
id="modal-modal-title"
|
||||
variant="h6"
|
||||
component="h2"
|
||||
align="center"
|
||||
>
|
||||
Delete Record
|
||||
</Typography>
|
||||
<Typography
|
||||
id="modal-modal-description"
|
||||
sx={{ mt: 2 }}
|
||||
align="center"
|
||||
>
|
||||
Are you sure you want to delete this record?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 4,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
type="button"
|
||||
sx={btnStyle}
|
||||
onClick={() => setDeleteModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="button"
|
||||
color="primary"
|
||||
sx={btnStyle}
|
||||
onClick={() => handleDelete(id || "")}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<Typography
|
||||
id="modal-modal-title"
|
||||
variant="h6"
|
||||
component="h2"
|
||||
align="center"
|
||||
>
|
||||
Delete Record
|
||||
</Typography>
|
||||
<Typography
|
||||
id="modal-modal-description"
|
||||
sx={{ mt: 2 }}
|
||||
align="center"
|
||||
>
|
||||
Are you sure you want to delete this record?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
mt: 4,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
type="button"
|
||||
sx={btnStyle}
|
||||
onClick={() => setDeleteModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="button"
|
||||
color="primary"
|
||||
sx={btnStyle}
|
||||
onClick={() => handleDelete(id || "")}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,20 +17,30 @@ const MenuItem = styled(MuiMenuItem)({
|
|||
margin: "2px 0",
|
||||
});
|
||||
|
||||
|
||||
export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setAnchorEl(event?.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
//Eknoor singh and jaanvi
|
||||
//date:- 12-Feb-2025
|
||||
//Made a navigation page for the profile page
|
||||
const navigate = useNavigate();
|
||||
const handleProfile = () =>{
|
||||
navigate("/auth/profile");
|
||||
}
|
||||
const handleProfile = () => {
|
||||
navigate("/auth/profile");
|
||||
};
|
||||
|
||||
//Eknoor singh and jaanvi
|
||||
//date:- 13-Feb-2025
|
||||
//Implemented logout functionality which was static previously
|
||||
const handlelogout = () => {
|
||||
localStorage.clear();
|
||||
navigate("/auth/login");
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MenuButton
|
||||
|
@ -69,8 +79,6 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
},
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
<MenuItem onClick={handleProfile}>Profile</MenuItem>
|
||||
<MenuItem onClick={handleClose}>My account</MenuItem>
|
||||
<Divider />
|
||||
|
@ -86,7 +94,10 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<ListItemText>Logout</ListItemText>
|
||||
{/* //Eknoor singh and jaanvi
|
||||
//date:- 13-Feb-2025
|
||||
//Implemented logout functionality which was static previously */}
|
||||
<ListItemText onClick={handlelogout}>Logout</ListItemText>
|
||||
<ListItemIcon>
|
||||
<LogoutRoundedIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
|
|
|
@ -6,13 +6,12 @@ import Stack from "@mui/material/Stack";
|
|||
import Typography from "@mui/material/Typography";
|
||||
import MenuContent from "../MenuContent";
|
||||
import OptionsMenu from "../OptionsMenu";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import React, { useEffect } from "react";
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@mui/x-date-pickers";
|
||||
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||
import { Button } from "@mui/material";
|
||||
import { fetchProfile } from "../../redux/slices/authSlice";
|
||||
import { AppDispatch } from "../../redux/store/store";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "../../redux/reducers";
|
||||
import { fetchAdminProfile } from "../../redux/slices/authSlice";
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
|
@ -30,16 +29,15 @@ const Drawer = styled(MuiDrawer)({
|
|||
export default function SideMenu() {
|
||||
const [open, setOpen] = React.useState(true);
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Dispatch is called with user from Authstate Interface
|
||||
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
|
||||
//Created by : jaanvi and Eknoor
|
||||
//date: 12-feb-25
|
||||
//Extract user profile data from Auth
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
const { user } = useSelector((state: RootState) => state?.auth);
|
||||
|
||||
useEffect(() => {
|
||||
// Dispatch the fetchProfile thunk to fetch user profile data
|
||||
dispatch(fetchProfile());
|
||||
dispatch(fetchAdminProfile());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
|
@ -88,8 +86,8 @@ export default function SideMenu() {
|
|||
>
|
||||
<Avatar
|
||||
sizes="small"
|
||||
alt="Riley Carter"
|
||||
src="/static/images/avatar/7.jpg"
|
||||
alt={user?.name || "User Avatar"}
|
||||
src={"/static/images/avatar/7.jpg"}
|
||||
sx={{ width: open ? 36 : 0, height: open ? 36 : 0 }}
|
||||
/>
|
||||
<Box sx={{ mr: "auto", display: !open ? "none" : "block" }}>
|
||||
|
@ -97,13 +95,13 @@ export default function SideMenu() {
|
|||
variant="body2"
|
||||
sx={{ fontWeight: 500, lineHeight: "16px" }}
|
||||
>
|
||||
{user?.name}
|
||||
{user?.name || "No Admin"}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{ color: "text.secondary" }}
|
||||
>
|
||||
{user?.email}
|
||||
{user?.email || "No Email"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<OptionsMenu avatar={open} />
|
||||
|
|
|
@ -15,9 +15,12 @@
|
|||
|
||||
// export default http;
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 10-Feb-2025
|
||||
//Made different functions for calling different backends and sent them in a function for clarity
|
||||
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
// Axios instance for the production backend
|
||||
const backendHttp = axios.create({
|
||||
baseURL: process.env.REACT_APP_BACKEND_URL,
|
||||
});
|
||||
|
@ -32,10 +35,7 @@ const addAuthInterceptor = (instance: AxiosInstance) => {
|
|||
instance.interceptors.request.use((config) => {
|
||||
const authToken = localStorage.getItem("authToken");
|
||||
if (authToken) {
|
||||
//Created by : jaanvi and Eknoor
|
||||
//date: 12-feb-25
|
||||
//changes in token fetching
|
||||
config.headers.Authorization = `Bearer ${authToken}`;
|
||||
config.headers.Authorization = `Bearer ${authToken}`; // <-- Add "Bearer "
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
|
|
@ -1,215 +1,287 @@
|
|||
import * as React from 'react';
|
||||
import { useForm, Controller, SubmitHandler } from 'react-hook-form';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import FormLabel from '@mui/material/FormLabel';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import Link from '@mui/material/Link';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import MuiCard from '@mui/material/Card';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import AppTheme from '../../../shared-theme/AppTheme.tsx';
|
||||
import { GoogleIcon, FacebookIcon, SitemarkIcon } from './CustomIcons.tsx';
|
||||
import ColorModeSelect from '../../../shared-theme/ColorModeSelect.tsx';
|
||||
import MuiPhoneNumber from 'mui-phone-number';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { registerUser } from '../../../redux/slices/authSlice.ts';
|
||||
import * as React from "react";
|
||||
import { useForm, Controller, SubmitHandler } from "react-hook-form";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
// import Checkbox from '@mui/material/Checkbox';
|
||||
// import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from "@mui/material/Divider";
|
||||
// import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import FormLabel from "@mui/material/FormLabel";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Link from "@mui/material/Link";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import MuiCard from "@mui/material/Card";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import AppTheme from "../../../shared-theme/AppTheme.tsx";
|
||||
// import { GoogleIcon, FacebookIcon, SitemarkIcon } from './CustomIcons.tsx';
|
||||
import ColorModeSelect from "../../../shared-theme/ColorModeSelect.tsx";
|
||||
import MuiPhoneNumber from "mui-phone-number";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { registerUser } from "../../../redux/slices/authSlice.ts";
|
||||
import { toast } from "react-toastify";
|
||||
import { InputLabel, MenuItem, Select } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const Card = styled(MuiCard)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignSelf: 'center',
|
||||
width: '100%',
|
||||
padding: theme.spacing(4),
|
||||
gap: theme.spacing(2),
|
||||
margin: 'auto',
|
||||
boxShadow:
|
||||
'hsla(220, 30%, 5%, 0.05) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.05) 0px 15px 35px -5px',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: '450px',
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
boxShadow:
|
||||
'hsla(220, 30%, 5%, 0.5) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.08) 0px 15px 35px -5px',
|
||||
}),
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignSelf: "center",
|
||||
width: "100%",
|
||||
padding: theme.spacing(4),
|
||||
gap: theme.spacing(2),
|
||||
margin: "auto",
|
||||
boxShadow:
|
||||
"hsla(220, 30%, 5%, 0.05) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.05) 0px 15px 35px -5px",
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
width: "450px",
|
||||
},
|
||||
...theme.applyStyles("dark", {
|
||||
boxShadow:
|
||||
"hsla(220, 30%, 5%, 0.5) 0px 5px 15px 0px, hsla(220, 25%, 10%, 0.08) 0px 15px 35px -5px",
|
||||
}),
|
||||
}));
|
||||
|
||||
const SignUpContainer = styled(Stack)(({ theme }) => ({
|
||||
height: 'calc((1 - var(--template-frame-height, 0)) * 100dvh)',
|
||||
minHeight: '100%',
|
||||
padding: theme.spacing(2),
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
padding: theme.spacing(4),
|
||||
},
|
||||
'&::before': {
|
||||
content: '""',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
inset: 0,
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse at 50% 50%, hsl(210, 100%, 97%), hsl(0, 0%, 100%))',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundImage:
|
||||
'radial-gradient(at 50% 50%, hsla(210, 100%, 16%, 0.5), hsl(220, 30%, 5%))',
|
||||
}),
|
||||
},
|
||||
height: "calc((1 - var(--template-frame-height, 0)) * 100dvh)",
|
||||
minHeight: "100%",
|
||||
padding: theme.spacing(2),
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
padding: theme.spacing(4),
|
||||
},
|
||||
"&::before": {
|
||||
content: '""',
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
zIndex: -1,
|
||||
inset: 0,
|
||||
backgroundImage:
|
||||
"radial-gradient(ellipse at 50% 50%, hsl(210, 100%, 97%), hsl(0, 0%, 100%))",
|
||||
backgroundRepeat: "no-repeat",
|
||||
...theme.applyStyles("dark", {
|
||||
backgroundImage:
|
||||
"radial-gradient(at 50% 50%, hsla(210, 100%, 16%, 0.5), hsl(220, 30%, 5%))",
|
||||
}),
|
||||
},
|
||||
}));
|
||||
interface IRegisterForm {
|
||||
name:string;
|
||||
email: string;
|
||||
password: string;
|
||||
phoneCountryCode:string;
|
||||
phoneNumber:number | null;
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phone: string; // Changed to string to handle formatted phone numbers
|
||||
role: string;
|
||||
}
|
||||
|
||||
//Eknoor singh and jaanvi
|
||||
//date:- 13-Feb-2025
|
||||
//Implemented SignUp functionality which was static initially
|
||||
export default function SignUp(props: { disableCustomTheme?: boolean }) {
|
||||
const dispatch = useDispatch();
|
||||
const { control, handleSubmit, formState: { errors }, setValue } = useForm<IRegisterForm>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
phoneCountryCode: '',
|
||||
phoneNumber: null,
|
||||
}
|
||||
});
|
||||
const [countryCode, setCountryCode] = React.useState('');
|
||||
const [phoneNumber, setPhoneNumber] = React.useState('');
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
} = useForm<IRegisterForm>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
role: "",
|
||||
phone: "",
|
||||
},
|
||||
});
|
||||
|
||||
const extractCountryCodeAndNumber = (phone) => {
|
||||
// Match the country code (e.g., +91) and the rest of the number
|
||||
const match = phone.match(/^(\+\d{1,3})\s*(\d+.*)/);
|
||||
if (match) {
|
||||
return { countryCode: match[1], numberWithoutCountryCode: match[2] };
|
||||
}
|
||||
return { countryCode: '', numberWithoutCountryCode: phone };
|
||||
};
|
||||
const handleOnChange = (newPhone) => {
|
||||
const { countryCode, numberWithoutCountryCode } = extractCountryCodeAndNumber(newPhone);
|
||||
console.log("numberWithoutCountryCode",numberWithoutCountryCode);
|
||||
setPhoneNumber(numberWithoutCountryCode)
|
||||
setCountryCode(countryCode);
|
||||
};
|
||||
const navigate = useNavigate();
|
||||
const [phone, setPhone] = React.useState("");
|
||||
|
||||
const onSubmit : SubmitHandler<IRegisterForm>= async (data: any) => {
|
||||
const payload = {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
phoneCountryCode: countryCode,
|
||||
phoneNumber: phoneNumber,
|
||||
};
|
||||
try {
|
||||
await dispatch(registerUser(payload)).unwrap();
|
||||
} catch (error) {
|
||||
toast(error)
|
||||
console.log("error",error)
|
||||
}
|
||||
};
|
||||
// Enhanced email validation regex
|
||||
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
|
||||
|
||||
return (
|
||||
<AppTheme {...props}>
|
||||
{/* <CssBaseline enableColorScheme /> */}
|
||||
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />/
|
||||
<SignUpContainer direction="column" justifyContent="space-between">
|
||||
<Card variant="outlined">
|
||||
Digi-EV
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h4"
|
||||
sx={{ width: '100%', fontSize: 'clamp(2rem, 10vw, 2.15rem)' }}
|
||||
>
|
||||
Sign up
|
||||
</Typography>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}
|
||||
>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="name">Full name</FormLabel>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: 'Name is required' }}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="name"
|
||||
fullWidth
|
||||
placeholder="Jon Snow"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
const handlePhoneChange = (value: string) => {
|
||||
// Remove all non-numeric characters except + for country code
|
||||
const cleanedNumber = value.replace(/[^\d+]/g, "");
|
||||
setValue("phone", cleanedNumber);
|
||||
};
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="email">Email</FormLabel>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
rules={{
|
||||
required: 'Email is required',
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message: 'Please enter a valid email address.'
|
||||
}
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="email"
|
||||
fullWidth
|
||||
placeholder="your@email.com"
|
||||
error={!!errors.email}
|
||||
helperText={errors.email?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
const onSubmit: SubmitHandler<IRegisterForm> = async (data) => {
|
||||
try {
|
||||
// Validate email
|
||||
if (!emailRegex.test(data.email)) {
|
||||
toast.error("Please enter a valid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="password">Password</FormLabel>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
rules={{
|
||||
required: 'Password is required',
|
||||
minLength: {
|
||||
value: 6,
|
||||
message: 'Password must be at least 6 characters long'
|
||||
}
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="password"
|
||||
fullWidth
|
||||
type="password"
|
||||
placeholder="••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<MuiPhoneNumber
|
||||
defaultCountry='in'
|
||||
onChange={handleOnChange}
|
||||
value={phoneNumber}
|
||||
/>
|
||||
// Validate phone number
|
||||
if (!data.phone || data.phone.length < 10) {
|
||||
toast.error("Please enter a valid phone number");
|
||||
return;
|
||||
}
|
||||
|
||||
{/* <FormControlLabel
|
||||
// Validate phone number
|
||||
const payload = {
|
||||
name: data.name.trim(),
|
||||
email: data.email.toLowerCase().trim(),
|
||||
password: data.password,
|
||||
phone: data.phone,
|
||||
role: data.role,
|
||||
};
|
||||
|
||||
await dispatch(registerUser(payload)).unwrap();
|
||||
toast.success("Registration successful!");
|
||||
navigate("/auth/login");
|
||||
} catch (error: any) {
|
||||
toast.error(error?.message || "Registration failed");
|
||||
console.error("Registration error:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AppTheme {...props}>
|
||||
{/* <CssBaseline enableColorScheme /> */}
|
||||
<ColorModeSelect
|
||||
sx={{ position: "fixed", top: "1rem", right: "1rem" }}
|
||||
/>
|
||||
<SignUpContainer direction="column" justifyContent="space-between">
|
||||
<Card variant="outlined">
|
||||
Digi-EV
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "100%",
|
||||
fontSize: "clamp(2rem, 10vw, 2.15rem)",
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</Typography>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="name">Full name</FormLabel>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: "Name is required" }}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="name"
|
||||
fullWidth
|
||||
placeholder="Jon Snow"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="email">Email</FormLabel>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
rules={{
|
||||
required: "Email is required",
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message:
|
||||
"Please enter a valid email address.",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="email"
|
||||
fullWidth
|
||||
placeholder="your@email.com"
|
||||
error={!!errors.email}
|
||||
helperText={errors.email?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="password">Password</FormLabel>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
rules={{
|
||||
required: "Password is required",
|
||||
minLength: {
|
||||
value: 6,
|
||||
message:
|
||||
"Password must be at least 6 characters long",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
id="password"
|
||||
fullWidth
|
||||
type="password"
|
||||
placeholder="••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel htmlFor="phone">Phone Number</FormLabel>
|
||||
<MuiPhoneNumber
|
||||
defaultCountry="in"
|
||||
onChange={handlePhoneChange}
|
||||
value={control._formValues.phone}
|
||||
required
|
||||
error={!!errors.phone}
|
||||
helperText={errors.phone?.message}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="role-select-label">Role</InputLabel>
|
||||
<Controller
|
||||
name="role"
|
||||
control={control}
|
||||
rules={{ required: "Role is required" }}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
labelId="role-select-label"
|
||||
id="role-select"
|
||||
value={field.value}
|
||||
onChange={(e) =>
|
||||
setValue("role", e.target.value)
|
||||
}
|
||||
error={!!errors.role}
|
||||
>
|
||||
<MenuItem value="superadmin">
|
||||
SuperAdmin
|
||||
</MenuItem>
|
||||
<MenuItem value="admin">Admin</MenuItem>
|
||||
<MenuItem value="user">User</MenuItem>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
{errors.role && (
|
||||
<Typography color="error" variant="caption">
|
||||
{errors.role.message}
|
||||
</Typography>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
{/* <FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name="allowExtraEmails"
|
||||
|
@ -222,14 +294,14 @@ const dispatch = useDispatch();
|
|||
label="I want to receive updates via email."
|
||||
/> */}
|
||||
|
||||
<Button type="submit" fullWidth variant="contained">
|
||||
Sign up
|
||||
</Button>
|
||||
</Box>
|
||||
{/* <Divider>
|
||||
<Button type="submit" fullWidth variant="contained">
|
||||
Sign up
|
||||
</Button>
|
||||
</Box>
|
||||
{/* <Divider>
|
||||
<Typography sx={{ color: 'text.secondary' }}>or</Typography>
|
||||
</Divider> */}
|
||||
{/* <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{/* <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
|
@ -257,21 +329,27 @@ const dispatch = useDispatch();
|
|||
</Link>
|
||||
</Typography>
|
||||
</Box> */}
|
||||
<Divider>or</Divider>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Typography sx={{ textAlign: 'center' }}>
|
||||
Already have an account?
|
||||
<Link
|
||||
href="/auth/login"
|
||||
variant="body2"
|
||||
sx={{ alignSelf: 'center' }}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
</SignUpContainer>
|
||||
</AppTheme>
|
||||
);
|
||||
<Divider>or</Divider>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ textAlign: "center" }}>
|
||||
Already have an account?
|
||||
<Link
|
||||
href="/auth/login"
|
||||
variant="body2"
|
||||
sx={{ alignSelf: "center" }}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
</SignUpContainer>
|
||||
</AppTheme>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import React, { useEffect } from "react";
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Made a special page for showing the profile details
|
||||
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
Container,
|
||||
Typography,
|
||||
|
@ -12,23 +16,19 @@ import {
|
|||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||
import { fetchProfile } from "../../redux/slices/authSlice";
|
||||
const ProfilePage = () => {
|
||||
// const dispatch = useDispatch();
|
||||
import { fetchAdminProfile } from "../../redux/slices/authSlice";
|
||||
|
||||
const ProfilePage = () => {
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Dispatch is called and user, isLoading, and error from Authstate Interface
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
//Created by : jaanvi and Eknoor
|
||||
//date: 12-feb-25
|
||||
//Extract user profile data from Auth
|
||||
// Extract profile, loading, and error state from Redux
|
||||
const { user, isLoading, error } = useSelector(
|
||||
(state: RootState) => state.auth
|
||||
(state: RootState) => state?.auth
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Dispatch the getProfile thunk to fetch user profile data
|
||||
|
||||
dispatch(fetchProfile());
|
||||
dispatch(fetchAdminProfile());
|
||||
}, [dispatch]);
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -50,19 +50,15 @@ const ProfilePage = () => {
|
|||
return (
|
||||
<Container>
|
||||
<Typography variant="h5" color="error" gutterBottom>
|
||||
Error: {error}
|
||||
<h2>An error occurred while loading profile</h2>
|
||||
</Typography>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Container>
|
||||
<Typography variant="h5">No profile data available</Typography>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
console.log(user?.name);
|
||||
console.log(user?.email);
|
||||
console.log(user?.role);
|
||||
|
||||
return (
|
||||
<Container sx={{ py: 4 }}>
|
||||
|
@ -74,6 +70,9 @@ const ProfilePage = () => {
|
|||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item>
|
||||
<Avatar
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//user is called for name and email
|
||||
alt={user?.name || "User Avatar"}
|
||||
src={"/static/images/avatar/7.jpg"}
|
||||
sx={{ width: 80, height: 80 }}
|
||||
|
@ -90,7 +89,7 @@ const ProfilePage = () => {
|
|||
Phone: {user?.phone || "N/A"}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Role: {user?.role || "N/A"}
|
||||
Role: <b>{user?.role || "N/A"}</b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -100,4 +99,4 @@ const ProfilePage = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
export default ProfilePage;
|
||||
|
|
|
@ -4,8 +4,9 @@ import { backendHttp, apiHttp } from "../../lib/https";
|
|||
import { toast } from "react-toastify";
|
||||
|
||||
// Define types for state
|
||||
//By jaanvi:: 12-feb-25
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Token for the user has been declared
|
||||
interface User {
|
||||
token: string | null;
|
||||
map(
|
||||
|
@ -21,32 +22,23 @@ interface User {
|
|||
phone: string;
|
||||
}
|
||||
|
||||
|
||||
interface Admin {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
user: User | null;
|
||||
admins: Admin[];
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
error: object | string | null;
|
||||
token: string | null; // Store token in Redux state
|
||||
|
||||
token: string | null;
|
||||
}
|
||||
|
||||
const initialState: AuthState = {
|
||||
user: null,
|
||||
admins: [],
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
token: null, //Intialize token
|
||||
|
||||
};
|
||||
|
||||
// Async thunk for login
|
||||
export const loginUser = createAsyncThunk<
|
||||
User,
|
||||
|
@ -58,10 +50,7 @@ export const loginUser = createAsyncThunk<
|
|||
email,
|
||||
password,
|
||||
});
|
||||
//changes By Jaanvi
|
||||
//Store token
|
||||
localStorage.setItem("authToken", response.data?.data?.token);
|
||||
|
||||
localStorage.setItem("authToken", response.data?.data?.token); // Save token
|
||||
toast.success(response.data?.message);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
|
@ -71,52 +60,20 @@ export const loginUser = createAsyncThunk<
|
|||
}
|
||||
});
|
||||
|
||||
//created by jaanvi
|
||||
//date: 12-feb-25
|
||||
//function for fetching Profile data function
|
||||
export const fetchProfile = createAsyncThunk<
|
||||
User,
|
||||
void,
|
||||
{ rejectValue: string }
|
||||
>("auth/fetchProfile", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
//Get the token from localStorage
|
||||
const token = localStorage.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await apiHttp.get("/auth/profile", {
|
||||
headers: { Authorization: `Bearer ${token}` }, // Ensuring 'Bearer' prefix
|
||||
});
|
||||
|
||||
console.log("API Response:", response.data); // Debugging
|
||||
|
||||
if (!response.data?.data) {
|
||||
throw new Error("Invalid API response");
|
||||
}
|
||||
|
||||
return response.data.data;
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
"Profile Fetch Error:",
|
||||
error.response?.data || error.message
|
||||
);
|
||||
return rejectWithValue(
|
||||
error.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Async thunk for register
|
||||
export const registerUser = createAsyncThunk<
|
||||
User,
|
||||
{ email: string; password: string },
|
||||
{
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
},
|
||||
{ rejectValue: string }
|
||||
>("auth/register", async (data, { rejectWithValue }) => {
|
||||
>("auth/signup", async (data, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
"https://health-digi.dmlabs.in/auth/register",
|
||||
data
|
||||
);
|
||||
const response = await apiHttp.post("auth/signup", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(
|
||||
|
@ -138,10 +95,16 @@ export const adminList = createAsyncThunk<
|
|||
const response = await apiHttp.get("/auth");
|
||||
console.log(response?.data?.data);
|
||||
return response?.data?.data?.map(
|
||||
(admin: { id: string; name: string; role: string }) => ({
|
||||
id: admin.id,
|
||||
(admin: {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
email: string;
|
||||
}) => ({
|
||||
id: admin?.id,
|
||||
name: admin?.name,
|
||||
role: admin?.role || "N/A",
|
||||
email: admin?.email,
|
||||
})
|
||||
);
|
||||
} catch (error: any) {
|
||||
|
@ -180,8 +143,8 @@ export const updateAdmin = createAsyncThunk(
|
|||
try {
|
||||
const response = await apiHttp.put(`/auth/${id}`, { name, role });
|
||||
toast.success("Admin updated successfully");
|
||||
console.log(response.data);
|
||||
return response.data;
|
||||
console.log(response?.data);
|
||||
return response?.data;
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(
|
||||
error.response?.data?.message || "An error occurred"
|
||||
|
@ -190,6 +153,52 @@ export const updateAdmin = createAsyncThunk(
|
|||
}
|
||||
);
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Function for fetching profile of a particular user has been implemented with Redux.
|
||||
export const fetchAdminProfile = createAsyncThunk<
|
||||
User,
|
||||
void,
|
||||
{ rejectValue: string }
|
||||
>("auth/fetchAdminProfile", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const token = localStorage?.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await apiHttp?.get("/auth/profile", {
|
||||
headers: { Authorization: `Bearer ${token}` }, // Ensure 'Bearer' prefix
|
||||
});
|
||||
|
||||
console.log("API Response:", response?.data); // Debugging
|
||||
|
||||
if (!response.data?.data) {
|
||||
throw new Error("Invalid API response");
|
||||
}
|
||||
|
||||
return response?.data?.data; // Fix: Return only `data`, assuming it contains user info.
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
"Profile Fetch Error:",
|
||||
error?.response?.data || error?.message
|
||||
);
|
||||
return rejectWithValue(
|
||||
error?.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const initialState: AuthState = {
|
||||
user: null,
|
||||
admins: [],
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//initial state of token set to null
|
||||
token: null,
|
||||
};
|
||||
|
||||
const authSlice = createSlice({
|
||||
name: "auth",
|
||||
initialState,
|
||||
|
@ -197,6 +206,9 @@ const authSlice = createSlice({
|
|||
logout: (state) => {
|
||||
state.user = null;
|
||||
state.isAuthenticated = false;
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Token is removed from local storage and set to null
|
||||
state.token = null;
|
||||
localStorage.removeItem("authToken");
|
||||
},
|
||||
|
@ -211,7 +223,7 @@ const authSlice = createSlice({
|
|||
.addCase(loginUser.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.isAuthenticated = true;
|
||||
state.user = action.payload;
|
||||
state.user = action.payload; // Fix: Extract correct payload
|
||||
state.token = action.payload.token; // Store token in Redux
|
||||
})
|
||||
.addCase(
|
||||
|
@ -286,8 +298,8 @@ const authSlice = createSlice({
|
|||
})
|
||||
.addCase(updateAdmin.fulfilled, (state, action) => {
|
||||
const updatedAdmin = action.payload;
|
||||
state.admins = state.admins.map((admin) =>
|
||||
admin.id === updatedAdmin.id ? updatedAdmin : admin
|
||||
state.admins = state?.admins?.map((admin) =>
|
||||
admin?.id === updatedAdmin?.id ? updatedAdmin : admin
|
||||
);
|
||||
|
||||
state.isLoading = false;
|
||||
|
@ -298,20 +310,20 @@ const authSlice = createSlice({
|
|||
action.payload ||
|
||||
"Something went wrong while updating Admin!!";
|
||||
})
|
||||
//Adding Cases for Profile Slice
|
||||
//Pending state
|
||||
.addCase(fetchProfile.pending, (state) => {
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Reducers for fetching profiles has been implemented
|
||||
.addCase(fetchAdminProfile.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
state.error = null;
|
||||
})
|
||||
//Fullfilled state
|
||||
.addCase(fetchProfile.fulfilled, (state, action) => {
|
||||
.addCase(fetchAdminProfile.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.user = action.payload;
|
||||
state.isAuthenticated = true;
|
||||
})
|
||||
//Failed to load state
|
||||
.addCase(fetchProfile.rejected, (state, action) => {
|
||||
.addCase(fetchAdminProfile.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.error = action.payload || "Failed to fetch admin profile";
|
||||
});
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
import rootReducer from "../reducers";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import authReducer from '../slices/authSlice.ts'
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
auth: authReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export default store;
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ import Dashboard from "./pages/Dashboard";
|
|||
import Vehicles from "./pages/Vehicles";
|
||||
import AdminList from "./pages/AdminList";
|
||||
import ProfilePage from "./pages/ProfilePage";
|
||||
// import SuperAdminRouter from "./components/SuperAdminRoute";
|
||||
// SuperAdminRouter // Fix: single import with correct path
|
||||
|
||||
import SuperAdminRouter from "./superAdminRouter";
|
||||
|
||||
|
|
|
@ -7,20 +7,19 @@ import { useSelector } from "react-redux";
|
|||
import { Navigate } from "react-router-dom";
|
||||
import { RootState } from "./redux/store/store";
|
||||
|
||||
|
||||
interface SuperAdminRouteProps {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
|
||||
const SuperAdminRouter: React.FC<SuperAdminRouteProps> = ({ children }) => {
|
||||
const userRole = useSelector((state: RootState) => state.auth.user?.role);
|
||||
|
||||
if (userRole !== 'superadmin') {
|
||||
// Redirect to dashboard if user is not a superadmin
|
||||
return <Navigate to="/panel/dashboard" replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
const userRole = useSelector((state: RootState) => state.auth.user?.role);
|
||||
|
||||
if (userRole !== "superadmin") {
|
||||
// Redirect to dashboard if user is not a superadmin
|
||||
return <Navigate to="/panel/dashboard" replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
|
||||
export default SuperAdminRouter;
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2023"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
}
|
||||
}
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue