dev-jaanvi #1

Open
jaanvi wants to merge 155 commits from dev-jaanvi into main
22 changed files with 25171 additions and 14419 deletions
Showing only changes of commit 7de0799b02 - Show all commits

View file

@ -1,6 +1,8 @@
{ {
"trailingComma": "es5",
"tabWidth": 4, "tabWidth": 4,
"semi": false, "useTabs": true,
"singleQuote": false "semi": true,
} "singleQuote": false,
"bracketSpacing": true
}

32473
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,75 +1,76 @@
{ {
"name": "digi-health-admin-dashboard", "name": "digi-ev-admin-dashboard",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.3.0", "@mui/icons-material": "^6.3.0",
"@mui/material": "^6.3.0", "@mui/material": "^6.3.0",
"@mui/x-charts": "^7.23.2", "@mui/x-charts": "^7.23.2",
"@mui/x-data-grid": "^7.23.5", "@mui/x-data-grid": "^7.23.5",
"@mui/x-date-pickers": "^7.23.3", "@mui/x-date-pickers": "^7.23.3",
"@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",
"AdapterDayjs": "link:@mui/x-date-pickers/AdapterDayjs", "AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs",
"AppBar": "link:@mui/material/AppBar", "add": "^2.0.6",
"Box": "link:@mui/material/Box", "AppBar": "file:@mui/material/AppBar",
"PieChart": "link:@mui/x-charts/PieChart", "axios": "^1.7.9",
"RichTreeView": "link:@mui/x-tree-view/RichTreeView", "Box": "file:@mui/material/Box",
"Stack": "link:@mui/material/Stack", "clsx": "^2.1.1",
"Tabs": "link:@mui/material/Tabs", "cra-template-typescript": "1.2.0",
"Toolbar": "link:@mui/material/Toolbar", "dayjs": "^1.11.13",
"Typography": "link:@mui/material/Typography", "hooks": "file:@mui/x-charts/hooks",
"add": "^2.0.6", "mui-phone-number": "^3.0.3",
"axios": "^1.7.9", "mui-tel-input": "^7.0.0",
"clsx": "^2.1.1", "PieChart": "file:@mui/x-charts/PieChart",
"cra-template-typescript": "1.2.0", "prop-types": "^15.8.1",
"dayjs": "^1.11.13", "react": "^18.0.0",
"hooks": "link:@mui/x-charts/hooks", "react-cookie": "^7.2.2",
"mui-phone-number": "^3.0.3", "react-dom": "^18.0.0",
"mui-tel-input": "^7.0.0", "react-dropzone": "^14.3.5",
"prop-types": "^15.8.1", "react-hook-form": "^7.54.2",
"react": "^19.0.0", "react-redux": "^9.2.0",
"react-dom": "^19.0.0", "react-router-dom": "^7.1.1",
"react-dropzone": "^14.3.5", "react-scripts": "5.0.1",
"react-hook-form": "^7.54.2", "react-toastify": "^11.0.2",
"react-redux": "^9.2.0", "RichTreeView": "file:@mui/x-tree-view/RichTreeView",
"react-router-dom": "^7.1.1", "Stack": "file:@mui/material/Stack",
"react-scripts": "5.0.1", "styles": "file:@mui/material/styles",
"react-toastify": "^11.0.2", "Tabs": "file:@mui/material/Tabs",
"styles": "link:@mui/material/styles", "Toolbar": "file:@mui/material/Toolbar",
"web-vitals": "^4.2.4" "Typography": "file:@mui/material/Typography",
}, "web-vitals": "^4.2.4"
"scripts": { },
"start": "react-scripts start", "scripts": {
"build": "react-scripts build", "start": "react-scripts start",
"test": "react-scripts test", "build": "react-scripts build",
"eject": "react-scripts eject" "test": "react-scripts test",
}, "eject": "react-scripts eject"
"eslintConfig": { },
"extends": [ "eslintConfig": {
"react-app", "extends": [
"react-app/jest" "react-app",
] "react-app/jest"
}, ]
"browserslist": { },
"production": [ "browserslist": {
">0.2%", "production": [
"not dead", ">0.2%",
"not op_mini all" "not dead",
], "not op_mini all"
"development": [ ],
"last 1 chrome version", "development": [
"last 1 firefox version", "last 1 chrome version",
"last 1 safari version" "last 1 firefox version",
] "last 1 safari version"
}, ]
"devDependencies": { },
"@types/node": "^22.10.5", "devDependencies": {
"@types/react": "^19.0.3", "@types/node": "^22.10.5",
"@types/react-dom": "^19.0.2", "@types/react": "^19.0.4",
"typescript": "^5.7.2" "@types/react-dom": "^19.0.2",
} "typescript": "^5.7.3"
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,43 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Web site created using create-react-app" content="Web site created using create-react-app"
/> />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
jaanvi marked this conversation as resolved Outdated

add app specific meta description.

add app specific meta description.
You can add webfonts, meta tags, or analytics to this file. <title>DigiEV - Eco-friendly Charge</title>
The build step will place the bundled scripts into the <body> tag. </head>
<body>
To begin the development, run `npm start` or `yarn start`. <div id="root"></div>
To create a production bundle, use `npm run build` or `yarn build`. </body>
-->
</body>
</html> </html>

View file

@ -1,12 +1,12 @@
import { BrowserRouter as Router} from 'react-router-dom'; import { BrowserRouter as Router } from "react-router-dom";
import AppRouter from './router'; import AppRouter from "./router";
function App() { function App() {
return ( return (
<Router> <Router>
<AppRouter /> <AppRouter />
</Router> </Router>
); );
} }
export default App; export default App;

View file

@ -1,82 +1,311 @@
import React,{useEffect} from "react"; // import React, { useEffect } from "react";
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField } from "@mui/material"; // 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,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
TextField,
} from "@mui/material";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
//By Jaanvi : Edit Model :: 11-feb-25
interface AddEditCategoryModalProps { interface AddEditCategoryModalProps {
open: boolean; open: boolean;
handleClose: () => void; handleClose: () => void;
editRow:any; editRow: any;
handleUpdate: (id: string, name: string, role: string) => void;
} }
interface FormData { interface FormData {
category: string; role: string;
name: string;
//category: string;
} }
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({ open, handleClose,editRow }) => { const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
const { control, handleSubmit, formState: { errors },setValue,reset } = useForm<FormData>({ open,
defaultValues: { handleClose,
category: "", editRow,
}, handleUpdate,
}); }) => {
const {
control,
handleSubmit,
formState: { errors },
setValue,
reset,
} = useForm<FormData>({
defaultValues: {
//category: "",
name: "",
role: "",
},
});
const onSubmit = (data: FormData) => { const onSubmit = (data: FormData) => {
console.log(data.category); if (editRow) {
handleClose(); handleUpdate(editRow.id, data.name, data.role);
reset(); }
}; // else {
// console.log(data.category);
// }
handleClose();
reset();
};
useEffect(() => { useEffect(() => {
if (editRow) { if (editRow) {
setValue('category', editRow.name); // setValue("category", editRow.name);
} else { setValue("name", editRow.name);
reset(); setValue("role", editRow.role);
} } else {
}, [editRow, setValue, reset]); reset();
}
}, [editRow, setValue, reset]);
return ( return (
<> <>
<Dialog <Dialog
open={open} open={open}
onClose={handleClose} onClose={handleClose}
maxWidth="md" maxWidth="md"
fullWidth fullWidth
PaperProps={{ PaperProps={{
component: 'form', component: "form",
onSubmit: handleSubmit(onSubmit), onSubmit: handleSubmit(onSubmit),
}} }}
> >
<DialogTitle>{editRow ? "Editwefwefwe" : 'Add'} Category</DialogTitle> <DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle>
<DialogContent> <DialogContent>
<Controller {/* <Controller
name="category" name="category"
control={control} control={control}
rules={{ rules={{
required: "Category Name is required", 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
render={({ field }) => ( name="role"
<TextField control={control}
{...field} rules={{
autoFocus required: "Role is required",
required }}
margin="dense" render={({ field }) => (
label="Add Category Name" <TextField
type="text" {...field}
fullWidth margin="dense"
variant="standard" label="Role"
error={!!errors.category} type="text"
helperText={errors.category?.message} fullWidth
/> variant="standard"
)} error={!!errors.role}
/> helperText={errors.role?.message}
</DialogContent> />
<DialogActions> )}
<Button onClick={handleClose}>Cancel</Button> />
<Button type="submit">Save</Button> </DialogContent>
</DialogActions> <DialogActions>
</Dialog> <Button onClick={handleClose}>Cancel</Button>
</> <Button type="submit">Save</Button>
); </DialogActions>
</Dialog>
</>
);
}; };
export default AddEditCategoryModal; export default AddEditCategoryModal;

View file

@ -1,187 +1,193 @@
import * as React from 'react'; import * as React from "react";
import { styled } from '@mui/material/styles'; import { styled } from "@mui/material/styles";
import Table from '@mui/material/Table'; import Table from "@mui/material/Table";
import TableBody from '@mui/material/TableBody'; import TableBody from "@mui/material/TableBody";
import TableCell, { tableCellClasses } from '@mui/material/TableCell'; import TableCell, { tableCellClasses } from "@mui/material/TableCell";
import TableContainer from '@mui/material/TableContainer'; import TableContainer from "@mui/material/TableContainer";
import TableHead from '@mui/material/TableHead'; 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 { import {
Box, Box,
Button, Button,
dividerClasses, dividerClasses,
IconButton, IconButton,
listClasses, listClasses,
Menu, Menu,
} from '@mui/material'; } from "@mui/material";
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded";
// Styled components for customization // Styled components for customization
const StyledTableCell = styled(TableCell)(({ theme }) => ({ const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: { [`&.${tableCellClasses.head}`]: {
backgroundColor: ' #1565c0', backgroundColor: " #1565c0",
color: theme.palette.common.white, color: theme.palette.common.white,
}, },
[`&.${tableCellClasses.body}`]: { [`&.${tableCellClasses.body}`]: {
fontSize: 14, fontSize: 14,
}, },
})); }));
const StyledTableRow = styled(TableRow)(({ theme }) => ({ const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': { "&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover, backgroundColor: theme.palette.action.hover,
}, },
'&:last-child td, &:last-child th': { "&:last-child td, &:last-child th": {
border: 0, border: 0,
}, },
})); }));
interface Column { interface Column {
id: string; id: string;
label: string; label: string;
align?: 'left' | 'center' | 'right'; align?: "left" | "center" | "right";
} }
interface Row { interface Row {
[key: string]: any; [key: string]: any;
} }
interface CustomTableProps { interface CustomTableProps {
columns: Column[]; columns: Column[];
rows: Row[]; rows: Row[];
setDeleteModal: Function; setDeleteModal: Function;
setRowData: Function; setRowData: Function;
setModalOpen: Function; setModalOpen: Function;
} }
const CustomTable: React.FC<CustomTableProps> = ({ const CustomTable: React.FC<CustomTableProps> = ({
columns, columns,
rows, rows,
setDeleteModal, setDeleteModal,
setRowData, setRowData,
setModalOpen, setModalOpen,
}) => { }) => {
console.log('columnsss', columns, rows); console.log("columnsss", columns, rows);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => { const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
}; };
const handleClose = () => { const handleClose = () => {
setAnchorEl(null); setAnchorEl(null);
}; };
const isImage = (value: any) => { const isImage = (value: any) => {
if (typeof value === 'string') { if (typeof value === "string") {
return value.startsWith('http') || value.startsWith('data:image'); // Check for URL or base64 image return value.startsWith("http") || value.startsWith("data:image"); // Check for URL or base64 image
} }
return false; return false;
}; };
return ( return (
<TableContainer component={Paper}> <TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table"> <Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead> <TableHead>
<TableRow> <TableRow>
{columns.map((column) => ( {columns.map((column) => (
<StyledTableCell key={column.id} align={column.align || 'left'}> <StyledTableCell
{column.label} key={column.id}
</StyledTableCell> align={column.align || "left"}
))} >
</TableRow> {column.label}
</TableHead> </StyledTableCell>
<TableBody> ))}
{rows.map((row, rowIndex) => ( </TableRow>
<StyledTableRow key={rowIndex}> </TableHead>
{columns.map((column) => ( <TableBody>
<StyledTableCell key={column.id} align={column.align || 'left'}> {rows.map((row, rowIndex) => (
{isImage(row[column.id]) ? ( <StyledTableRow key={rowIndex}>
<img {columns.map((column) => (
src={row[column.id]} <StyledTableCell
alt="Row " key={column.id}
style={{ align={column.align || "left"}
width: '50px', >
height: '50px', {isImage(row[column.id]) ? (
objectFit: 'cover', <img
}} src={row[column.id]}
/> alt="Row "
) : column.id !== 'action' ? ( style={{
row[column.id] width: "50px",
) : ( height: "50px",
<IconButton objectFit: "cover",
onClick={(e) => { }}
handleClick(e); />
setRowData(row); ) : column.id !== "action" ? (
}} row[column.id]
> ) : (
<MoreVertRoundedIcon /> <IconButton
</IconButton> onClick={(e) => {
)} handleClick(e);
</StyledTableCell> setRowData(row);
))} }}
</StyledTableRow> >
))} <MoreVertRoundedIcon />
</TableBody> </IconButton>
</Table> )}
{open && ( </StyledTableCell>
<Menu ))}
anchorEl={anchorEl} </StyledTableRow>
id="menu" ))}
open={open} </TableBody>
onClose={handleClose} </Table>
onClick={handleClose} {open && (
transformOrigin={{ horizontal: 'right', vertical: 'top' }} <Menu
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} anchorEl={anchorEl}
sx={{ id="menu"
[`& .${listClasses.root}`]: { open={open}
padding: '4px', onClose={handleClose}
}, onClick={handleClose}
[`& .${paperClasses.root}`]: { transformOrigin={{ horizontal: "right", vertical: "top" }}
padding: 0, anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
}, sx={{
[`& .${dividerClasses.root}`]: { [`& .${listClasses.root}`]: {
margin: '4px -4px', padding: "4px",
}, },
}} [`& .${paperClasses.root}`]: {
> padding: 0,
<Box },
sx={{ [`& .${dividerClasses.root}`]: {
display: 'flex', margin: "4px -4px",
flexDirection: 'column', },
justifyContent: 'flex-start', }}
}} >
> <Box
<Button sx={{
variant="text" display: "flex",
onClick={() => setModalOpen(true)} flexDirection: "column",
color="primary" justifyContent: "flex-start",
sx={{ }}
justifyContent: 'flex-start', >
py: 0, <Button
textTransform: 'capitalize', variant="text"
}} onClick={() => setModalOpen(true)}
> color="primary"
Edit sx={{
</Button> justifyContent: "flex-start",
<Button py: 0,
variant="text" textTransform: "capitalize",
onClick={() => setDeleteModal(true)} }}
color="error" >
sx={{ Edit
justifyContent: 'flex-start', </Button>
py: 0, <Button
textTransform: 'capitalize', variant="text"
}} onClick={() => setDeleteModal(true)}
> color="error"
Delete sx={{
</Button> justifyContent: "flex-start",
</Box> py: 0,
</Menu> textTransform: "capitalize",
)} }}
</TableContainer> >
); Delete
</Button>
</Box>
</Menu>
)}
</TableContainer>
);
}; };
export default CustomTable; export default CustomTable;

View file

@ -1,89 +1,89 @@
import * as React from 'react'; import * as React from "react";
import { styled } from '@mui/material/styles'; import { styled } from "@mui/material/styles";
import Divider, { dividerClasses } from '@mui/material/Divider'; import Divider, { dividerClasses } from "@mui/material/Divider";
import Menu from '@mui/material/Menu'; import Menu from "@mui/material/Menu";
import MuiMenuItem from '@mui/material/MenuItem'; import MuiMenuItem from "@mui/material/MenuItem";
import { paperClasses } from '@mui/material/Paper'; import { paperClasses } from "@mui/material/Paper";
import { listClasses } from '@mui/material/List'; import { listClasses } from "@mui/material/List";
import ListItemText from '@mui/material/ListItemText'; import ListItemText from "@mui/material/ListItemText";
import ListItemIcon, { listItemIconClasses } from '@mui/material/ListItemIcon'; import ListItemIcon, { listItemIconClasses } from "@mui/material/ListItemIcon";
import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded'; import LogoutRoundedIcon from "@mui/icons-material/LogoutRounded";
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; 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";
const MenuItem = styled(MuiMenuItem)({ const MenuItem = styled(MuiMenuItem)({
margin: '2px 0', margin: "2px 0",
}); });
export default function OptionsMenu({ avatar }: { avatar?: boolean }) { export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => { const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
}; };
const handleClose = () => { const handleClose = () => {
setAnchorEl(null); setAnchorEl(null);
}; };
return ( return (
<React.Fragment> <React.Fragment>
<MenuButton <MenuButton
aria-label="Open menu" aria-label="Open menu"
onClick={handleClick} onClick={handleClick}
sx={{ borderColor: 'transparent' }} sx={{ borderColor: "transparent" }}
> >
{avatar ? ( {avatar ? (
<MoreVertRoundedIcon /> <MoreVertRoundedIcon />
) : ( ) : (
<Avatar <Avatar
sizes="small" sizes="small"
alt="Riley Carter" alt="Riley Carter"
src="/static/images/avatar/7.jpg" src="/static/images/avatar/7.jpg"
sx={{ width: 36, height: 36 }} sx={{ width: 36, height: 36 }}
/> />
)} )}
</MenuButton> </MenuButton>
<Menu <Menu
anchorEl={anchorEl} anchorEl={anchorEl}
id="menu" id="menu"
open={open} open={open}
onClose={handleClose} onClose={handleClose}
onClick={handleClose} onClick={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }} transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
sx={{ sx={{
[`& .${listClasses.root}`]: { [`& .${listClasses.root}`]: {
padding: '4px', padding: "4px",
}, },
[`& .${paperClasses.root}`]: { [`& .${paperClasses.root}`]: {
padding: 0, padding: 0,
}, },
[`& .${dividerClasses.root}`]: { [`& .${dividerClasses.root}`]: {
margin: '4px -4px', margin: "4px -4px",
}, },
}} }}
> >
<MenuItem onClick={handleClose}>Profile</MenuItem> <MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem> <MenuItem onClick={handleClose}>My account</MenuItem>
<Divider /> <Divider />
<MenuItem onClick={handleClose}>Add another account</MenuItem> <MenuItem onClick={handleClose}>Add another account</MenuItem>
<MenuItem onClick={handleClose}>Settings</MenuItem> <MenuItem onClick={handleClose}>Settings</MenuItem>
<Divider /> <Divider />
<MenuItem <MenuItem
onClick={handleClose} onClick={handleClose}
sx={{ sx={{
[`& .${listItemIconClasses.root}`]: { [`& .${listItemIconClasses.root}`]: {
ml: 'auto', ml: "auto",
minWidth: 0, minWidth: 0,
}, },
}} }}
> >
<ListItemText>Logout</ListItemText> <ListItemText>Logout</ListItemText>
<ListItemIcon> <ListItemIcon>
<LogoutRoundedIcon fontSize="small" /> <LogoutRoundedIcon fontSize="small" />
</ListItemIcon> </ListItemIcon>
</MenuItem> </MenuItem>
</Menu> </Menu>
</React.Fragment> </React.Fragment>
); );
} }

View file

@ -1,39 +1,22 @@
import { intersection, uniq } from 'lodash';
import React from 'react'; import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { checkUserAuth } from '../redux/slices/authSlice'; import { checkUserAuth } from '../redux/slices/authSlice';
const useAuth = (caps, { matchAllCaps = false }) => { const useAuth = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [isAuthorized, setIsAuthorized] = React.useState(false); const [isAuthorized, setIsAuthorized] = React.useState(false);
const { isAuthenticated } = useSelector((state) => ({
const { isAuthenticated, userCapabilities } = useSelector((state) => ({
isAuthenticated: state.authReducer.isAuthenticated, isAuthenticated: state.authReducer.isAuthenticated,
userCapabilities: state.authReducer.userCapabilities,
})); }));
const requiredCaps = React.useMemo(
() => uniq(Array.isArray(caps) ? caps : [caps]),
[caps]
);
React.useEffect(() => { React.useEffect(() => {
const userMatchedCaps = intersection(userCapabilities, requiredCaps); if (isAuthenticated) {
let isUserAuthorized = matchAllCaps
? userMatchedCaps?.length === requiredCaps?.length
: userMatchedCaps?.length > 0;
if (requiredCaps.length === 0) {
isUserAuthorized = true;
}
// if (isAuthenticated === null) {
if (isAuthenticated === null || false || undefined) {
dispatch(checkUserAuth()); dispatch(checkUserAuth());
jaanvi marked this conversation as resolved Outdated

If its authenticated then why again checking checkUserAuth.

If its authenticated then why again checking checkUserAuth.
} else { } else {
setIsAuthorized(isAuthenticated && isUserAuthorized); setIsAuthorized(false);
} }
}, [dispatch, isAuthenticated, requiredCaps, userCapabilities]); }, [dispatch, isAuthenticated]);
return { isAuthenticated, isAuthorized }; return { isAuthenticated, isAuthorized };
}; };

View file

@ -1,26 +1,26 @@
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 { Slide, ToastContainer } from "react-toastify";
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 <ToastContainer
autoClose={2000} autoClose={2000}
hideProgressBar hideProgressBar
theme="dark" theme="dark"
transition={Slide} transition={Slide}
toastStyle={{ border: '1px solid dimgray' }} 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

@ -1,114 +1,129 @@
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react";
import { Box, Button, Typography } from "@mui/material" import { Box, Button, Typography } from "@mui/material";
import AddEditCategoryModal from "../../components/AddEditCategoryModal" import AddEditCategoryModal from "../../components/AddEditCategoryModal";
import { useForm } from "react-hook-form" // import { useForm } from "react-hook-form";
import CustomTable from "../../components/CustomTable" import { useForm } from "react-hook-form";
import DeleteModal from "../../components/Modals/DeleteModal/DeleteModal" import CustomTable from "../../components/CustomTable";
import { useDispatch, useSelector } from "react-redux" import DeleteModal from "../../components/Modals/DeleteModal/DeleteModal";
import { adminList } from "../../redux/slices/authSlice" import { useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "../../redux/store/store" // Import RootState for selector import { adminList, updateAdmin } from "../../redux/slices/authSlice";
import { AppDispatch, RootState } from "../../redux/store/store"; // Import RootState for selector
// Sample data for categories // Sample data for categories
export default function AdminList() { export default function AdminList() {
const [modalOpen, setModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(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] = React.useState<boolean>(false);
const [rowData, setRowData] = React.useState<any | null>(null) const [rowData, setRowData] = React.useState<any | null>(null);
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.auth.admins) const admins = useSelector((state: RootState) => state.auth.admins);
// Dispatching the API call when the component mounts // Dispatching the API call when the component mounts
useEffect(() => { useEffect(() => {
dispatch(adminList()) dispatch(adminList());
}, [dispatch]) }, [dispatch]);
const handleClickOpen = () => { const handleClickOpen = () => {
setModalOpen(true) setModalOpen(true);
setEditRow(null) setEditRow(null);
} };
const handleCloseModal = () => { const handleCloseModal = () => {
setModalOpen(false) setModalOpen(false);
reset() reset();
} };
const handleDelete = () => { const handleDelete = () => {
setDeleteModal(false) setDeleteModal(false);
} };
//By Jaanvi : Edit feature :: 11-feb-25
const handleUpdate = async (id: string, name: string, role: string) => {
try {
await dispatch(updateAdmin({ id, name, role }));
dispatch(adminList()); // Fetch updated admins list after update
} catch (error) {
console.error("Update failed", error);
}
};
const categoryColumns = [ const categoryColumns = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },
{ id: "name", label: "Name" }, { id: "name", label: "Name" },
{ id: "role", label: "Role" }, { id: "role", label: "Role" },
{ id: "action", label: "Action", align: "center" }, { id: "action", label: "Action", align: "center" },
] ];
// If no admins are available, display the sample data // If no admins are available, display the sample data
const categoryRows = admins?.length const categoryRows = admins?.length
? admins?.map( ? admins?.map(
(admin: { name: string; role: string }, index: number) => ({ (
srno: index + 1, admin: { id: string; name: string; role: string },
name: admin?.name, index: number
role: admin?.role || "N/A", ) => ({
}) srno: index + 1,
) id: admin?.id,
: [] name: admin?.name,
role: admin?.role || "N/A",
})
)
: [];
return ( return (
<> <>
<Box <Box
sx={{ sx={{
width: "100%", width: "100%",
maxWidth: { maxWidth: {
sm: "100%", sm: "100%",
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",
}, },
}} }}
> >
{/* Title and Add Category button */} {/* Title and Add Category button */}
<Typography <Typography
component="h2" component="h2"
variant="h6" variant="h6"
sx={{ mt: 2, fontWeight: 600 }} sx={{ mt: 2, fontWeight: 600 }}
> >
Admins Admins
</Typography> </Typography>
<Button <Button
variant="contained" variant="contained"
size="medium" size="medium"
sx={{ textAlign: "right" }} sx={{ textAlign: "right" }}
onClick={handleClickOpen} onClick={handleClickOpen}
> >
Add Category Add Category
</Button> </Button>
</Box> </Box>
<CustomTable <CustomTable
columns={categoryColumns} columns={categoryColumns}
rows={categoryRows} rows={categoryRows}
editRow={editRow} editRow={editRow}
setDeleteModal={setDeleteModal} setDeleteModal={setDeleteModal}
setRowData={setRowData} setRowData={setRowData}
setModalOpen={setModalOpen} setModalOpen={setModalOpen}
/> />
<AddEditCategoryModal <AddEditCategoryModal
open={modalOpen} open={modalOpen}
handleClose={handleCloseModal} handleClose={handleCloseModal}
editRow={rowData} editRow={rowData}
/> handleUpdate={handleUpdate}
<DeleteModal />
open={deleteModal} <DeleteModal
setDeleteModal={setDeleteModal} open={deleteModal}
handleDelete={handleDelete} setDeleteModal={setDeleteModal}
/> handleDelete={handleDelete}
</> />
) </>
);
} }

View file

@ -213,7 +213,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
<Typography sx={{ textAlign: 'center' }}> <Typography sx={{ textAlign: 'center' }}>
Don&apos;t have an account?{' '} Don&apos;t have an account?{' '}
<Link <Link
href="/signup/" href="/auth/signup"
variant="body2" variant="body2"
sx={{ alignSelf: 'center' }} sx={{ alignSelf: 'center' }}
> >

View file

@ -117,11 +117,11 @@ const dispatch = useDispatch();
return ( return (
<AppTheme {...props}> <AppTheme {...props}>
<CssBaseline enableColorScheme /> {/* <CssBaseline enableColorScheme /> */}
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} /> <ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />/
<SignUpContainer direction="column" justifyContent="space-between"> <SignUpContainer direction="column" justifyContent="space-between">
<Card variant="outlined"> <Card variant="outlined">
<SitemarkIcon /> Digi-EV
<Typography <Typography
component="h1" component="h1"
variant="h4" variant="h4"
@ -204,12 +204,12 @@ const dispatch = useDispatch();
/> />
</FormControl> </FormControl>
<MuiPhoneNumber <MuiPhoneNumber
defaultCountry='it' defaultCountry='in'
onChange={handleOnChange} onChange={handleOnChange}
value={phoneNumber} value={phoneNumber}
/> />
<FormControlLabel {/* <FormControlLabel
control={ control={
<Controller <Controller
name="allowExtraEmails" name="allowExtraEmails"
@ -220,16 +220,16 @@ const dispatch = useDispatch();
/> />
} }
label="I want to receive updates via email." label="I want to receive updates via email."
/> /> */}
<Button type="submit" fullWidth variant="contained"> <Button type="submit" fullWidth variant="contained">
Sign up Sign up
</Button> </Button>
</Box> </Box>
<Divider> {/* <Divider>
<Typography sx={{ color: 'text.secondary' }}>or</Typography> <Typography sx={{ color: 'text.secondary' }}>or</Typography>
</Divider> </Divider> */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> {/* <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button <Button
fullWidth fullWidth
variant="outlined" variant="outlined"
@ -256,6 +256,19 @@ const dispatch = useDispatch();
Sign in Sign in
</Link> </Link>
</Typography> </Typography>
</Box> */}
<Divider>or</Divider>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Typography sx={{ textAlign: 'center' }}>
Already have an account? &nbsp;
<Link
href="/auth/login"
variant="body2"
sx={{ alignSelf: 'center' }}
>
Sign in
</Link>
</Typography>
</Box> </Box>
</Card> </Card>
</SignUpContainer> </SignUpContainer>

View file

@ -0,0 +1,9 @@
import React from 'react'
function NotFoundPage() {
return (
<div>NotFoundPage</div>
)
}
export default NotFoundPage;

View file

@ -19,7 +19,7 @@ const categoryRows = [
]; ];
export default function Vehicles() { export default function Vehicles() {
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [editRow, setEditRow] = useState<any>(null); const [editRow, setEditRow] = useState<any>(null);
const { reset } = useForm(); const { reset } = useForm();

11
src/redux/reducers.ts Normal file
View file

@ -0,0 +1,11 @@
import { combineReducers } from "@reduxjs/toolkit";
import authReducer from "./slices/authSlice";
const rootReducer = combineReducers({
auth: authReducer,
});
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;

View file

@ -1,32 +0,0 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// Example: You might have an API call for deleting admin like this
export const deleteAdmin = createAsyncThunk(
'auth/deleteAdmin',
async (adminId: number, thunkAPI) => {
try {
const response = await axios.delete(`/api/admins/${adminId}`);
return adminId; // Return the ID of the deleted admin
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
}
);
const authSlice = createSlice({
name: 'auth',
initialState: {
admins: [], // Array of admins
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(deleteAdmin.fulfilled, (state, action) => {
// Remove the deleted admin from the state
state.admins = state.admins.filter((admin) => admin.id !== action.payload);
});
// Handle rejected state if needed
},
});
export default authSlice.reducer;

View file

@ -1,177 +1,201 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit" import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import axios from "axios" import axios from "axios";
import { backendHttp, apiHttp } from "../../lib/https" import { backendHttp, apiHttp } from "../../lib/https";
import { toast } from "react-toastify" import { toast } from "react-toastify";
// Define types for state // Define types for state
interface User { interface User {
map( id: string;
arg0: ( email: string;
admin: { name: string; role: string },
index: number
) => { srno: number; name: string; role: string }
): unknown
id: string
email: string
} }
interface Admin { interface Admin {
id: string id: string;
name: string name: string;
role: string role: string;
} }
interface AuthState { interface AuthState {
user: User | null user: User | null;
admins: Admin[] admins: Admin[];
isAuthenticated: boolean isAuthenticated: boolean;
isLoading: boolean isLoading: boolean;
error: string | null error: string | null;
} }
// Async thunk for login // Async thunk for login
export const loginUser = createAsyncThunk< export const loginUser = createAsyncThunk<
User, User,
{ email: string; password: string }, { email: string; password: string },
{ rejectValue: string } { rejectValue: string }
>("auth/login", async ({ email, password }, { rejectWithValue }) => { >("auth/login", async ({ email, password }, { rejectWithValue }) => {
try { try {
const response = await backendHttp.post("admin/login", { const response = await backendHttp.post("admin/login", {
email, email,
password, password,
}) });
localStorage.setItem("authToken", response.data?.data?.token) // Save token localStorage.setItem("authToken", response.data?.data?.token); // Save token
toast.success(response.data?.message) toast.success(response.data?.message);
return response.data return response.data;
} catch (error: any) { } catch (error: any) {
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
) );
} }
}) });
// Async thunk for register // Async thunk for register
export const registerUser = createAsyncThunk< export const registerUser = createAsyncThunk<
User, User,
{ email: string; password: string }, { email: string; password: string },
{ rejectValue: string } { rejectValue: string }
>("auth/register", async (data, { rejectWithValue }) => { >("auth/register", async (data, { rejectWithValue }) => {
try { try {
const response = await axios.post( const response = await axios.post(
"https://health-digi.dmlabs.in/auth/register", "https://health-digi.dmlabs.in/auth/register",
data data
) );
return response.data return response.data;
} catch (error: any) { } catch (error: any) {
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
) );
} }
}) });
export const adminList = createAsyncThunk< export const adminList = createAsyncThunk<
Admin[], Admin[],
void, void,
{ rejectValue: string } { rejectValue: string }
>("/auth", async (_, { rejectWithValue }) => { >("/auth", async (_, { rejectWithValue }) => {
try { try {
const response = await apiHttp.get("/auth") const response = await apiHttp.get("/auth");
console.log(response) return response?.data?.data?.map(
return response?.data?.data?.map( (admin: { id: string; name: string; role: string }) => ({
(admin: { name: string; role: string }) => ({ id: admin.id,
name: admin?.name, name: admin.name,
role: admin?.role, role: admin.role || "N/A",
}) })
) );
// console.log(response.data.data) } catch (error: any) {
} catch (error: any) { return rejectWithValue(
return rejectWithValue( error.response?.data?.message || "An error occurred"
error.response?.data?.message || "An error occurred" );
) }
} });
})
//By Jaanvi : Edit feature :: 11-feb-25
// updateAdmin Action
export const updateAdmin = createAsyncThunk(
"/auth/id",
async (
{ id, name, role }: { id: any; name: string; role: string },
{ rejectWithValue }
) => {
try {
const response = await apiHttp.put(`/auth/${id}`, { name, role });
toast.success("Admin updated successfully");
console.log(response.data);
return response.data;
} catch (error: any) {
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
}
);
const initialState: AuthState = { const initialState: AuthState = {
user: null, user: null,
admins: [], admins: [],
isAuthenticated: false, isAuthenticated: false,
isLoading: false, isLoading: false,
error: null, error: null,
} };
const authSlice = createSlice({ const authSlice = createSlice({
name: "auth", name: "auth",
initialState, initialState,
reducers: { reducers: {
logout: (state) => { logout: (state) => {
state.user = null state.user = null;
state.isAuthenticated = false state.isAuthenticated = false;
}, },
}, },
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( .addCase(
loginUser.fulfilled, loginUser.fulfilled,
(state, action: PayloadAction<User>) => { (state, action: PayloadAction<User>) => {
state.isLoading = false state.isLoading = false;
state.isAuthenticated = true state.isAuthenticated = true;
state.user = action.payload state.user = action.payload;
} }
) )
.addCase( .addCase(
loginUser.rejected, loginUser.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";
} }
) )
// 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,
(state, action: PayloadAction<User>) => { (state, action: PayloadAction<User>) => {
state.isLoading = false state.isLoading = false;
state.isAuthenticated = true state.isAuthenticated = true;
state.user = action.payload state.user = action.payload;
} }
) )
.addCase( .addCase(
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";
} }
) )
// Fetch admin list
.addCase(adminList.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(
adminList.fulfilled,
(state, action: PayloadAction<Admin[]>) => {
state.isLoading = false;
state.admins = action.payload;
}
)
.addCase(
adminList.rejected,
(state, action: PayloadAction<string | undefined>) => {
state.isLoading = false;
state.error = action.payload || "An error occurred";
}
)
// update admin cases
.addCase(updateAdmin.fulfilled, (state, action) => {
const updatedAdmin = action.payload;
state.admins = state.admins.map((admin) =>
admin.id === updatedAdmin.id ? updatedAdmin : admin
);
})
.addCase(updateAdmin.rejected, (state) => {
state.isLoading = false;
state.error = null;
});
},
});
// created by Jaanvi and Eknoor export const { logout } = authSlice.actions;
.addCase(adminList.pending, (state) => { export default authSlice.reducer;
state.isLoading = true
state.error = null
})
.addCase(
adminList.fulfilled,
(state, action: PayloadAction<Admin[]>) => {
state.isLoading = false
state.admins = action.payload // ✅ Store admins correctly
}
)
.addCase(
adminList.rejected,
(state, action: PayloadAction<string | undefined>) => {
state.isLoading = false
state.error = action.payload || "An error occurred"
}
)
},
})
export const { logout } = authSlice.actions
export default authSlice.reducer

View file

@ -1,13 +1,10 @@
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from "@reduxjs/toolkit";
import authReducer from '../slices/authSlice.ts'
const store = configureStore({
reducer: {
auth: authReducer,
},
});
import rootReducer from "../reducers";
export const store = configureStore({
reducer: rootReducer,
});
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

@ -1,74 +1,74 @@
import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom" import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom";
// import useAuth from "./hooks/useAuth"; // import useAuth from "./hooks/useAuth";
import React, { Suspense } from "react" import React, { Suspense } from "react";
import LoadingComponent from "./components/Loading" import LoadingComponent from "./components/Loading";
import DashboardLayout from "./layouts/DashboardLayout" import DashboardLayout from "./layouts/DashboardLayout";
import Login from "./pages/Auth/Login" import Login from "./pages/Auth/Login";
import SignUp from "./pages/Auth/SignUp" import SignUp from "./pages/Auth/SignUp";
import Dashboard from "./pages/Dashboard" import Dashboard from "./pages/Dashboard";
import Vehicles from "./pages/Vechiles" import Vehicles from "./pages/Vehicles";
import AdminList from "./pages/AdminList" import AdminList from "./pages/AdminList";
function ProtectedRoute({ function ProtectedRoute({
caps, caps,
component, component,
}: { }: {
caps: string[] caps: string[];
component: React.ReactNode component: React.ReactNode;
}) { }) {
if (!localStorage.getItem("authToken")) if (!localStorage.getItem("authToken"))
return <Navigate to={`/auth/login`} replace /> return <Navigate to={`/auth/login`} replace />;
return component return component;
} }
export default function AppRouter() { export default function AppRouter() {
return ( return (
<Suspense fallback={<LoadingComponent />}> <Suspense fallback={<LoadingComponent />}>
<BaseRoutes> <BaseRoutes>
<Route element={<Navigate to="/auth/login" replace />} index /> <Route element={<Navigate to="/auth/login" replace />} index />
<Route path="/auth"> <Route path="/auth">
<Route <Route
path="" path=""
element={<Navigate to="/auth/login" replace />} element={<Navigate to="/auth/login" replace />}
index index
/> />
<Route path="login" element={<Login />} /> <Route path="login" element={<Login />} />
<Route path="signup" element={<SignUp />} /> <Route path="signup" element={<SignUp />} />
</Route> </Route>
<Route path="/panel" element={<DashboardLayout />}> <Route path="/panel" element={<DashboardLayout />}>
<Route <Route
path="dashboard" path="dashboard"
element={ element={
<ProtectedRoute <ProtectedRoute
caps={[]} caps={[]}
component={<Dashboard />} component={<Dashboard />}
/> />
} }
/> />
<Route <Route
path="vehicles" path="vehicles"
element={ element={
<ProtectedRoute <ProtectedRoute
caps={[]} caps={[]}
component={<Vehicles />} component={<Vehicles />}
/> />
} }
/> />
<Route <Route
path="adminlist" path="adminlist"
element={ element={
<ProtectedRoute <ProtectedRoute
caps={[]} caps={[]}
component={<AdminList />} component={<AdminList />}
/> />
} }
/> />
<Route path="*" element={<>404</>} /> <Route path="*" element={<>404</>} />
</Route> </Route>
<Route path="*" element={<>404</>} /> <Route path="*" element={<>404</>} />
</BaseRoutes> </BaseRoutes>
</Suspense> </Suspense>
) );
} }

View file

@ -1,19 +1,20 @@
{ {
"files": [], "files": [],
"references": [ "references": [
{ {
"path": "./tsconfig.app.json" "path": "./tsconfig.app.json"
}, },
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.node.json"
} }
], ],
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "jsx": "react-jsx",
"@/*": [ "types": ["react", "react-dom"],
"./src/*" "lib": ["dom", "dom.iterable", "esnext"],
] "paths": {
} "@/*": ["./src/*"]
} }
}
} }