diff --git a/src/components/AddBookingModal/index.tsx b/src/components/AddBookingModal/index.tsx index c28f93a..42588c8 100644 --- a/src/components/AddBookingModal/index.tsx +++ b/src/components/AddBookingModal/index.tsx @@ -13,19 +13,20 @@ 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 { stationList } from "../../redux/slices/stationSlice.ts"; export default function AddBookingModal({ open, @@ -44,6 +45,13 @@ 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 + ); + + useEffect(() => { + dispatch(stationList()); + }, [dispatch]); useEffect(() => { // Fetch car names and car ports @@ -66,7 +74,7 @@ export default function AddBookingModal({ 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 +138,34 @@ export default function AddBookingModal({ {/* Station ID */} - Station ID + Select Station - + + Select Station + + {errors.stationName && ( + + {errors.stationName.message} + + )} + + Date @@ -190,7 +209,6 @@ export default function AddBookingModal({ size="small" error={!!errors.carName} > - Car Name + {stations.map((station) => ( + + {station.name} + + ))} + + {errors.stationName && ( + + {errors.stationName.message} + + )} + + {/* Email and Password */} {/* Email */} @@ -219,7 +251,6 @@ export default function AddManagerModal({ : "primary" } size="small" - // onMouseDown={togglePasswordVisibility} InputProps={{ endAdornment: ( @@ -247,9 +278,8 @@ export default function AddManagerModal({ - {/* Phone and Registered Address */} + {/* Phone Number */} - {/* Phone */} Phone Number @@ -272,7 +302,6 @@ export default function AddManagerModal({ })} /> - {/* Submit Button */} diff --git a/src/components/AddStationModal/index.tsx b/src/components/AddStationModal/index.tsx index 16cb174..7cb912b 100644 --- a/src/components/AddStationModal/index.tsx +++ b/src/components/AddStationModal/index.tsx @@ -1,6 +1,22 @@ 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 { vehicleList } from "../../redux/slices/VehicleSlice"; // Adjust this import path accordingly import { CustomIconButton, CustomTextField, @@ -18,10 +34,49 @@ 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 + + useEffect(() => { + dispatch(vehicleList()); // Fetch vehicles when the component mounts + }, [dispatch]); + + const [selectedVehicles, setSelectedVehicles] = useState([]); + + // Handle the change in selected vehicles (checkboxes) + const handleCheckboxChange = ( + event: React.ChangeEvent<{ value: unknown }> + ) => { + const value = event.target.value as string[]; + setSelectedVehicles(value); + }; + + // Function to map selected vehicle names to corresponding vehicle ids + const getVehicleIds = () => { + return vehicles + .filter((vehicle) => selectedVehicles.includes(vehicle.name)) + .map((vehicle) => vehicle.id); // Return an array of ids based on the selected names + }; + const onSubmit = (data: any) => { - handleAddStation(data); // Add station to the list + const vehicleIds = getVehicleIds(); // Get the ids of the selected vehicles + + // 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 + }; + + // Handle adding the station with the constructed payload + handleAddStation(payload); handleClose(); // Close modal after adding reset(); + setSelectedVehicles([]); // Reset selected vehicles after submission }; return ( @@ -77,7 +132,6 @@ export default function AddStationModal({ gap: 2, }} > - {/* First Row - Two Inputs */} + + {/* Vehicle Name Dropdown with Checkboxes */} + + + Vehicle Name + + + {/* Dropdown with Checkboxes */} + + Choose Vehicles + + + {errors.vehicleName + ? errors.vehicleName.message + : ""} + + + {/* Submit Button */} @@ -202,4 +303,4 @@ export default function AddStationModal({ ); -} +} \ No newline at end of file diff --git a/src/components/AddVehicleModal/index.tsx b/src/components/AddVehicleModal/index.tsx index f7747f8..1707720 100644 --- a/src/components/AddVehicleModal/index.tsx +++ b/src/components/AddVehicleModal/index.tsx @@ -145,7 +145,7 @@ export default function AddVehicleModal({ {...register("company", { required: "Company is required", minLength: { - value: 5, + value: 3, message: "Company must be at least 5 characters long", }, 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 ( + + + Available Slots + + + {loading ? ( + + ) : error ? ( +
Error: {error}
+ ) : Array.isArray(availableSlots) && + availableSlots.length > 0 ? ( + availableSlots.map((slot: availableSlots) => ( + handleSlotSelect(slot)} + selected={selectedSlot?.id === slot?.id} + > + {`${slot?.startTime} - ${slot?.endTime}`} + + )) + ) : ( +
No available slots
+ )} + +
+
+ ); +}; + +export default AvailableSlotsModal; diff --git a/src/components/EditStationModal/index.tsx b/src/components/EditStationModal/index.tsx index ada74a7..c6872ed 100644 --- a/src/components/EditStationModal/index.tsx +++ b/src/components/EditStationModal/index.tsx @@ -1,5 +1,16 @@ import React, { useEffect } from "react"; -import { Box, Button, Typography, Modal } from "@mui/material"; +import { + Box, + Button, + Typography, + Modal, + FormControl, + InputLabel, + Select, + MenuItem, + Checkbox, + ListItemText, +} from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import { useForm, Controller } from "react-hook-form"; import { @@ -7,6 +18,15 @@ import { CustomTextField, } from "../AddEditUserModel/styled.css.tsx"; +// Define the types for your form data +interface FormData { + name: string; + registeredAddress: string; + totalSlots: number; + status: number; + allowedCarIds: string[]; // Assuming allowedCarIds are the vehicle IDs +} + interface EditStationModalProps { open: boolean; handleClose: () => void; @@ -15,16 +35,10 @@ interface EditStationModalProps { name: string, registeredAddress: string, totalSlots: number, - status: number + allowedCarIds: string[] ) => void; - editRow: any; -} - -interface FormData { - name: string; - registeredAddress: string; - totalSlots: number; - status: number; + editRow?: any; + vehicles: { id: string; name: string }[]; } const EditStationModal: React.FC = ({ @@ -32,34 +46,42 @@ const EditStationModal: React.FC = ({ handleClose, handleUpdate, editRow, + vehicles, }) => { - const { - control, - handleSubmit, - formState: { errors }, - setValue, - reset, - } = useForm({ - defaultValues: { - name: "", - registeredAddress: "", - totalSlots: 0, - status: 1, - }, - }); + const { control, handleSubmit, setValue, reset, watch } = useForm( + { + defaultValues: { + name: "", + registeredAddress: "", + totalSlots: 0, + status: 1, + allowedCarIds: [], + }, + } + ); - // Set values if editRow is provided + // Watch allowedCarIds from the form state + const allowedCarIds = watch("allowedCarIds"); + + // Set values when editRow is provided useEffect(() => { if (editRow) { setValue("name", editRow.name); setValue("registeredAddress", editRow.registeredAddress); setValue("totalSlots", editRow.totalSlots); - setValue("status", editRow.number); + setValue("status", editRow.status); + + // Set the allowedCarIds with the previously selected vehicle IDs + setValue( + "allowedCarIds", + editRow.allowedCarIds?.map((car: any) => car.id) || [] + ); } else { reset(); } }, [editRow, setValue, reset]); + // Handle form submit const onSubmit = (data: FormData) => { if (editRow) { handleUpdate( @@ -67,10 +89,10 @@ const EditStationModal: React.FC = ({ data.name, data.registeredAddress, data.totalSlots, - data.status + data.allowedCarIds ); } - handleClose(); // Close the modal + handleClose(); // Close the modal after update reset(); // Reset the form fields }; @@ -140,27 +162,12 @@ const EditStationModal: React.FC = ({ ( )} /> @@ -183,19 +190,12 @@ const EditStationModal: React.FC = ({ ( )} /> @@ -221,13 +221,6 @@ const EditStationModal: React.FC = ({ ( = ({ placeholder="Enter Total Slots" size="small" type="number" - error={!!errors.totalSlots} - helperText={errors.totalSlots?.message} /> )} /> + {/* Vehicle Dropdown with Checkboxes */} + + + Vehicle Names + + + Choose Vehicles + + + {/* Submit Button */} diff --git a/src/lib/https.ts b/src/lib/https.ts index 817f2d9..612552c 100644 --- a/src/lib/https.ts +++ b/src/lib/https.ts @@ -16,9 +16,6 @@ http.interceptors.response.use( (error) => { if (error.response && error.response.status === 401) { - - - // window.location.href = "/login"; // const history = useHistory(); diff --git a/src/pages/StationList/index.tsx b/src/pages/StationList/index.tsx index 82395ed..fc0d6a0 100644 --- a/src/pages/StationList/index.tsx +++ b/src/pages/StationList/index.tsx @@ -1,18 +1,13 @@ -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, - stationList, - toggleStatus, - updateStation, -} from "../../redux/slices/stationSlice"; -import { Chip, Switch } from "@mui/material"; +import { createStation, stationList, toggleStatus, updateStation } from "../../redux/slices/stationSlice"; +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); @@ -28,6 +23,10 @@ export default function StationList() { const stations = useSelector( (state: RootState) => state.stationReducer.stations ); + const vehicles = useSelector( + (state: RootState) => state.vehicleReducer.vehicles + ); + useEffect(() => { dispatch(stationList()); @@ -63,7 +62,8 @@ export default function StationList() { id: string, name: string, registeredAddress: string, - totalSlots: string + totalSlots: string, + allowedCarIds: number[] ) => { try { await dispatch( @@ -72,7 +72,8 @@ export default function StationList() { name, registeredAddress, totalSlots, - + status: 0, + allowedCarIds, // Pass the updated allowedCarIds }) ); await dispatch(stationList()); @@ -82,56 +83,44 @@ 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()) ); + // Mapping and formatting vehicles 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, - })) + ? filterStations?.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 + }; + }) : []; const categoryColumns: Column[] = [ @@ -140,35 +129,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/redux/slices/stationSlice.ts b/src/redux/slices/stationSlice.ts index f5e4203..a6e4246 100644 --- a/src/redux/slices/stationSlice.ts +++ b/src/redux/slices/stationSlice.ts @@ -10,6 +10,7 @@ interface Station { registeredAddress: string; totalSlots: string; status: number; + allowedCarIds: number[]; } interface StationState { @@ -54,6 +55,7 @@ export const createStation = createAsyncThunk< name: string; registeredAddress: string; totalSlots: string; + allowedCarIds: number[]; }, { rejectValue: string } >("Station/createStation", async (data, { rejectWithValue }) => {