dev-jaanvi #1

Open
jaanvi wants to merge 155 commits from dev-jaanvi into main
13 changed files with 473 additions and 53 deletions
Showing only changes of commit e39499d574 - Show all commits

BIN
public/tablet-img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

View file

@ -213,8 +213,6 @@ const filteredRows = rows.filter((row) => {
); );
}); });
const indexOfLastRow = currentPage * usersPerPage; const indexOfLastRow = currentPage * usersPerPage;
const indexOfFirstRow = indexOfLastRow - usersPerPage; const indexOfFirstRow = indexOfLastRow - usersPerPage;
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow); const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
@ -248,13 +246,15 @@ const filteredRows = rows.filter((row) => {
{(() => { {(() => {
switch (tableType) { switch (tableType) {
case "admin": case "admin":
return "Admin"; return "Admins";
case "role": case "role":
return "Roles"; return "Roles";
case "user": case "user":
return "Users"; return "Users";
case "manager": case "manager":
return "Managers"; return "Managers";
case "all-managers":
return "Managers";
case "vehicle": case "vehicle":
return "Vehicles"; return "Vehicles";
case "station": case "station":
@ -264,7 +264,9 @@ const filteredRows = rows.filter((row) => {
case "booking": case "booking":
return "Booking"; return "Booking";
case "slots": case "slots":
return "Slot"; return "Slots";
case "all-available-slots":
return "Available Slots";
default: default:
return "List"; return "List";
} }
@ -326,8 +328,13 @@ const filteredRows = rows.filter((row) => {
}} }}
> >
{!( {!(
user?.userType === "user" && (user?.userType === "user" &&
(tableType === "slots" )) && ( tableType === "all-available-slots") ||
(user?.userType === "superadmin" &&
tableType === "all-managers") ||
(user?.userType === "superadmin" &&
tableType === "user")
) && (
<Button <Button
sx={{ sx={{
backgroundColor: "#52ACDF", backgroundColor: "#52ACDF",

View file

@ -38,7 +38,7 @@ export default function MenuContent({ hidden }: PropType) {
userRole === "superadmin" && { userRole === "superadmin" && {
text: "Manager", text: "Manager",
icon: <ManageAccountsOutlinedIcon />, icon: <ManageAccountsOutlinedIcon />,
url: "/panel/manager-list", url: "/panel/all-managers-list",
}, },
userRole === "superadmin" && { userRole === "superadmin" && {
text: "User", text: "User",
@ -89,7 +89,7 @@ export default function MenuContent({ hidden }: PropType) {
userRole === "user" && { userRole === "user" && {
text: "Available Slots", text: "Available Slots",
icon: <ChecklistSharpIcon />, icon: <ChecklistSharpIcon />,
url: "/panel/slot-list", // Placeholder for now url: "/panel/all-available-slots", // Placeholder for now
}, },
userRole === "user" && { userRole === "user" && {
text: "Near By Stations", text: "Near By Stations",

View file

@ -84,12 +84,13 @@ export default function AdminList() {
{ id: "email", label: "Email" }, { id: "email", label: "Email" },
{ id: "phone", label: "Phone" }, { id: "phone", label: "Phone" },
{ id: "registeredAddress", label: "Address" }, { id: "registeredAddress", label: "Address" },
{ id: "userType", label: "Role" }, // { id: "userType", label: "Role" },
{ id: "action", label: "Action", align: "center" }, { id: "action", label: "Action", align: "center" },
]; ];
const categoryRows = admins?.map( const categoryRows = admins
?.filter((admin) => admin?.userType === "admin").map(
( (
admin: { admin: {
id: string; id: string;

View 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"
/>
</>
);
}

View 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"
/>
</>
);
}

View file

@ -5,7 +5,7 @@ import { RootState, AppDispatch } from "../../redux/store/store";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { import {
createSlot, createSlot,
fetchAvailableSlots, fetchManagersSlots,
updateSlot, updateSlot,
} from "../../redux/slices/slotSlice"; } from "../../redux/slices/slotSlice";
import AddSlotModal from "../../components/AddSlotModal/addSlotModal"; import AddSlotModal from "../../components/AddSlotModal/addSlotModal";
@ -24,7 +24,7 @@ export default function EVSlotList() {
); );
const { user } = useSelector((state: RootState) => state?.profileReducer); const { user } = useSelector((state: RootState) => state?.profileReducer);
useEffect(() => { useEffect(() => {
dispatch(fetchAvailableSlots()); dispatch(fetchManagersSlots());
}, [dispatch]); }, [dispatch]);
const handleClickOpen = () => { const handleClickOpen = () => {
@ -46,7 +46,7 @@ export default function EVSlotList() {
}) => { }) => {
try { try {
await dispatch(createSlot(data)); await dispatch(createSlot(data));
await dispatch(fetchAvailableSlots()); await dispatch(fetchManagersSlots());
handleCloseModal(); handleCloseModal();
} catch (error) { } catch (error) {
console.error("Error adding slot", error); console.error("Error adding slot", error);
@ -74,7 +74,7 @@ export default function EVSlotList() {
}) })
).unwrap(); ).unwrap();
await dispatch(fetchAvailableSlots()); await dispatch(fetchManagersSlots());
handleCloseModal(); handleCloseModal();
} catch (error) { } catch (error) {
console.error("Update failed", error); console.error("Update failed", error);
@ -83,7 +83,7 @@ export default function EVSlotList() {
const slotColumns: Column[] = [ const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },
{ id: "name", label: "Station Name" }, { id: "stationName", label: "Station Name" },
{ id: "stationId", label: "Station Id" }, { id: "stationId", label: "Station Id" },
{ id: "date", label: "Date" }, { id: "date", label: "Date" },
{ id: "startTime", label: "Start Time" }, { id: "startTime", label: "Start Time" },
@ -104,7 +104,7 @@ export default function EVSlotList() {
srno: index + 1, srno: index + 1,
id: slot?.id ?? "NA", id: slot?.id ?? "NA",
stationId: slot?.stationId ?? "NA", stationId: slot?.stationId ?? "NA",
name: slot?.ChargingStation?.name ?? "NA", stationName: slot?.stationName?? "NA",
date: formattedDate ?? "NA", date: formattedDate ?? "NA",
startTime: startTime ?? "NA", startTime: startTime ?? "NA",
endTime: endTime ?? "NA", endTime: endTime ?? "NA",

View file

@ -1,7 +1,44 @@
import React from "react"; 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 { useNavigate } from "react-router-dom"; // Import useNavigate for navigation
import SearchIcon from "@mui/icons-material/Search"; 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 LandingPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -13,7 +50,6 @@ const LandingPage = () => {
return ( return (
<Box <Box
sx={{ sx={{
background: ` background: `
linear-gradient(135deg, #0D0D0D 20%, #1C1E22 80%), linear-gradient(135deg, #0D0D0D 20%, #1C1E22 80%),
radial-gradient(circle at 30%, rgb(241, 201, 119) 100%, rgba(255, 204, 102, 0) 50%) radial-gradient(circle at 30%, rgb(241, 201, 119) 100%, rgba(255, 204, 102, 0) 50%)
@ -21,6 +57,8 @@ const LandingPage = () => {
color: "white", color: "white",
minHeight: "100vh", minHeight: "100vh",
fontFamily: "Inter", fontFamily: "Inter",
//display: "flex", // Ensures the children align correctly
//flexDirection: "column",
}} }}
> >
{/* Navbar */} {/* Navbar */}
@ -57,8 +95,8 @@ const LandingPage = () => {
}} }}
/> />
<Button <Button
type="button" // Changed to "button" to avoid form submission type="button"
onClick={handleLoginClick} // Trigger navigation on click onClick={handleLoginClick}
sx={{ sx={{
backgroundColor: "#52ACDF", backgroundColor: "#52ACDF",
color: "white", color: "white",
@ -174,15 +212,18 @@ const LandingPage = () => {
<Container <Container
maxWidth="lg" maxWidth="lg"
sx={{ sx={{
// width: "1320px",
// height: "239px",
width: "100%",
position: "relative", 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: background:
"linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))", "linear-gradient(135deg, rgba(82, 172, 223, 0.2), rgba(0,0,0,0.3))",
borderRadius: "16px", borderRadius: "16px",
py: 4, py: 4,
px: 3, px: 3,
mt: 15,
}} }}
> >
<Grid container spacing={4} textAlign="center"> <Grid container spacing={4} textAlign="center">
@ -193,11 +234,18 @@ const LandingPage = () => {
fontWeight={600} fontWeight={600}
fontSize={"80px"} fontSize={"80px"}
lineHeight={"100px"} lineHeight={"100px"}
fontFamily={"Inter"} fontFamily={"Poppins"}
> >
50+ 50+
</Typography> </Typography>
<Typography variant="body2" color="#FFFFFF"> <Typography
variant="body2"
color="#FFFFFF"
fontFamily={"Inter"}
fontWeight={400}
fontSize={"16px"}
lineHeight={"22px"}
>
Successful Digital Transformations Successful Digital Transformations
</Typography> </Typography>
</Grid> </Grid>
@ -208,11 +256,18 @@ const LandingPage = () => {
fontWeight={600} fontWeight={600}
fontSize={"80px"} fontSize={"80px"}
lineHeight={"100px"} lineHeight={"100px"}
fontFamily={"Inter"} fontFamily={"Poppins"}
> >
100% 100%
</Typography> </Typography>
<Typography variant="body2" color="#FFFFFF"> <Typography
variant="body2"
color="#FFFFFF"
fontFamily={"Inter"}
fontWeight={400}
fontSize={"16px"}
lineHeight={"22px"}
>
Client Satisfaction Client Satisfaction
</Typography> </Typography>
</Grid> </Grid>
@ -223,11 +278,18 @@ const LandingPage = () => {
fontWeight={600} fontWeight={600}
fontSize={"80px"} fontSize={"80px"}
lineHeight={"100px"} lineHeight={"100px"}
fontFamily={"Inter"} fontFamily={"Poppins"}
> >
20+ 20+
</Typography> </Typography>
<Typography variant="body2" color="#FFFFFF"> <Typography
variant="body2"
color="#FFFFFF"
fontFamily={"Inter"}
fontWeight={400}
fontSize={"16px"}
lineHeight={"22px"}
>
Global Partnerships Global Partnerships
</Typography> </Typography>
</Grid> </Grid>
@ -239,16 +301,160 @@ const LandingPage = () => {
fontWeight={600} fontWeight={600}
fontSize={"80px"} fontSize={"80px"}
lineHeight={"100px"} lineHeight={"100px"}
fontFamily={"Inter"} fontFamily={"Poppins"}
> >
10+ 10+
</Typography> </Typography>
<Typography variant="body2" color="#FFFFFF"> <Typography
variant="body2"
color="#FFFFFF"
fontFamily={"Inter"}
fontWeight={400}
fontSize={"16px"}
lineHeight={"22px"}
>
Years of Innovation Years of Innovation
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>
</Container> </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> </Box>
); );
}; };

View file

@ -20,7 +20,7 @@ export default function UserList() {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const users = useSelector((state: RootState) => state.userReducer.users); const users = useSelector((state: RootState) => state.userReducer.users);
const { user } = useSelector((state: RootState) => state?.profileReducer);
useEffect(() => { useEffect(() => {
dispatch(userList()); dispatch(userList());
}, [dispatch]); }, [dispatch]);
@ -78,7 +78,9 @@ export default function UserList() {
{ id: "email", label: "Email" }, { id: "email", label: "Email" },
{ id: "phone", label: "Phone" }, { 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 const categoryRows = users?.length
? users.map((user, index) => ({ ? users.map((user, index) => ({

View file

@ -13,9 +13,10 @@ interface User {
} }
interface Admin { interface Admin {
Admins: any;
id: string; id: string;
name: string; name: string;
role: string; userType: string;
email: string; email: string;
phone: string; phone: string;
registeredAddress: string; registeredAddress: string;

View file

@ -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) // Create Manager (Async Thunk)
export const addManager = createAsyncThunk< export const addManager = createAsyncThunk<
Manager, Manager,
@ -132,6 +150,21 @@ const managerSlice = createSlice({
state.loading = false; state.loading = false;
state.error = action.payload || "Failed to fetch managers"; 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 // Add Manager
.addCase(addManager.pending, (state) => { .addCase(addManager.pending, (state) => {

View file

@ -10,7 +10,7 @@ interface Slot {
startTime: string; startTime: string;
endTime: string; endTime: string;
isAvailable: boolean; isAvailable: boolean;
stationName:string;
ChargingStation: { name: 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< export const createSlot = createAsyncThunk<
Slot, Slot,
{ {
@ -152,6 +172,22 @@ const slotSlice = createSlice({
state.error = state.error =
action.payload || "Failed to fetch available slots"; 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) => { .addCase(createSlot.pending, (state) => {
state.loading = true; state.loading = true;
}) })

View file

@ -24,7 +24,8 @@ const EVSlotManagement = lazy(() => import("./pages/EVSlotManagement"));
const BookingList = lazy(() => import("./pages/BookingList")); const BookingList = lazy(() => import("./pages/BookingList"));
const EvSlotList = lazy(() => import("./pages/EvSlotList")); const EvSlotList = lazy(() => import("./pages/EvSlotList"));
const ExternalStationList = lazy(() => import("./pages/ExternalStationList/externalStationList.tsx")); const ExternalStationList = lazy(() => import("./pages/ExternalStationList/externalStationList.tsx"));
const AllManagersList = lazy(() => import("./pages/AllMangersList"));
const AvailableSlotsList = lazy(() => import("./pages/AvailableSlotsList"));
interface ProtectedRouteProps { interface ProtectedRouteProps {
// caps: string[]; // caps: string[];
component: React.ReactNode; component: React.ReactNode;
@ -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> </Route>
{/* Catch-all Route */} {/* Catch-all Route */}