diff --git a/src/components/AddBookingModal/index.tsx b/src/components/AddBookingModal/index.tsx
index 42588c8..21fa1f8 100644
--- a/src/components/AddBookingModal/index.tsx
+++ b/src/components/AddBookingModal/index.tsx
@@ -27,6 +27,8 @@ import {
CustomTextField,
} from "../AddEditUserModel/styled.css.tsx";
import { stationList } from "../../redux/slices/stationSlice.ts";
+import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
+import dayjs from "dayjs";
export default function AddBookingModal({
open,
@@ -48,8 +50,12 @@ export default function AddBookingModal({
const stations = useSelector(
(state: RootState) => state?.stationReducer.stations
);
-
+ const availableSlots = useSelector(
+ (state: RootState) => state.slotReducer.availableSlots
+ );
+ console.log("first", availableSlots);
useEffect(() => {
+ dispatch(fetchAvailableSlots());
dispatch(stationList());
}, [dispatch]);
@@ -284,7 +290,7 @@ export default function AddBookingModal({
{/* Start Time and End Time */}
- {/* Start Time */}
+
Start Time
@@ -305,7 +311,7 @@ export default function AddBookingModal({
/>
- {/* End Time */}
+
End Time
@@ -346,6 +352,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/AddStationModal/index.tsx b/src/components/AddStationModal/index.tsx
index 7cb912b..ff7ba74 100644
--- a/src/components/AddStationModal/index.tsx
+++ b/src/components/AddStationModal/index.tsx
@@ -16,7 +16,10 @@ 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 { vehicleList } from "../../redux/slices/VehicleSlice"; // Adjust this import path accordingly
+import {
+ fetchVehicleBrands,
+ vehicleList,
+} from "../../redux/slices/VehicleSlice"; // Adjust this import path accordingly
import {
CustomIconButton,
CustomTextField,
@@ -40,10 +43,18 @@ export default function AddStationModal({
const vehicles = useSelector(
(state: RootState) => state.vehicleReducer.vehicles
); // Adjust according to your actual state structure
+ const vehicleBrands = useSelector(
+ (state: RootState) => state.vehicleReducer.vehicleBrands
+ );
+ const [selectedBrand, setSelectedBrand] = useState("");
useEffect(() => {
+ dispatch(fetchVehicleBrands());
dispatch(vehicleList()); // Fetch vehicles when the component mounts
}, [dispatch]);
+ const filteredVehicles = vehicles.filter(
+ (vehicle) => vehicle.company === selectedBrand
+ );
const [selectedVehicles, setSelectedVehicles] = useState([]);
@@ -57,26 +68,36 @@ export default function AddStationModal({
// Function to map selected vehicle names to corresponding vehicle ids
const getVehicleIds = () => {
+ console.log("Selected Vehicles: ", selectedVehicles);
+ console.log("Vehicles List: ", vehicles);
return vehicles
.filter((vehicle) => selectedVehicles.includes(vehicle.name))
- .map((vehicle) => vehicle.id); // Return an array of ids based on the selected names
+ .map((vehicle) => vehicle.id);
};
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
+ console.log("Vehicle IDs: ", vehicleIds);
// Prepare the data to be sent to the backend
const payload = {
...data,
status: 1, // Default status, can be adjusted if needed
allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend
+ 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);
handleClose(); // Close modal after adding
reset();
- setSelectedVehicles([]); // Reset selected vehicles after submission
+ setSelectedVehicles([]);
+ setSelectedBrand(""); // Reset selected vehicles after submission
};
return (
@@ -231,50 +252,103 @@ export default function AddStationModal({
{/* Vehicle Name Dropdown with Checkboxes */}
-
-
- Vehicle Name
-
+
+
+
+ Select Vehicle Brand
+
+
+ Choose Brand
+
+
+ {errors.vehicleBrand
+ ? errors.vehicleBrand.message
+ : ""}
+
+
+
- {/* Dropdown with Checkboxes */}
-
- Choose Vehicles
-
-
- {errors.vehicleName
- ? errors.vehicleName.message
- : ""}
-
-
+ {/* Vehicle Name Dropdown with Checkboxes */}
+
+
+ Vehicle Name
+
+
+
+ Choose Vehicles
+
+
+ {errors.vehicleName
+ ? errors.vehicleName.message
+ : ""}
+
+
+
@@ -303,4 +377,4 @@ export default function AddStationModal({
);
-}
\ No newline at end of file
+}
diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx
index b0a76f9..2337733 100644
--- a/src/components/CustomTable/index.tsx
+++ b/src/components/CustomTable/index.tsx
@@ -108,7 +108,7 @@ const CustomTable: React.FC = ({
const [searchQuery, setSearchQuery] = React.useState("");
const [currentPage, setCurrentPage] = React.useState(1);
const usersPerPage = 10;
- const { user, isLoading } = useSelector(
+ const { user, isLoading } = useSelector(
(state: RootState) => state?.profileReducer
);
const open = Boolean(anchorEl);
@@ -137,7 +137,6 @@ const CustomTable: React.FC = ({
}
switch (tableType) {
-
case "admin":
dispatch(deleteAdmin(id || ""));
break;
@@ -484,168 +483,175 @@ const CustomTable: React.FC = ({
/>
{/* Menu Actions */}
- {open && (
-
- )}
+
+ {tableType === "role" && (
+
+ )}
+ {tableType === "station" && (
+
+ )}
+
+
+
+
+ )}
{/* Modals */}
{deleteModal && (
= ({
email: data.email,
phone: data.phone,
stationId: data.stationId,
-
+ Manager: undefined,
+ id: 0,
+ chargingStation: ""
},
})
).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..f1ad819
--- /dev/null
+++ b/src/components/EditSlotModal/index.tsx
@@ -0,0 +1,263 @@
+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
+
+interface EditSlotModalProps {
+ open: boolean;
+ handleClose: () => void;
+ 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(); // Use dispatch to send Redux actions
+ 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
+ ); // Default to editRow availability
+
+ // Set values if editRow is provided
+ useEffect(() => {
+ if (editRow) {
+ setValue("date", editRow.date);
+ setValue("startTime", editRow.startTime);
+ setValue("endTime", editRow.endTime);
+ setIsAvailable(editRow.isAvailable); // Set the initial availability correctly
+ } else {
+ reset();
+ }
+ }, [editRow, setValue, reset]);
+
+ const onSubmit = async (data: FormData) => {
+ if (editRow) {
+ try {
+ // Convert boolean availability to 1/0
+ const availabilityStatus = isAvailable ? true : false;
+
+ await dispatch(
+ updateSlot({
+ id: editRow.id, // Slot ID
+ slotData: {
+ date: data.date,
+ startTime: data.startTime,
+ endTime: data.endTime,
+ isAvailable: availabilityStatus,
+ },
+ })
+ ).unwrap(); // Ensure that it throws an error if the update fails
+ dispatch(fetchAvailableSlots()); // Assuming this action fetches the updated slot list
+ handleClose(); // Close modal on success
+ reset(); // Reset form fields after submit
+ } catch (error) {
+ console.error(error);
+ // Handle the error or show a toast
+ } 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/lib/https.ts b/src/lib/https.ts
index 612552c..b6ad723 100644
--- a/src/lib/https.ts
+++ b/src/lib/https.ts
@@ -15,8 +15,8 @@ http.interceptors.response.use(
(response) => response,
(error) => {
- if (error.response && error.response.status === 401) {
- // window.location.href = "/login";
+ if (error.response && error.response.status === 403 ) {
+ window.location.href = "/login";
// const history = useHistory();
// history.push("/login");
diff --git a/src/pages/EvSlotList/index.tsx b/src/pages/EvSlotList/index.tsx
index 207f26e..4e8e0c7 100644
--- a/src/pages/EvSlotList/index.tsx
+++ b/src/pages/EvSlotList/index.tsx
@@ -4,9 +4,15 @@ 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 EditManagerModal from "../../components/EditManagerModal";
+import EditSlotModal from "../../components/EditSlotModal";
export default function EVSlotList() {
const [addModalOpen, setAddModalOpen] = useState(false);
const [editModalOpen, setEditModalOpen] = useState(false);
@@ -50,6 +56,31 @@ export default function EVSlotList() {
console.error("Error adding slot", error);
}
};
+ console.log("isAvailable type:", typeof isAvailable);
+ const handleUpdate = async (
+ id: string,
+ date: string,
+ startTime: string,
+ endTime: string,
+ isAvailable: boolean
+ ) => {
+ try {
+ // Update manager with stationId
+ await dispatch(
+ updateSlot({
+ id,
+ date,
+ startTime,
+ endTime,
+ isAvailable,
+ })
+ );
+ await dispatch(fetchAvailableSlots()); // Refresh the list after update
+ handleCloseModal(); // Close modal after update
+ } catch (error) {
+ console.error("Update failed", error);
+ }
+ };
const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" },
@@ -66,25 +97,23 @@ export default function EVSlotList() {
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");
+ console.log("availability", availableSlots.isAvailable);
+ 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",
+ };
+ })
: [];
-console.log("Rows",slotRows)
-
return (
<>
@@ -105,6 +134,12 @@ console.log("Rows",slotRows)
handleClose={handleCloseModal}
handleAddSlot={handleAddSlot}
/>
+
>
);
}
diff --git a/src/redux/slices/VehicleSlice.ts b/src/redux/slices/VehicleSlice.ts
index c4de897..ba71ebb 100644
--- a/src/redux/slices/VehicleSlice.ts
+++ b/src/redux/slices/VehicleSlice.ts
@@ -1,9 +1,14 @@
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https";
import { toast } from "sonner";
+interface VehicleBrand {
+ id: string;
+ company: string;
+}
interface Vehicle {
- id: number;
+ brandId: string;
+ id: string;
name: string;
company: string;
modelName: string;
@@ -11,15 +16,36 @@ 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");
+ }
+ // Assuming that 'data' contains an array of objects with a 'company' field
+ return response.data.data.map((item: any) => ({
+ id: item.company, // You can use 'company' as the unique identifier
+ name: item.company, // The name field will be used in the dropdown
+ }));
+ } catch (error: any) {
+ return rejectWithValue("Failed to fetch vehicle brands");
+ }
+});
export const vehicleList = createAsyncThunk<
Vehicle,
@@ -122,6 +148,21 @@ 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;
diff --git a/src/redux/slices/slotSlice.ts b/src/redux/slices/slotSlice.ts
index 2541727..467d2ee 100644
--- a/src/redux/slices/slotSlice.ts
+++ b/src/redux/slices/slotSlice.ts
@@ -92,7 +92,10 @@ export const updateSlot = createAsyncThunk<
{ 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
+ );
toast.success("Slot updated successfully");
return response.data.data; // Return updated slot data
} catch (error: any) {