From fdff9c1dd92addeaee32d0c558f957e3c1a9b07d Mon Sep 17 00:00:00 2001 From: jaanvi Date: Mon, 24 Mar 2025 18:33:32 +0530 Subject: [PATCH 1/7] Ui fixes --- src/components/CustomTable/index.tsx | 30 ++++++--- src/components/LineChartCard/index.tsx | 35 ++++++++--- src/components/MainGrid/index.tsx | 2 +- src/components/MenuContent/index.tsx | 41 +++++++++---- src/components/ResourcePieChart/index.tsx | 23 +++++-- src/components/SessionsChart/index.tsx | 66 +++++++++++++++----- src/components/SideMenu/index.tsx | 19 +++--- src/components/StatCard/index.tsx | 25 +++++++- src/components/barChartCard/index.tsx | 51 +++++++++------- src/lib/https.ts | 5 +- src/pages/AdminList/index.tsx | 6 +- src/pages/ManagerList/index.tsx | 2 +- src/pages/ProfilePage/index.tsx | 74 ++++++++++++++++++++--- src/redux/slices/managerSlice.ts | 1 + 14 files changed, 281 insertions(+), 99 deletions(-) diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index 2090518..b0a76f9 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -257,12 +257,11 @@ const CustomTable: React.FC = ({ ? "Slot" : "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,6 +316,7 @@ const CustomTable: React.FC = ({ color: "white", borderRadius: "8px", width: "184px", + marginRight: "16px", "&:hover": { backgroundColor: "#439BC1" }, }} onClick={() => handleClickOpen()} @@ -343,15 +348,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 +395,13 @@ const CustomTable: React.FC = ({ ))} - + {currentRows.map((row, rowIndex) => ( {columns.map((column) => ( @@ -433,7 +450,6 @@ const CustomTable: React.FC = ({ - {/* Pagination */} = ({ }} /> - {/* Menu Actions */} {open && ( = ({ )} - {/* Modals */} {deleteModal && ( @@ -43,7 +43,14 @@ export default function LineChartCard() { return (
Weekly - +
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..f8eeb7c 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,33 +36,33 @@ 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" && { @@ -67,17 +72,17 @@ export default function MenuContent({ hidden }: PropType) { // }, 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,7 +90,14 @@ export default function MenuContent({ hidden }: PropType) { const filteredMenuItems = baseMenuItems.filter(Boolean); return ( - + {filteredMenuItems.map((item, index) => ( 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..62e2469 100644 --- a/src/components/SideMenu/index.tsx +++ b/src/components/SideMenu/index.tsx @@ -61,22 +61,20 @@ export default function SideMenu() { flexDirection: "row", alignItems: "center", pt: 2, - pl: 2, + pl: 2, }} > - 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 (
Monthly - +
{ http.interceptors.response.use( (response) => response, (error) => { + if (error.response && error.response.status === 401) { - window.location.href = "/login"; + + + // window.location.href = "/login"; // const history = useHistory(); // history.push("/login"); 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/ManagerList/index.tsx b/src/pages/ManagerList/index.tsx index 650e756..6946dfc 100644 --- a/src/pages/ManagerList/index.tsx +++ b/src/pages/ManagerList/index.tsx @@ -146,7 +146,7 @@ export default function ManagerList() { const categoryRows = filteredManagers?.length ? filteredManagers.map((manager, index) => { - const station = manager?.ChargingStation; // Correct access to the ChargingStation data + const station = manager?.chargingStation; // Correct access to the ChargingStation data return { id: manager.id, srno: index + 1, 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/redux/slices/managerSlice.ts b/src/redux/slices/managerSlice.ts index 783668d..410c1c9 100644 --- a/src/redux/slices/managerSlice.ts +++ b/src/redux/slices/managerSlice.ts @@ -11,6 +11,7 @@ interface Manager { email: string; phone: string; stationId: string; + chargingStation:string; } interface ManagerState { From 4df93ec1d9ad35da5bcf5a7496268b59eb21a89a Mon Sep 17 00:00:00 2001 From: jaanvi Date: Tue, 25 Mar 2025 18:21:36 +0530 Subject: [PATCH 2/7] Charging station new updates in api integration and mangerModal bookingModal changes --- src/components/AddBookingModal/index.tsx | 56 ++++--- src/components/AddManagerModal/index.tsx | 77 +++++++--- src/components/AddStationModal/index.tsx | 109 +++++++++++++- src/components/AddVehicleModal/index.tsx | 2 +- src/components/AvailableSlotModal/index.tsx | 108 +++++++++++++ src/components/EditStationModal/index.tsx | 159 ++++++++++++-------- src/lib/https.ts | 3 - src/pages/StationList/index.tsx | 136 +++++++---------- src/redux/slices/stationSlice.ts | 2 + 9 files changed, 457 insertions(+), 195 deletions(-) create mode 100644 src/components/AvailableSlotModal/index.tsx 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 }) => { From e96759ef3b15ac1b043edee9f0f194f2d397a8d9 Mon Sep 17 00:00:00 2001 From: jaanvi Date: Wed, 26 Mar 2025 18:40:09 +0530 Subject: [PATCH 3/7] Booking Slots and EditModal for Availble slots Changes --- src/components/AddBookingModal/index.tsx | 120 +++++++- src/components/AddStationModal/index.tsx | 168 ++++++++---- src/components/CustomTable/index.tsx | 316 +++++++++++----------- src/components/EditManagerModal/index.tsx | 4 +- src/components/EditSlotModal/index.tsx | 263 ++++++++++++++++++ src/lib/https.ts | 4 +- src/pages/EvSlotList/index.tsx | 73 +++-- src/redux/slices/VehicleSlice.ts | 43 ++- src/redux/slices/slotSlice.ts | 5 +- 9 files changed, 767 insertions(+), 229 deletions(-) create mode 100644 src/components/EditSlotModal/index.tsx 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 && ( - - - - {viewModal && tableType === "admin" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "vehicle" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "manager" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "user" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "station" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - - - {tableType === "role" && ( - - )} - {tableType === "station" && ( - - )} + + {viewModal && tableType === "admin" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "vehicle" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "manager" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "user" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} + {viewModal && tableType === "station" && ( + + handleViewButton(selectedRow?.id) + } + open={viewModal} + setViewModal={setViewModal} + id={selectedRow?.id} + /> + )} - - - - )} + + {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) { From 0f33303a4287ce20f16c7afae54378a3df2564da Mon Sep 17 00:00:00 2001 From: jaanvi Date: Thu, 27 Mar 2025 18:39:38 +0530 Subject: [PATCH 4/7] made changes in Charging Station and slots booking modals --- src/components/AddBookingModal/index.tsx | 12 +- src/components/AddManagerModal/index.tsx | 21 +- src/components/AddStationModal/index.tsx | 87 ++--- src/components/AddVehicleModal/index.tsx | 61 ++-- src/components/CustomTable/index.tsx | 324 +++++++++--------- src/components/EditManagerModal/index.tsx | 4 +- src/components/EditSlotModal/index.tsx | 12 +- src/components/EditStationModal/index.tsx | 277 ++++++++++----- src/components/Header/index.tsx | 16 +- src/components/MenuContent/index.tsx | 10 +- .../Modals/ViewManagerModal/index.tsx | 8 +- src/layouts/DashboardLayout/index.tsx | 4 - src/lib/https.ts | 30 +- src/pages/EvSlotList/index.tsx | 6 +- src/pages/StationList/index.tsx | 17 +- src/redux/slices/VehicleSlice.ts | 14 +- src/redux/slices/bookSlice.ts | 45 ++- src/redux/slices/managerSlice.ts | 3 + src/redux/slices/slotSlice.ts | 11 +- src/redux/slices/stationSlice.ts | 79 ++++- 20 files changed, 633 insertions(+), 408 deletions(-) diff --git a/src/components/AddBookingModal/index.tsx b/src/components/AddBookingModal/index.tsx index 21fa1f8..06bfb82 100644 --- a/src/components/AddBookingModal/index.tsx +++ b/src/components/AddBookingModal/index.tsx @@ -26,7 +26,7 @@ import { CustomIconButton, CustomTextField, } from "../AddEditUserModel/styled.css.tsx"; -import { stationList } from "../../redux/slices/stationSlice.ts"; +import { getAllStations, stationList } from "../../redux/slices/stationSlice.ts"; import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts"; import dayjs from "dayjs"; @@ -56,7 +56,7 @@ export default function AddBookingModal({ console.log("first", availableSlots); useEffect(() => { dispatch(fetchAvailableSlots()); - dispatch(stationList()); + dispatch(getAllStations()); }, [dispatch]); useEffect(() => { @@ -74,7 +74,7 @@ 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]; @@ -266,9 +266,9 @@ export default function AddBookingModal({ {carPorts.map((port, index) => ( - {port.chargeType} + {port} ))} @@ -290,7 +290,6 @@ export default function AddBookingModal({ {/* Start Time and End Time */} - Start Time @@ -311,7 +310,6 @@ export default function AddBookingModal({ /> - End Time diff --git a/src/components/AddManagerModal/index.tsx b/src/components/AddManagerModal/index.tsx index 23f9fb4..41f745c 100644 --- a/src/components/AddManagerModal/index.tsx +++ b/src/components/AddManagerModal/index.tsx @@ -172,14 +172,21 @@ useEffect(() => { defaultValue="" size="small" > - {stations.map((station) => ( - - {station.name} + {Array.isArray(stations) && + stations.length > 0 ? ( + stations.map((station) => ( + + {station.name} + + )) + ) : ( + + No stations available - ))} + )} {errors.stationName && ( diff --git a/src/components/AddStationModal/index.tsx b/src/components/AddStationModal/index.tsx index ff7ba74..f73b639 100644 --- a/src/components/AddStationModal/index.tsx +++ b/src/components/AddStationModal/index.tsx @@ -46,58 +46,59 @@ export default function AddStationModal({ 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 - ); + // State for selected vehicle brand and vehicles + const [selectedBrands, setSelectedBrands] = useState([]); const [selectedVehicles, setSelectedVehicles] = useState([]); - // Handle the change in selected vehicles (checkboxes) - const handleCheckboxChange = ( + // 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[]; - setSelectedVehicles(value); + setSelectedBrands(value); // Update the selected vehicle brands + }; + + // Handle changes in selected vehicles (checkboxes) + const handleVehicleChange = ( + event: React.ChangeEvent<{ value: unknown }> + ) => { + setSelectedVehicles(event.target.value as string[]); }; // Function to map selected vehicle names to corresponding vehicle ids const getVehicleIds = () => { - console.log("Selected Vehicles: ", selectedVehicles); - console.log("Vehicles List: ", vehicles); return vehicles .filter((vehicle) => selectedVehicles.includes(vehicle.name)) .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 + status: 1, // Default status 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([]); - setSelectedBrand(""); // Reset selected vehicles after submission + setSelectedBrands([]); // Reset selected brands after submission }; return ( @@ -251,7 +252,7 @@ export default function AddStationModal({ - {/* Vehicle Name Dropdown with Checkboxes */} + {/* Vehicle Brand Dropdown with Checkboxes */} - Select Vehicle Brand + Select Vehicle Brands - Choose Brand + Choose Brands diff --git a/src/components/AddVehicleModal/index.tsx b/src/components/AddVehicleModal/index.tsx index 1707720..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/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index 2337733..7db5231 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -40,7 +40,7 @@ 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}`]: { @@ -155,6 +155,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; @@ -483,175 +486,174 @@ const CustomTable: React.FC = ({ /> {/* Menu Actions */} - {open && - ( - + + + {tableType === "role" && ( + - {viewModal && tableType === "admin" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "vehicle" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "manager" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "user" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} - {viewModal && tableType === "station" && ( - - handleViewButton(selectedRow?.id) - } - open={viewModal} - setViewModal={setViewModal} - id={selectedRow?.id} - /> - )} + {selectedRow?.statusValue === 1 + ? "Deactivate" + : "Activate"} + + )} + {tableType === "station" && ( + + )} - - {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 index f1ad819..dbf581e 100644 --- a/src/components/EditSlotModal/index.tsx +++ b/src/components/EditSlotModal/index.tsx @@ -52,7 +52,7 @@ const EditSlotModal: React.FC = ({ const [loading, setLoading] = useState(false); const [isAvailable, setIsAvailable] = useState( editRow?.isAvailable || false - ); // Default to editRow availability + ); // Set values if editRow is provided useEffect(() => { @@ -68,6 +68,8 @@ const EditSlotModal: React.FC = ({ const onSubmit = async (data: FormData) => { if (editRow) { + setLoading(true); // Start loading + try { // Convert boolean availability to 1/0 const availabilityStatus = isAvailable ? true : false; @@ -83,12 +85,14 @@ const EditSlotModal: React.FC = ({ }, }) ).unwrap(); // Ensure that it throws an error if the update fails - dispatch(fetchAvailableSlots()); // Assuming this action fetches the updated slot list + + // Refresh the list after updating the slot + dispatch(fetchAvailableSlots()); handleClose(); // Close modal on success reset(); // Reset form fields after submit } catch (error) { - console.error(error); - // Handle the error or show a toast + console.error("Error updating slot:", error); + // Handle the error or show a toast message } finally { setLoading(false); // Stop loading state } diff --git a/src/components/EditStationModal/index.tsx b/src/components/EditStationModal/index.tsx index c6872ed..047e6ef 100644 --- a/src/components/EditStationModal/index.tsx +++ b/src/components/EditStationModal/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { Box, Button, @@ -10,21 +10,27 @@ import { 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 -// Define the types for your form data interface FormData { name: string; registeredAddress: string; totalSlots: number; + allowedCarIds: string[]; status: number; - allowedCarIds: string[]; // Assuming allowedCarIds are the vehicle IDs } interface EditStationModalProps { @@ -35,10 +41,9 @@ interface EditStationModalProps { name: string, registeredAddress: string, totalSlots: number, - allowedCarIds: string[] + allowedCarIds: number[] ) => void; editRow?: any; - vehicles: { id: string; name: string }[]; } const EditStationModal: React.FC = ({ @@ -46,54 +51,82 @@ const EditStationModal: React.FC = ({ handleClose, handleUpdate, editRow, - vehicles, }) => { - const { control, handleSubmit, setValue, reset, watch } = useForm( - { - defaultValues: { - name: "", - registeredAddress: "", - totalSlots: 0, - status: 1, - allowedCarIds: [], - }, - } + const { + control, + handleSubmit, + setValue, + reset, + watch, + formState: { errors }, + } = useForm({ + defaultValues: { + name: "", + registeredAddress: "", + totalSlots: 0, + status: 1, + allowedCarIds: [], + }, + }); + + const dispatch = useDispatch(); + const vehicles = useSelector( + (state: RootState) => state.vehicleReducer.vehicles + ); + const vehicleBrands = useSelector( + (state: RootState) => state.vehicleReducer.vehicleBrands ); - // Watch allowedCarIds from the form state - const allowedCarIds = watch("allowedCarIds"); + const [selectedBrand, setSelectedBrand] = useState(""); + const [selectedVehicles, setSelectedVehicles] = useState([]); + + useEffect(() => { + dispatch(fetchVehicleBrands()); + dispatch(vehicleList()); // Fetch vehicles when the component mounts + }, [dispatch]); - // Set values when editRow is provided useEffect(() => { if (editRow) { setValue("name", editRow.name); setValue("registeredAddress", editRow.registeredAddress); setValue("totalSlots", editRow.totalSlots); setValue("status", editRow.status); - - // Set the allowedCarIds with the previously selected vehicle IDs - setValue( - "allowedCarIds", - editRow.allowedCarIds?.map((car: any) => car.id) || [] - ); + setValue("allowedCarIds", editRow.allowedCarIds || []); + setSelectedVehicles(editRow.allowedCarIds || []); } else { reset(); } }, [editRow, setValue, reset]); - // Handle form submit + + const filteredVehicles = vehicles.filter( + (vehicle) => vehicle.company === selectedBrand + ); + + const handleCheckboxChange = ( + event: React.ChangeEvent<{ value: unknown }> + ) => { + const value = event.target.value as string[]; + setSelectedVehicles(value); + setValue("allowedCarIds", value); // Update allowedCarIds in form state + }; + const onSubmit = (data: FormData) => { - if (editRow) { - handleUpdate( - editRow.id, - data.name, - data.registeredAddress, - data.totalSlots, - data.allowedCarIds - ); - } - handleClose(); // Close the modal after update - 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(); + setSelectedBrand(""); // Reset brand after submit + setSelectedVehicles([]); // Reset selected vehicles }; return ( @@ -103,7 +136,7 @@ const EditStationModal: React.FC = ({ if (reason === "backdropClick") { return; } - handleClose(); // Close modal when clicking cross or cancel + handleClose(); }} aria-labelledby="edit-station-modal" > @@ -141,9 +174,9 @@ const EditStationModal: React.FC = ({ {/* Horizontal Line */} - {/* Input Fields */} + {/* Form */} - {/* First Row - Two Inputs */} + {/* Station Name and Address */} = ({ fullWidth placeholder="Enter Station Name" size="small" + error={!!errors.name} + helperText={ + errors.name + ? errors.name.message + : "" + } /> )} /> - = ({ fullWidth placeholder="Enter Registered Address" size="small" + error={!!errors.registeredAddress} + helperText={ + errors.registeredAddress + ? errors.registeredAddress + .message + : "" + } /> )} /> - {/* Second Row - Total Slots */} + {/* Total Slots */} = ({ placeholder="Enter Total Slots" size="small" type="number" + error={!!errors.totalSlots} + helperText={ + errors.totalSlots + ? errors.totalSlots.message + : "" + } /> )} /> - {/* Vehicle Dropdown with Checkboxes */} - - - Vehicle Names - - - Choose Vehicles - - + {/* Vehicle Brand Selection */} + + + + Select Vehicle Brand + + + Choose Brand + + + + + {/* Vehicle Selection with Checkboxes */} + + + Vehicle Name + + + Choose Vehicles + + + {errors.allowedCarIds + ? errors.allowedCarIds.message + : ""} + + + @@ -300,7 +391,7 @@ const EditStationModal: React.FC = ({ "&: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 ( -
@@ -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/layouts/DashboardLayout/index.tsx b/src/layouts/DashboardLayout/index.tsx index 6cb37b1..eb1ca8e 100644 --- a/src/layouts/DashboardLayout/index.tsx +++ b/src/layouts/DashboardLayout/index.tsx @@ -50,12 +50,8 @@ const DashboardLayout: React.FC = ({ 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 === 403 ) { - 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/EvSlotList/index.tsx b/src/pages/EvSlotList/index.tsx index 4e8e0c7..4e34ad8 100644 --- a/src/pages/EvSlotList/index.tsx +++ b/src/pages/EvSlotList/index.tsx @@ -56,10 +56,10 @@ 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 @@ -69,7 +69,7 @@ export default function EVSlotList() { await dispatch( updateSlot({ id, - date, + startTime, endTime, isAvailable, diff --git a/src/pages/StationList/index.tsx b/src/pages/StationList/index.tsx index fc0d6a0..549b10c 100644 --- a/src/pages/StationList/index.tsx +++ b/src/pages/StationList/index.tsx @@ -47,7 +47,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 @@ -62,7 +63,7 @@ export default function StationList() { id: string, name: string, registeredAddress: string, - totalSlots: string, + totalSlots: number, allowedCarIds: number[] ) => { try { @@ -88,9 +89,14 @@ export default function StationList() { await dispatch(toggleStatus({ id, status: newStatus })); }; - const filterStations = stations?.filter((station) => - station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase()) - ); + const filterStations = Array.isArray(stations) + ? stations.filter((station) => + station.name + .toLocaleLowerCase() + .includes(searchTerm.toLowerCase()) + ) + : []; + // Mapping and formatting vehicles const categoryRows = filterStations?.length @@ -122,6 +128,7 @@ export default function StationList() { }; }) : []; + console.log("Rowssss",categoryRows) const categoryColumns: Column[] = [ { id: "srno", label: "Sr No" }, diff --git a/src/redux/slices/VehicleSlice.ts b/src/redux/slices/VehicleSlice.ts index ba71ebb..31cefed 100644 --- a/src/redux/slices/VehicleSlice.ts +++ b/src/redux/slices/VehicleSlice.ts @@ -2,6 +2,7 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; import http from "../../lib/https"; import { toast } from "sonner"; interface VehicleBrand { + name: string; id: string; company: string; } @@ -34,19 +35,23 @@ export const fetchVehicleBrands = createAsyncThunk< >("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 + + // 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, void, @@ -158,6 +163,7 @@ const vehicleSlice = createSlice({ state.vehicleBrands = action.payload; } ) + .addCase(fetchVehicleBrands.rejected, (state, action) => { state.loading = false; state.error = 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 410c1c9..e81282c 100644 --- a/src/redux/slices/managerSlice.ts +++ b/src/redux/slices/managerSlice.ts @@ -175,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 467d2ee..487d0ec 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,7 +88,7 @@ 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 }, // Argument type (slot update data) { rejectValue: string } >("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => { try { @@ -97,6 +97,7 @@ export const updateSlot = createAsyncThunk< slotData ); toast.success("Slot updated successfully"); + console.log("Slots",response.data.data) return response.data.data; // Return updated slot data } catch (error: any) { toast.error("Error updating the slot: " + error?.message); @@ -104,7 +105,7 @@ export const updateSlot = createAsyncThunk< error.response?.data?.message || "An error occurred" ); } -}); +}); export const deleteSlot = createAsyncThunk< string, // Return type (id of deleted slot) @@ -167,11 +168,11 @@ 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; } } ) diff --git a/src/redux/slices/stationSlice.ts b/src/redux/slices/stationSlice.ts index a6e4246..a9ead8a 100644 --- a/src/redux/slices/stationSlice.ts +++ b/src/redux/slices/stationSlice.ts @@ -8,7 +8,7 @@ interface Station { id: string; name: string; registeredAddress: string; - totalSlots: string; + totalSlots: number; status: number; allowedCarIds: number[]; } @@ -47,14 +47,35 @@ 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 } @@ -62,7 +83,7 @@ export const createStation = createAsyncThunk< 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: " + @@ -74,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, @@ -158,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; }) @@ -176,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) => { @@ -225,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; }) From a6928b7db94052cc56e5763db96b79600d558bb6 Mon Sep 17 00:00:00 2001 From: jaanvi Date: Fri, 28 Mar 2025 11:48:44 +0530 Subject: [PATCH 5/7] Latest changes --- src/components/EditSlotModal/index.tsx | 17 ++++----- src/pages/EvSlotList/index.tsx | 49 ++++++++++++-------------- src/redux/slices/slotSlice.ts | 9 ++--- 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/components/EditSlotModal/index.tsx b/src/components/EditSlotModal/index.tsx index dbf581e..1365b1e 100644 --- a/src/components/EditSlotModal/index.tsx +++ b/src/components/EditSlotModal/index.tsx @@ -57,7 +57,7 @@ const EditSlotModal: React.FC = ({ // Set values if editRow is provided useEffect(() => { if (editRow) { - setValue("date", editRow.date); + // setValue("date", editRow.date); setValue("startTime", editRow.startTime); setValue("endTime", editRow.endTime); setIsAvailable(editRow.isAvailable); // Set the initial availability correctly @@ -76,13 +76,10 @@ const EditSlotModal: React.FC = ({ await dispatch( updateSlot({ - id: editRow.id, // Slot ID - slotData: { - date: data.date, - startTime: data.startTime, - endTime: data.endTime, - isAvailable: availabilityStatus, - }, + id: editRow.id, // Slot ID// date: data.date, + startTime: data.startTime, + endTime: data.endTime, + isAvailable: availabilityStatus, }) ).unwrap(); // Ensure that it throws an error if the update fails @@ -147,7 +144,7 @@ const EditSlotModal: React.FC = ({ {/* Input Fields */} {/* Date */} - + {/* Date @@ -166,7 +163,7 @@ const EditSlotModal: React.FC = ({ /> )} /> - + */} {/* Start Time */} diff --git a/src/pages/EvSlotList/index.tsx b/src/pages/EvSlotList/index.tsx index 4e34ad8..d1fbaa7 100644 --- a/src/pages/EvSlotList/index.tsx +++ b/src/pages/EvSlotList/index.tsx @@ -11,7 +11,6 @@ import { } 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); @@ -57,30 +56,28 @@ export default function EVSlotList() { } }; - const handleUpdate = async ( - id: string, - - startTime: string, - endTime: string, - isAvailable: boolean - ) => { - try { - // Update manager with stationId - await dispatch( - updateSlot({ - id, - - startTime, - endTime, - isAvailable, - }) - ); - await dispatch(fetchAvailableSlots()); // Refresh the list after update - handleCloseModal(); // Close modal after update - } catch (error) { - console.error("Update failed", error); - } - }; +const handleUpdate = async ( + id: string, + startTime: string, + endTime: string, + isAvailable: boolean +) => { + try { + await dispatch( + updateSlot({ + id, // Pass id separately, not inside slotData + startTime, + endTime, + isAvailable, + }) + ).unwrap(); // Ensure you unwrap the result so you can handle the error properly + 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" }, @@ -100,7 +97,7 @@ export default function EVSlotList() { 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, diff --git a/src/redux/slices/slotSlice.ts b/src/redux/slices/slotSlice.ts index 487d0ec..1dbc59b 100644 --- a/src/redux/slices/slotSlice.ts +++ b/src/redux/slices/slotSlice.ts @@ -88,16 +88,16 @@ export const createSlot = createAsyncThunk< // Update Slot details export const updateSlot = createAsyncThunk< Slot, - { id: string; startTime: string; endTime: string }, // Argument type (slot update data) + { id: string; startTime: string; endTime: string; isAvailable: boolean }, // Argument type (slot update data) { rejectValue: string } >("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => { try { const response = await http.patch( `/update-availability/${id}`, - slotData + slotData // slotData is being sent here, but we should ensure its structure is correct ); toast.success("Slot updated successfully"); - console.log("Slots",response.data.data) + console.log("Slots", response.data.data); return response.data.data; // Return updated slot data } catch (error: any) { toast.error("Error updating the slot: " + error?.message); @@ -105,7 +105,7 @@ export const updateSlot = createAsyncThunk< error.response?.data?.message || "An error occurred" ); } -}); +}); export const deleteSlot = createAsyncThunk< string, // Return type (id of deleted slot) @@ -176,6 +176,7 @@ const slotSlice = createSlice({ } } ) + .addCase(updateSlot.rejected, (state, action) => { state.loading = false; state.error = action.payload || "Failed to update slot"; From e038428607611a5e63eda941b19303f545a13ffd Mon Sep 17 00:00:00 2001 From: jaanvi Date: Fri, 28 Mar 2025 14:58:24 +0530 Subject: [PATCH 6/7] add new changes in station and logo --- public/Group 14.svg | 4 + public/evLogo.png | Bin 0 -> 8099 bytes src/components/AddStationModal/index.tsx | 2 +- src/components/CustomTable/index.tsx | 91 ++++++++++++---------- src/components/EditManagerModal/index.tsx | 14 +++- src/components/EditSlotModal/index.tsx | 21 ++--- src/components/EditStationModal/index.tsx | 45 +++++++---- src/components/Modals/ViewModal/index.tsx | 1 + src/components/SideMenu/index.tsx | 26 +++++-- src/pages/Auth/Login/index.tsx | 7 +- src/pages/EvSlotList/index.tsx | 59 +++++++------- src/pages/ManagerList/index.tsx | 80 +++++-------------- src/pages/VehicleList/index.tsx | 43 +++++----- src/redux/slices/VehicleSlice.ts | 15 ++-- src/redux/slices/managerSlice.ts | 6 +- src/redux/slices/slotSlice.ts | 20 +++-- 16 files changed, 219 insertions(+), 215 deletions(-) create mode 100644 public/Group 14.svg create mode 100644 public/evLogo.png 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 0000000000000000000000000000000000000000..0c96ea10a64480ffa4a9234de9932920109b4394 GIT binary patch literal 8099 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 VviQ6A! z^#c%Jba)5D14EsgMyQ6?oBRM2n0VL7wEJ&uVSOG1+Nqf%n7wO0fy6y2av|<1ZX8a7{G(zJpiBL{^-pgu1uJ+Od4!D!{=W( zv8h)UfDY@8t5kmdhbNvi)hD)}6KG;-t)42dJi%Z2>la{aJkm~Bpf7|09QU|6JBqGX z6wYs8!37LAcOcC_MWeX@;1DFT< + /> )) ) : ( diff --git a/src/components/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index 7db5231..4eda242 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -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); @@ -199,7 +195,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(); }; @@ -216,7 +212,7 @@ const CustomTable: React.FC = ({ const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow); const handlePageChange = ( - event: React.ChangeEvent, + _event: React.ChangeEvent, value: number ) => { setCurrentPage(value); @@ -241,24 +237,30 @@ 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 */} = ({ 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"; + } + })()} )} diff --git a/src/components/EditManagerModal/index.tsx b/src/components/EditManagerModal/index.tsx index ec4165c..ac0d1f6 100644 --- a/src/components/EditManagerModal/index.tsx +++ b/src/components/EditManagerModal/index.tsx @@ -14,11 +14,19 @@ import { CustomIconButton, CustomTextField, } from "../AddEditUserModel/styled.css.tsx"; // Custom styled components +import { AppDispatch } from "../../redux/store/store.ts"; interface EditManagerModalProps { open: boolean; handleClose: () => 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 index 1365b1e..f227886 100644 --- a/src/components/EditSlotModal/index.tsx +++ b/src/components/EditSlotModal/index.tsx @@ -14,10 +14,17 @@ 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 } @@ -33,7 +40,7 @@ const EditSlotModal: React.FC = ({ handleClose, editRow, }) => { - const dispatch = useDispatch(); // Use dispatch to send Redux actions + const dispatch = useDispatch(); const { control, handleSubmit, @@ -54,13 +61,11 @@ const EditSlotModal: React.FC = ({ editRow?.isAvailable || false ); - // 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 + setIsAvailable(editRow.isAvailable); } else { reset(); } @@ -68,10 +73,10 @@ const EditSlotModal: React.FC = ({ const onSubmit = async (data: FormData) => { if (editRow) { - setLoading(true); // Start loading + setLoading(true); try { - // Convert boolean availability to 1/0 + const availabilityStatus = isAvailable ? true : false; await dispatch( @@ -81,9 +86,7 @@ const EditSlotModal: React.FC = ({ endTime: data.endTime, isAvailable: availabilityStatus, }) - ).unwrap(); // Ensure that it throws an error if the update fails - - // Refresh the list after updating the slot + ).unwrap(); dispatch(fetchAvailableSlots()); handleClose(); // Close modal on success reset(); // Reset form fields after submit diff --git a/src/components/EditStationModal/index.tsx b/src/components/EditStationModal/index.tsx index 047e6ef..12d671e 100644 --- a/src/components/EditStationModal/index.tsx +++ b/src/components/EditStationModal/index.tsx @@ -57,7 +57,6 @@ const EditStationModal: React.FC = ({ handleSubmit, setValue, reset, - watch, formState: { errors }, } = useForm({ defaultValues: { @@ -77,7 +76,7 @@ const EditStationModal: React.FC = ({ (state: RootState) => state.vehicleReducer.vehicleBrands ); - const [selectedBrand, setSelectedBrand] = useState(""); + const [selectedBrands, setSelectedBrands] = useState([]); const [selectedVehicles, setSelectedVehicles] = useState([]); useEffect(() => { @@ -98,11 +97,19 @@ const EditStationModal: React.FC = ({ } }, [editRow, setValue, reset]); - - const filteredVehicles = vehicles.filter( - (vehicle) => vehicle.company === selectedBrand + // 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 }> ) => { @@ -125,7 +132,7 @@ const EditStationModal: React.FC = ({ ); handleClose(); reset(); - setSelectedBrand(""); // Reset brand after submit + setSelectedBrands([]); // Reset brands after submit setSelectedVehicles([]); // Reset selected vehicles }; @@ -285,7 +292,7 @@ const EditStationModal: React.FC = ({ - {/* Vehicle Brand Selection */} + {/* Vehicle Brand Selection with Checkboxes */} = ({ }} > - Select Vehicle Brand + Select Vehicle Brands Choose Brand - - {/* Vehicle Selection with Checkboxes */} = ({ + + {/* Submit Button */} void; id?: string; }; diff --git a/src/components/SideMenu/index.tsx b/src/components/SideMenu/index.tsx index 62e2469..4efdf7c 100644 --- a/src/components/SideMenu/index.tsx +++ b/src/components/SideMenu/index.tsx @@ -64,15 +64,25 @@ export default function SideMenu() { pl: 2, }} > - - */} + {/* - {/* Digi EV Text Section */} + {user?.userType || "N/A"} - + */} ({ mode: "onChange" }); - const dispatch = useDispatch(); + const dispatch = useDispatch(); const router = useNavigate(); const handleClickOpen = () => { @@ -104,11 +105,11 @@ export default function Login(props: { disableCustomTheme?: boolean }) { }} > Logo diff --git a/src/pages/EvSlotList/index.tsx b/src/pages/EvSlotList/index.tsx index d1fbaa7..ef4f174 100644 --- a/src/pages/EvSlotList/index.tsx +++ b/src/pages/EvSlotList/index.tsx @@ -1,5 +1,4 @@ -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"; @@ -15,12 +14,10 @@ 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 @@ -55,29 +52,34 @@ export default function EVSlotList() { console.error("Error adding slot", error); } }; - -const handleUpdate = async ( - id: string, - startTime: string, - endTime: string, - isAvailable: boolean -) => { - try { - await dispatch( - updateSlot({ - id, // Pass id separately, not inside slotData - startTime, - endTime, - isAvailable, - }) - ).unwrap(); // Ensure you unwrap the result so you can handle the error properly - await dispatch(fetchAvailableSlots()); // Refresh the list after update - handleCloseModal(); // Close modal after update - } catch (error) { - console.error("Update failed", error); - } -}; + 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" }, @@ -90,15 +92,12 @@ const handleUpdate = async ( { 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", diff --git a/src/pages/ManagerList/index.tsx b/src/pages/ManagerList/index.tsx index 6946dfc..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/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 31cefed..77c3310 100644 --- a/src/redux/slices/VehicleSlice.ts +++ b/src/redux/slices/VehicleSlice.ts @@ -8,7 +8,7 @@ interface VehicleBrand { } interface Vehicle { - brandId: string; + brandId?: string; id: string; name: string; company: string; @@ -50,10 +50,8 @@ export const fetchVehicleBrands = createAsyncThunk< } }); - - export const vehicleList = createAsyncThunk< - Vehicle, + Vehicle[], void, { rejectValue: string } >("fetchVehicles", async (_, { rejectWithValue }) => { @@ -180,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/managerSlice.ts b/src/redux/slices/managerSlice.ts index e81282c..a32cf0f 100644 --- a/src/redux/slices/managerSlice.ts +++ b/src/redux/slices/managerSlice.ts @@ -6,12 +6,12 @@ import { toast } from "sonner"; interface Manager { Manager: any; - id: number; + id: string; name: string; email: string; phone: string; stationId: string; - chargingStation:string; + chargingStation:{name:string, registeredAddress:string}; } interface ManagerState { @@ -74,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) { diff --git a/src/redux/slices/slotSlice.ts b/src/redux/slices/slotSlice.ts index 1dbc59b..04a9b58 100644 --- a/src/redux/slices/slotSlice.ts +++ b/src/redux/slices/slotSlice.ts @@ -88,17 +88,23 @@ export const createSlot = createAsyncThunk< // Update Slot details export const updateSlot = createAsyncThunk< Slot, - { id: string; startTime: string; endTime: string; isAvailable: boolean }, // 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( - `/update-availability/${id}`, - slotData // slotData is being sent here, but we should ensure its structure is correct - ); + 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"); - console.log("Slots", response.data.data); - 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( From 1519e2ad931ca73c93d91cfe5f5d501f21dd2647 Mon Sep 17 00:00:00 2001 From: jaanvi Date: Mon, 31 Mar 2025 10:12:42 +0530 Subject: [PATCH 7/7] Resolve minor issues --- .../AddEditUserModel/styled.css.tsx | 5 +-- src/components/CustomTable/index.tsx | 5 ++- src/components/EditStationModal/index.tsx | 2 +- .../Modals/StationViewModal/index.tsx | 2 +- src/pages/Auth/Login/index.tsx | 4 +- src/pages/StationList/index.tsx | 38 ++++++++++--------- 6 files changed, 28 insertions(+), 28 deletions(-) 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/CustomTable/index.tsx b/src/components/CustomTable/index.tsx index 4eda242..9d9703d 100644 --- a/src/components/CustomTable/index.tsx +++ b/src/components/CustomTable/index.tsx @@ -34,7 +34,7 @@ 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, @@ -183,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; diff --git a/src/components/EditStationModal/index.tsx b/src/components/EditStationModal/index.tsx index 12d671e..11fdc3b 100644 --- a/src/components/EditStationModal/index.tsx +++ b/src/components/EditStationModal/index.tsx @@ -132,7 +132,7 @@ const EditStationModal: React.FC = ({ ); handleClose(); reset(); - setSelectedBrands([]); // Reset brands after submit + //setSelectedBrands([]); // Reset brands after submit setSelectedVehicles([]); // Reset selected vehicles }; 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/pages/Auth/Login/index.tsx b/src/pages/Auth/Login/index.tsx index 2a341b6..a9f82ff 100644 --- a/src/pages/Auth/Login/index.tsx +++ b/src/pages/Auth/Login/index.tsx @@ -330,9 +330,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) { ? "error" : "primary" } - onMouseDown={ - togglePasswordVisibility - } + InputProps={{ endAdornment: ( diff --git a/src/pages/StationList/index.tsx b/src/pages/StationList/index.tsx index 549b10c..67dae63 100644 --- a/src/pages/StationList/index.tsx +++ b/src/pages/StationList/index.tsx @@ -2,7 +2,12 @@ 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 { + createStation, + stationList, + toggleStatus, + updateStation, +} from "../../redux/slices/stationSlice"; import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../redux/reducers"; @@ -20,14 +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 ); - const vehicles = useSelector( - (state: RootState) => state.vehicleReducer.vehicles - ); - - useEffect(() => { dispatch(stationList()); }, [dispatch]); @@ -84,23 +88,21 @@ export default function StationList() { } }; - const handleStatusToggle = async (id: string, newStatus: number) => { await dispatch(toggleStatus({ id, status: newStatus })); }; - const filterStations = Array.isArray(stations) - ? stations.filter((station) => - station.name - .toLocaleLowerCase() - .includes(searchTerm.toLowerCase()) - ) - : []; - + // const filterStations = Array.isArray(stations) + // ? stations.filter((station) => + // station.name + // .toLocaleLowerCase() + // .includes(searchTerm.toLowerCase()) + // ) + // : []; // Mapping and formatting vehicles - const categoryRows = filterStations?.length - ? filterStations?.map((station: any, index: number) => { + 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 @@ -128,7 +130,7 @@ export default function StationList() { }; }) : []; - console.log("Rowssss",categoryRows) + console.log("Rowssss", categoryRows); const categoryColumns: Column[] = [ { id: "srno", label: "Sr No" },