dev-jaanvi #1
224
src/components/AddBookingModal/index.tsx
Normal file
224
src/components/AddBookingModal/index.tsx
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Modal,
|
||||||
|
IconButton,
|
||||||
|
InputAdornment,
|
||||||
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { addBooking, bookingList } from "../../redux/slices/bookSlice.ts";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
CustomIconButton,
|
||||||
|
CustomTextField,
|
||||||
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
|
|
||||||
|
export default function AddBookingModal({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
}) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
watch,
|
||||||
|
} = useForm();
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => setShowPassword((prev) => !prev);
|
||||||
|
|
||||||
|
const onSubmit = async (data: any) => {
|
||||||
|
const bookingData = {
|
||||||
|
stationId: data.stationId,
|
||||||
|
date: data.date,
|
||||||
|
startTime: data.startTime,
|
||||||
|
endTime: data.endTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dispatch(addBooking(bookingData));
|
||||||
|
dispatch(bookingList());
|
||||||
|
handleClose(); // Close modal after successful addition
|
||||||
|
reset(); // Reset form fields
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding booking:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={(e, reason) => {
|
||||||
|
if (reason === "backdropClick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleClose();
|
||||||
|
}}
|
||||||
|
aria-labelledby="add-booking-modal"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: 400,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 3,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6" fontWeight={600}>
|
||||||
|
Add Booking
|
||||||
|
</Typography>
|
||||||
|
<CustomIconButton onClick={handleClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</CustomIconButton>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
{/* Station ID Input */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
|
<Box sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Station ID
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Station Id"
|
||||||
|
size="small"
|
||||||
|
error={!!errors.stationId}
|
||||||
|
helperText={
|
||||||
|
errors.stationId
|
||||||
|
? errors.stationId.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("stationId", {
|
||||||
|
required: "Station ID is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Date Input */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
|
<Box sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Date
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Select Booking Date"
|
||||||
|
size="small"
|
||||||
|
type="date"
|
||||||
|
error={!!errors.date}
|
||||||
|
helperText={
|
||||||
|
errors.date ? errors.date.message : ""
|
||||||
|
}
|
||||||
|
{...register("date", {
|
||||||
|
required: "Date is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Start Time Input */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
|
<Box sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Start Time
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter Start Time"
|
||||||
|
size="small"
|
||||||
|
type="time"
|
||||||
|
error={!!errors.startTime}
|
||||||
|
helperText={
|
||||||
|
errors.startTime
|
||||||
|
? errors.startTime.message
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
{...register("startTime", {
|
||||||
|
required: "Start Time is required",
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* End Time Input */}
|
||||||
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
|
<Box sx={{ flex: 1 }}>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
End Time
|
||||||
|
</Typography>
|
||||||
|
<CustomTextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter End Time"
|
||||||
|
size="small"
|
||||||
|
type="time"
|
||||||
|
error={!!errors.endTime}
|
||||||
|
helperText={
|
||||||
|
errors.endTime ? errors.endTime.message : ""
|
||||||
|
}
|
||||||
|
{...register("endTime", {
|
||||||
|
required: "End Time is required",
|
||||||
|
validate: (value) => {
|
||||||
|
const startTime = watch("startTime");
|
||||||
|
if (value <= startTime) {
|
||||||
|
return "End time must be after start time.";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
mt: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#52ACDF",
|
||||||
|
color: "white",
|
||||||
|
borderRadius: "8px",
|
||||||
|
width: "117px",
|
||||||
|
"&:hover": { backgroundColor: "#439BC1" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Booking
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import { deleteUser, userList } from "../../redux/slices/userSlice.ts";
|
||||||
import { deleteStation } from "../../redux/slices/stationSlice.ts";
|
import { deleteStation } from "../../redux/slices/stationSlice.ts";
|
||||||
import StationViewModal from "../Modals/StationViewModal/index.tsx";
|
import StationViewModal from "../Modals/StationViewModal/index.tsx";
|
||||||
import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
|
import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
|
||||||
|
import { bookingList } from "../../redux/slices/bookSlice.ts";
|
||||||
// Styled components for customization
|
// Styled components for customization
|
||||||
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
||||||
[`&.${tableCellClasses.head}`]: {
|
[`&.${tableCellClasses.head}`]: {
|
||||||
|
@ -112,14 +113,6 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
setSelectedRow(row);
|
setSelectedRow(row);
|
||||||
setRowData(row);
|
setRowData(row);
|
||||||
};
|
};
|
||||||
// const handleViewButton = (id: string | undefined) => {
|
|
||||||
// if (!id) {
|
|
||||||
// console.error("ID not found for viewing.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// setViewModal(true);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
@ -150,6 +143,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
case "station":
|
case "station":
|
||||||
dispatch(deleteStation(id || ""));
|
dispatch(deleteStation(id || ""));
|
||||||
break;
|
break;
|
||||||
|
// case "booking":
|
||||||
|
// dispatch(deleteBooking(id || ""));
|
||||||
|
// break;
|
||||||
default:
|
default:
|
||||||
console.error("Unknown table type:", tableType);
|
console.error("Unknown table type:", tableType);
|
||||||
return;
|
return;
|
||||||
|
@ -173,8 +169,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
case "user":
|
case "user":
|
||||||
dispatch(userList());
|
dispatch(userList());
|
||||||
break;
|
break;
|
||||||
case "slot":
|
case "booking":
|
||||||
dispatch(fetchAvailableSlots(1));
|
dispatch(bookingList());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Unknown table type:", tableType);
|
console.error("Unknown table type:", tableType);
|
||||||
|
@ -242,6 +238,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
? "Vehicles"
|
? "Vehicles"
|
||||||
: tableType === "station"
|
: tableType === "station"
|
||||||
? "Charging Station"
|
? "Charging Station"
|
||||||
|
: tableType === "booking"
|
||||||
|
? "Booking"
|
||||||
: "List"}
|
: "List"}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
@ -315,6 +313,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
? "Vehicle"
|
? "Vehicle"
|
||||||
: tableType === "station"
|
: tableType === "station"
|
||||||
? "Charging Station"
|
? "Charging Station"
|
||||||
|
: tableType === "booking"
|
||||||
|
? "Booking"
|
||||||
: "Item"}
|
: "Item"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -65,10 +65,10 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
icon: <ManageAccountsOutlinedIcon />,
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
url: "/panel/EVslots", // Placeholder for now
|
url: "/panel/EVslots", // Placeholder for now
|
||||||
},
|
},
|
||||||
userRole === "manager" && {
|
userRole === "user" && {
|
||||||
text: "Slot List",
|
text: "Bookings",
|
||||||
icon: <ManageAccountsOutlinedIcon />,
|
icon: <ManageAccountsOutlinedIcon />,
|
||||||
url: "/panel/slot-list", // Placeholder for now
|
url: "/panel/booking-list", // Placeholder for now
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
116
src/pages/BookingList/index.tsx
Normal file
116
src/pages/BookingList/index.tsx
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Box, Button, TextField, Typography } from "@mui/material";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { RootState, AppDispatch } from "../../redux/store/store";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { addBooking, bookingList } from "../../redux/slices/bookSlice";
|
||||||
|
import AddBookingModal from "../../components/AddBookingModal";
|
||||||
|
|
||||||
|
export default function ManagerList() {
|
||||||
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
|
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
|
||||||
|
const [editRow, setEditRow] = useState<any>(null);
|
||||||
|
const { reset } = useForm();
|
||||||
|
const [deleteModal, setDeleteModal] = useState<boolean>(false);
|
||||||
|
const [viewModal, setViewModal] = useState<boolean>(false);
|
||||||
|
const [rowData, setRowData] = useState<any | null>(null);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
const bookings = useSelector(
|
||||||
|
(state: RootState) => state?.bookReducer.bookings
|
||||||
|
);
|
||||||
|
|
||||||
|
const users = useSelector((state: RootState) => state?.profileReducer.user);
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(bookingList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setRowData(null);
|
||||||
|
setAddModalOpen(true);
|
||||||
|
};
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setAddModalOpen(false);
|
||||||
|
setEditModalOpen(false);
|
||||||
|
setRowData(null);
|
||||||
|
reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddBooking = async (data: {
|
||||||
|
stationId: string;
|
||||||
|
date: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
await dispatch(addBooking(data));
|
||||||
|
await dispatch(bookingList());
|
||||||
|
|
||||||
|
handleCloseModal(); // Close the modal
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding manager", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryColumns: Column[] = [
|
||||||
|
{ id: "srno", label: "Sr No" },
|
||||||
|
{ id: "name", label: "User Name" },
|
||||||
|
// { id: "stationName", label: "Station Name" },
|
||||||
|
// { id: "registeredAddress", label: "Station Location" },
|
||||||
|
{ id: "stationId", label: "Station Id" },
|
||||||
|
|
||||||
|
{ id: "date", label: "Date" },
|
||||||
|
{ id: "startTime", label: "Start Time" },
|
||||||
|
{ id: "endTime", label: "End Time" },
|
||||||
|
{ id: "action", label: "Action", align: "center" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const categoryRows = bookings?.length
|
||||||
|
? bookings?.map(function (
|
||||||
|
booking: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
stationId: string;
|
||||||
|
date: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: booking?.id ?? "NA",
|
||||||
|
srno: index + 1,
|
||||||
|
name: users?.name ?? "NA",
|
||||||
|
stationId: booking?.stationId ?? "NA",
|
||||||
|
date: booking?.date ?? "NA",
|
||||||
|
startTime: booking?.startTime ?? "NA",
|
||||||
|
endTime: booking?.endTime ?? "NA",
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
// console.log("Rowa", categoryRows);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CustomTable
|
||||||
|
columns={categoryColumns}
|
||||||
|
rows={categoryRows}
|
||||||
|
setDeleteModal={setDeleteModal}
|
||||||
|
deleteModal={deleteModal}
|
||||||
|
setViewModal={setViewModal}
|
||||||
|
viewModal={viewModal}
|
||||||
|
setRowData={setRowData}
|
||||||
|
setModalOpen={() => setEditModalOpen(true)}
|
||||||
|
tableType="booking"
|
||||||
|
handleClickOpen={handleClickOpen}
|
||||||
|
/>
|
||||||
|
{/* Add Manager Modal */}
|
||||||
|
<AddBookingModal
|
||||||
|
open={addModalOpen}
|
||||||
|
handleClose={handleCloseModal}
|
||||||
|
handleAddManager={handleAddBooking}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -49,45 +49,36 @@ export default function EVSlotManagement() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
// Fetch slots from the Redux state
|
|
||||||
const { slots, availableSlots } = useSelector(
|
const { slots, availableSlots } = useSelector(
|
||||||
(state: RootState) => state?.slotReducer
|
(state: RootState) => state?.slotReducer
|
||||||
);
|
);
|
||||||
const manager = useSelector((state: RootState) => state.managerReducer.managers);
|
|
||||||
// console.log("availableSlots",availableSlots);
|
console.log("availableSlots", availableSlots);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchAvailableSlots());
|
dispatch(fetchAvailableSlots());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
navigate("/panel/dashboard");
|
navigate("/panel/dashboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const addSlot = (start: string, end: string) => {
|
const addSlot = (start: string, end: string) => {
|
||||||
|
const selectedDateFormatted = selectedDate?.format("YYYY-MM-DD");
|
||||||
const selectedDateFormatted = selectedDate.format("YYYY-MM-DD");
|
|
||||||
|
|
||||||
|
|
||||||
const startTime = start;
|
const startTime = start;
|
||||||
const endTime = end;
|
const endTime = end;
|
||||||
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
createSlot({
|
createSlot({
|
||||||
stationId: manager.stationId,
|
|
||||||
date: selectedDateFormatted,
|
date: selectedDateFormatted,
|
||||||
startHour: startTime,
|
startHour: startTime,
|
||||||
endHour: endTime,
|
endHour: endTime,
|
||||||
isAvailable: true, // Ensure it's available by default
|
isAvailable: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
// dispatch(fetchAvailableSlots());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Delete slot function that dispatches deleteSlot
|
// Delete slot function that dispatches deleteSlot
|
||||||
const addBreak = (start: string, end: string) => {
|
const addBreak = (start: string, end: string) => {
|
||||||
if (!start || !end) {
|
if (!start || !end) {
|
||||||
|
@ -99,22 +90,11 @@ const addSlot = (start: string, end: string) => {
|
||||||
setError("");
|
setError("");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delete slot function
|
|
||||||
// const deleteSlot = (start: string, end: string) => {
|
|
||||||
// const updatedSlots = slots[selectedDay].filter(
|
|
||||||
// (slot: any) => !(slot.start === start && slot.end === end)
|
|
||||||
// );
|
|
||||||
// setSlots({
|
|
||||||
// ...slots,
|
|
||||||
// [selectedDay]: updatedSlots,
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
const handleDeleteSlot = (slotId: number) => {
|
const handleDeleteSlot = (slotId: number) => {
|
||||||
// Call the deleteSlot action with the correct slotId
|
|
||||||
dispatch(deleteSlot(slotId));
|
dispatch(deleteSlot(slotId));
|
||||||
|
dispatch(fetchAvailableSlots());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Delete break function
|
// Delete break function
|
||||||
const deleteBreak = (start: string, end: string) => {
|
const deleteBreak = (start: string, end: string) => {
|
||||||
const updatedBreaks = breakTime.filter(
|
const updatedBreaks = breakTime.filter(
|
||||||
|
@ -138,11 +118,10 @@ const addSlot = (start: string, end: string) => {
|
||||||
setError(""); // Clear any previous errors
|
setError(""); // Clear any previous errors
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close the success message
|
|
||||||
const handleCloseSnackbar = () => {
|
const handleCloseSnackbar = () => {
|
||||||
setSuccessMessage(null);
|
setSuccessMessage(null);
|
||||||
};
|
};
|
||||||
console.log("Slots: ",slots)
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ maxWidth: 600, mx: "auto", p: 2 }}>
|
<Box sx={{ maxWidth: 600, mx: "auto", p: 2 }}>
|
||||||
<Typography variant="h4" gutterBottom align="center">
|
<Typography variant="h4" gutterBottom align="center">
|
||||||
|
@ -201,27 +180,6 @@ const addSlot = (start: string, end: string) => {
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<Card sx={{ mt: 2, p: 2 }}>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Set Operating Hours for {selectedDay}
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ display: "flex", gap: 2, mt: 1 }}>
|
|
||||||
<CustomTextField
|
|
||||||
type="time"
|
|
||||||
label="Opening Time"
|
|
||||||
value={openingTime}
|
|
||||||
onChange={(e) => setOpeningTime(e.target.value)}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
<CustomTextField
|
|
||||||
type="time"
|
|
||||||
label="Closing Time"
|
|
||||||
value={closingTime}
|
|
||||||
onChange={(e) => setClosingTime(e.target.value)}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Card>
|
|
||||||
<Card sx={{ mt: 2, p: 2 }}>
|
<Card sx={{ mt: 2, p: 2 }}>
|
||||||
<Typography variant="h6">Add Slots</Typography>
|
<Typography variant="h6">Add Slots</Typography>
|
||||||
<Box sx={{ display: "flex", gap: 2, mt: 1 }}>
|
<Box sx={{ display: "flex", gap: 2, mt: 1 }}>
|
||||||
|
@ -299,17 +257,8 @@ const addSlot = (start: string, end: string) => {
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
Slots for {selectedDay}
|
Slots for {selectedDay}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Ensure that `slots` is always an array */}
|
|
||||||
{Array.isArray(availableSlots) && availableSlots.length ? (
|
{Array.isArray(availableSlots) && availableSlots.length ? (
|
||||||
availableSlots
|
availableSlots.map((slot: any, index: number) => (
|
||||||
.filter(
|
|
||||||
(slot: any) =>
|
|
||||||
dayjs(slot.date).format("YYYY-MM-DD") ===
|
|
||||||
selectedDate.format("YYYY-MM-DD") &&
|
|
||||||
dayjs(slot.date).format("dddd") === selectedDay
|
|
||||||
)
|
|
||||||
.map((slot: any, index: number) => (
|
|
||||||
<Box
|
<Box
|
||||||
key={index}
|
key={index}
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -320,8 +269,9 @@ const addSlot = (start: string, end: string) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
{dayjs(slot.startHour).format("HH:mm")} -{" "}
|
{dayjs(slot.date).format("YYYY-MM-DD")} -{" "}
|
||||||
{dayjs(slot.endHour).format("HH:mm")}
|
{dayjs(slot.startTime).format("HH:mm")} -{" "}
|
||||||
|
{dayjs(slot.endTime).format("HH:mm")}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => handleDeleteSlot(slot.id)}
|
onClick={() => handleDeleteSlot(slot.id)}
|
||||||
|
@ -366,7 +316,7 @@ const addSlot = (start: string, end: string) => {
|
||||||
<Typography>No break times added</Typography>
|
<Typography>No break times added</Typography>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
<Box
|
{/* <Box
|
||||||
sx={{ display: "flex", justifyContent: "space-between", mt: 3 }}
|
sx={{ display: "flex", justifyContent: "space-between", mt: 3 }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
@ -394,15 +344,15 @@ const addSlot = (start: string, end: string) => {
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box> */}
|
||||||
|
|
||||||
{/* Success Snackbar */}
|
{/* Success Snackbar */}
|
||||||
<Snackbar
|
{/* <Snackbar
|
||||||
open={!!successMessage}
|
open={!!successMessage}
|
||||||
autoHideDuration={6000}
|
autoHideDuration={6000}
|
||||||
onClose={handleCloseSnackbar}
|
onClose={handleCloseSnackbar}
|
||||||
message={successMessage}
|
message={successMessage}
|
||||||
/>
|
/> */}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import CustomTable, { Column } from "../../components/CustomTable";
|
|
||||||
import { RootState } from "../../redux/reducers";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import { AppDispatch } from "../../redux/store/store";
|
|
||||||
import {
|
|
||||||
createSlot,
|
|
||||||
updateSlot,
|
|
||||||
fetchAvailableSlots,
|
|
||||||
deleteSlot,
|
|
||||||
} from "../../redux/slices/slotSlice";
|
|
||||||
import EVSlotManagement from "../EVSlotManagement";
|
|
||||||
import AddSlotModal from "../../components/AddSlotModal";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
// import AddSlotModal from "../../components/AddSlotModal";
|
|
||||||
// import EditSlotModal from "../../components/EditSlotModal";
|
|
||||||
|
|
||||||
export default function EvSlotList() {
|
|
||||||
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
|
||||||
const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
|
|
||||||
const [editRow, setEditRow] = useState<any>(null);
|
|
||||||
const { reset } = useForm();
|
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState<boolean>(false);
|
|
||||||
const [viewModal, setViewModal] = useState<boolean>(false);
|
|
||||||
const [rowData, setRowData] = useState<any | null>(null);
|
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
|
||||||
const slots = useSelector((state: RootState) => state.slotReducer.slots);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(fetchAvailableSlots(1)); // Fetch slots when component mounts
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
|
||||||
setRowData(null); // Reset row data when opening for new slot
|
|
||||||
setAddModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
|
||||||
setAddModalOpen(false);
|
|
||||||
setEditModalOpen(false);
|
|
||||||
setRowData(null);
|
|
||||||
reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddSlot = async (data: {
|
|
||||||
startHour: string;
|
|
||||||
endHour: string;
|
|
||||||
stationId: number;
|
|
||||||
date: string;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
await dispatch(createSlot(data)); // Create the slot with all necessary fields
|
|
||||||
await dispatch(fetchAvailableSlots()); // Refetch updated slots
|
|
||||||
handleCloseModal(); // Close modal after successful creation
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error adding slot", error);
|
|
||||||
toast.error("Failed to add slot");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// const handleUpdateSlot = async (
|
|
||||||
// id: number,
|
|
||||||
// startHour: string,
|
|
||||||
// endHour: string
|
|
||||||
// ) => {
|
|
||||||
// try {
|
|
||||||
// await dispatch(updateSlot({ id, startHour, endHour }));
|
|
||||||
// await dispatch(fetchAvailableSlots());
|
|
||||||
// handleCloseModal();
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Update failed", error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const handleDeleteSlot = async (id: number) => {
|
|
||||||
// try {
|
|
||||||
// await dispatch(deleteSlot(id));
|
|
||||||
// await dispatch(fetchAvailableSlots());
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Delete failed", error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
const slotColumns: Column[] = [
|
|
||||||
{ id: "srno", label: "Sr No" },
|
|
||||||
{ id: "stationId", label: "Station Id" },
|
|
||||||
{ id: "date", label: "Date" },
|
|
||||||
{ id: "startHour", label: "Start Hour" },
|
|
||||||
{ id: "endHour", label: "End Hour" },
|
|
||||||
{ id: "action", label: "Action", align: "center" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const filteredSlots = slots?.filter(
|
|
||||||
(slot) =>
|
|
||||||
dayjs(slot.startTime)
|
|
||||||
.format("HH:mm")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase()) ||
|
|
||||||
dayjs(slot.endTime)
|
|
||||||
.format("HH:mm")
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
const slotRows = filteredSlots?.length
|
|
||||||
? filteredSlots.map((slot: any, index: number) => ({
|
|
||||||
id: slot?.id,
|
|
||||||
srno: index + 1,
|
|
||||||
startHour: dayjs(slot?.startTime).format("HH:mm"), // Format startTime
|
|
||||||
endHour: dayjs(slot?.endTime).format("HH:mm"), // Format endTime
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<CustomTable
|
|
||||||
columns={slotColumns}
|
|
||||||
rows={slotRows}
|
|
||||||
setDeleteModal={setDeleteModal}
|
|
||||||
deleteModal={deleteModal}
|
|
||||||
setViewModal={setViewModal}
|
|
||||||
viewModal={viewModal}
|
|
||||||
setRowData={setRowData}
|
|
||||||
setModalOpen={() => setEditModalOpen(true)}
|
|
||||||
tableType="slot"
|
|
||||||
handleClickOpen={handleClickOpen}
|
|
||||||
// handleDelete={handleDeleteSlot} // Allow deleting
|
|
||||||
/>
|
|
||||||
<AddSlotModal
|
|
||||||
open={addModalOpen}
|
|
||||||
handleClose={handleCloseModal}
|
|
||||||
handleAddSlot={handleAddSlot}
|
|
||||||
/>
|
|
||||||
{/* <EditSlotModal
|
|
||||||
open={editModalOpen}
|
|
||||||
handleClose={handleCloseModal}
|
|
||||||
handleUpdateSlot={handleUpdateSlot}
|
|
||||||
editRow={rowData}
|
|
||||||
/> */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ import vehicleReducer from "./slices/VehicleSlice.ts";
|
||||||
import managerReducer from "../redux/slices/managerSlice.ts";
|
import managerReducer from "../redux/slices/managerSlice.ts";
|
||||||
import stationReducer from "../redux/slices/stationSlice.ts";
|
import stationReducer from "../redux/slices/stationSlice.ts";
|
||||||
import slotReducer from "../redux/slices/slotSlice.ts";
|
import slotReducer from "../redux/slices/slotSlice.ts";
|
||||||
|
import bookReducer from "../redux/slices/bookSlice.ts";
|
||||||
|
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
|
@ -21,6 +22,7 @@ const rootReducer = combineReducers({
|
||||||
managerReducer,
|
managerReducer,
|
||||||
stationReducer,
|
stationReducer,
|
||||||
slotReducer,
|
slotReducer,
|
||||||
|
bookReducer,
|
||||||
// Add other reducers here...
|
// Add other reducers here...
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
107
src/redux/slices/bookSlice.ts
Normal file
107
src/redux/slices/bookSlice.ts
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import http from "../../lib/https";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
interface Booking {
|
||||||
|
id: number;
|
||||||
|
name:string;
|
||||||
|
stationId: string;
|
||||||
|
date: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
}
|
||||||
|
interface BookingState {
|
||||||
|
bookings: Booking[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
const initialState: BookingState = {
|
||||||
|
bookings: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bookingList = createAsyncThunk<
|
||||||
|
Booking,
|
||||||
|
void,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("fetchBooking", async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
|
const response = await http.get("/user-bookings");
|
||||||
|
|
||||||
|
if (!response.data?.data) throw new Error("Invalid API response");
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching User Booking List" + error);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addBooking = createAsyncThunk<
|
||||||
|
Booking,
|
||||||
|
{
|
||||||
|
stationId: string;
|
||||||
|
date: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
},
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("/AddBooking", async (data, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.post("/book-slot", data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bookSlice = createSlice({
|
||||||
|
name: "booking",
|
||||||
|
initialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(bookingList.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
bookingList.fulfilled,
|
||||||
|
(state, action: PayloadAction<Booking[]>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.bookings = action.payload;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(bookingList.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.error.message || "Failed to fetch users";
|
||||||
|
})
|
||||||
|
.addCase(addBooking.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
// state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
addBooking.fulfilled,
|
||||||
|
(state, action: PayloadAction<Booking>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.bookings.push(action.payload);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addCase(
|
||||||
|
addBooking.rejected,
|
||||||
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
state.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default bookSlice.reducer;
|
|
@ -6,16 +6,14 @@ import { toast } from "sonner";
|
||||||
interface Slot {
|
interface Slot {
|
||||||
id: number;
|
id: number;
|
||||||
date: string;
|
date: string;
|
||||||
stationId: number;
|
|
||||||
startHour: Date;
|
startHour: Date;
|
||||||
endHour: Date;
|
endHour: Date;
|
||||||
isAvailable: boolean;
|
isAvailable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface SlotState {
|
interface SlotState {
|
||||||
slots: Slot[];
|
slots: Slot[];
|
||||||
availableSlots: Slot[]; // Ensure it's initialized as an empty array
|
availableSlots: Slot[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +26,6 @@ const initialState: SlotState = {
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const fetchAvailableSlots = createAsyncThunk<
|
export const fetchAvailableSlots = createAsyncThunk<
|
||||||
Slot[],
|
Slot[],
|
||||||
void,
|
void,
|
||||||
|
@ -54,7 +51,6 @@ export const fetchAvailableSlots = createAsyncThunk<
|
||||||
export const createSlot = createAsyncThunk<
|
export const createSlot = createAsyncThunk<
|
||||||
Slot,
|
Slot,
|
||||||
{
|
{
|
||||||
stationId: number;
|
|
||||||
date: string;
|
date: string;
|
||||||
startHour: string;
|
startHour: string;
|
||||||
endHour: string;
|
endHour: string;
|
||||||
|
@ -63,26 +59,19 @@ export const createSlot = createAsyncThunk<
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>(
|
>(
|
||||||
"slots/createSlot",
|
"slots/createSlot",
|
||||||
async (
|
async (payload, { rejectWithValue }) => {
|
||||||
{ stationId, date, startHour, endHour, isAvailable },
|
|
||||||
{ rejectWithValue }
|
|
||||||
) => {
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
// const payload = {
|
||||||
stationId,
|
// date,
|
||||||
date, // This should be in the format 'YYYY-MM-DD'
|
// startHour,
|
||||||
startHour, // This should be in 'HH:mm' format
|
// endHour,
|
||||||
endHour,
|
// isAvailable,
|
||||||
isAvailable,
|
// };
|
||||||
};
|
|
||||||
|
|
||||||
// Send the request to create the slot
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
const response = await http.post("/create-slot", payload);
|
const response = await http.post("/create-slot", payload);
|
||||||
|
|
||||||
// Show success message
|
|
||||||
toast.success("Slot created successfully");
|
toast.success("Slot created successfully");
|
||||||
|
|
||||||
// Return the response data (created slot)
|
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Show error message
|
// Show error message
|
||||||
|
@ -98,7 +87,7 @@ export const createSlot = createAsyncThunk<
|
||||||
|
|
||||||
// Update Slot details
|
// Update Slot details
|
||||||
export const updateSlot = createAsyncThunk<
|
export const updateSlot = createAsyncThunk<
|
||||||
Slot, // Return type
|
Slot,
|
||||||
{ id: number; startTime: string; endTime: string }, // Argument type (slot update data)
|
{ id: number; startTime: string; endTime: string }, // Argument type (slot update data)
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
|
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
|
||||||
|
@ -108,14 +97,15 @@ export const updateSlot = createAsyncThunk<
|
||||||
return response.data.data; // Return updated slot data
|
return response.data.data; // Return updated slot data
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error("Error updating the slot: " + error?.message);
|
toast.error("Error updating the slot: " + error?.message);
|
||||||
return rejectWithValue(error.response?.data?.message || "An error occurred");
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete Slot
|
|
||||||
export const deleteSlot = createAsyncThunk<
|
export const deleteSlot = createAsyncThunk<
|
||||||
number, // Return type (id of deleted slot)
|
number, // Return type (id of deleted slot)
|
||||||
number, // Argument type (id of the slot)
|
number,
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("slots/deleteSlot", async (id, { rejectWithValue }) => {
|
>("slots/deleteSlot", async (id, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
|
@ -124,7 +114,9 @@ export const deleteSlot = createAsyncThunk<
|
||||||
return id; // Return the id of the deleted slot
|
return id; // Return the id of the deleted slot
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error("Error deleting the slot: " + error?.message);
|
toast.error("Error deleting the slot: " + error?.message);
|
||||||
return rejectWithValue(error.response?.data?.message || "An error occurred");
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -138,21 +130,28 @@ const slotSlice = createSlice({
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
})
|
})
|
||||||
.addCase(fetchAvailableSlots.fulfilled, (state, action: PayloadAction<Slot[]>) => {
|
.addCase(
|
||||||
|
fetchAvailableSlots.fulfilled,
|
||||||
|
(state, action: PayloadAction<Slot[]>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.availableSlots = action.payload;
|
state.availableSlots = action.payload;
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.addCase(fetchAvailableSlots.rejected, (state, action) => {
|
.addCase(fetchAvailableSlots.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to fetch available slots";
|
state.error =
|
||||||
|
action.payload || "Failed to fetch available slots";
|
||||||
})
|
})
|
||||||
.addCase(createSlot.pending, (state) => {
|
.addCase(createSlot.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
.addCase(createSlot.fulfilled, (state, action: PayloadAction<Slot>) => {
|
.addCase(
|
||||||
|
createSlot.fulfilled,
|
||||||
|
(state, action: PayloadAction<Slot>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.slots.push(action.payload);
|
state.slots.push(action.payload);
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.addCase(createSlot.rejected, (state, action) => {
|
.addCase(createSlot.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to create slot";
|
state.error = action.payload || "Failed to create slot";
|
||||||
|
@ -160,14 +159,19 @@ const slotSlice = createSlice({
|
||||||
.addCase(updateSlot.pending, (state) => {
|
.addCase(updateSlot.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
.addCase(updateSlot.fulfilled, (state, action: PayloadAction<Slot>) => {
|
.addCase(
|
||||||
|
updateSlot.fulfilled,
|
||||||
|
(state, action: PayloadAction<Slot>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
// Update the slot in the state with the updated data
|
// Update the slot in the state with the updated data
|
||||||
const index = state.slots.findIndex((slot) => slot.id === action.payload.id);
|
const index = state.slots.findIndex(
|
||||||
|
(slot) => slot.id === action.payload.id
|
||||||
|
);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
state.slots[index] = action.payload;
|
state.slots[index] = action.payload;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.addCase(updateSlot.rejected, (state, action) => {
|
.addCase(updateSlot.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to update slot";
|
state.error = action.payload || "Failed to update slot";
|
||||||
|
@ -175,11 +179,16 @@ const slotSlice = createSlice({
|
||||||
.addCase(deleteSlot.pending, (state) => {
|
.addCase(deleteSlot.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
.addCase(deleteSlot.fulfilled, (state, action: PayloadAction<number>) => {
|
.addCase(
|
||||||
|
deleteSlot.fulfilled,
|
||||||
|
(state, action: PayloadAction<number>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
// Remove the deleted slot from the state
|
// Remove the deleted slot from the state
|
||||||
state.slots = state.slots.filter((slot) => slot.id !== action.payload);
|
state.slots = state.slots.filter(
|
||||||
})
|
(slot) => slot.id !== action.payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
.addCase(deleteSlot.rejected, (state, action) => {
|
.addCase(deleteSlot.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to delete slot";
|
state.error = action.payload || "Failed to delete slot";
|
||||||
|
|
|
@ -20,7 +20,8 @@ const RoleList = lazy(() => import("./pages/RoleList"));
|
||||||
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
const ManagerList = lazy(() => import("./pages/ManagerList"));
|
||||||
const StationList = lazy(() => import("./pages/StationList"));
|
const StationList = lazy(() => import("./pages/StationList"));
|
||||||
const EVSlotManagement = lazy(() => import("./pages/EVSlotManagement"));
|
const EVSlotManagement = lazy(() => import("./pages/EVSlotManagement"));
|
||||||
const EvSlotList = lazy(() => import("./pages/EvSlotList"));
|
const BookingList = lazy(() => import("./pages/BookingList"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
|
@ -100,9 +101,9 @@ export default function AppRouter() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="slot-list"
|
path="booking-list"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute component={<EvSlotList />} />
|
<ProtectedRoute component={<BookingList />} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
Loading…
Reference in a new issue