charging Prices API integration

This commit is contained in:
jaanvi 2025-04-25 13:38:52 +05:30
parent 99e3f8c9f9
commit 156925abfa
8 changed files with 245 additions and 156 deletions

View file

@ -24,7 +24,7 @@ html, body {
@font-face {
font-family: 'Publica Sans Round Medium';
font-family: '"Publica Sans Round Medium", sans-serif';
src: url('../public/fonts/PublicaSansRound-Md.otf') format('otf');

View file

@ -61,9 +61,9 @@ export default function ManagerGrid() {
{/* Charts */}
<Grid container spacing={3} mt={1}>
<Grid item xs={12} sm={12} md={6} lg={6}>
{/* <Grid item xs={12} sm={12} md={6} lg={6}>
<SessionsChart />
</Grid>
</Grid> */}
<Grid item xs={12} sm={12} md={6} lg={6}>
<ResourcesPieChart />
</Grid>

View file

@ -86,11 +86,12 @@ export default function UserDashboard() {
sx={{
width: "100%",
color: "#DE0E1E9",
py: 3,
py: 0,
mt:0
}}
>
{/* Greeting */}
<Box display="flex" alignItems="center" px={3} mb={4}>
<Box display="flex" alignItems="center" px={3} mb={3} >
{/* <Avatar
sx={{
width: 64,
@ -106,7 +107,7 @@ export default function UserDashboard() {
{user?.name?.charAt(0).toUpperCase() || "U"}
</Avatar> */}
<Box>
<Typography variant="h3">
<Typography variant="h4">
Welcome back, {user?.name || "User"}!
</Typography>
<Typography color="#DE0E1E9">

View file

@ -1,4 +1,5 @@
import * as React from "react";
import { useDispatch, useSelector } from "react-redux";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
@ -8,24 +9,54 @@ import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { useTheme } from "@mui/material/styles";
import { fetchDashboardData } from "../../redux/slices/dashboardSlice";
import { RootState } from "../../redux/store/store"; // Adjust the path accordingly
export default function SessionsChart() {
const theme = useTheme();
const [selectedStation, setSelectedStation] = React.useState(
"Delhi NCR EV Station"
);
const theme = useTheme();
const dispatch = useDispatch();
const [selectedStation, setSelectedStation] = React.useState<string>("");
const handleChange = (event: { target: { value: React.SetStateAction<string> } }) => {
setSelectedStation(event.target.value);
};
// Get the dashboard data from Redux state
const { basicPrice, loading } = useSelector(
(state: RootState) => state.dashboardReducer
);
return (
// Get unique station names from basicPrice for the dropdown
const stationNames = Array.from(
new Set(
basicPrice?.map(
(price: { stationName: string }) => price.stationName
)
)
);
// Set default station when basicPrice loads
React.useEffect(() => {
if (stationNames.length > 0 && !selectedStation) {
setSelectedStation(stationNames[0]);
}
}, [basicPrice, stationNames, selectedStation]);
// Handle station selection from dropdown
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
setSelectedStation(event.target.value as string);
};
// Filter basic prices for the selected station
const selectedBasePrices =
basicPrice?.filter(
(price: { stationName: string }) =>
price.stationName === selectedStation
) || [];
return (
<Card
variant="outlined"
sx={{
width: "100%",
height: "auto",
minHeight: { xs: "260px", sm: "270px", md: "290px" },
minHeight: { xs: "360px", sm: "400px", md: "421px" },
gap: "16px",
borderRadius: "30px",
padding: { xs: "12px", sm: "16px", md: "20px" },
@ -34,18 +65,23 @@ export default function SessionsChart() {
}}
>
<CardContent
sx={{ padding: 0, "&:last-child": { paddingBottom: 0 } }}
sx={{
flex: 1,
padding: 0,
"&:last-child": { paddingBottom: 0 },
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
}}
>
<Typography
variant="h6"
align="left"
// color="#F2F2F2"
sx={{
fontWeight: 600,
fontSize: { xs: "12px", sm: "14px", md: "16px" },
lineHeight: "24px",
letterSpacing: "0%",
// color: "#FAFAFA",
marginBottom: { xs: 1, sm: 0.7, md: 1 },
}}
>
@ -65,7 +101,8 @@ export default function SessionsChart() {
<Select
value={selectedStation}
onChange={handleChange}
label="Select Station"
displayEmpty
disabled={loading || stationNames.length === 0}
sx={{
color: "#000000",
"& .MuiSvgIcon-root": { color: "#000000" },
@ -73,106 +110,144 @@ export default function SessionsChart() {
}}
IconComponent={ExpandMoreIcon}
>
<MenuItem value="Delhi NCR EV Station">
Delhi NCR EV Station
</MenuItem>
<MenuItem value="Mumbai EV Station">
Mumbai EV Station
</MenuItem>
<MenuItem value="Bangalore EV Station">
Bangalore EV Station
</MenuItem>
<MenuItem value="Pune EV Station">
Pune EV Station
</MenuItem>
{stationNames.length === 0 && (
<MenuItem value="" disabled>
Loading stations...
</MenuItem>
)}
{stationNames.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
{/* Grid container for the four boxes */}
{/* Display the base prices for the selected station */}
<Box
sx={{
display: "grid",
gridTemplateColumns: {
xs: "repeat(1, 1fr)", // 1 column on mobile
sm: "repeat(2, 1fr)", // 2 columns on tablets
md: "repeat(2, 1fr)", // 2x2 grid on desktop
xs: "repeat(1, 1fr)",
sm: "repeat(2, 1fr)",
md: "repeat(2, 1fr)",
},
gap: { xs: 1, sm: 1.5, md: 2 },
width: "100%",
}}
>
{[1, 2, 3, 4].map((item) => (
<Box
key={item}
{selectedBasePrices.length > 0 ? (
selectedBasePrices.map(
(price: {
stationId: any;
port: string;
price: number;
}) => (
<Box
key={`${price.stationId}-${price.port}`}
sx={{
height: {
xs: "105px",
sm: "115px",
md: "128px",
},
borderRadius: "8px",
p: {
xs: "10px",
sm: "12px",
md: "14px",
},
backgroundColor: "#D9E7ED",
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
<Typography
variant="body2"
sx={{
fontWeight: 600,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: {
xs: "20px",
md: "24px",
},
marginBottom: "4px",
color: "#111111",
}}
gutterBottom
>
{price.port}
</Typography>
<Box
display="flex"
gap={1}
alignItems="center"
>
<Typography
variant="subtitle2"
sx={{
fontWeight: 500,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: {
xs: "20px",
md: "24px",
},
}}
color="#454545"
gutterBottom
>
{price.price.toFixed(2)}{" "}
{/* Ensure consistent decimal places */}
</Typography>
<Typography
sx={{
fontWeight: 500,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: {
xs: "20px",
md: "24px",
},
}}
color="#454545"
>
cents/kWh
</Typography>
</Box>
</Box>
)
)
) : (
<Typography
variant="body2"
sx={{
height: {
xs: "105px",
sm: "115px",
md: "128px",
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
borderRadius: "8px",
p: { xs: "10px", sm: "12px", md: "14px" },
// backgroundColor: "#272727",
// color: "#D9D8D8",
backgroundColor: "#D9E7ED",
display: "flex",
flexDirection: "column",
justifyContent: "center",
color: "#454545",
textAlign: "center",
gridColumn: "span 2",
}}
>
<Typography
variant="body2"
sx={{
fontWeight: 600,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: { xs: "20px", md: "24px" },
marginBottom: "4px",
color: "#111111",
}}
gutterBottom
>
Basic Charging
</Typography>
<Box display="flex" gap={1} alignItems="center">
<Typography
variant="subtitle2"
sx={{
fontWeight: 500,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: { xs: "20px", md: "24px" },
}}
color="#454545"
gutterBottom
>
16.83
</Typography>
<Typography
sx={{
fontWeight: 500,
fontSize: {
xs: "12px",
sm: "14px",
md: "16px",
},
lineHeight: { xs: "20px", md: "24px" },
}}
color="#454545"
>
cents/kWh
</Typography>
</Box>
</Box>
))}
No pricing data available for this station.
</Typography>
)}
</Box>
</CardContent>
</Card>
);
);
}

View file

@ -1,32 +1,31 @@
body {
margin: 0;
font-family: "Gilroy";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
font-family: '"Publica Sans Round Medium", sans-serif';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
.mui-typography {
font-family: "Gilroy";
background-color: rgb(7, 127, 233);
font-family: "Gilroy";
background-color: rgb(7, 127, 233);
}
.css-1w8ddxu-MuiBarElement-root {
width: 19px !important;
border-radius: 50px !important;
rx: 8;
ry: 8
width: 19px !important;
border-radius: 50px !important;
rx: 8;
ry: 8;
}
@font-face {
font-family: 'Publica Sans Round Medium';
src: url('../public/fonts/PublicaSansRound-Md.otf') format('otf');
font-weight: 500;
font-style: normal;
font-display: swap;
}
font-family: '"Publica Sans Round Medium", sans-serif';
src: url("../public/fonts/PublicaSansRound-Md.otf") format("otf");
font-weight: 500;
font-style: normal;
font-display: swap;
}

View file

@ -48,24 +48,25 @@ const Dashboard: React.FC<DashboardProps> = ({
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("")
// return (
// <Box
// sx={{
// display: "flex",
// justifyContent: "center",
// alignItems: "center",
// backgroundColor: "#D0E1E9",
// height: "100vh",
// textAlign: "center",
// padding: 2,
// }}
// >
// <Typography color="black">
// Access Denied: You do not have permission to view
// this dashboard.
// </Typography>
// </Box>
// );
}
};

View file

@ -2,7 +2,6 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https";
import { toast } from "sonner";
// Define interfaces for the dashboard data structure
interface CarPortCount {
carPort: string;
count: number;
@ -17,6 +16,13 @@ interface TopStation {
};
}
interface BasicPrice {
port: string;
price: number;
stationId: number;
stationName: string;
}
interface DashboardState {
totalAdmins?: number;
totalManagers?: number;
@ -24,6 +30,7 @@ interface DashboardState {
totalStations?: number;
carPortCounts?: CarPortCount[];
topStations?: TopStation[];
basicPrice?: BasicPrice[];
totalBookings?: number;
loading: boolean;
error: string | null;
@ -36,6 +43,7 @@ const initialState: DashboardState = {
totalStations: 0,
carPortCounts: [],
topStations: [],
basicPrice: [],
totalBookings: 0,
loading: false,
error: null,
@ -44,12 +52,17 @@ const initialState: DashboardState = {
// Async thunk for fetching dashboard data
export const fetchDashboardData = createAsyncThunk<
DashboardState,
{ startDateBookings?: string; endDateBookings?: string; startDateStations?: string; endDateStations?: string}, // Accept startDate and endDate as optional parameters
{
startDateBookings?: string;
endDateBookings?: string;
startDateStations?: string;
endDateStations?: string;
},
{ rejectValue: string }
>("dashboard/fetchDashboardData", async (params, { rejectWithValue }) => {
try {
const response = await http.get("/dashboard", { params }); // Pass startDate and endDate as query params
return response.data;
const response = await http.get("/dashboard", { params });
return response.data as DashboardState; // Ensure response matches DashboardState
} catch (error: any) {
toast.error("Error Fetching Dashboard Data: " + error.message);
return rejectWithValue(
@ -73,15 +86,16 @@ const dashboardSlice = createSlice({
fetchDashboardData.fulfilled,
(state, action: PayloadAction<DashboardState>) => {
state.loading = false;
state.totalAdmins = action.payload.totalAdmins;
state.totalManagers = action.payload.totalManagers;
state.totalUsers = action.payload.totalUsers;
state.totalStations = action.payload.totalStations;
state.carPortCounts = action.payload.carPortCounts;
state.topStations = action.payload.topStations;
state.totalBookings = action.payload.totalBookings;
// Map response fields to state
state.totalAdmins = action.payload.totalAdmins ?? 0;
state.totalManagers = action.payload.totalManagers ?? 0;
state.totalUsers = action.payload.totalUsers ?? 0;
state.totalStations = action.payload.totalStations ?? 0;
state.carPortCounts = action.payload.carPortCounts ?? [];
state.topStations = action.payload.topStations ?? [];
state.basicPrice = action.payload.basicPrice ?? [];
state.totalBookings = action.payload.totalBookings ?? 0;
}
)
.addCase(fetchDashboardData.rejected, (state, action) => {
state.loading = false;

View file

@ -35,7 +35,7 @@ export const stationDetailList = createAsyncThunk<
const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found");
const response = await http.get("/get-station-details");
const response = await http.get("/get-station-detail");
if (!response.data?.data) throw new Error("Invalid API response");
return response.data.data;
} catch (error: any) {
@ -88,8 +88,7 @@ export const updateStationDetails = createAsyncThunk<
try {
const response = await http.patch(
`/update-station-details/${id}`,
managerStationData,
managerStationData
);
toast.success("Station Details updated successfully");
return response.data; // Return the updated data
@ -207,7 +206,7 @@ const managerStationSlice = createSlice({
}
}
)
.addCase(deleteStationDetails.rejected, (state, action) => {
state.loading = false;
state.error =