made changes in Charging Station and slots booking modals

This commit is contained in:
jaanvi 2025-03-27 18:39:38 +05:30
parent e96759ef3b
commit 0f33303a42
20 changed files with 633 additions and 408 deletions

View file

@ -26,7 +26,7 @@ import {
CustomIconButton, CustomIconButton,
CustomTextField, CustomTextField,
} from "../AddEditUserModel/styled.css.tsx"; } from "../AddEditUserModel/styled.css.tsx";
import { stationList } from "../../redux/slices/stationSlice.ts"; import { getAllStations, stationList } from "../../redux/slices/stationSlice.ts";
import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts"; import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -56,7 +56,7 @@ export default function AddBookingModal({
console.log("first", availableSlots); console.log("first", availableSlots);
useEffect(() => { useEffect(() => {
dispatch(fetchAvailableSlots()); dispatch(fetchAvailableSlots());
dispatch(stationList()); dispatch(getAllStations());
}, [dispatch]); }, [dispatch]);
useEffect(() => { useEffect(() => {
@ -74,7 +74,7 @@ export default function AddBookingModal({
// Fetch the bookings after this // Fetch the bookings after this
dispatch(bookingList()); dispatch(bookingList());
}, [dispatch]); }, [dispatch]);
console.log("Car Ports: ", carPorts);
// Get today's date in yyyy-mm-dd format // Get today's date in yyyy-mm-dd format
const today = new Date().toISOString().split("T")[0]; const today = new Date().toISOString().split("T")[0];
@ -266,9 +266,9 @@ export default function AddBookingModal({
{carPorts.map((port, index) => ( {carPorts.map((port, index) => (
<MenuItem <MenuItem
key={index} key={index}
value={port.chargeType} value={port}
> >
{port.chargeType} {port}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
@ -290,7 +290,6 @@ export default function AddBookingModal({
{/* Start Time and End Time */} {/* Start Time and End Time */}
<Box sx={{ display: "flex", gap: 2, mb: 2 }}> <Box sx={{ display: "flex", gap: 2, mb: 2 }}>
<Box sx={{ flex: 1 }}> <Box sx={{ flex: 1 }}>
<Typography variant="body2" fontWeight={500}> <Typography variant="body2" fontWeight={500}>
Start Time Start Time
@ -311,7 +310,6 @@ export default function AddBookingModal({
/> />
</Box> </Box>
<Box sx={{ flex: 1 }}> <Box sx={{ flex: 1 }}>
<Typography variant="body2" fontWeight={500}> <Typography variant="body2" fontWeight={500}>
End Time End Time

View file

@ -172,14 +172,21 @@ useEffect(() => {
defaultValue="" defaultValue=""
size="small" size="small"
> >
{stations.map((station) => ( {Array.isArray(stations) &&
<MenuItem stations.length > 0 ? (
key={station.id} stations.map((station) => (
value={station.name} <MenuItem
> key={station.id}
{station.name} value={station.name}
>
{station.name}
</MenuItem>
))
) : (
<MenuItem disabled>
No stations available
</MenuItem> </MenuItem>
))} )}
</Select> </Select>
{errors.stationName && ( {errors.stationName && (
<Typography color="error" variant="body2"> <Typography color="error" variant="body2">

View file

@ -46,58 +46,59 @@ export default function AddStationModal({
const vehicleBrands = useSelector( const vehicleBrands = useSelector(
(state: RootState) => state.vehicleReducer.vehicleBrands (state: RootState) => state.vehicleReducer.vehicleBrands
); );
const [selectedBrand, setSelectedBrand] = useState<string | "">("");
useEffect(() => {
dispatch(fetchVehicleBrands());
dispatch(vehicleList()); // Fetch vehicles when the component mounts
}, [dispatch]);
const filteredVehicles = vehicles.filter(
(vehicle) => vehicle.company === selectedBrand
);
// State for selected vehicle brand and vehicles
const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]); const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
// Handle the change in selected vehicles (checkboxes) // Fetch vehicle brands and vehicle list when the component mounts
const handleCheckboxChange = ( useEffect(() => {
dispatch(fetchVehicleBrands());
dispatch(vehicleList());
}, [dispatch]);
// Filter vehicles based on selected vehicle brands
const filteredVehicles = vehicles.filter((vehicle) =>
selectedBrands.includes(vehicle.company)
);
// Handle changes in selected vehicle brands (checkboxes)
const handleBrandChange = (
event: React.ChangeEvent<{ value: unknown }> event: React.ChangeEvent<{ value: unknown }>
) => { ) => {
const value = event.target.value as string[]; const value = event.target.value as string[];
setSelectedVehicles(value); setSelectedBrands(value); // Update the selected vehicle brands
};
// Handle changes in selected vehicles (checkboxes)
const handleVehicleChange = (
event: React.ChangeEvent<{ value: unknown }>
) => {
setSelectedVehicles(event.target.value as string[]);
}; };
// Function to map selected vehicle names to corresponding vehicle ids // Function to map selected vehicle names to corresponding vehicle ids
const getVehicleIds = () => { const getVehicleIds = () => {
console.log("Selected Vehicles: ", selectedVehicles);
console.log("Vehicles List: ", vehicles);
return vehicles return vehicles
.filter((vehicle) => selectedVehicles.includes(vehicle.name)) .filter((vehicle) => selectedVehicles.includes(vehicle.name))
.map((vehicle) => vehicle.id); .map((vehicle) => vehicle.id);
}; };
const onSubmit = (data: any) => { const onSubmit = (data: any) => {
// Log the data before sending to backend
console.log("Payload being sent to hfgghfhbackend:", data);
const vehicleIds = getVehicleIds(); // Get the ids of the selected vehicles const vehicleIds = getVehicleIds(); // Get the ids of the selected vehicles
console.log("Vehicle IDs: ", vehicleIds);
// Prepare the data to be sent to the backend
const payload = { const payload = {
...data, ...data,
status: 1, // Default status, can be adjusted if needed status: 1, // Default status
allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend
totalSlots: Number(data.totalSlots), // Ensure this is a number totalSlots: Number(data.totalSlots), // Ensure this is a number
}; };
console.log("Payload to backend:", payload); // Log the final payload
// Handle adding the station with the constructed payload
handleAddStation(payload); handleAddStation(payload);
handleClose(); // Close modal after adding handleClose(); // Close modal after adding
reset(); reset();
setSelectedVehicles([]); setSelectedVehicles([]);
setSelectedBrand(""); // Reset selected vehicles after submission setSelectedBrands([]); // Reset selected brands after submission
}; };
return ( return (
@ -251,7 +252,7 @@ export default function AddStationModal({
</Box> </Box>
</Box> </Box>
{/* Vehicle Name Dropdown with Checkboxes */} {/* Vehicle Brand Dropdown with Checkboxes */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -261,26 +262,33 @@ export default function AddStationModal({
}} }}
> >
<Typography variant="body2" fontWeight={500}> <Typography variant="body2" fontWeight={500}>
Select Vehicle Brand Select Vehicle Brands
</Typography> </Typography>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel>Choose Brand</InputLabel> <InputLabel>Choose Brands</InputLabel>
<Select <Select
value={selectedBrand} multiple
onChange={(e) => value={selectedBrands}
setSelectedBrand(e.target.value) onChange={handleBrandChange}
renderValue={(selected) =>
(selected as string[]).join(", ")
} }
label="Choose Brand" label="Choose Brands"
> >
{Array.isArray(vehicleBrands) && {vehicleBrands.length > 0 ? (
vehicleBrands.length > 0 ? (
vehicleBrands.map((brand) => ( vehicleBrands.map((brand) => (
<MenuItem <MenuItem
key={brand.id} key={brand.id}
value={brand.id} value={brand.id}
> >
{brand.name}{" "} <Checkbox
{/* Make sure 'brand.id' is set as value */} checked={selectedBrands.includes(
brand.id
)}
/>
<ListItemText
primary={brand.name}
/>
</MenuItem> </MenuItem>
)) ))
) : ( ) : (
@ -296,8 +304,9 @@ export default function AddStationModal({
</FormHelperText> </FormHelperText>
</FormControl> </FormControl>
</Box> </Box>
{/* Vehicle Name Dropdown with Checkboxes */}
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -314,7 +323,7 @@ export default function AddStationModal({
<Select <Select
multiple multiple
value={selectedVehicles} value={selectedVehicles}
onChange={handleCheckboxChange} onChange={handleVehicleChange}
renderValue={(selected) => renderValue={(selected) =>
(selected as string[]).join(", ") (selected as string[]).join(", ")
} }
@ -337,8 +346,8 @@ export default function AddStationModal({
)) ))
) : ( ) : (
<MenuItem disabled> <MenuItem disabled>
No vehicles available for this No vehicles available for the
brand selected brands
</MenuItem> </MenuItem>
)} )}
</Select> </Select>

View file

@ -83,6 +83,36 @@ export default function AddVehicleModal({
> >
{/* First Row - Two Inputs */} {/* First Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Company Name
</Typography>
<CustomTextField
fullWidth
placeholder="Enter Company Name"
size="small"
error={!!errors.company}
helperText={
errors.company
? errors.company.message
: ""
}
{...register("company", {
required: "Company is required",
minLength: {
value: 3,
message:
"Company must be at least 5 characters long",
},
})}
/>
</Box>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -121,37 +151,6 @@ export default function AddVehicleModal({
})} })}
/> />
</Box> </Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Company
</Typography>
<CustomTextField
fullWidth
placeholder="Enter Company Name"
size="small"
error={!!errors.company}
helperText={
errors.company
? errors.company.message
: ""
}
{...register("company", {
required: "Company is required",
minLength: {
value: 3,
message:
"Company must be at least 5 characters long",
},
})}
/>
</Box>
</Box> </Box>
{/* Second Row - Two Inputs */} {/* Second Row - Two Inputs */}

View file

@ -40,7 +40,7 @@ import {
deleteSlot, deleteSlot,
fetchAvailableSlots, fetchAvailableSlots,
} from "../../redux/slices/slotSlice.ts"; } from "../../redux/slices/slotSlice.ts";
import { bookingList } from "../../redux/slices/bookSlice.ts"; import { bookingList, deleteBooking } 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}`]: {
@ -155,6 +155,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
case "slots": case "slots":
dispatch(deleteSlot(id || "")); dispatch(deleteSlot(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;
@ -483,175 +486,174 @@ const CustomTable: React.FC<CustomTableProps> = ({
/> />
</Box> </Box>
{/* Menu Actions */} {/* Menu Actions */}
{open && {open && (
( <Menu
<Menu anchorEl={anchorEl}
anchorEl={anchorEl} id="menu"
id="menu" open={open}
open={open} onClose={handleClose}
onClose={handleClose} onClick={handleClose}
onClick={handleClose} transformOrigin={{
transformOrigin={{ horizontal: "right",
horizontal: "right", vertical: "top",
vertical: "top", }}
}} anchorOrigin={{
anchorOrigin={{ horizontal: "right",
horizontal: "right", vertical: "bottom",
vertical: "bottom", }}
sx={{
[`& .${paperClasses.root}`]: {
padding: 0,
},
"& .MuiList-root": {
background: "#272727", // Remove any divider under menu items
},
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
// setSelectedRow(row);
setViewModal(true);
}} }}
color="primary"
sx={{ sx={{
[`& .${paperClasses.root}`]: { justifyContent: "flex-start",
padding: 0, py: 0,
}, fontWeight: "bold",
"& .MuiList-root": { color: "#52ACDF",
background: "#272727", // Remove any divider under menu items
},
}} }}
> >
<Box View
</Button>
{viewModal && tableType === "admin" && (
<ViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "vehicle" && (
<VehicleViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "manager" && (
<ManagerViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "user" && (
<UserViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "station" && (
<StationViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
<Button
variant="text"
onClick={() => setModalOpen(true)}
color="primary"
sx={{
justifyContent: "flex-start",
py: 0,
textTransform: "capitalize",
}}
>
Edit
</Button>
{tableType === "role" && (
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
handleToggleStatus();
}}
color="secondary"
sx={{ sx={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start", justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}} }}
> >
<Button {selectedRow?.statusValue === 1
variant="text" ? "Deactivate"
onClick={(e) => { : "Activate"}
e.stopPropagation(); </Button>
// setSelectedRow(row); )}
setViewModal(true); {tableType === "station" && (
}} <Button
color="primary" variant="text"
sx={{ onClick={(e) => {
justifyContent: "flex-start", e.stopPropagation();
py: 0, handleToggleStatus();
fontWeight: "bold", }}
color: "#52ACDF", color="secondary"
}} sx={{
> justifyContent: "flex-start",
View py: 0,
</Button> fontWeight: "bold",
{viewModal && tableType === "admin" && ( }}
<ViewModal >
handleView={() => {selectedRow?.statusValue === 1
handleViewButton(selectedRow?.id) ? "Not Available"
} : "Available"}
open={viewModal} </Button>
setViewModal={setViewModal} )}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "vehicle" && (
<VehicleViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "manager" && (
<ManagerViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "user" && (
<UserViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "station" && (
<StationViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
<Button <Button
variant="text" variant="text"
onClick={() => setModalOpen(true)} onClick={(e) => {
color="primary" e.stopPropagation();
sx={{ setDeleteModal(true);
justifyContent: "flex-start", }}
py: 0, color="error"
textTransform: "capitalize", sx={{
}} justifyContent: "flex-start",
> py: 0,
Edit fontWeight: "bold",
</Button> }}
{tableType === "role" && ( >
<Button Delete
variant="text" </Button>
onClick={(e) => { </Box>
e.stopPropagation(); </Menu>
handleToggleStatus(); )}
}}
color="secondary"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
{selectedRow?.statusValue === 1
? "Deactivate"
: "Activate"}
</Button>
)}
{tableType === "station" && (
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
handleToggleStatus();
}}
color="secondary"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
{selectedRow?.statusValue === 1
? "Not Available"
: "Available"}
</Button>
)}
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
setDeleteModal(true);
}}
color="error"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
Delete
</Button>
</Box>
</Menu>
)}
{/* Modals */} {/* Modals */}
{deleteModal && ( {deleteModal && (
<DeleteModal <DeleteModal

View file

@ -74,9 +74,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
email: data.email, email: data.email,
phone: data.phone, phone: data.phone,
stationId: data.stationId, stationId: data.stationId,
Manager: undefined,
id: 0,
chargingStation: ""
}, },
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).unwrap(); // Ensure that it throws an error if the update fails

View file

@ -52,7 +52,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isAvailable, setIsAvailable] = useState<boolean>( const [isAvailable, setIsAvailable] = useState<boolean>(
editRow?.isAvailable || false editRow?.isAvailable || false
); // Default to editRow availability );
// Set values if editRow is provided // Set values if editRow is provided
useEffect(() => { useEffect(() => {
@ -68,6 +68,8 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const onSubmit = async (data: FormData) => { const onSubmit = async (data: FormData) => {
if (editRow) { if (editRow) {
setLoading(true); // Start loading
try { try {
// Convert boolean availability to 1/0 // Convert boolean availability to 1/0
const availabilityStatus = isAvailable ? true : false; const availabilityStatus = isAvailable ? true : false;
@ -83,12 +85,14 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
}, },
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).unwrap(); // Ensure that it throws an error if the update fails
dispatch(fetchAvailableSlots()); // Assuming this action fetches the updated slot list
// Refresh the list after updating the slot
dispatch(fetchAvailableSlots());
handleClose(); // Close modal on success handleClose(); // Close modal on success
reset(); // Reset form fields after submit reset(); // Reset form fields after submit
} catch (error) { } catch (error) {
console.error(error); console.error("Error updating slot:", error);
// Handle the error or show a toast // Handle the error or show a toast message
} finally { } finally {
setLoading(false); // Stop loading state setLoading(false); // Stop loading state
} }

View file

@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { import {
Box, Box,
Button, Button,
@ -10,21 +10,27 @@ import {
MenuItem, MenuItem,
Checkbox, Checkbox,
ListItemText, ListItemText,
FormHelperText,
} from "@mui/material"; } from "@mui/material";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "../../redux/reducers.ts";
import {
fetchVehicleBrands,
vehicleList,
} from "../../redux/slices/VehicleSlice";
import { import {
CustomIconButton, CustomIconButton,
CustomTextField, CustomTextField,
} from "../AddEditUserModel/styled.css.tsx"; } from "../AddEditUserModel/styled.css.tsx"; // Assuming custom styled components
// Define the types for your form data
interface FormData { interface FormData {
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: number; totalSlots: number;
allowedCarIds: string[];
status: number; status: number;
allowedCarIds: string[]; // Assuming allowedCarIds are the vehicle IDs
} }
interface EditStationModalProps { interface EditStationModalProps {
@ -35,10 +41,9 @@ interface EditStationModalProps {
name: string, name: string,
registeredAddress: string, registeredAddress: string,
totalSlots: number, totalSlots: number,
allowedCarIds: string[] allowedCarIds: number[]
) => void; ) => void;
editRow?: any; editRow?: any;
vehicles: { id: string; name: string }[];
} }
const EditStationModal: React.FC<EditStationModalProps> = ({ const EditStationModal: React.FC<EditStationModalProps> = ({
@ -46,54 +51,82 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
handleClose, handleClose,
handleUpdate, handleUpdate,
editRow, editRow,
vehicles,
}) => { }) => {
const { control, handleSubmit, setValue, reset, watch } = useForm<FormData>( const {
{ control,
defaultValues: { handleSubmit,
name: "", setValue,
registeredAddress: "", reset,
totalSlots: 0, watch,
status: 1, formState: { errors },
allowedCarIds: [], } = useForm<FormData>({
}, defaultValues: {
} name: "",
registeredAddress: "",
totalSlots: 0,
status: 1,
allowedCarIds: [],
},
});
const dispatch = useDispatch();
const vehicles = useSelector(
(state: RootState) => state.vehicleReducer.vehicles
);
const vehicleBrands = useSelector(
(state: RootState) => state.vehicleReducer.vehicleBrands
); );
// Watch allowedCarIds from the form state const [selectedBrand, setSelectedBrand] = useState<string | "">("");
const allowedCarIds = watch("allowedCarIds"); const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
useEffect(() => {
dispatch(fetchVehicleBrands());
dispatch(vehicleList()); // Fetch vehicles when the component mounts
}, [dispatch]);
// Set values when editRow is provided
useEffect(() => { useEffect(() => {
if (editRow) { if (editRow) {
setValue("name", editRow.name); setValue("name", editRow.name);
setValue("registeredAddress", editRow.registeredAddress); setValue("registeredAddress", editRow.registeredAddress);
setValue("totalSlots", editRow.totalSlots); setValue("totalSlots", editRow.totalSlots);
setValue("status", editRow.status); setValue("status", editRow.status);
setValue("allowedCarIds", editRow.allowedCarIds || []);
// Set the allowedCarIds with the previously selected vehicle IDs setSelectedVehicles(editRow.allowedCarIds || []);
setValue(
"allowedCarIds",
editRow.allowedCarIds?.map((car: any) => car.id) || []
);
} else { } else {
reset(); reset();
} }
}, [editRow, setValue, reset]); }, [editRow, setValue, reset]);
// Handle form submit
const filteredVehicles = vehicles.filter(
(vehicle) => vehicle.company === selectedBrand
);
const handleCheckboxChange = (
event: React.ChangeEvent<{ value: unknown }>
) => {
const value = event.target.value as string[];
setSelectedVehicles(value);
setValue("allowedCarIds", value); // Update allowedCarIds in form state
};
const onSubmit = (data: FormData) => { const onSubmit = (data: FormData) => {
if (editRow) { const vehicleIds = vehicles
handleUpdate( .filter((vehicle) => selectedVehicles.includes(vehicle.name))
editRow.id, .map((vehicle) => vehicle.id);
data.name,
data.registeredAddress, handleUpdate(
data.totalSlots, editRow.id,
data.allowedCarIds data.name,
); data.registeredAddress,
} data.totalSlots,
handleClose(); // Close the modal after update vehicleIds
reset(); // Reset the form fields );
handleClose();
reset();
setSelectedBrand(""); // Reset brand after submit
setSelectedVehicles([]); // Reset selected vehicles
}; };
return ( return (
@ -103,7 +136,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
if (reason === "backdropClick") { if (reason === "backdropClick") {
return; return;
} }
handleClose(); // Close modal when clicking cross or cancel handleClose();
}} }}
aria-labelledby="edit-station-modal" aria-labelledby="edit-station-modal"
> >
@ -141,9 +174,9 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
{/* Horizontal Line */} {/* Horizontal Line */}
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} /> <Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
{/* Input Fields */} {/* Form */}
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}> <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* First Row - Two Inputs */} {/* Station Name and Address */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -168,11 +201,16 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth fullWidth
placeholder="Enter Station Name" placeholder="Enter Station Name"
size="small" size="small"
error={!!errors.name}
helperText={
errors.name
? errors.name.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -196,13 +234,20 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth fullWidth
placeholder="Enter Registered Address" placeholder="Enter Registered Address"
size="small" size="small"
error={!!errors.registeredAddress}
helperText={
errors.registeredAddress
? errors.registeredAddress
.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
</Box> </Box>
{/* Second Row - Total Slots */} {/* Total Slots */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -228,61 +273,107 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
placeholder="Enter Total Slots" placeholder="Enter Total Slots"
size="small" size="small"
type="number" type="number"
error={!!errors.totalSlots}
helperText={
errors.totalSlots
? errors.totalSlots.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
</Box> </Box>
{/* Vehicle Dropdown with Checkboxes */} {/* Vehicle Brand Selection */}
<Box <Box sx={{ display: "flex", gap: 2 }}>
sx={{ <Box
display: "flex", sx={{
flexDirection: "column", display: "flex",
width: "100%", flexDirection: "column",
}} width: "100%",
> }}
<Typography variant="body2" fontWeight={500}> >
Vehicle Names <Typography variant="body2" fontWeight={500}>
</Typography> Select Vehicle Brand
<FormControl fullWidth> </Typography>
<InputLabel>Choose Vehicles</InputLabel> <FormControl fullWidth>
<Select <InputLabel>Choose Brand</InputLabel>
multiple <Select
value={allowedCarIds || []} // Watch the allowedCarIds to reflect selected values value={selectedBrand}
onChange={(e) => { onChange={(e) =>
// Update the allowedCarIds when the selection changes setSelectedBrand(e.target.value)
setValue( }
"allowedCarIds", label="Choose Brand"
e.target.value as string[] >
); // Update allowedCarIds on change {vehicleBrands.length > 0 ? (
}} vehicleBrands.map((brand) => (
renderValue={(selected) => { <MenuItem
return (selected as string[]) key={brand.id}
.map((id) => { value={brand.id}
const vehicle = vehicles?.find( >
(vehicle) => vehicle.id === id {brand.name}
); </MenuItem>
return vehicle ? vehicle.name : ""; ))
}) ) : (
.join(", "); <MenuItem disabled>
}} No vehicle brands available
> </MenuItem>
{vehicles?.map((vehicle) => ( )}
<MenuItem </Select>
key={vehicle.id} </FormControl>
value={vehicle.id} </Box>
>
<Checkbox {/* Vehicle Selection with Checkboxes */}
checked={allowedCarIds?.includes( <Box
vehicle.id sx={{
)} display: "flex",
/> flexDirection: "column",
<ListItemText primary={vehicle.name} /> width: "100%",
</MenuItem> }}
))} >
</Select> <Typography variant="body2" fontWeight={500}>
</FormControl> Vehicle Name
</Typography>
<FormControl fullWidth>
<InputLabel>Choose Vehicles</InputLabel>
<Select
multiple
value={selectedVehicles}
onChange={handleCheckboxChange}
renderValue={(selected) =>
(selected as string[]).join(", ")
}
>
{filteredVehicles.length > 0 ? (
filteredVehicles.map((vehicle) => (
<MenuItem
key={vehicle.id}
value={vehicle.name}
>
<Checkbox
checked={selectedVehicles.includes(
vehicle.name
)}
/>
<ListItemText
primary={vehicle.name}
/>
</MenuItem>
))
) : (
<MenuItem disabled>
No vehicles available for this brand
</MenuItem>
)}
</Select>
<FormHelperText>
{errors.allowedCarIds
? errors.allowedCarIds.message
: ""}
</FormHelperText>
</FormControl>
</Box>
</Box> </Box>
</Box> </Box>
@ -300,7 +391,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
"&:hover": { backgroundColor: "#439BC1" }, "&:hover": { backgroundColor: "#439BC1" },
}} }}
> >
Update Charging Station Update Station
</Button> </Button>
</Box> </Box>
</Box> </Box>

View file

@ -15,22 +15,21 @@ import { AppDispatch, RootState } from "../../redux/store/store";
import { fetchAdminProfile } from "../../redux/slices/profileSlice"; import { fetchAdminProfile } from "../../redux/slices/profileSlice";
import OptionsMenu from "../OptionsMenu"; import OptionsMenu from "../OptionsMenu";
import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone";
import ColorModeIconDropdown from "../../shared-theme/ColorModeIconDropdown";
export default function Header() { export default function Header() {
const [showNotifications, setShowNotifications] = React.useState(false); const [showNotifications, setShowNotifications] = React.useState(false);
const toggleNotifications = () => { const toggleNotifications = () => {
setShowNotifications((prev) => !prev); setShowNotifications((prev) => !prev);
}; };
const [open, setOpen] = React.useState(true); const [open, setOpen] = React.useState(true);
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { user } = useSelector( const { user } = useSelector((state: RootState) => state?.profileReducer);
(state: RootState) => state?.profileReducer
);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchAdminProfile()); dispatch(fetchAdminProfile());
}, [dispatch]); }, [dispatch]);
return ( return (
<Box <Box
sx={{ sx={{
@ -89,7 +88,6 @@ export default function Header() {
display: { xs: "none", sm: "flex" }, // Hide on mobile, show on larger screens display: { xs: "none", sm: "flex" }, // Hide on mobile, show on larger screens
}} }}
> >
<NotificationsNoneIcon onClick={toggleNotifications} /> <NotificationsNoneIcon onClick={toggleNotifications} />
<Avatar <Avatar
alt="User Avatar" alt="User Avatar"

View file

@ -66,9 +66,9 @@ export default function MenuContent({ hidden }: PropType) {
url: "/panel/vehicle-list", // Placeholder for now url: "/panel/vehicle-list", // Placeholder for now
}, },
// userRole === "manager" && { // userRole === "manager" && {
// text: "Add Slots", // text: "Add Slots",
// icon: <ManageAccountsOutlinedIcon />, // icon: <ManageAccountsOutlinedIcon />,
// url: "/panel/EVslots", // Placeholder for now // url: "/panel/EVslots", // Placeholder for now
// }, // },
userRole === "user" && { userRole === "user" && {
text: "Bookings", text: "Bookings",
@ -103,13 +103,13 @@ export default function MenuContent({ hidden }: PropType) {
<ListItem <ListItem
key={index} key={index}
disablePadding disablePadding
sx={{ display: "block", py: 1 }} sx={{ display: "block", py: 1, px: 0.9 }}
> >
<ListItemButton <ListItemButton
component={Link} component={Link}
to={item.url} to={item.url}
selected={item.url === location.pathname} selected={item.url === location.pathname}
sx={{ alignItems: "center", columnGap: 1 }} sx={{ alignItems: "center", columnGap: 0.5 }}
> >
<ListItemIcon <ListItemIcon
sx={{ sx={{

View file

@ -32,7 +32,7 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
(state: RootState) => state.managerReducer (state: RootState) => state.managerReducer
); );
const [selectedManager, setSelectedManager] = useState<any>(null); const [selectedManager, setSelectedManager] = useState<any>(null);
useEffect(() => { useEffect(() => {
if (id) { if (id) {
@ -110,7 +110,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
<Typography variant="body1"> <Typography variant="body1">
<strong>Station Location:</strong> <strong>Station Location:</strong>
<Typography variant="body2"> <Typography variant="body2">
{selectedManager.registeredAddress} {selectedManager.chargingStation
?.registeredAddress || "Not Available"}
</Typography> </Typography>
</Typography> </Typography>
</Grid> </Grid>
@ -118,7 +119,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
<Typography variant="body1"> <Typography variant="body1">
<strong>Station Name:</strong> <strong>Station Name:</strong>
<Typography variant="body2"> <Typography variant="body2">
{selectedManager.stationName} {selectedManager.chargingStation?.name ||
"Not Available"}
</Typography> </Typography>
</Typography> </Typography>
</Grid> </Grid>

View file

@ -50,12 +50,8 @@ const DashboardLayout: React.FC<LayoutProps> = ({ customStyles }) => {
...customStyles, ...customStyles,
mt: { xs: 8, md: 0 }, mt: { xs: 8, md: 0 },
padding: 0, padding: 0,
})} })}
> >
<Stack <Stack
spacing={2} spacing={2}
sx={{ sx={{

View file

@ -1,27 +1,37 @@
import axios from "axios"; import axios from "axios";
// import { useHistory } from "react-router-dom";
const http = axios.create({ const http = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL, baseURL: process.env.REACT_APP_BACKEND_URL,
}); });
http.interceptors.request.use((config) => { http.interceptors.request.use((config) => {
const authToken = localStorage.getItem("authToken"); const authToken = localStorage.getItem("authToken");
if (authToken) { if (authToken) {
config.headers.Authorization = `Bearer ${authToken}`; config.headers.Authorization = `Bearer ${authToken}`;
} }
return config; return config;
}); });
http.interceptors.response.use( http.interceptors.response.use(
(response) => response, (response) => response,
(error) => { (error) => {
if (error.response) {
if (error.response && error.response.status === 403 ) { const status = error.response.status;
window.location.href = "/login"; const requestUrl = error.config.url; // Get the API route
// const history = useHistory(); // Handle token expiration (401) but NOT for login failures
// history.push("/login"); if (status === 401 && !requestUrl.includes("/login")) {
localStorage.removeItem("authToken");
window.location.href = "/login";
}
// Handle forbidden access
if (status === 403) {
localStorage.removeItem("authToken");
window.location.href = "/login";
}
} }
return Promise.reject(error); return Promise.reject(error);
} }
); );

View file

@ -56,10 +56,10 @@ export default function EVSlotList() {
console.error("Error adding slot", error); console.error("Error adding slot", error);
} }
}; };
console.log("isAvailable type:", typeof isAvailable);
const handleUpdate = async ( const handleUpdate = async (
id: string, id: string,
date: string,
startTime: string, startTime: string,
endTime: string, endTime: string,
isAvailable: boolean isAvailable: boolean
@ -69,7 +69,7 @@ export default function EVSlotList() {
await dispatch( await dispatch(
updateSlot({ updateSlot({
id, id,
date,
startTime, startTime,
endTime, endTime,
isAvailable, isAvailable,

View file

@ -47,7 +47,8 @@ export default function StationList() {
const handleAddStation = async (data: { const handleAddStation = async (data: {
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: string; totalSlots: number;
allowedCarIds: number[];
}) => { }) => {
try { try {
await dispatch(createStation(data)); // Dispatch action to add Station await dispatch(createStation(data)); // Dispatch action to add Station
@ -62,7 +63,7 @@ export default function StationList() {
id: string, id: string,
name: string, name: string,
registeredAddress: string, registeredAddress: string,
totalSlots: string, totalSlots: number,
allowedCarIds: number[] allowedCarIds: number[]
) => { ) => {
try { try {
@ -88,9 +89,14 @@ export default function StationList() {
await dispatch(toggleStatus({ id, status: newStatus })); await dispatch(toggleStatus({ id, status: newStatus }));
}; };
const filterStations = stations?.filter((station) => const filterStations = Array.isArray(stations)
station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase()) ? stations.filter((station) =>
); station.name
.toLocaleLowerCase()
.includes(searchTerm.toLowerCase())
)
: [];
// Mapping and formatting vehicles // Mapping and formatting vehicles
const categoryRows = filterStations?.length const categoryRows = filterStations?.length
@ -122,6 +128,7 @@ export default function StationList() {
}; };
}) })
: []; : [];
console.log("Rowssss",categoryRows)
const categoryColumns: Column[] = [ const categoryColumns: Column[] = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },

View file

@ -2,6 +2,7 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https"; import http from "../../lib/https";
import { toast } from "sonner"; import { toast } from "sonner";
interface VehicleBrand { interface VehicleBrand {
name: string;
id: string; id: string;
company: string; company: string;
} }
@ -34,19 +35,23 @@ export const fetchVehicleBrands = createAsyncThunk<
>("vehicle/fetchVehicleBrands", async (_, { rejectWithValue }) => { >("vehicle/fetchVehicleBrands", async (_, { rejectWithValue }) => {
try { try {
const response = await http.get("/get-vehicle-brand"); const response = await http.get("/get-vehicle-brand");
if (!response.data || !Array.isArray(response.data.data)) { if (!response.data || !Array.isArray(response.data.data)) {
throw new Error("Expected array of vehicle brands"); throw new Error("Expected array of vehicle brands");
} }
// Assuming that 'data' contains an array of objects with a 'company' field
return response.data.data.map((item: any) => ({ // Map the brand names (strings) to objects with 'id' and 'name' properties
id: item.company, // You can use 'company' as the unique identifier return response.data.data.map((brand: string) => ({
name: item.company, // The name field will be used in the dropdown id: brand, // Use brand name as the ID
name: brand, // Use brand name as the label
})); }));
} catch (error: any) { } catch (error: any) {
return rejectWithValue("Failed to fetch vehicle brands"); return rejectWithValue("Failed to fetch vehicle brands");
} }
}); });
export const vehicleList = createAsyncThunk< export const vehicleList = createAsyncThunk<
Vehicle, Vehicle,
void, void,
@ -158,6 +163,7 @@ const vehicleSlice = createSlice({
state.vehicleBrands = action.payload; state.vehicleBrands = action.payload;
} }
) )
.addCase(fetchVehicleBrands.rejected, (state, action) => { .addCase(fetchVehicleBrands.rejected, (state, action) => {
state.loading = false; state.loading = false;
state.error = state.error =

View file

@ -60,6 +60,7 @@ export const getCarPorts = createAsyncThunk<
>("fetchCarPorts", async (_, { rejectWithValue }) => { >("fetchCarPorts", async (_, { rejectWithValue }) => {
try { try {
const response = await http.get("/get-vehicle-port-dropdown"); const response = await http.get("/get-vehicle-port-dropdown");
return response.data.data; // Adjust based on actual API response return response.data.data; // Adjust based on actual API response
} catch (error: any) { } catch (error: any) {
return rejectWithValue( return rejectWithValue(
@ -119,7 +120,22 @@ export const addBooking = createAsyncThunk<
); );
} }
}); });
export const deleteBooking = createAsyncThunk<
string, // Return type (id of deleted slot)
string,
{ rejectValue: string }
>("booking/deleteBooking", async (id, { rejectWithValue }) => {
try {
const response = await http.delete(`/delete-booking/${id}`);
toast.success("Slot deleted successfully");
return id; // Return the id of the deleted slot
} catch (error: any) {
toast.error("Error deleting the slot: " + error?.message);
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
});
const bookSlice = createSlice({ const bookSlice = createSlice({
name: "booking", name: "booking",
initialState, initialState,
@ -167,7 +183,32 @@ const bookSlice = createSlice({
(state, action: PayloadAction<string[]>) => { (state, action: PayloadAction<string[]>) => {
state.carPorts = action.payload; state.carPorts = action.payload;
} }
); )
.addCase(deleteBooking.pending, (state) => {
state.loading = true;
})
.addCase(
deleteBooking.fulfilled,
(state, action: PayloadAction<string>) => {
state.loading = false;
// Ensure we're filtering the correct array (bookings)
state.bookings = state.bookings.filter(
(booking) =>
String(booking.id) !== String(action.payload)
);
// Also update slots array if it exists
if (state.bookings) {
state.bookings = state.bookings.filter(
(booking) =>
String(booking.id) !== String(action.payload)
);
}
}
)
.addCase(deleteBooking.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to delete slot";
});
}, },
}); });

View file

@ -175,6 +175,9 @@ const managerSlice = createSlice({
}) })
.addCase(deleteManager.fulfilled, (state, action) => { .addCase(deleteManager.fulfilled, (state, action) => {
state.loading = false; state.loading = false;
state.managers = state.managers.filter(
(manager) => String(manager.id) !== String(action.payload)
);
}) })
.addCase(deleteManager.rejected, (state, action) => { .addCase(deleteManager.rejected, (state, action) => {
state.loading = false; state.loading = false;

View file

@ -5,7 +5,7 @@ import { toast } from "sonner";
// Define TypeScript types // Define TypeScript types
interface Slot { interface Slot {
id: string; id: string;
stationId:string; stationId: string;
date: string; date: string;
startTime: string; startTime: string;
endTime: string; endTime: string;
@ -88,7 +88,7 @@ export const createSlot = createAsyncThunk<
// Update Slot details // Update Slot details
export const updateSlot = createAsyncThunk< export const updateSlot = createAsyncThunk<
Slot, Slot,
{ id: number; startTime: string; endTime: string }, // Argument type (slot update data) { id: string; 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 }) => {
try { try {
@ -97,6 +97,7 @@ export const updateSlot = createAsyncThunk<
slotData slotData
); );
toast.success("Slot updated successfully"); toast.success("Slot updated successfully");
console.log("Slots",response.data.data)
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);
@ -104,7 +105,7 @@ export const updateSlot = createAsyncThunk<
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
} }
}); });
export const deleteSlot = createAsyncThunk< export const deleteSlot = createAsyncThunk<
string, // Return type (id of deleted slot) string, // Return type (id of deleted slot)
@ -167,11 +168,11 @@ const slotSlice = createSlice({
(state, action: PayloadAction<Slot>) => { (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( const index = state.availableSlots.findIndex(
(slot) => slot.id === action.payload.id (slot) => slot.id === action.payload.id
); );
if (index !== -1) { if (index !== -1) {
state.slots[index] = action.payload; state.availableSlots[index] = action.payload;
} }
} }
) )

View file

@ -8,7 +8,7 @@ interface Station {
id: string; id: string;
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: string; totalSlots: number;
status: number; status: number;
allowedCarIds: number[]; allowedCarIds: number[];
} }
@ -47,14 +47,35 @@ export const stationList = createAsyncThunk<any, void, { rejectValue: string }>(
} }
} }
); );
export const getAllStations = createAsyncThunk<
any,
void,
{ rejectValue: string }
>("getAllStations", async (_, { rejectWithValue }) => {
try {
const token = localStorage?.getItem("authToken");
if (!token) throw new Error("No token found");
const response = await http.get("/get-all-stations");
if (!response.data) throw new Error("Invalid API response");
// Return the full response to handle in the reducer
return response.data;
} catch (error: any) {
toast.error("Error Fetching Stations: " + error.message);
return rejectWithValue(
error?.response?.data?.message || "An error occurred"
);
}
});
// Create Station // Create Station
export const createStation = createAsyncThunk< export const createStation = createAsyncThunk<
any, any,
{ {
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: string; totalSlots: number;
allowedCarIds: number[]; allowedCarIds: number[];
}, },
{ rejectValue: string } { rejectValue: string }
@ -62,7 +83,7 @@ export const createStation = createAsyncThunk<
try { try {
const response = await http.post("/create-station", data); const response = await http.post("/create-station", data);
toast.success("Station created successfully"); toast.success("Station created successfully");
return response.data; return response.data; // Assuming the response contains the created station data
} catch (error: any) { } catch (error: any) {
toast.error( toast.error(
"Failed to create Station: " + "Failed to create Station: " +
@ -74,25 +95,31 @@ export const createStation = createAsyncThunk<
} }
}); });
// Update Station details
// Update Station details // Update Station details
export const updateStation = createAsyncThunk( export const updateStation = createAsyncThunk(
"updateStation", "updateStation",
async ({ id, ...stationData }: Station, { rejectWithValue }) => { async ({ id, ...stationData }: Station, { rejectWithValue }) => {
try { try {
// Exclude the `status` from the update payload
const { status, ...updateData } = stationData;
// Send the update request without the `status`
const response = await http.patch( const response = await http.patch(
`/update-station/${id}`, `/update-station/${id}`,
stationData updateData
); );
toast.success("Station Deatils updated successfully"); toast.success("Station Details updated successfully");
return response?.data; return response?.data;
} catch (error: any) { } catch (error: any) {
toast.error("Error updating the user: " + error); toast.error("Error updating the station: " + error);
return rejectWithValue( return rejectWithValue(
error.response?.data?.message || "An error occurred" error.response?.data?.message || "An error occurred"
); );
} }
} }
); );
export const deleteStation = createAsyncThunk< export const deleteStation = createAsyncThunk<
string, string,
string, string,
@ -158,17 +185,30 @@ const stationSlice = createSlice({
stationList.fulfilled, stationList.fulfilled,
(state, action: PayloadAction<any>) => { (state, action: PayloadAction<any>) => {
state.loading = false; state.loading = false;
// Properly extract stations from the response data structure // Correct data extraction
state.stations = state.stations = action.payload.data?.stations || [];
action.payload.data?.results ||
action.payload.data ||
[];
} }
) )
.addCase(stationList.rejected, (state, action) => { .addCase(stationList.rejected, (state, action) => {
state.loading = false; state.loading = false;
state.error = action.payload || "Failed to fetch stations"; state.error = action.payload || "Failed to fetch stations";
}) })
.addCase(getAllStations.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(
getAllStations.fulfilled,
(state, action: PayloadAction<any>) => {
state.loading = false;
state.stations = action.payload.data || [];
}
)
.addCase(getAllStations.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to fetch stations";
})
.addCase(createStation.pending, (state) => { .addCase(createStation.pending, (state) => {
state.loading = true; state.loading = true;
}) })
@ -176,12 +216,12 @@ const stationSlice = createSlice({
createStation.fulfilled, createStation.fulfilled,
(state, action: PayloadAction<any>) => { (state, action: PayloadAction<any>) => {
state.loading = false; state.loading = false;
// Add the newly created station to the state if it exists in the response
if (action.payload.data) { if (action.payload.data) {
state.stations.push(action.payload.data); state.stations.push(action.payload.data);
} }
} }
) )
.addCase( .addCase(
createStation.rejected, createStation.rejected,
(state, action: PayloadAction<string | undefined>) => { (state, action: PayloadAction<string | undefined>) => {
@ -225,8 +265,21 @@ const stationSlice = createSlice({
}) })
.addCase(updateStation.fulfilled, (state, action) => { .addCase(updateStation.fulfilled, (state, action) => {
state.loading = false; state.loading = false;
state.error = action.payload; // If the station was updated, find and update it in the state
const updatedStation = action.payload;
const stationIndex = state.stations.findIndex(
(station) => station.id === updatedStation.id
);
if (stationIndex !== -1) {
// Here, merge the updated station with the existing one
// Ensure `status` is not overwritten if not explicitly updated
state.stations[stationIndex] = {
...state.stations[stationIndex],
...updatedStation,
};
}
}) })
.addCase(updateStation.rejected, (state) => { .addCase(updateStation.rejected, (state) => {
state.loading = false; state.loading = false;
}) })