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,
"semi": false,
"singleQuote": false
}
"useTabs": true,
"semi": true,
"singleQuote": false,
"bracketSpacing": true
}

15491
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
{
"name": "digi-health-admin-dashboard",
"name": "digi-ev-admin-dashboard",
"version": "0.1.0",
"private": true,
"dependencies": {
@ -13,33 +13,34 @@
"@mui/x-tree-view": "^7.23.2",
"@react-spring/web": "^9.7.5",
"@reduxjs/toolkit": "^2.5.0",
"AdapterDayjs": "link:@mui/x-date-pickers/AdapterDayjs",
"AppBar": "link:@mui/material/AppBar",
"Box": "link:@mui/material/Box",
"PieChart": "link:@mui/x-charts/PieChart",
"RichTreeView": "link:@mui/x-tree-view/RichTreeView",
"Stack": "link:@mui/material/Stack",
"Tabs": "link:@mui/material/Tabs",
"Toolbar": "link:@mui/material/Toolbar",
"Typography": "link:@mui/material/Typography",
"AdapterDayjs": "file:@mui/x-date-pickers/AdapterDayjs",
"add": "^2.0.6",
"AppBar": "file:@mui/material/AppBar",
"axios": "^1.7.9",
"Box": "file:@mui/material/Box",
"clsx": "^2.1.1",
"cra-template-typescript": "1.2.0",
"dayjs": "^1.11.13",
"hooks": "link:@mui/x-charts/hooks",
"hooks": "file:@mui/x-charts/hooks",
"mui-phone-number": "^3.0.3",
"mui-tel-input": "^7.0.0",
"PieChart": "file:@mui/x-charts/PieChart",
"prop-types": "^15.8.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react": "^18.0.0",
"react-cookie": "^7.2.2",
"react-dom": "^18.0.0",
"react-dropzone": "^14.3.5",
"react-hook-form": "^7.54.2",
"react-redux": "^9.2.0",
"react-router-dom": "^7.1.1",
"react-scripts": "5.0.1",
"react-toastify": "^11.0.2",
"styles": "link:@mui/material/styles",
"RichTreeView": "file:@mui/x-tree-view/RichTreeView",
"Stack": "file:@mui/material/Stack",
"styles": "file:@mui/material/styles",
"Tabs": "file:@mui/material/Tabs",
"Toolbar": "file:@mui/material/Toolbar",
"Typography": "file:@mui/material/Typography",
"web-vitals": "^4.2.4"
},
"scripts": {
@ -68,8 +69,8 @@
},
"devDependencies": {
"@types/node": "^22.10.5",
"@types/react": "^19.0.3",
"@types/react": "^19.0.4",
"@types/react-dom": "^19.0.2",
"typescript": "^5.7.2"
"typescript": "^5.7.3"
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,34 +10,12 @@
content="Web site created using create-react-app"
/>
<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
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>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
jaanvi marked this conversation as resolved Outdated

add app specific meta description.

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

View file

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

View file

@ -1,33 +1,221 @@
import React,{useEffect} from "react";
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField } from "@mui/material";
// 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,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
TextField,
} from "@mui/material";
import { useForm, Controller } from "react-hook-form";
//By Jaanvi : Edit Model :: 11-feb-25
interface AddEditCategoryModalProps {
open: boolean;
handleClose: () => void;
editRow:any;
editRow: any;
handleUpdate: (id: string, name: string, role: string) => void;
}
interface FormData {
category: string;
role: string;
name: string;
//category: string;
}
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({ open, handleClose,editRow }) => {
const { control, handleSubmit, formState: { errors },setValue,reset } = useForm<FormData>({
const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({
open,
handleClose,
editRow,
handleUpdate,
}) => {
const {
control,
handleSubmit,
formState: { errors },
setValue,
reset,
} = useForm<FormData>({
defaultValues: {
category: "",
//category: "",
name: "",
role: "",
},
});
const onSubmit = (data: FormData) => {
console.log(data.category);
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 {
reset();
}
@ -41,18 +229,17 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({ open, handl
maxWidth="md"
fullWidth
PaperProps={{
component: 'form',
component: "form",
onSubmit: handleSubmit(onSubmit),
}}
>
<DialogTitle>{editRow ? "Editwefwefwe" : 'Add'} Category</DialogTitle>
<DialogTitle>{editRow ? "Edit" : "Add"} Category</DialogTitle>
<DialogContent>
<Controller
{/* <Controller
name="category"
control={control}
rules={{
required: "Category Name is required",
}}
render={({ field }) => (
<TextField
@ -68,6 +255,48 @@ const AddEditCategoryModal: React.FC<AddEditCategoryModalProps> = ({ open, handl
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>

View file

@ -1,12 +1,12 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper, { paperClasses } from '@mui/material/Paper';
import * as React from "react";
import { styled } from "@mui/material/styles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell, { tableCellClasses } from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper, { paperClasses } from "@mui/material/Paper";
import {
Box,
Button,
@ -14,13 +14,13 @@ import {
IconButton,
listClasses,
Menu,
} from '@mui/material';
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded';
} from "@mui/material";
import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded";
// Styled components for customization
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: ' #1565c0',
backgroundColor: " #1565c0",
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
@ -29,10 +29,10 @@ const StyledTableCell = styled(TableCell)(({ theme }) => ({
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': {
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
'&:last-child td, &:last-child th': {
"&:last-child td, &:last-child th": {
border: 0,
},
}));
@ -40,7 +40,7 @@ const StyledTableRow = styled(TableRow)(({ theme }) => ({
interface Column {
id: string;
label: string;
align?: 'left' | 'center' | 'right';
align?: "left" | "center" | "right";
}
interface Row {
@ -62,7 +62,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
setRowData,
setModalOpen,
}) => {
console.log('columnsss', columns, rows);
console.log("columnsss", columns, rows);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
@ -74,8 +74,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
};
const isImage = (value: any) => {
if (typeof value === 'string') {
return value.startsWith('http') || value.startsWith('data:image'); // Check for URL or base64 image
if (typeof value === "string") {
return value.startsWith("http") || value.startsWith("data:image"); // Check for URL or base64 image
}
return false;
};
@ -86,7 +86,10 @@ const CustomTable: React.FC<CustomTableProps> = ({
<TableHead>
<TableRow>
{columns.map((column) => (
<StyledTableCell key={column.id} align={column.align || 'left'}>
<StyledTableCell
key={column.id}
align={column.align || "left"}
>
{column.label}
</StyledTableCell>
))}
@ -96,18 +99,21 @@ const CustomTable: React.FC<CustomTableProps> = ({
{rows.map((row, rowIndex) => (
<StyledTableRow key={rowIndex}>
{columns.map((column) => (
<StyledTableCell key={column.id} align={column.align || 'left'}>
<StyledTableCell
key={column.id}
align={column.align || "left"}
>
{isImage(row[column.id]) ? (
<img
src={row[column.id]}
alt="Row "
style={{
width: '50px',
height: '50px',
objectFit: 'cover',
width: "50px",
height: "50px",
objectFit: "cover",
}}
/>
) : column.id !== 'action' ? (
) : column.id !== "action" ? (
row[column.id]
) : (
<IconButton
@ -132,25 +138,25 @@ const CustomTable: React.FC<CustomTableProps> = ({
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
sx={{
[`& .${listClasses.root}`]: {
padding: '4px',
padding: "4px",
},
[`& .${paperClasses.root}`]: {
padding: 0,
},
[`& .${dividerClasses.root}`]: {
margin: '4px -4px',
margin: "4px -4px",
},
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<Button
@ -158,9 +164,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
onClick={() => setModalOpen(true)}
color="primary"
sx={{
justifyContent: 'flex-start',
justifyContent: "flex-start",
py: 0,
textTransform: 'capitalize',
textTransform: "capitalize",
}}
>
Edit
@ -170,9 +176,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
onClick={() => setDeleteModal(true)}
color="error"
sx={{
justifyContent: 'flex-start',
justifyContent: "flex-start",
py: 0,
textTransform: 'capitalize',
textTransform: "capitalize",
}}
>
Delete

View file

@ -1,19 +1,19 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Divider, { dividerClasses } from '@mui/material/Divider';
import Menu from '@mui/material/Menu';
import MuiMenuItem from '@mui/material/MenuItem';
import { paperClasses } from '@mui/material/Paper';
import { listClasses } from '@mui/material/List';
import ListItemText from '@mui/material/ListItemText';
import ListItemIcon, { listItemIconClasses } from '@mui/material/ListItemIcon';
import LogoutRoundedIcon from '@mui/icons-material/LogoutRounded';
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded';
import MenuButton from '../MenuButton';
import { Avatar } from '@mui/material';
import * as React from "react";
import { styled } from "@mui/material/styles";
import Divider, { dividerClasses } from "@mui/material/Divider";
import Menu from "@mui/material/Menu";
import MuiMenuItem from "@mui/material/MenuItem";
import { paperClasses } from "@mui/material/Paper";
import { listClasses } from "@mui/material/List";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon, { listItemIconClasses } from "@mui/material/ListItemIcon";
import LogoutRoundedIcon from "@mui/icons-material/LogoutRounded";
import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded";
import MenuButton from "../MenuButton";
import { Avatar } from "@mui/material";
const MenuItem = styled(MuiMenuItem)({
margin: '2px 0',
margin: "2px 0",
});
export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
@ -30,7 +30,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
<MenuButton
aria-label="Open menu"
onClick={handleClick}
sx={{ borderColor: 'transparent' }}
sx={{ borderColor: "transparent" }}
>
{avatar ? (
<MoreVertRoundedIcon />
@ -49,17 +49,17 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
transformOrigin={{ horizontal: "right", vertical: "top" }}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
sx={{
[`& .${listClasses.root}`]: {
padding: '4px',
padding: "4px",
},
[`& .${paperClasses.root}`]: {
padding: 0,
},
[`& .${dividerClasses.root}`]: {
margin: '4px -4px',
margin: "4px -4px",
},
}}
>
@ -73,7 +73,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
onClick={handleClose}
sx={{
[`& .${listItemIconClasses.root}`]: {
ml: 'auto',
ml: "auto",
minWidth: 0,
},
}}

View file

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

View file

@ -1,13 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import App from './App';
import { Provider } from 'react-redux';
import store from './redux/store/store.ts';
import { Slide, ToastContainer } from 'react-toastify';
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import reportWebVitals from "./reportWebVitals";
import App from "./App";
import { Provider } from "react-redux";
import store from "./redux/store/store.ts";
import { Slide, ToastContainer } from "react-toastify";
const root = ReactDOM.createRoot(document.getElementById('root'));
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
@ -17,7 +17,7 @@ root.render(
hideProgressBar
theme="dark"
transition={Slide}
toastStyle={{ border: '1px solid dimgray' }}
toastStyle={{ border: "1px solid dimgray" }}
/>
</Provider>
</React.StrictMode>

View file

@ -1,64 +1,78 @@
import React, { useEffect, useState } from "react"
import { Box, Button, Typography } from "@mui/material"
import AddEditCategoryModal from "../../components/AddEditCategoryModal"
import { useForm } from "react-hook-form"
import CustomTable from "../../components/CustomTable"
import DeleteModal from "../../components/Modals/DeleteModal/DeleteModal"
import { useDispatch, useSelector } from "react-redux"
import { adminList } from "../../redux/slices/authSlice"
import { AppDispatch, RootState } from "../../redux/store/store" // Import RootState for selector
import React, { useEffect, useState } from "react";
import { Box, Button, Typography } from "@mui/material";
import AddEditCategoryModal from "../../components/AddEditCategoryModal";
// import { useForm } from "react-hook-form";
import { useForm } from "react-hook-form";
import CustomTable from "../../components/CustomTable";
import DeleteModal from "../../components/Modals/DeleteModal/DeleteModal";
import { useDispatch, useSelector } from "react-redux";
import { adminList, updateAdmin } from "../../redux/slices/authSlice";
import { AppDispatch, RootState } from "../../redux/store/store"; // Import RootState for selector
// Sample data for categories
export default function AdminList() {
const [modalOpen, setModalOpen] = useState(false)
const [editRow, setEditRow] = useState<any>(null)
const { reset } = useForm()
const [modalOpen, setModalOpen] = useState(false);
const [editRow, setEditRow] = useState<any>(null);
const { reset } = useForm();
const [deleteModal, setDeleteModal] = React.useState<boolean>(false)
const [rowData, setRowData] = React.useState<any | null>(null)
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
const [rowData, setRowData] = React.useState<any | null>(null);
const dispatch = useDispatch<AppDispatch>()
const dispatch = useDispatch<AppDispatch>();
// 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
useEffect(() => {
dispatch(adminList())
}, [dispatch])
dispatch(adminList());
}, [dispatch]);
const handleClickOpen = () => {
setModalOpen(true)
setEditRow(null)
}
setModalOpen(true);
setEditRow(null);
};
const handleCloseModal = () => {
setModalOpen(false)
reset()
}
setModalOpen(false);
reset();
};
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 = [
{ id: "srno", label: "Sr No" },
{ id: "name", label: "Name" },
{ id: "role", label: "Role" },
{ id: "action", label: "Action", align: "center" },
]
];
// If no admins are available, display the sample data
const categoryRows = admins?.length
? admins?.map(
(admin: { name: string; role: string }, index: number) => ({
(
admin: { id: string; name: string; role: string },
index: number
) => ({
srno: index + 1,
id: admin?.id,
name: admin?.name,
role: admin?.role || "N/A",
})
)
: []
: [];
return (
<>
@ -103,6 +117,7 @@ export default function AdminList() {
open={modalOpen}
handleClose={handleCloseModal}
editRow={rowData}
handleUpdate={handleUpdate}
/>
<DeleteModal
open={deleteModal}
@ -110,5 +125,5 @@ export default function AdminList() {
handleDelete={handleDelete}
/>
</>
)
);
}

View file

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

View file

@ -117,11 +117,11 @@ const dispatch = useDispatch();
return (
<AppTheme {...props}>
<CssBaseline enableColorScheme />
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />
{/* <CssBaseline enableColorScheme /> */}
<ColorModeSelect sx={{ position: 'fixed', top: '1rem', right: '1rem' }} />/
<SignUpContainer direction="column" justifyContent="space-between">
<Card variant="outlined">
<SitemarkIcon />
Digi-EV
<Typography
component="h1"
variant="h4"
@ -204,12 +204,12 @@ const dispatch = useDispatch();
/>
</FormControl>
<MuiPhoneNumber
defaultCountry='it'
defaultCountry='in'
onChange={handleOnChange}
value={phoneNumber}
/>
<FormControlLabel
{/* <FormControlLabel
control={
<Controller
name="allowExtraEmails"
@ -220,16 +220,16 @@ const dispatch = useDispatch();
/>
}
label="I want to receive updates via email."
/>
/> */}
<Button type="submit" fullWidth variant="contained">
Sign up
</Button>
</Box>
<Divider>
{/* <Divider>
<Typography sx={{ color: 'text.secondary' }}>or</Typography>
</Divider>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
</Divider> */}
{/* <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Button
fullWidth
variant="outlined"
@ -256,6 +256,19 @@ const dispatch = useDispatch();
Sign in
</Link>
</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>
</Card>
</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() {
const [modalOpen, setModalOpen] = useState(false);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [editRow, setEditRow] = useState<any>(null);
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,31 +1,26 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"
import axios from "axios"
import { backendHttp, apiHttp } from "../../lib/https"
import { toast } from "react-toastify"
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import axios from "axios";
import { backendHttp, apiHttp } from "../../lib/https";
import { toast } from "react-toastify";
// Define types for state
interface User {
map(
arg0: (
admin: { name: string; role: string },
index: number
) => { srno: number; name: string; role: string }
): unknown
id: string
email: string
id: string;
email: string;
}
interface Admin {
id: string
name: string
role: string
id: string;
name: string;
role: string;
}
interface AuthState {
user: User | null
admins: Admin[]
isAuthenticated: boolean
isLoading: boolean
error: string | null
user: User | null;
admins: Admin[];
isAuthenticated: boolean;
isLoading: boolean;
error: string | null;
}
// Async thunk for login
@ -38,16 +33,16 @@ export const loginUser = createAsyncThunk<
const response = await backendHttp.post("admin/login", {
email,
password,
})
localStorage.setItem("authToken", response.data?.data?.token) // Save token
toast.success(response.data?.message)
return response.data
});
localStorage.setItem("authToken", response.data?.data?.token); // Save token
toast.success(response.data?.message);
return response.data;
} catch (error: any) {
return rejectWithValue(
error.response?.data?.message || "An error occurred"
)
);
}
})
});
// Async thunk for register
export const registerUser = createAsyncThunk<
@ -59,14 +54,14 @@ export const registerUser = createAsyncThunk<
const response = await axios.post(
"https://health-digi.dmlabs.in/auth/register",
data
)
return response.data
);
return response.data;
} catch (error: any) {
return rejectWithValue(
error.response?.data?.message || "An error occurred"
)
);
}
})
});
export const adminList = createAsyncThunk<
Admin[],
@ -74,21 +69,41 @@ export const adminList = createAsyncThunk<
{ rejectValue: string }
>("/auth", async (_, { rejectWithValue }) => {
try {
const response = await apiHttp.get("/auth")
console.log(response)
const response = await apiHttp.get("/auth");
return response?.data?.data?.map(
(admin: { name: string; role: string }) => ({
name: admin?.name,
role: admin?.role,
(admin: { id: string; name: string; role: string }) => ({
id: admin.id,
name: admin.name,
role: admin.role || "N/A",
})
)
// console.log(response.data.data)
);
} catch (error: any) {
return rejectWithValue(
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 = {
user: null,
@ -96,82 +111,91 @@ const initialState: AuthState = {
isAuthenticated: false,
isLoading: false,
error: null,
}
};
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
logout: (state) => {
state.user = null
state.isAuthenticated = false
state.user = null;
state.isAuthenticated = false;
},
},
extraReducers: (builder) => {
builder
// Login
.addCase(loginUser.pending, (state) => {
state.isLoading = true
state.error = null
state.isLoading = true;
state.error = null;
})
.addCase(
loginUser.fulfilled,
(state, action: PayloadAction<User>) => {
state.isLoading = false
state.isAuthenticated = true
state.user = action.payload
state.isLoading = false;
state.isAuthenticated = true;
state.user = action.payload;
}
)
.addCase(
loginUser.rejected,
(state, action: PayloadAction<string | undefined>) => {
state.isLoading = false
state.error = action.payload || "An error occurred"
state.isLoading = false;
state.error = action.payload || "An error occurred";
}
)
// Register
.addCase(registerUser.pending, (state) => {
state.isLoading = true
state.error = null
state.isLoading = true;
state.error = null;
})
.addCase(
registerUser.fulfilled,
(state, action: PayloadAction<User>) => {
state.isLoading = false
state.isAuthenticated = true
state.user = action.payload
state.isLoading = false;
state.isAuthenticated = true;
state.user = action.payload;
}
)
.addCase(
registerUser.rejected,
(state, action: PayloadAction<string | undefined>) => {
state.isLoading = false
state.error = action.payload || "An error occurred"
state.isLoading = false;
state.error = action.payload || "An error occurred";
}
)
// created by Jaanvi and Eknoor
// Fetch admin list
.addCase(adminList.pending, (state) => {
state.isLoading = true
state.error = null
state.isLoading = true;
state.error = null;
})
.addCase(
adminList.fulfilled,
(state, action: PayloadAction<Admin[]>) => {
state.isLoading = false
state.admins = action.payload // ✅ Store admins correctly
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"
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;
});
},
})
});
export const { logout } = authSlice.actions
export default authSlice.reducer
export const { logout } = authSlice.actions;
export default authSlice.reducer;

View file

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

View file

@ -1,25 +1,25 @@
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 React, { Suspense } from "react"
import LoadingComponent from "./components/Loading"
import DashboardLayout from "./layouts/DashboardLayout"
import Login from "./pages/Auth/Login"
import SignUp from "./pages/Auth/SignUp"
import Dashboard from "./pages/Dashboard"
import Vehicles from "./pages/Vechiles"
import AdminList from "./pages/AdminList"
import React, { Suspense } from "react";
import LoadingComponent from "./components/Loading";
import DashboardLayout from "./layouts/DashboardLayout";
import Login from "./pages/Auth/Login";
import SignUp from "./pages/Auth/SignUp";
import Dashboard from "./pages/Dashboard";
import Vehicles from "./pages/Vehicles";
import AdminList from "./pages/AdminList";
function ProtectedRoute({
caps,
component,
}: {
caps: string[]
component: React.ReactNode
caps: string[];
component: React.ReactNode;
}) {
if (!localStorage.getItem("authToken"))
return <Navigate to={`/auth/login`} replace />
return <Navigate to={`/auth/login`} replace />;
return component
return component;
}
export default function AppRouter() {
@ -70,5 +70,5 @@ export default function AppRouter() {
<Route path="*" element={<>404</>} />
</BaseRoutes>
</Suspense>
)
);
}

View file

@ -10,10 +10,11 @@
],
"compilerOptions": {
"baseUrl": ".",
"jsx": "react-jsx",
"types": ["react", "react-dom"],
"lib": ["dom", "dom.iterable", "esnext"],
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
}
}
}