Add Welcome Section of Landing Page and Implements AllMangers and AllAvailableSlots APi Integration
This commit is contained in:
parent
3a103a4b36
commit
e39499d574
BIN
public/tablet-img.png
Normal file
BIN
public/tablet-img.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
|
@ -203,17 +203,15 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
|||
handleClose();
|
||||
};
|
||||
|
||||
const filteredRows = rows.filter((row) => {
|
||||
if (!searchQuery.trim()) return true; // Return all rows if searchQuery is empty or whitespace
|
||||
const lowerCaseQuery = searchQuery.toLowerCase().trim();
|
||||
return (
|
||||
(row.name && row.name.toLowerCase().includes(lowerCaseQuery)) ||
|
||||
(row.registeredAddress &&
|
||||
row.registeredAddress.toLowerCase().includes(lowerCaseQuery))
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
const filteredRows = rows.filter((row) => {
|
||||
if (!searchQuery.trim()) return true; // Return all rows if searchQuery is empty or whitespace
|
||||
const lowerCaseQuery = searchQuery.toLowerCase().trim();
|
||||
return (
|
||||
(row.name && row.name.toLowerCase().includes(lowerCaseQuery)) ||
|
||||
(row.registeredAddress &&
|
||||
row.registeredAddress.toLowerCase().includes(lowerCaseQuery))
|
||||
);
|
||||
});
|
||||
|
||||
const indexOfLastRow = currentPage * usersPerPage;
|
||||
const indexOfFirstRow = indexOfLastRow - usersPerPage;
|
||||
|
@ -248,13 +246,15 @@ const filteredRows = rows.filter((row) => {
|
|||
{(() => {
|
||||
switch (tableType) {
|
||||
case "admin":
|
||||
return "Admin";
|
||||
return "Admins";
|
||||
case "role":
|
||||
return "Roles";
|
||||
case "user":
|
||||
return "Users";
|
||||
case "manager":
|
||||
return "Managers";
|
||||
case "all-managers":
|
||||
return "Managers";
|
||||
case "vehicle":
|
||||
return "Vehicles";
|
||||
case "station":
|
||||
|
@ -264,7 +264,9 @@ const filteredRows = rows.filter((row) => {
|
|||
case "booking":
|
||||
return "Booking";
|
||||
case "slots":
|
||||
return "Slot";
|
||||
return "Slots";
|
||||
case "all-available-slots":
|
||||
return "Available Slots";
|
||||
default:
|
||||
return "List";
|
||||
}
|
||||
|
@ -326,8 +328,13 @@ const filteredRows = rows.filter((row) => {
|
|||
}}
|
||||
>
|
||||
{!(
|
||||
user?.userType === "user" &&
|
||||
(tableType === "slots" )) && (
|
||||
(user?.userType === "user" &&
|
||||
tableType === "all-available-slots") ||
|
||||
(user?.userType === "superadmin" &&
|
||||
tableType === "all-managers") ||
|
||||
(user?.userType === "superadmin" &&
|
||||
tableType === "user")
|
||||
) && (
|
||||
<Button
|
||||
sx={{
|
||||
backgroundColor: "#52ACDF",
|
||||
|
|
|
@ -38,7 +38,7 @@ export default function MenuContent({ hidden }: PropType) {
|
|||
userRole === "superadmin" && {
|
||||
text: "Manager",
|
||||
icon: <ManageAccountsOutlinedIcon />,
|
||||
url: "/panel/manager-list",
|
||||
url: "/panel/all-managers-list",
|
||||
},
|
||||
userRole === "superadmin" && {
|
||||
text: "User",
|
||||
|
@ -89,7 +89,7 @@ export default function MenuContent({ hidden }: PropType) {
|
|||
userRole === "user" && {
|
||||
text: "Available Slots",
|
||||
icon: <ChecklistSharpIcon />,
|
||||
url: "/panel/slot-list", // Placeholder for now
|
||||
url: "/panel/all-available-slots", // Placeholder for now
|
||||
},
|
||||
userRole === "user" && {
|
||||
text: "Near By Stations",
|
||||
|
|
|
@ -84,12 +84,13 @@ export default function AdminList() {
|
|||
{ id: "email", label: "Email" },
|
||||
{ id: "phone", label: "Phone" },
|
||||
{ id: "registeredAddress", label: "Address" },
|
||||
{ id: "userType", label: "Role" },
|
||||
// { id: "userType", label: "Role" },
|
||||
|
||||
{ id: "action", label: "Action", align: "center" },
|
||||
];
|
||||
|
||||
const categoryRows = admins?.map(
|
||||
const categoryRows = admins
|
||||
?.filter((admin) => admin?.userType === "admin").map(
|
||||
(
|
||||
admin: {
|
||||
id: string;
|
||||
|
|
54
src/pages/AllMangersList/index.tsx
Normal file
54
src/pages/AllMangersList/index.tsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { useEffect } from "react";
|
||||
import CustomTable, { Column } from "../../components/CustomTable/customTable";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||
import { AllmanagerList,} from "../../redux/slices/managerSlice";
|
||||
|
||||
export default function ManagerList() {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const managers = useSelector(
|
||||
(state: RootState) => state.managerReducer.managers
|
||||
);
|
||||
|
||||
// Fetch manager list on component mount
|
||||
useEffect(() => {
|
||||
dispatch(AllmanagerList());
|
||||
}, [dispatch]);
|
||||
|
||||
// Columns for the manager table
|
||||
const categoryColumns: Column[] = [
|
||||
{ id: "srno", label: "Sr No" },
|
||||
{ id: "name", label: "Name" },
|
||||
{ id: "email", label: "Email" },
|
||||
{ id: "phone", label: "Phone" },
|
||||
{ id: "stationName", label: "Station Name" },
|
||||
{ id: "registeredAddress", label: "Station Location" },
|
||||
];
|
||||
|
||||
// Prepare rows for the table
|
||||
const categoryRows = managers?.length
|
||||
? managers.map((manager, index) => {
|
||||
const station = manager?.chargingStation;
|
||||
return {
|
||||
id: manager.id,
|
||||
srno: index + 1,
|
||||
name: manager.name,
|
||||
email: manager.email,
|
||||
phone: manager.phone ?? "NA",
|
||||
stationName: station?.name ?? "NA",
|
||||
registeredAddress: station?.registeredAddress ?? "NA",
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Display manager list */}
|
||||
<CustomTable
|
||||
columns={categoryColumns}
|
||||
rows={categoryRows}
|
||||
tableType="all-managers"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
67
src/pages/AvailableSlotsList/index.tsx
Normal file
67
src/pages/AvailableSlotsList/index.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import CustomTable, { Column } from "../../components/CustomTable/customTable";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||
|
||||
import {
|
||||
fetchAvailableSlots,
|
||||
|
||||
} from "../../redux/slices/slotSlice";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default function EVSlotList() {
|
||||
|
||||
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const availableSlots = useSelector(
|
||||
(state: RootState) => state?.slotReducer.availableSlots
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchAvailableSlots());
|
||||
}, [dispatch]);
|
||||
|
||||
|
||||
|
||||
const slotColumns: Column[] = [
|
||||
{ id: "srno", label: "Sr No" },
|
||||
{ id: "name", label: "Station Name" },
|
||||
{ id: "stationId", label: "Station Id" },
|
||||
{ id: "date", label: "Date" },
|
||||
{ id: "startTime", label: "Start Time" },
|
||||
{ id: "endTime", label: "End Time" },
|
||||
{ id: "isAvailable", label: "Is Available", align: "center" },
|
||||
|
||||
];
|
||||
|
||||
const slotRows = availableSlots?.length
|
||||
? availableSlots.map((slot, index) => {
|
||||
const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD");
|
||||
const startTime = dayjs(slot?.startTime).format("HH:mm");
|
||||
const endTime = dayjs(slot?.endTime).format("HH:mm");
|
||||
|
||||
return {
|
||||
srno: index + 1,
|
||||
id: slot?.id ?? "NA",
|
||||
stationId: slot?.stationId ?? "NA",
|
||||
name: slot?.ChargingStation?.name ?? "NA",
|
||||
date: formattedDate ?? "NA",
|
||||
startTime: startTime ?? "NA",
|
||||
endTime: endTime ?? "NA",
|
||||
isAvailable: slot?.isAvailable ? "Yes" : "No",
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomTable
|
||||
columns={slotColumns}
|
||||
rows={slotRows}
|
||||
tableType="all-available-slots"
|
||||
|
||||
/>
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -5,7 +5,7 @@ import { RootState, AppDispatch } from "../../redux/store/store";
|
|||
import { useForm } from "react-hook-form";
|
||||
import {
|
||||
createSlot,
|
||||
fetchAvailableSlots,
|
||||
fetchManagersSlots,
|
||||
updateSlot,
|
||||
} from "../../redux/slices/slotSlice";
|
||||
import AddSlotModal from "../../components/AddSlotModal/addSlotModal";
|
||||
|
@ -21,10 +21,10 @@ export default function EVSlotList() {
|
|||
const dispatch = useDispatch<AppDispatch>();
|
||||
const availableSlots = useSelector(
|
||||
(state: RootState) => state?.slotReducer.availableSlots
|
||||
);
|
||||
);
|
||||
const { user } = useSelector((state: RootState) => state?.profileReducer);
|
||||
useEffect(() => {
|
||||
dispatch(fetchAvailableSlots());
|
||||
dispatch(fetchManagersSlots());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
|
@ -46,7 +46,7 @@ export default function EVSlotList() {
|
|||
}) => {
|
||||
try {
|
||||
await dispatch(createSlot(data));
|
||||
await dispatch(fetchAvailableSlots());
|
||||
await dispatch(fetchManagersSlots());
|
||||
handleCloseModal();
|
||||
} catch (error) {
|
||||
console.error("Error adding slot", error);
|
||||
|
@ -74,16 +74,16 @@ export default function EVSlotList() {
|
|||
})
|
||||
).unwrap();
|
||||
|
||||
await dispatch(fetchAvailableSlots());
|
||||
await dispatch(fetchManagersSlots());
|
||||
handleCloseModal();
|
||||
} catch (error) {
|
||||
console.error("Update failed", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const slotColumns: Column[] = [
|
||||
{ id: "srno", label: "Sr No" },
|
||||
{ id: "name", label: "Station Name" },
|
||||
{ id: "stationName", label: "Station Name" },
|
||||
{ id: "stationId", label: "Station Id" },
|
||||
{ id: "date", label: "Date" },
|
||||
{ id: "startTime", label: "Start Time" },
|
||||
|
@ -104,7 +104,7 @@ export default function EVSlotList() {
|
|||
srno: index + 1,
|
||||
id: slot?.id ?? "NA",
|
||||
stationId: slot?.stationId ?? "NA",
|
||||
name: slot?.ChargingStation?.name ?? "NA",
|
||||
stationName: slot?.stationName?? "NA",
|
||||
date: formattedDate ?? "NA",
|
||||
startTime: startTime ?? "NA",
|
||||
endTime: endTime ?? "NA",
|
||||
|
|
|
@ -1,19 +1,55 @@
|
|||
import React from "react";
|
||||
import { Box, Button, Container, Grid, Typography } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
Container,
|
||||
Grid,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom"; // Import useNavigate for navigation
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import EvStationIcon from "@mui/icons-material/EvStation";
|
||||
import BatteryChargingFullIcon from "@mui/icons-material/BatteryChargingFull";
|
||||
import LocationOnIcon from "@mui/icons-material/LocationOn";
|
||||
const features = [
|
||||
{
|
||||
icon: <EvStationIcon fontSize="large" />,
|
||||
title: "Seamless Charging Experience",
|
||||
description:
|
||||
"Find and book EV charging stations effortlessly with DigiEv's smart platform.",
|
||||
},
|
||||
{
|
||||
icon: <BatteryChargingFullIcon fontSize="large" />,
|
||||
title: "Real-Time Availability",
|
||||
description:
|
||||
"Check live station availability and optimize your route for an efficient charge.",
|
||||
},
|
||||
{
|
||||
icon: <LocationOnIcon fontSize="large" />,
|
||||
title: "Widespread Network",
|
||||
description:
|
||||
"Access a vast network of EV stations across multiple locations with ease.",
|
||||
},
|
||||
{
|
||||
icon: <SearchIcon fontSize="large" />, // New feature icon
|
||||
title: "Smart Search Functionality",
|
||||
description:
|
||||
"Find EV stations nearby with advanced filters for location, availability, and pricing.",
|
||||
},
|
||||
];
|
||||
|
||||
const LandingPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLoginClick = () => {
|
||||
navigate("/login");
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
background: `
|
||||
linear-gradient(135deg, #0D0D0D 20%, #1C1E22 80%),
|
||||
radial-gradient(circle at 30%, rgb(241, 201, 119) 100%, rgba(255, 204, 102, 0) 50%)
|
||||
|
@ -21,6 +57,8 @@ const LandingPage = () => {
|
|||
color: "white",
|
||||
minHeight: "100vh",
|
||||
fontFamily: "Inter",
|
||||
//display: "flex", // Ensures the children align correctly
|
||||
//flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{/* Navbar */}
|
||||
|
@ -57,8 +95,8 @@ const LandingPage = () => {
|
|||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button" // Changed to "button" to avoid form submission
|
||||
onClick={handleLoginClick} // Trigger navigation on click
|
||||
type="button"
|
||||
onClick={handleLoginClick}
|
||||
sx={{
|
||||
backgroundColor: "#52ACDF",
|
||||
color: "white",
|
||||
|
@ -174,15 +212,18 @@ const LandingPage = () => {
|
|||
|
||||
<Container
|
||||
maxWidth="lg"
|
||||
|
||||
sx={{
|
||||
// width: "1320px",
|
||||
// height: "239px",
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
boxShadow: "0px 8px 20px rgba(166, 210, 235, 0.5)", // Bluish box shadow
|
||||
boxShadow: "0px 8px 20px rgba(166, 210, 235, 0.2)", // Bluish box shadow
|
||||
background:
|
||||
"linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))",
|
||||
borderRadius: "16px",
|
||||
py: 4,
|
||||
px: 3,
|
||||
"linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))",
|
||||
borderRadius: "16px",
|
||||
py: 4,
|
||||
px: 3,
|
||||
mt: 15,
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={4} textAlign="center">
|
||||
|
@ -193,11 +234,18 @@ const LandingPage = () => {
|
|||
fontWeight={600}
|
||||
fontSize={"80px"}
|
||||
lineHeight={"100px"}
|
||||
fontFamily={"Inter"}
|
||||
fontFamily={"Poppins"}
|
||||
>
|
||||
50+
|
||||
</Typography>
|
||||
<Typography variant="body2" color="#FFFFFF">
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="#FFFFFF"
|
||||
fontFamily={"Inter"}
|
||||
fontWeight={400}
|
||||
fontSize={"16px"}
|
||||
lineHeight={"22px"}
|
||||
>
|
||||
Successful Digital Transformations
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
@ -208,11 +256,18 @@ const LandingPage = () => {
|
|||
fontWeight={600}
|
||||
fontSize={"80px"}
|
||||
lineHeight={"100px"}
|
||||
fontFamily={"Inter"}
|
||||
fontFamily={"Poppins"}
|
||||
>
|
||||
100%
|
||||
</Typography>
|
||||
<Typography variant="body2" color="#FFFFFF">
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="#FFFFFF"
|
||||
fontFamily={"Inter"}
|
||||
fontWeight={400}
|
||||
fontSize={"16px"}
|
||||
lineHeight={"22px"}
|
||||
>
|
||||
Client Satisfaction
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
@ -223,11 +278,18 @@ const LandingPage = () => {
|
|||
fontWeight={600}
|
||||
fontSize={"80px"}
|
||||
lineHeight={"100px"}
|
||||
fontFamily={"Inter"}
|
||||
fontFamily={"Poppins"}
|
||||
>
|
||||
20+
|
||||
</Typography>
|
||||
<Typography variant="body2" color="#FFFFFF">
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="#FFFFFF"
|
||||
fontFamily={"Inter"}
|
||||
fontWeight={400}
|
||||
fontSize={"16px"}
|
||||
lineHeight={"22px"}
|
||||
>
|
||||
Global Partnerships
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
@ -239,16 +301,160 @@ const LandingPage = () => {
|
|||
fontWeight={600}
|
||||
fontSize={"80px"}
|
||||
lineHeight={"100px"}
|
||||
fontFamily={"Inter"}
|
||||
fontFamily={"Poppins"}
|
||||
>
|
||||
10+
|
||||
</Typography>
|
||||
<Typography variant="body2" color="#FFFFFF">
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="#FFFFFF"
|
||||
fontFamily={"Inter"}
|
||||
fontWeight={400}
|
||||
fontSize={"16px"}
|
||||
lineHeight={"22px"}
|
||||
>
|
||||
Years of Innovation
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
|
||||
<Container
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
minHeight: "100vh",
|
||||
textAlign: "center",
|
||||
// width: "1320px",
|
||||
// height: "349.82px",
|
||||
width: "100%",
|
||||
boxShadow: "0px 8px 20px rgba(166, 210, 235, 0.2)", // Bluish box shadow
|
||||
background:
|
||||
"linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))",
|
||||
borderRadius: "16px",
|
||||
py: 4,
|
||||
px: 3,
|
||||
mt: 10,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={600}
|
||||
fontFamily={"Inter"}
|
||||
fontSize={"40px"}
|
||||
lineHeight={"100%"}
|
||||
color="#FFFFFF"
|
||||
>
|
||||
Welcome to <span style={{ color: "#52ACDF" }}>DigiEv</span>{" "}
|
||||
- Your EV Charging Partner
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h6" mt={1} fontWeight={600}>
|
||||
Simplifying Electric Vehicle Charging
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
mt={2}
|
||||
maxWidth="600px"
|
||||
mx="auto"
|
||||
fontFamily={"Inter"}
|
||||
fontSize={"20px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"100%"}
|
||||
color="#D9D8D8"
|
||||
>
|
||||
DigiEv helps EV owners locate, book, and manage their
|
||||
charging needs efficiently. With our intuitive platform, you
|
||||
can ensure a smooth and hassle-free charging experience.
|
||||
</Typography>
|
||||
<Box mt={5}>
|
||||
<img
|
||||
src="/tablet-img.png"
|
||||
alt="DigiEv Dashboard"
|
||||
style={{
|
||||
width: "80%",
|
||||
maxWidth: "800px",
|
||||
borderRadius: "10px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
mt={2}
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
<Grid item xs={12} sm={6} md={3} key={index}>
|
||||
<Card
|
||||
sx={{
|
||||
boxShadow:
|
||||
"0px 8px 20px rgba(166, 210, 235, 0.2)", // Bluish box shadow
|
||||
background:
|
||||
"linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))",
|
||||
borderRadius: "18px",
|
||||
width: "247px", // Fixed width
|
||||
height: "200px", // Fixed height
|
||||
display: "flex", // Flexbox for centering content
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<CardContent
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "100%", // Fills the parent card
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center", // Centers the icon horizontally
|
||||
alignItems: "center", // Centers the icon vertically
|
||||
mt: 1,
|
||||
mb: 2,
|
||||
color: "#D9D8D8",
|
||||
}}
|
||||
>
|
||||
{feature.icon}
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color: "#52ACDF",
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 600,
|
||||
fontSize: "18px", // Slightly reduced to fit uniformly
|
||||
lineHeight: "160%",
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
color: "#D9D8D8",
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 400,
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
mt: 1,
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ export default function UserList() {
|
|||
const dispatch = useDispatch<AppDispatch>();
|
||||
|
||||
const users = useSelector((state: RootState) => state.userReducer.users);
|
||||
|
||||
const { user } = useSelector((state: RootState) => state?.profileReducer);
|
||||
useEffect(() => {
|
||||
dispatch(userList());
|
||||
}, [dispatch]);
|
||||
|
@ -78,7 +78,9 @@ export default function UserList() {
|
|||
{ id: "email", label: "Email" },
|
||||
{ id: "phone", label: "Phone" },
|
||||
|
||||
{ id: "action", label: "Action", align: "center" },
|
||||
...(user?.userType === "admin"
|
||||
? [{ id: "action", label: "Action", align: "center" as const }]
|
||||
: []),
|
||||
];
|
||||
const categoryRows = users?.length
|
||||
? users.map((user, index) => ({
|
||||
|
|
|
@ -13,9 +13,10 @@ interface User {
|
|||
}
|
||||
|
||||
interface Admin {
|
||||
Admins: any;
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
userType: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
registeredAddress: string;
|
||||
|
|
|
@ -46,7 +46,25 @@ export const managerList = createAsyncThunk<
|
|||
);
|
||||
}
|
||||
});
|
||||
export const AllmanagerList = createAsyncThunk<
|
||||
Manager[],
|
||||
void,
|
||||
{ rejectValue: string }
|
||||
>("fetchAllManagers", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const token = localStorage?.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await http.get("/all-manager-list");
|
||||
if (!response.data?.data) throw new Error("Invalid API response");
|
||||
return response.data.data;
|
||||
} catch (error: any) {
|
||||
toast.error("Error Fetching Managers: " + error.message);
|
||||
return rejectWithValue(
|
||||
error?.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
// Create Manager (Async Thunk)
|
||||
export const addManager = createAsyncThunk<
|
||||
Manager,
|
||||
|
@ -132,6 +150,21 @@ const managerSlice = createSlice({
|
|||
state.loading = false;
|
||||
state.error = action.payload || "Failed to fetch managers";
|
||||
})
|
||||
.addCase(AllmanagerList.pending, (state) => {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(
|
||||
AllmanagerList.fulfilled,
|
||||
(state, action: PayloadAction<Manager[]>) => {
|
||||
state.loading = false;
|
||||
state.managers = action.payload;
|
||||
}
|
||||
)
|
||||
.addCase(AllmanagerList.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.payload || "Failed to fetch managers";
|
||||
})
|
||||
|
||||
// Add Manager
|
||||
.addCase(addManager.pending, (state) => {
|
||||
|
|
|
@ -10,7 +10,7 @@ interface Slot {
|
|||
startTime: string;
|
||||
endTime: string;
|
||||
isAvailable: boolean;
|
||||
|
||||
stationName:string;
|
||||
ChargingStation: { name: string };
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,27 @@ export const fetchAvailableSlots = createAsyncThunk<
|
|||
);
|
||||
}
|
||||
});
|
||||
export const fetchManagersSlots = createAsyncThunk<
|
||||
Slot[],
|
||||
void,
|
||||
{ rejectValue: string }
|
||||
>("fetchManagersSlots", async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const token = localStorage?.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await http.get("/manager-slots");
|
||||
|
||||
if (!response.data?.data) throw new Error("Invalid API response");
|
||||
|
||||
return response.data.data;
|
||||
} catch (error: any) {
|
||||
toast.error("Error Fetching Profile" + error);
|
||||
return rejectWithValue(
|
||||
error?.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
export const createSlot = createAsyncThunk<
|
||||
Slot,
|
||||
{
|
||||
|
@ -152,6 +172,22 @@ const slotSlice = createSlice({
|
|||
state.error =
|
||||
action.payload || "Failed to fetch available slots";
|
||||
})
|
||||
.addCase(fetchManagersSlots.pending, (state) => {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(
|
||||
fetchManagersSlots.fulfilled,
|
||||
(state, action: PayloadAction<Slot[]>) => {
|
||||
state.loading = false;
|
||||
state.availableSlots = action.payload;
|
||||
}
|
||||
)
|
||||
.addCase(fetchManagersSlots.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error =
|
||||
action.payload || "Failed to fetch available slots";
|
||||
})
|
||||
.addCase(createSlot.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
|
|
|
@ -24,7 +24,8 @@ const EVSlotManagement = lazy(() => import("./pages/EVSlotManagement"));
|
|||
const BookingList = lazy(() => import("./pages/BookingList"));
|
||||
const EvSlotList = lazy(() => import("./pages/EvSlotList"));
|
||||
const ExternalStationList = lazy(() => import("./pages/ExternalStationList/externalStationList.tsx"));
|
||||
|
||||
const AllManagersList = lazy(() => import("./pages/AllMangersList"));
|
||||
const AvailableSlotsList = lazy(() => import("./pages/AvailableSlotsList"));
|
||||
interface ProtectedRouteProps {
|
||||
// caps: string[];
|
||||
component: React.ReactNode;
|
||||
|
@ -44,7 +45,7 @@ export default function AppRouter() {
|
|||
<Suspense fallback={<LoadingComponent />}>
|
||||
<BaseRoutes>
|
||||
{/* Default Route */}
|
||||
<Route path="" element={<LandingPage /> } />
|
||||
<Route path="" element={<LandingPage />} />
|
||||
|
||||
{/* Auth Routes */}
|
||||
<Route path="">
|
||||
|
@ -117,6 +118,18 @@ export default function AppRouter() {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="all-managers-list"
|
||||
element={
|
||||
<ProtectedRoute component={<AllManagersList />} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="all-available-slots"
|
||||
element={
|
||||
<ProtectedRoute component={<AvailableSlotsList />} />
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Catch-all Route */}
|
||||
|
|
Loading…
Reference in a new issue