diff --git a/public/Group 14.svg b/public/Group 14.svg new file mode 100644 index 0000000..503ae50 --- /dev/null +++ b/public/Group 14.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/evLogo.png b/public/evLogo.png new file mode 100644 index 0000000..0c96ea1 Binary files /dev/null and b/public/evLogo.png differ diff --git a/src/components/AddStationModal/index.tsx b/src/components/AddStationModal/index.tsx index f73b639..2919838 100644 --- a/src/components/AddStationModal/index.tsx +++ b/src/components/AddStationModal/index.tsx @@ -341,7 +341,7 @@ export default function AddStationModal({ /> + /> )) ) : ( 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(