diff --git a/public/Group 14.svg b/public/Group 14.svg
new file mode 100644
index 0000000..503ae50
--- /dev/null
+++ b/public/Group 14.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/evLogo.png b/public/evLogo.png
new file mode 100644
index 0000000..0c96ea1
Binary files /dev/null and b/public/evLogo.png differ
diff --git a/src/components/AddBookingModal/index.tsx b/src/components/AddBookingModal/index.tsx
index c28f93a..06bfb82 100644
--- a/src/components/AddBookingModal/index.tsx
+++ b/src/components/AddBookingModal/index.tsx
@@ -13,19 +13,22 @@ import {
TextField,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import {
addBooking,
bookingList,
getCarNames,
getCarPorts,
} from "../../redux/slices/bookSlice";
-import { AppDispatch } from "../../redux/store/store";
+import { AppDispatch, RootState } from "../../redux/store/store";
import { toast } from "sonner";
import {
CustomIconButton,
CustomTextField,
} from "../AddEditUserModel/styled.css.tsx";
+import { getAllStations, stationList } from "../../redux/slices/stationSlice.ts";
+import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
+import dayjs from "dayjs";
export default function AddBookingModal({
open,
@@ -44,6 +47,17 @@ export default function AddBookingModal({
} = useForm();
const [carNames, setCarNames] = useState([]); // To hold the car names
const [carPorts, setCarPorts] = useState([]); // To hold the car ports
+ const stations = useSelector(
+ (state: RootState) => state?.stationReducer.stations
+ );
+ const availableSlots = useSelector(
+ (state: RootState) => state.slotReducer.availableSlots
+ );
+ console.log("first", availableSlots);
+ useEffect(() => {
+ dispatch(fetchAvailableSlots());
+ dispatch(getAllStations());
+ }, [dispatch]);
useEffect(() => {
// Fetch car names and car ports
@@ -60,13 +74,13 @@ export default function AddBookingModal({
// Fetch the bookings after this
dispatch(bookingList());
}, [dispatch]);
-
+console.log("Car Ports: ", carPorts);
// Get today's date in yyyy-mm-dd format
const today = new Date().toISOString().split("T")[0];
const onSubmit = async (data: any) => {
const bookingData = {
- stationId: data.stationId,
+ stationId: data.stationId, // Using stationId here for the backend
date: data.date,
startTime: data.startTime,
endTime: data.endTime,
@@ -130,23 +144,34 @@ export default function AddBookingModal({
{/* Station ID */}
- Station ID
+ Select Station
-
+
+ Select Station
+
+ {errors.stationName && (
+
+ {errors.stationName.message}
+
+ )}
+
+
Date
@@ -190,7 +215,6 @@ export default function AddBookingModal({
size="small"
error={!!errors.carName}
>
- Car Name
@@ -266,7 +290,6 @@ export default function AddBookingModal({
{/* Start Time and End Time */}
- {/* Start Time */}
Start Time
@@ -287,7 +310,6 @@ export default function AddBookingModal({
/>
- {/* End Time */}
End Time
@@ -328,6 +350,114 @@ export default function AddBookingModal({
})}
/>
+
+ {/*
+
+ {" Time Slot "}
+
+ (
+
+
+ Select Time Slot
+
+
+ {errors.timeSlot && (
+
+ {errors.timeSlot.message}
+
+ )}
+
+ )}
+ rules={{ required: "Time Slot is required" }}
+ />
+ */}
{/* Submit Button */}
diff --git a/src/components/AddEditUserModel/styled.css.tsx b/src/components/AddEditUserModel/styled.css.tsx
index 7bd3874..ebed712 100644
--- a/src/components/AddEditUserModel/styled.css.tsx
+++ b/src/components/AddEditUserModel/styled.css.tsx
@@ -3,7 +3,7 @@ import { IconButton, TextField } from "@mui/material";
export const CustomIconButton = styled(IconButton)({
backgroundColor: "transparent",
"&:hover": {
- backgroundColor: "#272727",
+ backgroundColor: "transparent",
},
"*:where([data-mui-color-scheme='dark']) &": {
backgroundColor: "transparent",
@@ -20,7 +20,4 @@ export const CustomTextField = styled(TextField)({
"& .MuiInputBase-root.Mui-focused .MuiInputBase-input::placeholder": {
color: "darkgray",
},
-
});
-
-
diff --git a/src/components/AddManagerModal/index.tsx b/src/components/AddManagerModal/index.tsx
index 5309be9..41f745c 100644
--- a/src/components/AddManagerModal/index.tsx
+++ b/src/components/AddManagerModal/index.tsx
@@ -6,21 +6,28 @@ import {
Modal,
IconButton,
InputAdornment,
+ Select,
+ MenuItem,
+ InputLabel,
+ FormControl,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import { Visibility, VisibilityOff } from "@mui/icons-material";
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { addManager, managerList } from "../../redux/slices/managerSlice";
import {
CustomIconButton,
CustomTextField,
} from "../AddEditUserModel/styled.css.tsx";
-import React, { useState, useRef } from "react";
+import React, { useEffect, useState } from "react";
+import { RootState } from "../../redux/reducers.ts";
+import { stationList } from "../../redux/slices/stationSlice.ts";
export default function AddManagerModal({
open,
handleClose,
handleAddManager,
+ // Assume the stations prop contains a list of station objects with 'id' and 'name'
}) {
const dispatch = useDispatch();
const {
@@ -31,16 +38,26 @@ export default function AddManagerModal({
reset,
} = useForm();
const [showPassword, setShowPassword] = useState(false);
+ const stations = useSelector(
+ (state: RootState) => state?.stationReducer.stations
+ );
+
+useEffect(() => {
+ dispatch(stationList());
+}, [dispatch]);
// Handle form submission
const onSubmit = async (data: any) => {
+ // Retrieve the stationId based on the stationName selected
+ const selectedStation = stations.find(
+ (station) => station.name === data.stationName
+ );
const managerData = {
name: data.name,
email: data.email,
phone: data.phone,
password: data.password,
- stationId: data.stationId, // Send stationId here
-
+ stationId: selectedStation?.id, // Send stationId here
};
try {
@@ -131,33 +148,55 @@ export default function AddManagerModal({
"Maximum 30 characters allowed",
},
pattern: {
- value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
+ value: /^[A-Za-z\s]+$/,
message:
"Manager Name must only contain letters and spaces",
},
})}
/>
+
+
+ {/* Station Dropdown */}
+
- Station Id
+ Select Station
-
+
+ Select Station
+
+ {errors.stationName && (
+
+ {errors.stationName.message}
+
+ )}
+
+
{/* Email and Password */}
{/* Email */}
@@ -219,7 +258,6 @@ export default function AddManagerModal({
: "primary"
}
size="small"
- // onMouseDown={togglePasswordVisibility}
InputProps={{
endAdornment: (
@@ -247,9 +285,8 @@ export default function AddManagerModal({
- {/* Phone and Registered Address */}
+ {/* Phone Number */}
- {/* Phone */}
Phone Number
@@ -272,7 +309,6 @@ export default function AddManagerModal({
})}
/>
-
{/* Submit Button */}
diff --git a/src/components/AddStationModal/index.tsx b/src/components/AddStationModal/index.tsx
index 16cb174..2919838 100644
--- a/src/components/AddStationModal/index.tsx
+++ b/src/components/AddStationModal/index.tsx
@@ -1,6 +1,25 @@
import { useForm } from "react-hook-form";
-import { Box, Button, Typography, Modal } from "@mui/material";
+import {
+ Box,
+ Button,
+ Typography,
+ Modal,
+ FormControl,
+ FormHelperText,
+ Select,
+ MenuItem,
+ Checkbox,
+ ListItemText,
+ InputLabel,
+} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
+import { useSelector, useDispatch } from "react-redux";
+import { useEffect, useState } from "react";
+import { RootState } from "../../redux/reducers.ts";
+import {
+ fetchVehicleBrands,
+ vehicleList,
+} from "../../redux/slices/VehicleSlice"; // Adjust this import path accordingly
import {
CustomIconButton,
CustomTextField,
@@ -18,10 +37,68 @@ export default function AddStationModal({
reset,
} = useForm();
+ const dispatch = useDispatch();
+
+ // Get vehicle names from the Redux store
+ const vehicles = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicles
+ ); // Adjust according to your actual state structure
+ const vehicleBrands = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicleBrands
+ );
+
+ // State for selected vehicle brand and vehicles
+ const [selectedBrands, setSelectedBrands] = useState([]);
+ const [selectedVehicles, setSelectedVehicles] = useState([]);
+
+ // Fetch vehicle brands and vehicle list when the component mounts
+ 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 }>
+ ) => {
+ const value = event.target.value as string[];
+ 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
+ const getVehicleIds = () => {
+ return vehicles
+ .filter((vehicle) => selectedVehicles.includes(vehicle.name))
+ .map((vehicle) => vehicle.id);
+ };
+
const onSubmit = (data: any) => {
- handleAddStation(data); // Add station to the list
+ const vehicleIds = getVehicleIds(); // Get the ids of the selected vehicles
+
+ const payload = {
+ ...data,
+ status: 1, // Default status
+ allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend
+ totalSlots: Number(data.totalSlots), // Ensure this is a number
+ };
+
+ handleAddStation(payload);
handleClose(); // Close modal after adding
reset();
+ setSelectedVehicles([]);
+ setSelectedBrands([]); // Reset selected brands after submission
};
return (
@@ -77,7 +154,6 @@ export default function AddStationModal({
gap: 2,
}}
>
- {/* First Row - Two Inputs */}
+
+ {/* Vehicle Brand Dropdown with Checkboxes */}
+
+
+
+ Select Vehicle Brands
+
+
+ Choose Brands
+
+
+ {errors.vehicleBrand
+ ? errors.vehicleBrand.message
+ : ""}
+
+
+
+
+
+
+
+
+ Vehicle Name
+
+
+
+ Choose Vehicles
+
+
+ {errors.vehicleName
+ ? errors.vehicleName.message
+ : ""}
+
+
+
+
{/* Submit Button */}
diff --git a/src/components/AddVehicleModal/index.tsx b/src/components/AddVehicleModal/index.tsx
index f7747f8..3334d40 100644
--- a/src/components/AddVehicleModal/index.tsx
+++ b/src/components/AddVehicleModal/index.tsx
@@ -83,6 +83,36 @@ export default function AddVehicleModal({
>
{/* First Row - Two Inputs */}
+
+
+ Company Name
+
+
+
-
-
-
- Company
-
-
-
{/* Second Row - Two Inputs */}
diff --git a/src/components/AvailableSlotModal/index.tsx b/src/components/AvailableSlotModal/index.tsx
new file mode 100644
index 0000000..fd27d0c
--- /dev/null
+++ b/src/components/AvailableSlotModal/index.tsx
@@ -0,0 +1,108 @@
+import React, { useEffect, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ Button,
+ Dialog,
+ DialogTitle,
+ Box,
+ CircularProgress,
+} from "@mui/material";
+import { styled } from "@mui/system";
+import {
+ fetchAvailableSlots,
+ availableSlots,
+} from "../../redux/slices/slotSlice"; // Update with the correct import path
+
+const SlotButton = styled(Button)<{ selected: boolean }>(({ selected }) => ({
+ backgroundColor: selected ? "#1976d2" : "#333",
+ color: "#fff",
+ border: "1px solid #555",
+ width: "120px",
+ height: "40px",
+ margin: "5px",
+ "&:hover": {
+ backgroundColor: selected ? "#1565c0" : "#444",
+ },
+}));
+
+const AvailableSlotsModal = ({
+ open,
+ onClose,
+}: {
+ open: boolean;
+ onClose: () => void;
+}) => {
+ const dispatch = useDispatch();
+
+ // Fetch slots from the Redux store
+ const { availableSlots, loading, error } = useSelector(
+ (state: any) => state.slotReducer.availableSlots
+ ); // Adjust selector path
+ const [selectedSlot, setSelectedSlot] = useState(
+ null
+ );
+
+ useEffect(() => {
+ if (open) {
+ // Fetch available slots when the modal opens
+ dispatch(fetchAvailableSlots());
+ }
+ }, [open, dispatch]);
+
+ const handleSlotSelect = (slot: availableSlots) => {
+ setSelectedSlot(slot); // Update the selected slot
+ };
+
+ const handleSave = () => {
+ // Save the selected slot (you can dispatch an action here if needed)
+ console.log("Selected Slot: ", selectedSlot);
+ onClose(); // Close the modal after saving
+ };
+
+ return (
+
+ );
+};
+
+export default AvailableSlotsModal;
diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx
index 2090518..9d9703d 100644
--- a/src/components/CustomTable/index.tsx
+++ b/src/components/CustomTable/index.tsx
@@ -34,13 +34,13 @@ import { CustomIconButton } from "../AddEditUserModel/styled.css.tsx";
import ManagerViewModal from "../Modals/ViewManagerModal";
import UserViewModal from "../Modals/UserViewModal/index.tsx";
import { deleteUser, userList } from "../../redux/slices/userSlice.ts";
-import { deleteStation } from "../../redux/slices/stationSlice.ts";
+import { deleteStation, stationList } from "../../redux/slices/stationSlice.ts";
import StationViewModal from "../Modals/StationViewModal/index.tsx";
import {
deleteSlot,
fetchAvailableSlots,
} from "../../redux/slices/slotSlice.ts";
-import { bookingList } from "../../redux/slices/bookSlice.ts";
+import { bookingList, deleteBooking } from "../../redux/slices/bookSlice.ts";
// Styled components for customization
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
@@ -83,10 +83,9 @@ interface CustomTableProps {
viewModal: boolean;
setViewModal: Function;
deleteModal: boolean;
- handleStatusToggle: (id: string, currentStatus: number) => void;
+ handleStatusToggle?: (id: string, currentStatus: number) => void;
tableType: string; // Adding tableType prop to change header text dynamically
handleClickOpen: () => void;
- //handleDeleteButton: (id: string | number | undefined) => void;
}
const CustomTable: React.FC = ({
@@ -108,11 +107,8 @@ const CustomTable: React.FC = ({
const [searchQuery, setSearchQuery] = React.useState("");
const [currentPage, setCurrentPage] = React.useState(1);
const usersPerPage = 10;
- const { user, isLoading } = useSelector(
- (state: RootState) => state?.profileReducer
- );
+ const { user } = useSelector((state: RootState) => state?.profileReducer);
const open = Boolean(anchorEl);
- console.log("Rows", rows);
const handleClick = (event: React.MouseEvent, row: Row) => {
setAnchorEl(event.currentTarget);
setSelectedRow(row);
@@ -137,7 +133,6 @@ const CustomTable: React.FC = ({
}
switch (tableType) {
-
case "admin":
dispatch(deleteAdmin(id || ""));
break;
@@ -156,6 +151,9 @@ const CustomTable: React.FC = ({
case "slots":
dispatch(deleteSlot(id || ""));
break;
+ case "booking":
+ dispatch(deleteBooking(id || ""));
+ break;
default:
console.error("Unknown table type:", tableType);
return;
@@ -185,6 +183,9 @@ const CustomTable: React.FC = ({
case "slots":
dispatch(fetchAvailableSlots());
break;
+ case "station":
+ dispatch(stationList());
+ break;
default:
console.error("Unknown table type:", tableType);
return;
@@ -197,7 +198,7 @@ const CustomTable: React.FC = ({
if (selectedRow) {
// Toggle the opposite of current status
const newStatus = selectedRow.statusValue === 1 ? 0 : 1;
- handleStatusToggle(selectedRow.id, newStatus);
+ handleStatusToggle?.(selectedRow.id, newStatus);
}
handleClose();
};
@@ -214,7 +215,7 @@ const CustomTable: React.FC = ({
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
const handlePageChange = (
- event: React.ChangeEvent,
+ _event: React.ChangeEvent,
value: number
) => {
setCurrentPage(value);
@@ -239,30 +240,35 @@ const CustomTable: React.FC = ({
}}
>
{/* Dynamic title based on the page type */}
- {tableType === "admin"
- ? "Admin"
- : tableType === "role"
- ? "Roles"
- : tableType === "user"
- ? "Users"
- : tableType === "manager"
- ? "Managers"
- : tableType === "vehicle"
- ? "Vehicles"
- : tableType === "station"
- ? "Charging Station"
- : tableType === "booking"
- ? "Booking"
- : tableType === "slots"
- ? "Slot"
- : "List"}
+ {(() => {
+ switch (tableType) {
+ case "admin":
+ return "Admin";
+ case "role":
+ return "Roles";
+ case "user":
+ return "Users";
+ case "manager":
+ return "Managers";
+ case "vehicle":
+ return "Vehicles";
+ case "station":
+ return "Charging Station";
+ case "booking":
+ return "Booking";
+ case "slots":
+ return "Slot";
+ default:
+ return "List";
+ }
+ })()}
{/* Search & Buttons Section */}
= ({
backgroundColor: "#272727",
"& .MuiOutlinedInput-root": {
borderRadius: "12px",
+ width: "422px",
+ height: "44px",
+ borderWidth: "1px",
+ padding: "14px 12px 14px 12px",
+ gap: "16px",
"& fieldset": { borderColor: "#FFFFFF" },
"&:hover fieldset": { borderColor: "#FFFFFF" },
"&.Mui-focused fieldset": {
@@ -312,28 +323,34 @@ const CustomTable: React.FC = ({
color: "white",
borderRadius: "8px",
width: "184px",
+ marginRight: "16px",
"&:hover": { backgroundColor: "#439BC1" },
}}
onClick={() => handleClickOpen()}
>
Add{" "}
- {tableType === "admin"
- ? "Admin"
- : tableType === "role"
- ? "Role"
- : tableType === "user"
- ? "User"
- : tableType === "manager"
- ? "Manager"
- : tableType === "vehicle"
- ? "Vehicle"
- : tableType === "station"
- ? "Charging Station"
- : tableType === "booking"
- ? "Booking"
- : tableType === "slots"
- ? "Slot"
- : "Item"}
+ {(() => {
+ switch (tableType) {
+ case "admin":
+ return "Admin";
+ case "role":
+ return "Role";
+ case "user":
+ return "User";
+ case "manager":
+ return "Manager";
+ case "vehicle":
+ return "Vehicle";
+ case "station":
+ return "Charging Station";
+ case "booking":
+ return "Booking";
+ case "slots":
+ return "Slot";
+ default:
+ return "Item";
+ }
+ })()}
)}
@@ -343,15 +360,17 @@ const CustomTable: React.FC = ({
width: "44px",
height: "44px",
borderRadius: "8px",
- backgroundColor: "#272727",
+ backgroundColor: "#1C1C1C",
color: "#52ACDF",
"&:hover": { backgroundColor: "#333333" },
+ "*:where([data-mui-color-scheme='dark']) &": {
+ backgroundColor: "#1C1C1C",
+ },
}}
>
-
{/* Table Section */}
= ({
sx={{
backgroundColor: "#272727",
borderBottom: "none",
+ ".css-1ex4ubw-MuiTableCell-root.MuiTableCell-head ":
+ {
+ backgroundColor: "#272727",
+ },
}}
>
{" "}
@@ -384,7 +407,13 @@ const CustomTable: React.FC = ({
))}
-
+
{currentRows.map((row, rowIndex) => (
{columns.map((column) => (
@@ -433,7 +462,6 @@ const CustomTable: React.FC = ({
-
{/* Pagination */}
= ({
}}
/>
-
{/* Menu Actions */}
{open && (
)}
-
{/* Modals */}
{deleteModal && (
void;
- editRow: any; // Manager data including id
+ handleUpdate: (
+ id: string,
+ name: string,
+ email: string,
+ phone: string,
+ stationId: string
+ ) => Promise;
+ editRow: any;
}
interface FormData {
@@ -33,7 +41,7 @@ const EditManagerModal: React.FC = ({
handleClose,
editRow,
}) => {
- const dispatch = useDispatch(); // Use dispatch to send Redux actions
+ const dispatch = useDispatch();
const {
control,
handleSubmit,
@@ -74,7 +82,7 @@ const EditManagerModal: React.FC = ({
email: data.email,
phone: data.phone,
stationId: data.stationId,
-
+
},
})
).unwrap(); // Ensure that it throws an error if the update fails
diff --git a/src/components/EditSlotModal/index.tsx b/src/components/EditSlotModal/index.tsx
new file mode 100644
index 0000000..f227886
--- /dev/null
+++ b/src/components/EditSlotModal/index.tsx
@@ -0,0 +1,267 @@
+import React, { useEffect, useState } from "react";
+import {
+ Box,
+ Button,
+ Typography,
+ Modal,
+ CircularProgress,
+} from "@mui/material";
+import CloseIcon from "@mui/icons-material/Close";
+import { useForm, Controller } from "react-hook-form";
+import { useDispatch } from "react-redux";
+import { updateSlot, fetchAvailableSlots } from "../../redux/slices/slotSlice"; // Update with correct action
+import {
+ CustomIconButton,
+ CustomTextField,
+} from "../AddEditUserModel/styled.css.tsx"; // Custom styled components
+import { AppDispatch } from "../../redux/store/store.ts";
+
+interface EditSlotModalProps {
+ open: boolean;
+ handleClose: () => void;
+ handleUpdate: (
+ id: string,
+ startTime: string,
+ endTime: string,
+ isAvailable: boolean
+ ) => Promise;
+ editRow: any; // Slot data including id
+}
+
+interface FormData {
+ date: string;
+ startTime: string;
+ endTime: string;
+ isAvailable: boolean;
+}
+
+const EditSlotModal: React.FC = ({
+ open,
+ handleClose,
+ editRow,
+}) => {
+ const dispatch = useDispatch();
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ setValue,
+ reset,
+ } = useForm({
+ defaultValues: {
+ date: "",
+ startTime: "",
+ endTime: "",
+ isAvailable: false,
+ },
+ });
+
+ const [loading, setLoading] = useState(false);
+ const [isAvailable, setIsAvailable] = useState(
+ editRow?.isAvailable || false
+ );
+
+ useEffect(() => {
+ if (editRow) {
+ setValue("startTime", editRow.startTime);
+ setValue("endTime", editRow.endTime);
+ setIsAvailable(editRow.isAvailable);
+ } else {
+ reset();
+ }
+ }, [editRow, setValue, reset]);
+
+ const onSubmit = async (data: FormData) => {
+ if (editRow) {
+ setLoading(true);
+
+ try {
+
+ const availabilityStatus = isAvailable ? true : false;
+
+ await dispatch(
+ updateSlot({
+ id: editRow.id, // Slot ID// date: data.date,
+ startTime: data.startTime,
+ endTime: data.endTime,
+ isAvailable: availabilityStatus,
+ })
+ ).unwrap();
+ dispatch(fetchAvailableSlots());
+ handleClose(); // Close modal on success
+ reset(); // Reset form fields after submit
+ } catch (error) {
+ console.error("Error updating slot:", error);
+ // Handle the error or show a toast message
+ } finally {
+ setLoading(false); // Stop loading state
+ }
+ }
+ };
+
+ return (
+ {
+ if (reason === "backdropClick") {
+ return;
+ }
+ handleClose(); // Close modal when clicking cross or cancel
+ }}
+ aria-labelledby="edit-slot-modal"
+ >
+
+ {/* Header */}
+
+
+ Edit Slot
+
+
+
+
+
+
+ {/* Horizontal Line */}
+
+
+ {/* Input Fields */}
+
+ {/* Date */}
+ {/*
+
+ Date
+
+ (
+
+ )}
+ />
+ */}
+
+ {/* Start Time */}
+
+
+ Start Time
+
+ (
+
+ )}
+ />
+
+
+ {/* End Time */}
+
+
+ End Time
+
+ (
+
+ )}
+ />
+
+
+ {/* Availability Toggle */}
+
+
+
+ {isAvailable ? "Available" : "Not Available"}
+
+
+
+
+ {/* Submit Button */}
+
+
+
+
+
+ );
+};
+
+export default EditSlotModal;
diff --git a/src/components/EditStationModal/index.tsx b/src/components/EditStationModal/index.tsx
index ada74a7..11fdc3b 100644
--- a/src/components/EditStationModal/index.tsx
+++ b/src/components/EditStationModal/index.tsx
@@ -1,11 +1,37 @@
-import React, { useEffect } from "react";
-import { Box, Button, Typography, Modal } from "@mui/material";
+import React, { useEffect, useState } from "react";
+import {
+ Box,
+ Button,
+ Typography,
+ Modal,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ Checkbox,
+ ListItemText,
+ FormHelperText,
+} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
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 {
CustomIconButton,
CustomTextField,
-} from "../AddEditUserModel/styled.css.tsx";
+} from "../AddEditUserModel/styled.css.tsx"; // Assuming custom styled components
+
+interface FormData {
+ name: string;
+ registeredAddress: string;
+ totalSlots: number;
+ allowedCarIds: string[];
+ status: number;
+}
interface EditStationModalProps {
open: boolean;
@@ -15,16 +41,9 @@ interface EditStationModalProps {
name: string,
registeredAddress: string,
totalSlots: number,
- status: number
+ allowedCarIds: number[]
) => void;
- editRow: any;
-}
-
-interface FormData {
- name: string;
- registeredAddress: string;
- totalSlots: number;
- status: number;
+ editRow?: any;
}
const EditStationModal: React.FC = ({
@@ -36,42 +55,85 @@ const EditStationModal: React.FC = ({
const {
control,
handleSubmit,
- formState: { errors },
setValue,
reset,
+ formState: { errors },
} = useForm({
defaultValues: {
name: "",
registeredAddress: "",
totalSlots: 0,
status: 1,
+ allowedCarIds: [],
},
});
- // Set values if editRow is provided
+ const dispatch = useDispatch();
+ const vehicles = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicles
+ );
+ const vehicleBrands = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicleBrands
+ );
+
+ const [selectedBrands, setSelectedBrands] = useState([]);
+ const [selectedVehicles, setSelectedVehicles] = useState([]);
+
+ useEffect(() => {
+ dispatch(fetchVehicleBrands());
+ dispatch(vehicleList()); // Fetch vehicles when the component mounts
+ }, [dispatch]);
+
useEffect(() => {
if (editRow) {
setValue("name", editRow.name);
setValue("registeredAddress", editRow.registeredAddress);
setValue("totalSlots", editRow.totalSlots);
- setValue("status", editRow.number);
+ setValue("status", editRow.status);
+ setValue("allowedCarIds", editRow.allowedCarIds || []);
+ setSelectedVehicles(editRow.allowedCarIds || []);
} else {
reset();
}
}, [editRow, setValue, reset]);
+ // Filter vehicles based on selected brands
+ const filteredVehicles = vehicles.filter((vehicle) =>
+ selectedBrands.includes(vehicle.company)
+ );
+
+ // Handle changes in vehicle brand selection
+ const handleBrandChange = (
+ event: React.ChangeEvent<{ value: unknown }>
+ ) => {
+ setSelectedBrands(event.target.value as string[]);
+ };
+
+ // Handle changes in vehicle selection
+ 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) => {
- if (editRow) {
- handleUpdate(
- editRow.id,
- data.name,
- data.registeredAddress,
- data.totalSlots,
- data.status
- );
- }
- handleClose(); // Close the modal
- reset(); // Reset the form fields
+ const vehicleIds = vehicles
+ .filter((vehicle) => selectedVehicles.includes(vehicle.name))
+ .map((vehicle) => vehicle.id);
+
+ handleUpdate(
+ editRow.id,
+ data.name,
+ data.registeredAddress,
+ data.totalSlots,
+ vehicleIds
+ );
+ handleClose();
+ reset();
+ //setSelectedBrands([]); // Reset brands after submit
+ setSelectedVehicles([]); // Reset selected vehicles
};
return (
@@ -81,7 +143,7 @@ const EditStationModal: React.FC = ({
if (reason === "backdropClick") {
return;
}
- handleClose(); // Close modal when clicking cross or cancel
+ handleClose();
}}
aria-labelledby="edit-station-modal"
>
@@ -119,9 +181,9 @@ const EditStationModal: React.FC = ({
{/* Horizontal Line */}
- {/* Input Fields */}
+ {/* Form */}
- {/* First Row - Two Inputs */}
+ {/* Station Name and Address */}
= ({
(
= ({
placeholder="Enter Station Name"
size="small"
error={!!errors.name}
- helperText={errors.name?.message}
+ helperText={
+ errors.name
+ ? errors.name.message
+ : ""
+ }
/>
)}
/>
-
= ({
(
= ({
size="small"
error={!!errors.registeredAddress}
helperText={
- errors.registeredAddress?.message
+ errors.registeredAddress
+ ? errors.registeredAddress
+ .message
+ : ""
}
/>
)}
@@ -202,7 +254,7 @@ const EditStationModal: React.FC = ({
- {/* Second Row - Total Slots */}
+ {/* Total Slots */}
= ({
(
= ({
size="small"
type="number"
error={!!errors.totalSlots}
- helperText={errors.totalSlots?.message}
+ helperText={
+ errors.totalSlots
+ ? errors.totalSlots.message
+ : ""
+ }
/>
)}
/>
+ {/* Vehicle Brand Selection with Checkboxes */}
+
+
+
+ Select Vehicle Brands
+
+
+ Choose Brand
+
+
+
+
+
+ Vehicle Name
+
+
+ Choose Vehicles
+
+
+ {errors.allowedCarIds
+ ? errors.allowedCarIds.message
+ : ""}
+
+
+
+
+
+
{/* Submit Button */}
= ({
"&:hover": { backgroundColor: "#439BC1" },
}}
>
- Update Charging Station
+ Update Station
diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx
index 83dca3c..80247c2 100644
--- a/src/components/Header/index.tsx
+++ b/src/components/Header/index.tsx
@@ -15,22 +15,21 @@ import { AppDispatch, RootState } from "../../redux/store/store";
import { fetchAdminProfile } from "../../redux/slices/profileSlice";
import OptionsMenu from "../OptionsMenu";
import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone";
+import ColorModeIconDropdown from "../../shared-theme/ColorModeIconDropdown";
export default function Header() {
const [showNotifications, setShowNotifications] = React.useState(false);
const toggleNotifications = () => {
setShowNotifications((prev) => !prev);
};
- const [open, setOpen] = React.useState(true);
+ const [open, setOpen] = React.useState(true);
- const dispatch = useDispatch();
- const { user } = useSelector(
- (state: RootState) => state?.profileReducer
- );
+ const dispatch = useDispatch();
+ const { user } = useSelector((state: RootState) => state?.profileReducer);
- React.useEffect(() => {
- dispatch(fetchAdminProfile());
- }, [dispatch]);
+ React.useEffect(() => {
+ dispatch(fetchAdminProfile());
+ }, [dispatch]);
return (
-
@@ -43,7 +43,14 @@ export default function LineChartCard() {
return (
diff --git a/src/components/MainGrid/index.tsx b/src/components/MainGrid/index.tsx
index 491dc4e..f3bf217 100644
--- a/src/components/MainGrid/index.tsx
+++ b/src/components/MainGrid/index.tsx
@@ -45,7 +45,7 @@ export default function MainGrid() {
theme.spacing(2) }}
diff --git a/src/components/MenuContent/index.tsx b/src/components/MenuContent/index.tsx
index 9285c40..8d5df61 100644
--- a/src/components/MenuContent/index.tsx
+++ b/src/components/MenuContent/index.tsx
@@ -12,7 +12,12 @@ import { useSelector } from "react-redux";
import { RootState } from "../../redux/store/store";
import DashboardOutlinedIcon from "@mui/icons-material/DashboardOutlined";
import ManageAccountsOutlinedIcon from "@mui/icons-material/ManageAccountsOutlined";
-
+import EvStationOutlinedIcon from "@mui/icons-material/EvStationOutlined";
+import EvStationIcon from "@mui/icons-material/EvStation";
+import BookOnlineOutlinedIcon from "@mui/icons-material/BookOnlineOutlined";
+import ChecklistSharpIcon from "@mui/icons-material/ChecklistSharp";
+import AnalyticsOutlinedIcon from "@mui/icons-material/AnalyticsOutlined";
+import PeopleOutlinedIcon from "@mui/icons-material/PeopleOutlined";
type PropType = {
hidden: boolean;
};
@@ -31,53 +36,53 @@ export default function MenuContent({ hidden }: PropType) {
},
userRole === "superadmin" && {
text: "Admins",
- icon: ,
+ icon: ,
url: "/panel/admin-list",
},
userRole === "superadmin" && {
text: "Roles",
- icon: ,
+ icon: ,
url: "/panel/role-list",
},
userRole === "admin" && {
text: "Users",
- icon: ,
+ icon: ,
url: "/panel/user-list",
},
userRole === "admin" && {
text: "Charging Stations",
- icon: ,
+ icon: ,
url: "/panel/station-list", // Placeholder for now
},
userRole === "admin" && {
text: "Managers",
- icon: ,
+ icon: ,
url: "/panel/manager-list", // Placeholder for now
},
userRole === "admin" && {
text: "Vehicles",
- icon: ,
+ icon: ,
url: "/panel/vehicle-list", // Placeholder for now
},
// userRole === "manager" && {
- // text: "Add Slots",
- // icon: ,
- // url: "/panel/EVslots", // Placeholder for now
+ // text: "Add Slots",
+ // icon: ,
+ // url: "/panel/EVslots", // Placeholder for now
// },
userRole === "user" && {
text: "Bookings",
- icon: ,
+ icon: ,
url: "/panel/booking-list", // Placeholder for now
},
userRole === "manager" && {
text: "Available Slots",
- icon: ,
+ icon: ,
url: "/panel/slot-list", // Placeholder for now
},
userRole === "user" && {
text: "Available Slots",
- icon: ,
+ icon: ,
url: "/panel/slot-list", // Placeholder for now
},
];
@@ -85,25 +90,33 @@ export default function MenuContent({ hidden }: PropType) {
const filteredMenuItems = baseMenuItems.filter(Boolean);
return (
-
+
{filteredMenuItems.map((item, index) => (
@@ -113,8 +126,14 @@ export default function MenuContent({ hidden }: PropType) {
sx={{
display: !hidden ? "none" : "",
transition: "all 0.5s ease",
+
".MuiListItemText-primary": {
+ width: "118px",
+ height: "19px",
fontSize: "16px",
+ letterSpacing: "0%",
+ lineHeight: "100%",
+ color: "#D9D8D8",
},
}}
primary={item.text}
diff --git a/src/components/Modals/StationViewModal/index.tsx b/src/components/Modals/StationViewModal/index.tsx
index b47ac08..9efc2a0 100644
--- a/src/components/Modals/StationViewModal/index.tsx
+++ b/src/components/Modals/StationViewModal/index.tsx
@@ -8,7 +8,7 @@ type Props = {
open: boolean;
setViewModal: Function;
handleView: (id: string | undefined) => void;
- id?: number | undefined;
+ id?: string | undefined;
};
const style = {
diff --git a/src/components/Modals/ViewManagerModal/index.tsx b/src/components/Modals/ViewManagerModal/index.tsx
index 5ee6b2f..37d40aa 100644
--- a/src/components/Modals/ViewManagerModal/index.tsx
+++ b/src/components/Modals/ViewManagerModal/index.tsx
@@ -32,7 +32,7 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
(state: RootState) => state.managerReducer
);
const [selectedManager, setSelectedManager] = useState(null);
-
+
useEffect(() => {
if (id) {
@@ -110,7 +110,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
Station Location:
- {selectedManager.registeredAddress}
+ {selectedManager.chargingStation
+ ?.registeredAddress || "Not Available"}
@@ -118,7 +119,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
Station Name:
- {selectedManager.stationName}
+ {selectedManager.chargingStation?.name ||
+ "Not Available"}
diff --git a/src/components/Modals/ViewModal/index.tsx b/src/components/Modals/ViewModal/index.tsx
index 68d24db..138d34d 100644
--- a/src/components/Modals/ViewModal/index.tsx
+++ b/src/components/Modals/ViewModal/index.tsx
@@ -7,6 +7,7 @@ import CloseIcon from "@mui/icons-material/Close";
type Props = {
open: boolean;
setViewModal: Function;
+ handleView: (id: string | undefined) => void;
id?: string;
};
diff --git a/src/components/ResourcePieChart/index.tsx b/src/components/ResourcePieChart/index.tsx
index f257558..2c0f779 100644
--- a/src/components/ResourcePieChart/index.tsx
+++ b/src/components/ResourcePieChart/index.tsx
@@ -28,11 +28,15 @@ export default function ResourcePieChart() {
sx={{
display: "flex",
flexDirection: "column",
- gap: "8px",
+ gap: "12px",
flexGrow: 1,
- width: "100%",
- height: "100%",
- backgroundColor: "#202020",
+ width: "553px",
+ height: "324px",
+ padding: "16px",
+ borderRadius: "16px",
+ "*:where([data-mui-color-scheme='dark']) &": {
+ backgroundColor: "#202020",
+ },
}}
>
@@ -40,11 +44,13 @@ export default function ResourcePieChart() {
component="h2"
variant="subtitle2"
color="#F2F2F2"
+ width="84px"
+ height="24px"
sx={{
- fontFamily: "Gilroy",
fontWeight: 500,
fontSize: "18px",
lineHeight: "24px",
+ color: "#FFFFFF",
}}
>
Resources
@@ -93,7 +99,12 @@ export default function ResourcePieChart() {
borderRadius: "50%",
}}
/>
-
+
{entry.title}
diff --git a/src/components/SessionsChart/index.tsx b/src/components/SessionsChart/index.tsx
index 381df18..2ff16df 100644
--- a/src/components/SessionsChart/index.tsx
+++ b/src/components/SessionsChart/index.tsx
@@ -5,15 +5,21 @@ import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
+
export default function SessionsChart() {
return (
@@ -21,11 +27,16 @@ export default function SessionsChart() {
variant="h6"
align="left"
color="#F2F2F2"
+ width="132px"
+ height="24px"
+ gap={"12px"}
sx={{
fontFamily: "Gilroy",
fontWeight: 500,
fontSize: "18px",
lineHeight: "24px",
+ letterSpacing: "0%",
+ color: "#FAFAFA",
}}
>
Charging prices
@@ -50,13 +61,15 @@ export default function SessionsChart() {
}}
>
Delhi NCR EV Station
@@ -78,7 +91,6 @@ export default function SessionsChart() {
mx: "auto",
}}
>
- {/* You can map over your data here; for simplicity, we’re using static boxes */}
{[1, 2, 3, 4].map((item) => (
Basic Charging
-
- 16.83 cents/kWh
-
+
+
+ 16.83
+
+
+
+ cents/kWh
+
+
))}
diff --git a/src/components/SideMenu/index.tsx b/src/components/SideMenu/index.tsx
index f5b2f2b..4efdf7c 100644
--- a/src/components/SideMenu/index.tsx
+++ b/src/components/SideMenu/index.tsx
@@ -61,44 +61,51 @@ export default function SideMenu() {
flexDirection: "row",
alignItems: "center",
pt: 2,
- pl: 2,
+ pl: 2,
}}
>
-
-
- */}
+ {/*
- {/* Digi EV Text Section */}
+
Digi EV
-
+
{user?.userType || "N/A"}
-
-
+ */}
{title}
@@ -33,8 +48,12 @@ export default function StatCard({ title, value }: StatCardProps) {
component="h1"
variant="body1"
color="#F2F2F2"
- fontSize={30}
- fontWeight={700}
+ width={"36px"}
+ height={"36px"}
+ fontSize={"32px"}
+ fontWeight={600}
+ lineHeight={"36px"}
+ letterSpacing={"0%"}
gutterBottom
>
{value}
diff --git a/src/components/barChartCard/index.tsx b/src/components/barChartCard/index.tsx
index 156f343..5bf70ec 100644
--- a/src/components/barChartCard/index.tsx
+++ b/src/components/barChartCard/index.tsx
@@ -1,20 +1,8 @@
import * as React from "react";
import { useTheme } from "@mui/material/styles";
-import {
- BarChart,
- Bar,
- XAxis,
- YAxis,
- CartesianGrid,
-} from "recharts";
-import {
- Card,
- CardContent,
- Typography,
- Box,
-
-} from "@mui/material";
-import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from "recharts";
+import { Card, CardContent, Typography, Box } from "@mui/material";
+import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
const data = [
{ name: "Jan", v1: 40 },
{ name: "Feb", v1: 50 },
@@ -30,7 +18,14 @@ export default function RoundedBarChart() {
return (
= ({ customStyles }) => {
...customStyles,
mt: { xs: 8, md: 0 },
padding: 0,
-
})}
>
-
-
-
{
const authToken = localStorage.getItem("authToken");
if (authToken) {
config.headers.Authorization = `Bearer ${authToken}`;
}
-
return config;
});
+
http.interceptors.response.use(
- (response) => response,
+ (response) => response,
(error) => {
- if (error.response && error.response.status === 401) {
-
- window.location.href = "/login";
-
- // const history = useHistory();
- // history.push("/login");
+ if (error.response) {
+ const status = error.response.status;
+ const requestUrl = error.config.url; // Get the API route
+
+ // Handle token expiration (401) but NOT for login failures
+ 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);
}
);
diff --git a/src/pages/AdminList/index.tsx b/src/pages/AdminList/index.tsx
index 7af0318..b064f9c 100644
--- a/src/pages/AdminList/index.tsx
+++ b/src/pages/AdminList/index.tsx
@@ -83,6 +83,8 @@ export default function AdminList() {
{ id: "email", label: "Email" },
{ id: "phone", label: "Phone" },
{ id: "registeredAddress", label: "Address" },
+ { id: "userType", label: "Role" },
+
{ id: "action", label: "Action", align: "center" },
];
@@ -93,7 +95,8 @@ export default function AdminList() {
name: string;
email: string;
phone: string;
- Admins: { registeredAddress: string }[]; // Adjusted to support array of Admins
+ Admins: { registeredAddress: string }[];
+ userType:string;
},
index: number
) => ({
@@ -103,6 +106,7 @@ export default function AdminList() {
email: admin?.email,
phone: admin?.phone,
registeredAddress: admin?.Admins?.[0]?.registeredAddress || "NA",
+ userType:admin?.userType||"NA",
})
);
diff --git a/src/pages/Auth/Login/index.tsx b/src/pages/Auth/Login/index.tsx
index ec96af4..a9f82ff 100644
--- a/src/pages/Auth/Login/index.tsx
+++ b/src/pages/Auth/Login/index.tsx
@@ -23,6 +23,7 @@ import { useNavigate } from "react-router-dom";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import { Card, SignInContainer } from "./styled.css.tsx";
import { CustomIconButton } from "../../../components/AddEditUserModel/styled.css.tsx";
+import { AppDispatch } from "../../../redux/store/store.ts";
interface ILoginForm {
email: string;
password: string;
@@ -37,7 +38,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
handleSubmit,
formState: { errors, isValid },
} = useForm({ mode: "onChange" });
- const dispatch = useDispatch();
+ const dispatch = useDispatch();
const router = useNavigate();
const handleClickOpen = () => {
@@ -104,11 +105,11 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
}}
>
@@ -329,9 +330,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
? "error"
: "primary"
}
- onMouseDown={
- togglePasswordVisibility
- }
+
InputProps={{
endAdornment: (
diff --git a/src/pages/EvSlotList/index.tsx b/src/pages/EvSlotList/index.tsx
index 207f26e..ef4f174 100644
--- a/src/pages/EvSlotList/index.tsx
+++ b/src/pages/EvSlotList/index.tsx
@@ -1,21 +1,23 @@
-import React, { useEffect, useState } from "react";
-import { Box, Button, TextField, Typography } from "@mui/material";
+import { useEffect, useState } from "react";
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 { createSlot, fetchAvailableSlots } from "../../redux/slices/slotSlice";
+import {
+ createSlot,
+ fetchAvailableSlots,
+ updateSlot,
+} from "../../redux/slices/slotSlice";
import AddSlotModal from "../../components/AddSlotModal";
import dayjs from "dayjs";
+import EditSlotModal from "../../components/EditSlotModal";
export default function EVSlotList() {
const [addModalOpen, setAddModalOpen] = useState(false);
const [editModalOpen, setEditModalOpen] = useState(false);
- const [editRow, setEditRow] = useState(null);
const { reset } = useForm();
const [deleteModal, setDeleteModal] = useState(false);
const [viewModal, setViewModal] = useState(false);
const [rowData, setRowData] = useState(null);
- const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch();
const availableSlots = useSelector(
(state: RootState) => state?.slotReducer.availableSlots
@@ -51,6 +53,34 @@ export default function EVSlotList() {
}
};
+ const handleUpdate = async (
+ id: string,
+ startTime: string,
+ endTime: string,
+ isAvailable: boolean
+ ) => {
+ try {
+ const formattedStartTime = dayjs(startTime, "HH:mm").format(
+ "HH:mm"
+ );
+ const formattedEndTime = dayjs(endTime, "HH:mm").format("HH:mm");
+
+ await dispatch(
+ updateSlot({
+ id,
+ startTime: formattedStartTime,
+ endTime: formattedEndTime,
+ isAvailable,
+ })
+ ).unwrap();
+
+ await dispatch(fetchAvailableSlots());
+ handleCloseModal();
+ } catch (error) {
+ console.error("Update failed", error);
+ }
+ };
+
const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" },
{ id: "name", label: "Station Name" },
@@ -62,29 +92,24 @@ export default function EVSlotList() {
{ id: "action", label: "Action", align: "center" },
];
- // Make sure dayjs is imported
-
const slotRows = availableSlots?.length
? availableSlots.map((slot, index) => {
- const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD");
- const startTime = dayjs(slot?.startTime).format("HH:mm");
- const endTime = dayjs(slot?.endTime).format("HH:mm");
-
- console.log("first", startTime);
- return {
- srno: index + 1,
- id: slot?.id ?? "NA",
- stationId: slot?.stationId ?? "NA",
- name: slot?.ChargingStation?.name ?? "NA",
- date: formattedDate ?? "NA",
- startTime: startTime ?? "NA",
- endTime: endTime ?? "NA",
- isAvailable: slot?.isAvailable ? "Yes" : "No",
- };
- })
+ const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD");
+ const startTime = dayjs(slot?.startTime).format("HH:mm");
+ const endTime = dayjs(slot?.endTime).format("HH:mm");
+
+ return {
+ srno: index + 1,
+ id: slot?.id ?? "NA",
+ stationId: slot?.stationId ?? "NA",
+ name: slot?.ChargingStation?.name ?? "NA",
+ date: formattedDate ?? "NA",
+ startTime: startTime ?? "NA",
+ endTime: endTime ?? "NA",
+ isAvailable: slot?.isAvailable ? "Yes" : "No",
+ };
+ })
: [];
-console.log("Rows",slotRows)
-
return (
<>
@@ -105,6 +130,12 @@ console.log("Rows",slotRows)
handleClose={handleCloseModal}
handleAddSlot={handleAddSlot}
/>
+
>
);
}
diff --git a/src/pages/ManagerList/index.tsx b/src/pages/ManagerList/index.tsx
index 650e756..a2db5ca 100644
--- a/src/pages/ManagerList/index.tsx
+++ b/src/pages/ManagerList/index.tsx
@@ -9,11 +9,9 @@ import {
managerList,
addManager,
updateManager,
- deleteManager,
} from "../../redux/slices/managerSlice";
import { useForm } from "react-hook-form";
-
export default function ManagerList() {
const [addModalOpen, setAddModalOpen] = useState(false);
const [editModalOpen, setEditModalOpen] = useState(false);
@@ -67,7 +65,7 @@ export default function ManagerList() {
// Handle updating an existing manager
const handleUpdate = async (
- id: number,
+ id: string,
name: string,
email: string,
phone: string,
@@ -101,65 +99,29 @@ export default function ManagerList() {
{ id: "registeredAddress", label: "Station Location" },
{ id: "action", label: "Action", align: "center" },
];
- // const categoryColumns: Column[] = [
- // { id: "srno", label: "Sr No" },
- // { id: "name", label: "Name" },
- // { id: "email", label: "Email" },
- // { id: "phone", label: "Phone" },
- // { id: "stationName", label: "Station Name" }, // Added station name column
- // { id: "stationAddress", label: "Station Location" }, // Added station address column
- // { id: "action", label: "Action", align: "center" },
- // ];
-
// Filter managers based on search term
- const filteredManagers = managers?.filter(
- (manager) =>
- manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
- manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) ||
- manager.phone?.toLowerCase().includes(searchTerm.toLowerCase())
-
- );
-
- // Format rows to display manager details
- // const categoryRows = filteredManagers?.length
- // ? filteredManagers?.map(
- // (
- // manager: {
- // id: number;
- // name: string;
- // email: string;
- // phone: string;
-
-
- // },
- // index: number
- // ) => ({
- // id: manager?.id,
- // srno: index + 1,
- // name: manager?.name,
- // email: manager?.email,
- // phone: manager.phone ?? "NA",
- // })
- // )
- // : [];
-
-const categoryRows = filteredManagers?.length
- ? filteredManagers.map((manager, index) => {
- const station = manager?.ChargingStation; // Correct access to the ChargingStation data
- return {
- id: manager.id,
- srno: index + 1,
- name: manager.name,
- email: manager.email,
- phone: manager.phone ?? "NA",
- stationName: station?.name ?? "NA", // Corrected station name
- registeredAddress: station?.registeredAddress ?? "NA", // Corrected station address
- };
- })
- : [];
-
+ // const filteredManagers = managers?.filter(
+ // (manager) =>
+ // manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ // manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ // manager.phone?.toLowerCase().includes(searchTerm.toLowerCase())
+ // );
+ const categoryRows = managers?.length
+ ? managers.map((manager, index) => {
+ const station = manager?.chargingStation; // Correct access to the ChargingStation data
+ return {
+ id: manager.id,
+ srno: index + 1,
+ name: manager.name,
+ email: manager.email,
+ phone: manager.phone ?? "NA",
+ stationName: station?.name ?? "NA", // Corrected station name
+ registeredAddress: station?.registeredAddress ?? "NA", // Corrected station address
+ };
+ })
+ : [];
return (
<>
diff --git a/src/pages/ProfilePage/index.tsx b/src/pages/ProfilePage/index.tsx
index f6567a2..0de4db3 100644
--- a/src/pages/ProfilePage/index.tsx
+++ b/src/pages/ProfilePage/index.tsx
@@ -42,14 +42,24 @@ const ProfilePage = () => {
}
return (
-
+
Account Info
@@ -62,16 +72,29 @@ const ProfilePage = () => {
{user?.name || "No Admin"}
-
+
{user?.userType || "N/A"}
@@ -88,6 +111,11 @@ const ProfilePage = () => {
>
Personal Information
@@ -95,23 +123,39 @@ const ProfilePage = () => {
Edit
-
-
+
+
Full Name:
-
+
{user?.name || "N/A"}
@@ -139,10 +183,20 @@ const ProfilePage = () => {
-
+
Bio:
-
+
{user?.bio || "No bio available."}
diff --git a/src/pages/StationList/index.tsx b/src/pages/StationList/index.tsx
index 82395ed..67dae63 100644
--- a/src/pages/StationList/index.tsx
+++ b/src/pages/StationList/index.tsx
@@ -1,10 +1,6 @@
-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 { Chip } from "@mui/material";
import AddStationModal from "../../components/AddStationModal";
+import CustomTable, { Column } from "../../components/CustomTable";
import EditStationModal from "../../components/EditStationModal";
import {
createStation,
@@ -12,7 +8,11 @@ import {
toggleStatus,
updateStation,
} from "../../redux/slices/stationSlice";
-import { Chip, Switch } from "@mui/material";
+import { useEffect, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "../../redux/reducers";
+import { AppDispatch } from "../../redux/store/store";
+import { useForm } from "react-hook-form";
export default function StationList() {
const [addModalOpen, setAddModalOpen] = useState(false);
@@ -25,10 +25,13 @@ export default function StationList() {
const [rowData, setRowData] = useState(null);
const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch();
+
+ const vehicles = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicles
+ );
const stations = useSelector(
(state: RootState) => state.stationReducer.stations
);
-
useEffect(() => {
dispatch(stationList());
}, [dispatch]);
@@ -48,7 +51,8 @@ export default function StationList() {
const handleAddStation = async (data: {
name: string;
registeredAddress: string;
- totalSlots: string;
+ totalSlots: number;
+ allowedCarIds: number[];
}) => {
try {
await dispatch(createStation(data)); // Dispatch action to add Station
@@ -63,7 +67,8 @@ export default function StationList() {
id: string,
name: string,
registeredAddress: string,
- totalSlots: string
+ totalSlots: number,
+ allowedCarIds: number[]
) => {
try {
await dispatch(
@@ -72,7 +77,8 @@ export default function StationList() {
name,
registeredAddress,
totalSlots,
-
+ status: 0,
+ allowedCarIds, // Pass the updated allowedCarIds
})
);
await dispatch(stationList());
@@ -82,57 +88,49 @@ export default function StationList() {
}
};
- const handleStatusToggle = async(id: string, newStatus: number) => {
- await dispatch(toggleStatus({ id, status: newStatus }));
-
+ const handleStatusToggle = async (id: string, newStatus: number) => {
+ await dispatch(toggleStatus({ id, status: newStatus }));
};
- // Toggle station status
- // const handleStatusToggle = async (id: string, currentStatus: number) => {
- // try {
- // const newStatus = currentStatus === 1 ? 0 : 1; // Toggle between Active(1) and Inactive(0)
- // await dispatch(
- // updateStation({
- // id,
- // status: newStatus,
- // name: stations.name,
- // registeredAddress: stations.registeredAddress,
- // totalSlots: stations.totalSlots,
- // })
- // );
- // await dispatch(stationList());
- // } catch (error) {
- // console.error("Error toggling status", error);
- // }
- // };
- const filterStations = stations?.filter((station) =>
- station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
- );
- const categoryRows = filterStations?.length
- ? filterStations?.map((station: Station, index: number) => ({
- id: station.id,
- srno: index + 1,
- name: station.name,
- registeredAddress:station.registeredAddress,
- totalSlots:station.totalSlots,
- status: (
-
- ),
- statusValue: station.status,
- }))
+ // const filterStations = Array.isArray(stations)
+ // ? stations.filter((station) =>
+ // station.name
+ // .toLocaleLowerCase()
+ // .includes(searchTerm.toLowerCase())
+ // )
+ // : [];
+
+ // Mapping and formatting vehicles
+ const categoryRows = stations?.length
+ ? stations?.map((station: any, index: number) => {
+ // Format the selected vehicles from the allowedCars array
+ const formattedVehicles = station.allowedCars?.map(
+ (car: any) => car.name
+ );
+
+ // Format the vehicle list like "Tata Punch Electric, Royal" or similar
+ const vehicleDisplay = formattedVehicles
+ ? formattedVehicles.length > 1
+ ? `${formattedVehicles.slice(0, 2).join(", ")} + ${
+ formattedVehicles.length - 2
+ }`
+ : formattedVehicles.join(", ")
+ : "No vehicles"; // In case there are no vehicles selected
+
+ return {
+ id: station.id,
+ srno: index + 1,
+ name: station.name,
+ registeredAddress: station.registeredAddress,
+ totalSlots: station.totalSlots,
+ vehicles: vehicleDisplay, // Add the formatted vehicle display here
+ status:
+ station.status === 1 ? "Available" : "Not Available", // Format status text
+ statusValue: station.status, // Status value for toggling
+ };
+ })
: [];
+ console.log("Rowssss", categoryRows);
const categoryColumns: Column[] = [
{ id: "srno", label: "Sr No" },
@@ -140,35 +138,11 @@ export default function StationList() {
{ id: "name", label: "Station Name" },
{ id: "registeredAddress", label: "Station Location" },
{ id: "totalSlots", label: "Total Slots" },
- //{ id: "status", label: "Status" },
+ { id: "vehicles", label: "Vehicles" }, // Add Vehicles column here
+ { id: "status", label: "Status" }, // Add Status column here
{ id: "action", label: "Action", align: "center" },
];
- // Filter stations based on search term
- // const filterStations = stations?.filter((station) =>
- // station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
- // );
-
- // // Prepare categoryRows with toggle switch for status
- // const categoryRows = filterStations?.length
- // ? filterStations?.map((station: any, index: number) => ({
- // id: station.id,
- // srno: index + 1,
- // name: station.name,
- // status: (
- //
- // handleStatusToggle(station.id, station.status)
- // }
- // color="primary"
- // inputProps={{ "aria-label": "station-status-toggle" }}
- // />
- // ),
- // statusValue: station.status,
- // }))
- // : [];
-
return (
<>
>
);
diff --git a/src/pages/VehicleList/index.tsx b/src/pages/VehicleList/index.tsx
index 317e1d4..5edaa6d 100644
--- a/src/pages/VehicleList/index.tsx
+++ b/src/pages/VehicleList/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import CustomTable, { Column } from "../../components/CustomTable";
import { RootState } from "../../redux/reducers";
@@ -15,13 +15,11 @@ import EditVehicleModal from "../../components/EditVehicleModal";
export default function VehicleList() {
const [addModalOpen, setAddModalOpen] = useState(false);
const [editModalOpen, setEditModalOpen] = useState(false);
- const [editRow, setEditRow] = useState(null);
const { reset } = useForm();
const [deleteModal, setDeleteModal] = useState(false);
const [viewModal, setViewModal] = useState(false);
const [rowData, setRowData] = useState(null);
- const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch();
const vehicles = useSelector(
(state: RootState) => state.vehicleReducer.vehicles
@@ -32,7 +30,7 @@ export default function VehicleList() {
}, [dispatch]);
const handleClickOpen = () => {
- setRowData(null); // Reset row data when opening for new admin
+ setRowData(null);
setAddModalOpen(true);
};
@@ -44,7 +42,7 @@ export default function VehicleList() {
};
const handleAddVehicle = async (data: {
- vehicleName: string;
+ name: string;
company: string;
modelName: string;
chargeType: string;
@@ -60,7 +58,7 @@ export default function VehicleList() {
};
const handleUpdate = async (
- id: number,
+ id: string,
name: string,
company: string,
modelName: string,
@@ -95,24 +93,24 @@ export default function VehicleList() {
{ id: "action", label: "Action", align: "center" },
];
- const filteredVehicles = vehicles?.filter(
- (vehicle) =>
- vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
- vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) ||
- vehicle.modelName
- ?.toLowerCase()
- .includes(searchTerm.toLowerCase()) ||
- vehicle.chargeType
- ?.toLowerCase()
- .includes(searchTerm.toLowerCase()) ||
- vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase())
- );
+ // const filteredVehicles = vehicles?.filter(
+ // (vehicle) =>
+ // vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ // vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ // vehicle.modelName
+ // ?.toLowerCase()
+ // .includes(searchTerm.toLowerCase()) ||
+ // vehicle.chargeType
+ // ?.toLowerCase()
+ // .includes(searchTerm.toLowerCase()) ||
+ // vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase())
+ // );
- const categoryRows = filteredVehicles?.length
- ? filteredVehicles?.map(
+ const categoryRows = vehicles?.length
+ ? vehicles?.map(
(
vehicle: {
- id: number;
+ id: string;
name: string;
company: string;
modelName: string;
@@ -132,8 +130,6 @@ export default function VehicleList() {
)
: [];
-
-
return (
<>
-
>
);
}
diff --git a/src/redux/slices/VehicleSlice.ts b/src/redux/slices/VehicleSlice.ts
index c4de897..77c3310 100644
--- a/src/redux/slices/VehicleSlice.ts
+++ b/src/redux/slices/VehicleSlice.ts
@@ -1,9 +1,15 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https";
import { toast } from "sonner";
+interface VehicleBrand {
+ name: string;
+ id: string;
+ company: string;
+}
interface Vehicle {
- id: number;
+ brandId?: string;
+ id: string;
name: string;
company: string;
modelName: string;
@@ -11,18 +17,41 @@ interface Vehicle {
imageUrl: string;
}
interface VehicleState {
+ vehicleBrands: VehicleBrand[];
vehicles: Vehicle[];
loading: boolean;
error: string | null;
}
const initialState: VehicleState = {
+ vehicleBrands: [],
vehicles: [],
loading: false,
error: null,
};
+export const fetchVehicleBrands = createAsyncThunk<
+ VehicleBrand[],
+ void,
+ { rejectValue: string }
+>("vehicle/fetchVehicleBrands", async (_, { rejectWithValue }) => {
+ try {
+ const response = await http.get("/get-vehicle-brand");
+
+ if (!response.data || !Array.isArray(response.data.data)) {
+ throw new Error("Expected array of vehicle brands");
+ }
+
+ // Map the brand names (strings) to objects with 'id' and 'name' properties
+ return response.data.data.map((brand: string) => ({
+ id: brand, // Use brand name as the ID
+ name: brand, // Use brand name as the label
+ }));
+ } catch (error: any) {
+ return rejectWithValue("Failed to fetch vehicle brands");
+ }
+});
export const vehicleList = createAsyncThunk<
- Vehicle,
+ Vehicle[],
void,
{ rejectValue: string }
>("fetchVehicles", async (_, { rejectWithValue }) => {
@@ -122,6 +151,22 @@ const vehicleSlice = createSlice({
state.loading = false;
state.error = action.error.message || "Failed to fetch users";
})
+ .addCase(fetchVehicleBrands.pending, (state) => {
+ state.loading = true;
+ })
+ .addCase(
+ fetchVehicleBrands.fulfilled,
+ (state, action: PayloadAction) => {
+ state.loading = false;
+ state.vehicleBrands = action.payload;
+ }
+ )
+
+ .addCase(fetchVehicleBrands.rejected, (state, action) => {
+ state.loading = false;
+ state.error =
+ action.payload || "Failed to fetch vehicle brands";
+ })
.addCase(addVehicle.pending, (state) => {
state.loading = true;
// state.error = null;
@@ -133,12 +178,9 @@ const vehicleSlice = createSlice({
state.vehicles.push(action.payload);
}
)
- .addCase(
- addVehicle.rejected,
- (state, action: PayloadAction) => {
- state.loading = false;
- }
- )
+ .addCase(addVehicle.rejected, (state) => {
+ state.loading = false;
+ })
.addCase(updateVehicle.pending, (state) => {
state.loading = true;
})
diff --git a/src/redux/slices/bookSlice.ts b/src/redux/slices/bookSlice.ts
index b96e81c..33335aa 100644
--- a/src/redux/slices/bookSlice.ts
+++ b/src/redux/slices/bookSlice.ts
@@ -60,6 +60,7 @@ export const getCarPorts = createAsyncThunk<
>("fetchCarPorts", async (_, { rejectWithValue }) => {
try {
const response = await http.get("/get-vehicle-port-dropdown");
+
return response.data.data; // Adjust based on actual API response
} catch (error: any) {
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({
name: "booking",
initialState,
@@ -167,7 +183,32 @@ const bookSlice = createSlice({
(state, action: PayloadAction) => {
state.carPorts = action.payload;
}
- );
+ )
+ .addCase(deleteBooking.pending, (state) => {
+ state.loading = true;
+ })
+ .addCase(
+ deleteBooking.fulfilled,
+ (state, action: PayloadAction) => {
+ 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";
+ });
},
});
diff --git a/src/redux/slices/managerSlice.ts b/src/redux/slices/managerSlice.ts
index 783668d..a32cf0f 100644
--- a/src/redux/slices/managerSlice.ts
+++ b/src/redux/slices/managerSlice.ts
@@ -6,11 +6,12 @@ import { toast } from "sonner";
interface Manager {
Manager: any;
- id: number;
+ id: string;
name: string;
email: string;
phone: string;
stationId: string;
+ chargingStation:{name:string, registeredAddress:string};
}
interface ManagerState {
@@ -73,7 +74,7 @@ export const addManager = createAsyncThunk<
// Update Manager (Async Thunk)
export const updateManager = createAsyncThunk<
Manager,
- { id: number; managerData: Manager },
+ { id: string; managerData: Manager },
{ rejectValue: string }
>("updateManager", async ({ id, managerData }, { rejectWithValue }) => {
if (!id) {
@@ -174,6 +175,9 @@ const managerSlice = createSlice({
})
.addCase(deleteManager.fulfilled, (state, action) => {
state.loading = false;
+ state.managers = state.managers.filter(
+ (manager) => String(manager.id) !== String(action.payload)
+ );
})
.addCase(deleteManager.rejected, (state, action) => {
state.loading = false;
diff --git a/src/redux/slices/slotSlice.ts b/src/redux/slices/slotSlice.ts
index 2541727..04a9b58 100644
--- a/src/redux/slices/slotSlice.ts
+++ b/src/redux/slices/slotSlice.ts
@@ -5,7 +5,7 @@ import { toast } from "sonner";
// Define TypeScript types
interface Slot {
id: string;
- stationId:string;
+ stationId: string;
date: string;
startTime: string;
endTime: string;
@@ -88,13 +88,23 @@ export const createSlot = createAsyncThunk<
// Update Slot details
export const updateSlot = createAsyncThunk<
Slot,
- { id: number; startTime: string; endTime: string }, // Argument type (slot update data)
+ {
+ id: string;
+ startTime: string;
+ endTime: string;
+ isAvailable: boolean;
+ },
{ rejectValue: string }
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
try {
- const response = await http.patch(`/slots/${id}`, slotData);
+ const response = await http.patch(`/update-availability/${id}`, {
+ ...slotData,
+ // Ensure data matches exactly what backend expects
+ startHour: slotData.startTime,
+ endHour: slotData.endTime,
+ });
toast.success("Slot updated successfully");
- return response.data.data; // Return updated slot data
+ return response.data.data;
} catch (error: any) {
toast.error("Error updating the slot: " + error?.message);
return rejectWithValue(
@@ -164,14 +174,15 @@ const slotSlice = createSlice({
(state, action: PayloadAction) => {
state.loading = false;
// 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
);
if (index !== -1) {
- state.slots[index] = action.payload;
+ state.availableSlots[index] = action.payload;
}
}
)
+
.addCase(updateSlot.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to update slot";
diff --git a/src/redux/slices/stationSlice.ts b/src/redux/slices/stationSlice.ts
index f5e4203..a9ead8a 100644
--- a/src/redux/slices/stationSlice.ts
+++ b/src/redux/slices/stationSlice.ts
@@ -8,8 +8,9 @@ interface Station {
id: string;
name: string;
registeredAddress: string;
- totalSlots: string;
+ totalSlots: number;
status: number;
+ allowedCarIds: number[];
}
interface StationState {
@@ -46,21 +47,43 @@ export const stationList = createAsyncThunk(
}
}
);
+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
export const createStation = createAsyncThunk<
any,
{
name: string;
registeredAddress: string;
- totalSlots: string;
+ totalSlots: number;
+ allowedCarIds: number[];
},
{ rejectValue: string }
>("Station/createStation", async (data, { rejectWithValue }) => {
try {
const response = await http.post("/create-station", data);
toast.success("Station created successfully");
- return response.data;
+ return response.data; // Assuming the response contains the created station data
} catch (error: any) {
toast.error(
"Failed to create Station: " +
@@ -72,25 +95,31 @@ export const createStation = createAsyncThunk<
}
});
+// Update Station details
// Update Station details
export const updateStation = createAsyncThunk(
"updateStation",
async ({ id, ...stationData }: Station, { rejectWithValue }) => {
try {
+ // Exclude the `status` from the update payload
+ const { status, ...updateData } = stationData;
+
+ // Send the update request without the `status`
const response = await http.patch(
`/update-station/${id}`,
- stationData
+ updateData
);
- toast.success("Station Deatils updated successfully");
+ toast.success("Station Details updated successfully");
return response?.data;
} catch (error: any) {
- toast.error("Error updating the user: " + error);
+ toast.error("Error updating the station: " + error);
return rejectWithValue(
error.response?.data?.message || "An error occurred"
);
}
}
);
+
export const deleteStation = createAsyncThunk<
string,
string,
@@ -156,17 +185,30 @@ const stationSlice = createSlice({
stationList.fulfilled,
(state, action: PayloadAction) => {
state.loading = false;
- // Properly extract stations from the response data structure
- state.stations =
- action.payload.data?.results ||
- action.payload.data ||
- [];
+ // Correct data extraction
+ state.stations = action.payload.data?.stations || [];
}
)
.addCase(stationList.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to fetch stations";
})
+ .addCase(getAllStations.pending, (state) => {
+ state.loading = true;
+ state.error = null;
+ })
+ .addCase(
+ getAllStations.fulfilled,
+ (state, action: PayloadAction) => {
+ 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) => {
state.loading = true;
})
@@ -174,12 +216,12 @@ const stationSlice = createSlice({
createStation.fulfilled,
(state, action: PayloadAction) => {
state.loading = false;
- // Add the newly created station to the state if it exists in the response
if (action.payload.data) {
state.stations.push(action.payload.data);
}
}
)
+
.addCase(
createStation.rejected,
(state, action: PayloadAction) => {
@@ -223,8 +265,21 @@ const stationSlice = createSlice({
})
.addCase(updateStation.fulfilled, (state, action) => {
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) => {
state.loading = false;
})