manager modal changes, dashboard apis implemented and add dashboard page for user created

This commit is contained in:
jaanvi 2025-04-24 18:26:22 +05:30
parent 7f4914c695
commit 99e3f8c9f9
11 changed files with 639 additions and 44 deletions

View file

@ -57,9 +57,9 @@ export default function AddManagerModal({ open, handleClose }) {
try {
await dispatch(addManager(managerData));
dispatch(managerList());
clearErrors();
reset();
handleClose();
clearErrors();
reset();
handleClose();
} catch (error) {
console.error("Error adding manager:", error);
}
@ -67,8 +67,8 @@ export default function AddManagerModal({ open, handleClose }) {
// Handle modal close, clearing errors and resetting form
const handleModalClose = () => {
clearErrors();
reset();
clearErrors();
reset();
handleClose();
};
@ -348,6 +348,9 @@ export default function AddManagerModal({ open, handleClose }) {
togglePasswordVisibility
}
edge="end"
sx={{
color: "#000000",
}}
>
{showPassword ? (
<VisibilityOff />

View file

@ -0,0 +1,105 @@
import React, { useEffect } from "react";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import SessionsChart from "../SessionsChart/sessionChart";
import ResourcesPieChart from "../ResourcePieChart/resourcePieChart";
import RoundedBarChart from "../barChartCard/barChartCard";
import LineChartCard from "../LineChartCard/lineChartCard";
import { AppDispatch, RootState } from "../../redux/store/store";
import { useDispatch, useSelector } from "react-redux";
import { fetchDashboardData } from "../../redux/slices/dashboardSlice";
import AppTheme from "../../shared-theme/AppTheme";
import StatCard from "../StatCard/statCard";
export default function AdminGrid() {
const dispatch = useDispatch<AppDispatch>();
const { totalManagers, totalUsers, totalStations, loading, error } =
useSelector((state: RootState) => state.dashboardReducer);
useEffect(() => {
dispatch(fetchDashboardData());
}, [dispatch]);
// Fallback defaults if data is undefined
const data = {
totalManagers: totalManagers ?? 12,
totalUsers: totalUsers ?? 24,
totalStations: totalStations ?? 8,
};
const statCards = [
{ title: "Total Managers", value: data.totalManagers },
{ title: "Total Users", value: data.totalUsers },
{ title: "Total Stations", value: data.totalStations },
];
return (
<AppTheme>
<Box
sx={{
width: "100%",
maxWidth: "1800px",
mx: "auto",
px: { xs: 2, sm: 1, md: 0 },
background: (theme) => theme.palette.background.default,
}}
>
{/* Header */}
<Typography variant="h4" component="h2" sx={{ mb: 3 }}>
Admin Dashboard
</Typography>
<Grid container spacing={3} justifyContent="center">
{statCards.map((card, index) => (
<Grid
key={index}
item
xs={12}
sm={6}
md={
statCards.length === 1
? 12
: statCards.length === 2
? 6
: 4
}
lg={
statCards.length === 1
? 12
: statCards.length === 2
? 6
: 4
}
xl={
statCards.length === 1
? 8
: statCards.length === 2
? 6
: 4
}
>
<StatCard {...card} />
</Grid>
))}
</Grid>
{/* Charts */}
<Grid container spacing={3} sx={{ mt: 1 }}>
<Grid item xs={12} md={6}>
<SessionsChart />
</Grid>
<Grid item xs={12} md={6}>
<ResourcesPieChart />
</Grid>
<Grid item xs={12} md={6}>
<RoundedBarChart />
</Grid>
<Grid item xs={12} md={6}>
<LineChartCard />
</Grid>
</Grid>
</Box>
</AppTheme>
);
}

View file

@ -1,3 +1,120 @@
// import React, { useEffect } from "react";
// import Grid from "@mui/material/Grid";
// import Box from "@mui/material/Box";
// import Typography from "@mui/material/Typography";
// import SessionsChart from "../SessionsChart/sessionChart";
// import ResourcesPieChart from "../ResourcePieChart/resourcePieChart";
// import RoundedBarChart from "../barChartCard/barChartCard";
// import LineChartCard from "../LineChartCard/lineChartCard";
// import { AppDispatch, RootState } from "../../redux/store/store";
// import { useDispatch, useSelector } from "react-redux";
// import { fetchDashboardData } from "../../redux/slices/dashboardSlice";
// import AppTheme from "../../shared-theme/AppTheme"; // Import the custom theme
// import StatCard from "../StatCard/statCard"; // Adjusted import for consistency
// export default function MainGrid() {
// const dispatch = useDispatch<AppDispatch>();
// const {
// totalAdmins,
// totalManagers,
// totalUsers,
// totalStations,
// } = useSelector((state: RootState) => state.dashboardReducer);
// const userType = useSelector(
// (state: RootState) => state.profileReducer.user?.userType
// );
// useEffect(() => {
// dispatch(fetchDashboardData());
// }, [dispatch]);
// const data = {
// totalAdmins: totalAdmins ?? 86,
// totalManagers: totalManagers ?? 12,
// totalUsers: totalUsers ?? 24,
// totalStations: totalStations ?? 8,
// };
// const getStatCards = () => {
// switch (userType) {
// case "superadmin":
// return [
// { title: "Total Admins", value: data.totalAdmins },
// { title: "Total Managers", value: data.totalManagers },
// { title: "Total Users", value: data.totalUsers },
// { title: "Total Stations", value: data.totalStations },
// ];
// case "admin":
// return [
// { title: "Total Managers", value: data.totalManagers },
// { title: "Total Users", value: data.totalUsers },
// { title: "Total Stations", value: data.totalStations },
// ];
// case "manager":
// return [
// { title: "Total Users", value: data.totalUsers },
// ];
// default:
// return [];
// }
// };
// return (
// <AppTheme>
// <Box
// sx={{
// width: "100%",
// maxWidth: "1800px",
// mx: "auto",
// px: { xs: 2, sm: 1, md: 0 },
// background: (theme) => theme.palette.background.default,
// }}
// >
// {/* Dashboard Header */}
// <Typography
// component="h2"
// variant="h4"
// sx={{
// mb: 3,
// }}
// >
// Dashboard
// </Typography>
// {/* Grid Layout */}
// <Grid container spacing={3} columns={12}>
// {/* Statistic Cards */}
// {getStatCards().map((card, index) => (
// <Grid key={index} item xs={12} sm={6} md={3} lg={3}>
// <StatCard {...card} />
// </Grid>
// ))}
// {/* Charts */}
// {userType !== "user" && (
// <>
// <Grid item xs={12} sm={12} md={6} lg={6}>
// <SessionsChart />
// </Grid>
// <Grid item xs={12} sm={12} md={6} lg={6}>
// <ResourcesPieChart />
// </Grid>
// <Grid item xs={12} sm={12} md={6} lg={6}>
// <RoundedBarChart />
// </Grid>
// <Grid item xs={12} sm={12} md={6} lg={6}>
// <LineChartCard />
// </Grid>
// </>
// )}
// </Grid>
// </Box>
// </AppTheme>
// );
// }
import React, { useEffect } from "react";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
@ -50,20 +167,18 @@ export default function MainGrid() {
maxWidth: "1800px",
mx: "auto",
px: { xs: 2, sm: 1, md: 0 },
background: (theme) => theme.palette.background.default, // #DFECF1 from theme
background: (theme) => theme.palette.background.default,
}}
>
{/* Dashboard Header */}
<Typography
component="h2"
variant="h4" // Use h4 to match the "Good morning, James!" size and weight
variant="h4"
sx={{
mb: 3,
}}
>
Dashboard
Dashboard
</Typography>
{/* Grid Layout */}

View file

@ -0,0 +1,77 @@
import React, { useEffect } from "react";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import SessionsChart from "../SessionsChart/sessionChart";
import ResourcesPieChart from "../ResourcePieChart/resourcePieChart";
import RoundedBarChart from "../barChartCard/barChartCard";
import LineChartCard from "../LineChartCard/lineChartCard";
import { AppDispatch, RootState } from "../../redux/store/store";
import { useDispatch, useSelector } from "react-redux";
import { fetchDashboardData } from "../../redux/slices/dashboardSlice";
import AppTheme from "../../shared-theme/AppTheme";
import StatCard from "../StatCard/statCard";
export default function ManagerGrid() {
const dispatch = useDispatch<AppDispatch>();
const { totalUsers, totalStations } = useSelector(
(state: RootState) => state.dashboardReducer
);
useEffect(() => {
dispatch(fetchDashboardData());
}, [dispatch]);
const statCards = [{ title: "Total Users", value: totalUsers ?? 24 }];
return (
<AppTheme>
<Box
sx={{
width: "100%",
maxWidth: "1800px",
mx: "auto",
px: { xs: 2, sm: 2, md: 3 },
background: (theme) => theme.palette.background.default,
}}
>
{/* Header */}
<Typography
component="h2"
variant="h4"
sx={{ mb: 3, fontWeight: 600 }}
>
Dashboard
</Typography>
{/* Stat Cards - Centered */}
<Grid
container
spacing={3}
justifyContent="center"
alignItems="stretch"
sx={{ mb: 1 }}
>
{statCards.map((card, index) => (
<Grid key={index} item xs={12} sm={8} md={4} lg={3}>
<StatCard {...card} />
</Grid>
))}
</Grid>
{/* Charts */}
<Grid container spacing={3} mt={1}>
<Grid item xs={12} sm={12} md={6} lg={6}>
<SessionsChart />
</Grid>
<Grid item xs={12} sm={12} md={6} lg={6}>
<ResourcesPieChart />
</Grid>
<Grid item xs={12} sm={12} md={6} lg={6}>
<LineChartCard />
</Grid>
</Grid>
</Box>
</AppTheme>
);
}

View file

@ -0,0 +1,248 @@
import React, { useEffect } from "react";
import { Box, Grid, Typography, Divider, Avatar, Chip } from "@mui/material";
import {
EvStation,
AccessTime,
DirectionsCar,
BookOnline,
} from "@mui/icons-material";
import { useDispatch, useSelector } from "react-redux";
import { fetchDashboardData } from "../../redux/slices/dashboardSlice";
import { AppDispatch, RootState } from "../../redux/store/store";
import { BarChart } from "@mui/x-charts/BarChart";
import { LineChart } from "@mui/x-charts/LineChart";
import AppTheme from "../../shared-theme/AppTheme";
import StatCard from "../StatCard/statCard";
export default function UserDashboard() {
const dispatch = useDispatch<AppDispatch>();
const { user } = useSelector((state: RootState) => state.profileReducer);
const { totalStations } = useSelector(
(state: RootState) => state.dashboardReducer
);
useEffect(() => {
dispatch(fetchDashboardData());
}, [dispatch]);
const statCards = [
{
title: "Active Bookings",
value: 2,
icon: <BookOnline color="primary" />,
},
{
title: "Nearby Stations",
value: totalStations || 8,
icon: <EvStation color="success" />,
},
{
title: "Charges This Month",
value: 15,
icon: <DirectionsCar color="warning" />,
},
{
title: "Available Slots",
value: 4,
icon: <AccessTime color="secondary" />,
},
];
const upcomingBookings = [
{ title: "Indian EV Station - Slot 1", time: "Apr 25, 6:00 PM" },
{ title: "Mumbai EV Station - Slot 3", time: "Apr 26, 2:00 PM" },
];
const recentSessions = [
{
station: "Indian EV Station",
time: "Yesterday at 3:00 PM",
duration: "1h 30m",
},
{
station: "Delhi EV Station",
time: "Apr 22, 5:00 PM",
duration: "2h",
},
];
const barChartData = [
{ category: "Station A", bookings: 5 },
{ category: "Station B", bookings: 3 },
{ category: "Station C", bookings: 8 },
];
const lineChartData = [
{ date: "2025-04-01", charges: 5 },
{ date: "2025-04-02", charges: 6 },
{ date: "2025-04-03", charges: 4 },
{ date: "2025-04-04", charges: 7 },
{ date: "2025-04-05", charges: 5 },
];
return (
<AppTheme>
<Box
sx={{
width: "100%",
color: "#DE0E1E9",
py: 3,
}}
>
{/* Greeting */}
<Box display="flex" alignItems="center" px={3} mb={4}>
{/* <Avatar
sx={{
width: 64,
height: 64,
mr: 2,
bgcolor: "",
boxShadow: "0 4px 12px rgba(0,0,0,0.2)",
border: "2px solid white",
fontSize: "2rem",
fontWeight: "bold",
}}
>
{user?.name?.charAt(0).toUpperCase() || "U"}
</Avatar> */}
<Box>
<Typography variant="h3">
Welcome back, {user?.name || "User"}!
</Typography>
<Typography color="#DE0E1E9">
Heres a quick snapshot of your EV activity.
</Typography>
</Box>
</Box>
{/* Stat Cards */}
<Grid container spacing={3} px={3}>
{statCards.map((card, index) => (
<Grid item xs={12} sm={6} md={3} key={index}>
<StatCard
title={card.title}
value={card.value}
icon={card.icon}
/>
</Grid>
))}
</Grid>
{/* Charts */}
<Grid container spacing={3} mt={1} px={3}>
<Grid item xs={12} md={6}>
<Box bgcolor="#D0E1E9" borderRadius={2} p={2}>
<Typography variant="h6" color="#000">
Bookings by Station
</Typography>
<Divider sx={{ my: 2 }} />
<BarChart
dataset={barChartData}
xAxis={[
{
scaleType: "band",
dataKey: "category",
label: "Station",
},
]}
series={[
{
dataKey: "bookings",
label: "Bookings",
color: "#000000",
},
]}
width={500}
height={300}
/>
</Box>
</Grid>
<Grid item xs={12} md={6}>
<Box bgcolor="#D0E1E9" borderRadius={2} p={2}>
<Typography variant="h6" color="#000">
Daily Charges
</Typography>
<Divider sx={{ my: 2 }} />
<LineChart
dataset={lineChartData}
xAxis={[
{
scaleType: "point",
dataKey: "date",
label: "Date",
},
]}
series={[
{
dataKey: "charges",
label: "Charges",
color: "#000000",
},
]}
width={500}
height={300}
/>
</Box>
</Grid>
</Grid>
{/* Bookings & Sessions */}
<Grid container spacing={3} mt={1} px={3}>
<Grid item xs={12} md={6}>
<Box bgcolor="#D0E1E9" borderRadius={2} p={2}>
<Typography variant="h6" color="#000">
Upcoming Bookings
</Typography>
<Divider sx={{ my: 2 }} />
{upcomingBookings.map((booking, idx) => (
<Box key={idx} mb={2}>
<Typography variant="subtitle1">
{booking.title}
</Typography>
<Chip
icon={
<AccessTime
sx={{
color: "#D0E1E9 !important",
}}
/>
}
label={booking.time}
sx={{
bgcolor: "#000000",
color: "#D0E1E9 !important",
"& .MuiChip-label": {
color: "#D0E1E9 !important",
},
}}
/>
</Box>
))}
</Box>
</Grid>
<Grid item xs={12} md={6}>
<Box bgcolor="#D0E1E9" borderRadius={2} p={2}>
<Typography variant="h6" color="#000">
Recent Charging Sessions
</Typography>
<Divider sx={{ my: 2 }} />
{recentSessions.map((session, idx) => (
<Box key={idx} mb={2}>
<Typography variant="subtitle1">
{session.station}
</Typography>
<Typography
variant="body2"
color="text.secondary"
>
{session.time} {session.duration}
</Typography>
</Box>
))}
</Box>
</Grid>
</Grid>
</Box>
</AppTheme>
);
}

View file

@ -89,17 +89,17 @@ export default function MenuContent({ hidden }: PropType) {
userRole === "manager" && {
text: "Station Details",
icon: <ChecklistSharpIcon />,
url: "/panel/manager-station-details", // Placeholder for now
url: "/panel/manager-station-details",
},
userRole === "user" && {
text: "Available Slots",
icon: <ChecklistSharpIcon />,
url: "/panel/all-available-slots", // Placeholder for now
url: "/panel/all-available-slots",
},
userRole === "user" && {
text: "Near By Stations",
icon: <EvStationIcon />,
url: "/panel/external-station-list", // Placeholder for now
url: "/panel/external-station-list",
},
];
@ -119,7 +119,7 @@ export default function MenuContent({ hidden }: PropType) {
<ListItem
key={index}
disablePadding
sx={{ display: "block",mt:2 }}
sx={{ display: "block", mt: 2 }}
>
<ListItemButton
component={Link}

View file

@ -0,0 +1,19 @@
// src/components/RoleBasedRedirect.tsx
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { RootState } from "../../redux/reducers";
const RoleBasedRedirect = () => {
const userType = useSelector(
(state: RootState) => state.profileReducer.user?.userType
);
if (userType === "user") {
return <Navigate to="/panel/booking-list" replace />;
}
// Optional: route others to dashboard or their own start page
return <Navigate to="/panel/dashboard" replace />;
};
export default RoleBasedRedirect;

View file

@ -84,11 +84,12 @@ export default function RoundedBarChart() {
const chartSetting = getChartSettings();
// Data transformation for BarChart
const chartData = topStations.map((station) => ({
name: station?.ChargingStation?.name,
count: parseInt(station.count, 10), // Ensure count is a number
const chartData = (topStations ?? []).map((station) => ({
name: station?.ChargingStation?.name ?? "Unknown Station",
count: parseInt(station?.count ?? "0", 10),
}));
return (
<Card
variant="outlined"

View file

@ -1,7 +1,7 @@
// src/pages/Dashboard
import * as React from "react";
import { Box, CssBaseline, Typography } from "@mui/material";
import { useSelector } from "react-redux";
import { RootState } from "../../redux/store/store";
import {
chartsCustomizations,
@ -9,9 +9,12 @@ import {
datePickersCustomizations,
treeViewCustomizations,
} from "./theme/customizations";
import AppTheme from "../../shared-theme/AppTheme";
import MainGrid from "../../components/MainGrid/mainGrid";
import AdminGrid from "../../components/MainGrid/adminGrid";
import ManagerGrid from "../../components/MainGrid/managerGrid";
import UserDashboard from "../../components/MainGrid/userDashboard";
const xThemeComponents = {
...chartsCustomizations,
@ -27,29 +30,52 @@ interface DashboardProps {
const Dashboard: React.FC<DashboardProps> = ({
disableCustomTheme = false,
}) => {
const userType = useSelector(
(state: RootState) => state.profileReducer.user?.userType
);
const renderGrid = () => {
switch (userType?.toLowerCase()) {
case "superadmin":
return <MainGrid />;
case "admin":
return <AdminGrid />;
case "manager":
return <ManagerGrid />;
case "user":
return <UserDashboard />;
default:
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#202020",
height: "100vh",
textAlign: "center",
padding: 2,
}}
>
<Typography color="white">
Access Denied: You do not have permission to view
this dashboard.
</Typography>
</Box>
);
}
};
return (
<AppTheme
{...{ disableCustomTheme }}
themeComponents={xThemeComponents}
>
<CssBaseline enableColorScheme />
{!disableCustomTheme ? (
<>
<MainGrid />
</>
) : (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#202020",
height: "100vh",
textAlign: "center",
padding: 2,
}}
></Box>
)}
{!disableCustomTheme && renderGrid()}
</AppTheme>
);
};

View file

@ -18,13 +18,13 @@ interface TopStation {
}
interface DashboardState {
totalAdmins: number;
totalManagers: number;
totalUsers: number;
totalStations: number;
carPortCounts: CarPortCount[];
topStations: TopStation[];
totalBookings: number;
totalAdmins?: number;
totalManagers?: number;
totalUsers?: number;
totalStations?: number;
carPortCounts?: CarPortCount[];
topStations?: TopStation[];
totalBookings?: number;
loading: boolean;
error: string | null;
}

View file

@ -62,6 +62,7 @@ export default function AppRouter() {
{/* Dashboard Routes */}
<Route path="/panel" element={<DashboardLayout />}>
<Route
path="dashboard"
element={<ProtectedRoute component={<Dashboard />} />}