after pull changes

This commit is contained in:
Eknoor Singh 2025-03-28 12:19:54 +05:30
commit 23d4dbaabd
16 changed files with 588 additions and 431 deletions

View file

@ -47,59 +47,58 @@ export default function AddStationModal({
(state: RootState) => state.vehicleReducer.vehicleBrands (state: RootState) => state.vehicleReducer.vehicleBrands
); );
console.log("vehicle Brands", vehicleBrands); // State for selected vehicle brand and vehicles
const [selectedBrand, setSelectedBrand] = useState<string | "">(""); const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
useEffect(() => {
dispatch(fetchVehicleBrands());
dispatch(vehicleList()); // Fetch vehicles when the component mounts
}, [dispatch]);
const filteredVehicles = vehicles.filter(
(vehicle) => vehicle.company === selectedBrand
);
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]); const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
// Handle the change in selected vehicles (checkboxes) // Fetch vehicle brands and vehicle list when the component mounts
const handleCheckboxChange = ( 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 }> event: React.ChangeEvent<{ value: unknown }>
) => { ) => {
const value = event.target.value as string[]; 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 // Function to map selected vehicle names to corresponding vehicle ids
const getVehicleIds = () => { const getVehicleIds = () => {
console.log("Selected Vehicles: ", selectedVehicles);
console.log("Vehicles List: ", vehicles);
return vehicles return vehicles
.filter((vehicle) => selectedVehicles.includes(vehicle.name)) .filter((vehicle) => selectedVehicles.includes(vehicle.name))
.map((vehicle) => vehicle.id); .map((vehicle) => vehicle.id);
}; };
const onSubmit = (data: any) => { 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 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 = { const payload = {
...data, ...data,
status: 1, // Default status, can be adjusted if needed status: 1, // Default status
allowedCars: vehicleIds, // Pass the vehicle ids to the backend allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend
totalSlots: Number(data.totalSlots), // Ensure this is a number 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); handleAddStation(payload);
handleClose(); // Close modal after adding handleClose(); // Close modal after adding
reset(); reset();
setSelectedVehicles([]); setSelectedVehicles([]);
setSelectedBrand(""); // Reset selected vehicles after submission setSelectedBrands([]); // Reset selected brands after submission
}; };
return ( return (
@ -253,7 +252,7 @@ export default function AddStationModal({
</Box> </Box>
</Box> </Box>
{/* Vehicle Name Dropdown with Checkboxes */} {/* Vehicle Brand Dropdown with Checkboxes */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -263,26 +262,33 @@ export default function AddStationModal({
}} }}
> >
<Typography variant="body2" fontWeight={500}> <Typography variant="body2" fontWeight={500}>
Select Vehicle Brand Select Vehicle Brands
</Typography> </Typography>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel>Choose Brand</InputLabel> <InputLabel>Choose Brands</InputLabel>
<Select <Select
value={selectedBrand} multiple
onChange={(e) => value={selectedBrands}
setSelectedBrand(e.target.value) onChange={handleBrandChange}
renderValue={(selected) =>
(selected as string[]).join(", ")
} }
label="Choose Brand" label="Choose Brands"
> >
{Array.isArray(vehicleBrands) && {vehicleBrands.length > 0 ? (
vehicleBrands.length > 0 ? (
vehicleBrands.map((brand) => ( vehicleBrands.map((brand) => (
<MenuItem <MenuItem
key={brand.id} key={brand.id}
value={brand.id} value={brand.id}
> >
{brand.name}{" "} <Checkbox
{/* Make sure 'brand.id' is set as value */} checked={selectedBrands.includes(
brand.id
)}
/>
<ListItemText
primary={brand.name}
/>
</MenuItem> </MenuItem>
)) ))
) : ( ) : (
@ -298,8 +304,9 @@ export default function AddStationModal({
</FormHelperText> </FormHelperText>
</FormControl> </FormControl>
</Box> </Box>
{/* Vehicle Name Dropdown with Checkboxes */}
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -316,7 +323,7 @@ export default function AddStationModal({
<Select <Select
multiple multiple
value={selectedVehicles} value={selectedVehicles}
onChange={handleCheckboxChange} onChange={handleVehicleChange}
renderValue={(selected) => renderValue={(selected) =>
(selected as string[]).join(", ") (selected as string[]).join(", ")
} }
@ -339,8 +346,8 @@ export default function AddStationModal({
)) ))
) : ( ) : (
<MenuItem disabled> <MenuItem disabled>
No vehicles available for this No vehicles available for the
brand selected brands
</MenuItem> </MenuItem>
)} )}
</Select> </Select>

View file

@ -83,6 +83,36 @@ export default function AddVehicleModal({
> >
{/* First Row - Two Inputs */} {/* First Row - Two Inputs */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Company Name
</Typography>
<CustomTextField
fullWidth
placeholder="Enter Company Name"
size="small"
error={!!errors.company}
helperText={
errors.company
? errors.company.message
: ""
}
{...register("company", {
required: "Company is required",
minLength: {
value: 3,
message:
"Company must be at least 5 characters long",
},
})}
/>
</Box>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -121,37 +151,6 @@ export default function AddVehicleModal({
})} })}
/> />
</Box> </Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Company
</Typography>
<CustomTextField
fullWidth
placeholder="Enter Company Name"
size="small"
error={!!errors.company}
helperText={
errors.company
? errors.company.message
: ""
}
{...register("company", {
required: "Company is required",
minLength: {
value: 3,
message:
"Company must be at least 5 characters long",
},
})}
/>
</Box>
</Box> </Box>
{/* Second Row - Two Inputs */} {/* Second Row - Two Inputs */}

View file

@ -40,7 +40,7 @@ import {
deleteSlot, deleteSlot,
fetchAvailableSlots, fetchAvailableSlots,
} from "../../redux/slices/slotSlice.ts"; } from "../../redux/slices/slotSlice.ts";
import { bookingList } from "../../redux/slices/bookSlice.ts"; import { bookingList, deleteBooking } from "../../redux/slices/bookSlice.ts";
// Styled components for customization // Styled components for customization
const StyledTableCell = styled(TableCell)(({ theme }) => ({ const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: { [`&.${tableCellClasses.head}`]: {
@ -155,6 +155,9 @@ const CustomTable: React.FC<CustomTableProps> = ({
case "slots": case "slots":
dispatch(deleteSlot(id || "")); dispatch(deleteSlot(id || ""));
break; break;
case "booking":
dispatch(deleteBooking(id || ""));
break;
default: default:
console.error("Unknown table type:", tableType); console.error("Unknown table type:", tableType);
return; return;
@ -483,175 +486,174 @@ const CustomTable: React.FC<CustomTableProps> = ({
/> />
</Box> </Box>
{/* Menu Actions */} {/* Menu Actions */}
{open && {open && (
( <Menu
<Menu anchorEl={anchorEl}
anchorEl={anchorEl} id="menu"
id="menu" open={open}
open={open} onClose={handleClose}
onClose={handleClose} onClick={handleClose}
onClick={handleClose} transformOrigin={{
transformOrigin={{ horizontal: "right",
horizontal: "right", vertical: "top",
vertical: "top", }}
}} anchorOrigin={{
anchorOrigin={{ horizontal: "right",
horizontal: "right", vertical: "bottom",
vertical: "bottom", }}
sx={{
[`& .${paperClasses.root}`]: {
padding: 0,
},
"& .MuiList-root": {
background: "#272727", // Remove any divider under menu items
},
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
}}
>
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
// setSelectedRow(row);
setViewModal(true);
}} }}
color="primary"
sx={{ sx={{
[`& .${paperClasses.root}`]: { justifyContent: "flex-start",
padding: 0, py: 0,
}, fontWeight: "bold",
"& .MuiList-root": { color: "#52ACDF",
background: "#272727", // Remove any divider under menu items
},
}} }}
> >
<Box View
</Button>
{viewModal && tableType === "admin" && (
<ViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "vehicle" && (
<VehicleViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "manager" && (
<ManagerViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "user" && (
<UserViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "station" && (
<StationViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
<Button
variant="text"
onClick={() => setModalOpen(true)}
color="primary"
sx={{
justifyContent: "flex-start",
py: 0,
textTransform: "capitalize",
}}
>
Edit
</Button>
{tableType === "role" && (
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
handleToggleStatus();
}}
color="secondary"
sx={{ sx={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start", justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}} }}
> >
<Button {selectedRow?.statusValue === 1
variant="text" ? "Deactivate"
onClick={(e) => { : "Activate"}
e.stopPropagation(); </Button>
// setSelectedRow(row); )}
setViewModal(true); {tableType === "station" && (
}} <Button
color="primary" variant="text"
sx={{ onClick={(e) => {
justifyContent: "flex-start", e.stopPropagation();
py: 0, handleToggleStatus();
fontWeight: "bold", }}
color: "#52ACDF", color="secondary"
}} sx={{
> justifyContent: "flex-start",
View py: 0,
</Button> fontWeight: "bold",
{viewModal && tableType === "admin" && ( }}
<ViewModal >
handleView={() => {selectedRow?.statusValue === 1
handleViewButton(selectedRow?.id) ? "Not Available"
} : "Available"}
open={viewModal} </Button>
setViewModal={setViewModal} )}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "vehicle" && (
<VehicleViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "manager" && (
<ManagerViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "user" && (
<UserViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
{viewModal && tableType === "station" && (
<StationViewModal
handleView={() =>
handleViewButton(selectedRow?.id)
}
open={viewModal}
setViewModal={setViewModal}
id={selectedRow?.id}
/>
)}
<Button <Button
variant="text" variant="text"
onClick={() => setModalOpen(true)} onClick={(e) => {
color="primary" e.stopPropagation();
sx={{ setDeleteModal(true);
justifyContent: "flex-start", }}
py: 0, color="error"
textTransform: "capitalize", sx={{
}} justifyContent: "flex-start",
> py: 0,
Edit fontWeight: "bold",
</Button> }}
{tableType === "role" && ( >
<Button Delete
variant="text" </Button>
onClick={(e) => { </Box>
e.stopPropagation(); </Menu>
handleToggleStatus(); )}
}}
color="secondary"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
{selectedRow?.statusValue === 1
? "Deactivate"
: "Activate"}
</Button>
)}
{tableType === "station" && (
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
handleToggleStatus();
}}
color="secondary"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
{selectedRow?.statusValue === 1
? "Not Available"
: "Available"}
</Button>
)}
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
setDeleteModal(true);
}}
color="error"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
Delete
</Button>
</Box>
</Menu>
)}
{/* Modals */} {/* Modals */}
{deleteModal && ( {deleteModal && (
<DeleteModal <DeleteModal

View file

@ -74,9 +74,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
email: data.email, email: data.email,
phone: data.phone, phone: data.phone,
stationId: data.stationId, stationId: data.stationId,
Manager: undefined,
id: 0,
chargingStation: ""
}, },
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).unwrap(); // Ensure that it throws an error if the update fails

View file

@ -52,12 +52,12 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isAvailable, setIsAvailable] = useState<boolean>( const [isAvailable, setIsAvailable] = useState<boolean>(
editRow?.isAvailable || false editRow?.isAvailable || false
); // Default to editRow availability );
// Set values if editRow is provided // Set values if editRow is provided
useEffect(() => { useEffect(() => {
if (editRow) { if (editRow) {
setValue("date", editRow.date); // setValue("date", editRow.date);
setValue("startTime", editRow.startTime); setValue("startTime", editRow.startTime);
setValue("endTime", editRow.endTime); setValue("endTime", editRow.endTime);
setIsAvailable(editRow.isAvailable); // Set the initial availability correctly setIsAvailable(editRow.isAvailable); // Set the initial availability correctly
@ -68,27 +68,28 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const onSubmit = async (data: FormData) => { const onSubmit = async (data: FormData) => {
if (editRow) { if (editRow) {
setLoading(true); // Start loading
try { try {
// Convert boolean availability to 1/0 // Convert boolean availability to 1/0
const availabilityStatus = isAvailable ? true : false; const availabilityStatus = isAvailable ? true : false;
await dispatch( await dispatch(
updateSlot({ updateSlot({
id: editRow.id, // Slot ID id: editRow.id, // Slot ID// date: data.date,
slotData: { startTime: data.startTime,
date: data.date, endTime: data.endTime,
startTime: data.startTime, isAvailable: availabilityStatus,
endTime: data.endTime,
isAvailable: availabilityStatus,
},
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).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 handleClose(); // Close modal on success
reset(); // Reset form fields after submit reset(); // Reset form fields after submit
} catch (error) { } catch (error) {
console.error(error); console.error("Error updating slot:", error);
// Handle the error or show a toast // Handle the error or show a toast message
} finally { } finally {
setLoading(false); // Stop loading state setLoading(false); // Stop loading state
} }
@ -143,7 +144,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
{/* Input Fields */} {/* Input Fields */}
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 2 }}> <Box sx={{ display: "flex", flexWrap: "wrap", gap: 2 }}>
{/* Date */} {/* Date */}
<Box sx={{ flex: "1 1 100%" }}> {/* <Box sx={{ flex: "1 1 100%" }}>
<Typography variant="body2" fontWeight={500} mb={0.5}> <Typography variant="body2" fontWeight={500} mb={0.5}>
Date Date
</Typography> </Typography>
@ -162,7 +163,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
/> />
)} )}
/> />
</Box> </Box> */}
{/* Start Time */} {/* Start Time */}
<Box sx={{ flex: "1 1 48%" }}> <Box sx={{ flex: "1 1 48%" }}>

View file

@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { import {
Box, Box,
Button, Button,
@ -10,21 +10,27 @@ import {
MenuItem, MenuItem,
Checkbox, Checkbox,
ListItemText, ListItemText,
FormHelperText,
} from "@mui/material"; } from "@mui/material";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { useForm, Controller } from "react-hook-form"; 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 { import {
CustomIconButton, CustomIconButton,
CustomTextField, CustomTextField,
} from "../AddEditUserModel/styled.css.tsx"; } from "../AddEditUserModel/styled.css.tsx"; // Assuming custom styled components
// Define the types for your form data
interface FormData { interface FormData {
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: number; totalSlots: number;
allowedCarIds: string[];
status: number; status: number;
allowedCarIds: string[]; // Assuming allowedCarIds are the vehicle IDs
} }
interface EditStationModalProps { interface EditStationModalProps {
@ -35,10 +41,9 @@ interface EditStationModalProps {
name: string, name: string,
registeredAddress: string, registeredAddress: string,
totalSlots: number, totalSlots: number,
allowedCarIds: string[] allowedCarIds: number[]
) => void; ) => void;
editRow?: any; editRow?: any;
vehicles: { id: string; name: string }[];
} }
const EditStationModal: React.FC<EditStationModalProps> = ({ const EditStationModal: React.FC<EditStationModalProps> = ({
@ -46,54 +51,82 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
handleClose, handleClose,
handleUpdate, handleUpdate,
editRow, editRow,
vehicles,
}) => { }) => {
const { control, handleSubmit, setValue, reset, watch } = useForm<FormData>( const {
{ control,
defaultValues: { handleSubmit,
name: "", setValue,
registeredAddress: "", reset,
totalSlots: 0, watch,
status: 1, formState: { errors },
allowedCarIds: [], } = useForm<FormData>({
}, 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 [selectedBrand, setSelectedBrand] = useState<string | "">("");
const allowedCarIds = watch("allowedCarIds"); const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
useEffect(() => {
dispatch(fetchVehicleBrands());
dispatch(vehicleList()); // Fetch vehicles when the component mounts
}, [dispatch]);
// Set values when editRow is provided
useEffect(() => { useEffect(() => {
if (editRow) { if (editRow) {
setValue("name", editRow.name); setValue("name", editRow.name);
setValue("registeredAddress", editRow.registeredAddress); setValue("registeredAddress", editRow.registeredAddress);
setValue("totalSlots", editRow.totalSlots); setValue("totalSlots", editRow.totalSlots);
setValue("status", editRow.status); setValue("status", editRow.status);
setValue("allowedCarIds", editRow.allowedCarIds || []);
// Set the allowedCarIds with the previously selected vehicle IDs setSelectedVehicles(editRow.allowedCarIds || []);
setValue(
"allowedCarIds",
editRow.allowedCarIds?.map((car: any) => car.id) || []
);
} else { } else {
reset(); reset();
} }
}, [editRow, setValue, 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) => { const onSubmit = (data: FormData) => {
if (editRow) { const vehicleIds = vehicles
handleUpdate( .filter((vehicle) => selectedVehicles.includes(vehicle.name))
editRow.id, .map((vehicle) => vehicle.id);
data.name,
data.registeredAddress, handleUpdate(
data.totalSlots, editRow.id,
data.allowedCarIds data.name,
); data.registeredAddress,
} data.totalSlots,
handleClose(); // Close the modal after update vehicleIds
reset(); // Reset the form fields );
handleClose();
reset();
setSelectedBrand(""); // Reset brand after submit
setSelectedVehicles([]); // Reset selected vehicles
}; };
return ( return (
@ -103,7 +136,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
if (reason === "backdropClick") { if (reason === "backdropClick") {
return; return;
} }
handleClose(); // Close modal when clicking cross or cancel handleClose();
}} }}
aria-labelledby="edit-station-modal" aria-labelledby="edit-station-modal"
> >
@ -141,9 +174,9 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
{/* Horizontal Line */} {/* Horizontal Line */}
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} /> <Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
{/* Input Fields */} {/* Form */}
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}> <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* First Row - Two Inputs */} {/* Station Name and Address */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -168,11 +201,16 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth fullWidth
placeholder="Enter Station Name" placeholder="Enter Station Name"
size="small" size="small"
error={!!errors.name}
helperText={
errors.name
? errors.name.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -196,13 +234,20 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth fullWidth
placeholder="Enter Registered Address" placeholder="Enter Registered Address"
size="small" size="small"
error={!!errors.registeredAddress}
helperText={
errors.registeredAddress
? errors.registeredAddress
.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
</Box> </Box>
{/* Second Row - Total Slots */} {/* Total Slots */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -228,61 +273,107 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
placeholder="Enter Total Slots" placeholder="Enter Total Slots"
size="small" size="small"
type="number" type="number"
error={!!errors.totalSlots}
helperText={
errors.totalSlots
? errors.totalSlots.message
: ""
}
/> />
)} )}
/> />
</Box> </Box>
</Box> </Box>
{/* Vehicle Dropdown with Checkboxes */} {/* Vehicle Brand Selection */}
<Box <Box sx={{ display: "flex", gap: 2 }}>
sx={{ <Box
display: "flex", sx={{
flexDirection: "column", display: "flex",
width: "100%", flexDirection: "column",
}} width: "100%",
> }}
<Typography variant="body2" fontWeight={500}> >
Vehicle Names <Typography variant="body2" fontWeight={500}>
</Typography> Select Vehicle Brand
<FormControl fullWidth> </Typography>
<InputLabel>Choose Vehicles</InputLabel> <FormControl fullWidth>
<Select <InputLabel>Choose Brand</InputLabel>
multiple <Select
value={allowedCarIds || []} // Watch the allowedCarIds to reflect selected values value={selectedBrand}
onChange={(e) => { onChange={(e) =>
// Update the allowedCarIds when the selection changes setSelectedBrand(e.target.value)
setValue( }
"allowedCarIds", label="Choose Brand"
e.target.value as string[] >
); // Update allowedCarIds on change {vehicleBrands.length > 0 ? (
}} vehicleBrands.map((brand) => (
renderValue={(selected) => { <MenuItem
return (selected as string[]) key={brand.id}
.map((id) => { value={brand.id}
const vehicle = vehicles?.find( >
(vehicle) => vehicle.id === id {brand.name}
); </MenuItem>
return vehicle ? vehicle.name : ""; ))
}) ) : (
.join(", "); <MenuItem disabled>
}} No vehicle brands available
> </MenuItem>
{vehicles?.map((vehicle) => ( )}
<MenuItem </Select>
key={vehicle.id} </FormControl>
value={vehicle.id} </Box>
>
<Checkbox {/* Vehicle Selection with Checkboxes */}
checked={allowedCarIds?.includes( <Box
vehicle.id sx={{
)} display: "flex",
/> flexDirection: "column",
<ListItemText primary={vehicle.name} /> width: "100%",
</MenuItem> }}
))} >
</Select> <Typography variant="body2" fontWeight={500}>
</FormControl> Vehicle Name
</Typography>
<FormControl fullWidth>
<InputLabel>Choose Vehicles</InputLabel>
<Select
multiple
value={selectedVehicles}
onChange={handleCheckboxChange}
renderValue={(selected) =>
(selected as string[]).join(", ")
}
>
{filteredVehicles.length > 0 ? (
filteredVehicles.map((vehicle) => (
<MenuItem
key={vehicle.id}
value={vehicle.name}
>
<Checkbox
checked={selectedVehicles.includes(
vehicle.name
)}
/>
<ListItemText
primary={vehicle.name}
/>
</MenuItem>
))
) : (
<MenuItem disabled>
No vehicles available for this brand
</MenuItem>
)}
</Select>
<FormHelperText>
{errors.allowedCarIds
? errors.allowedCarIds.message
: ""}
</FormHelperText>
</FormControl>
</Box>
</Box> </Box>
</Box> </Box>
@ -300,7 +391,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
"&:hover": { backgroundColor: "#439BC1" }, "&:hover": { backgroundColor: "#439BC1" },
}} }}
> >
Update Charging Station Update Station
</Button> </Button>
</Box> </Box>
</Box> </Box>

View file

@ -15,22 +15,21 @@ import { AppDispatch, RootState } from "../../redux/store/store";
import { fetchAdminProfile } from "../../redux/slices/profileSlice"; import { fetchAdminProfile } from "../../redux/slices/profileSlice";
import OptionsMenu from "../OptionsMenu"; import OptionsMenu from "../OptionsMenu";
import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone";
import ColorModeIconDropdown from "../../shared-theme/ColorModeIconDropdown";
export default function Header() { export default function Header() {
const [showNotifications, setShowNotifications] = React.useState(false); const [showNotifications, setShowNotifications] = React.useState(false);
const toggleNotifications = () => { const toggleNotifications = () => {
setShowNotifications((prev) => !prev); setShowNotifications((prev) => !prev);
}; };
const [open, setOpen] = React.useState(true); const [open, setOpen] = React.useState(true);
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { user } = useSelector( const { user } = useSelector((state: RootState) => state?.profileReducer);
(state: RootState) => state?.profileReducer
);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchAdminProfile()); dispatch(fetchAdminProfile());
}, [dispatch]); }, [dispatch]);
return ( return (
<Box <Box
sx={{ sx={{
@ -89,7 +88,6 @@ export default function Header() {
display: { xs: "none", sm: "flex" }, // Hide on mobile, show on larger screens display: { xs: "none", sm: "flex" }, // Hide on mobile, show on larger screens
}} }}
> >
<NotificationsNoneIcon onClick={toggleNotifications} /> <NotificationsNoneIcon onClick={toggleNotifications} />
<Avatar <Avatar
alt="User Avatar" alt="User Avatar"

View file

@ -66,9 +66,9 @@ export default function MenuContent({ hidden }: PropType) {
url: "/panel/vehicle-list", // Placeholder for now url: "/panel/vehicle-list", // Placeholder for now
}, },
// userRole === "manager" && { // userRole === "manager" && {
// text: "Add Slots", // text: "Add Slots",
// icon: <ManageAccountsOutlinedIcon />, // icon: <ManageAccountsOutlinedIcon />,
// url: "/panel/EVslots", // Placeholder for now // url: "/panel/EVslots", // Placeholder for now
// }, // },
userRole === "user" && { userRole === "user" && {
text: "Bookings", text: "Bookings",

View file

@ -32,7 +32,7 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
(state: RootState) => state.managerReducer (state: RootState) => state.managerReducer
); );
const [selectedManager, setSelectedManager] = useState<any>(null); const [selectedManager, setSelectedManager] = useState<any>(null);
useEffect(() => { useEffect(() => {
if (id) { if (id) {
@ -110,7 +110,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
<Typography variant="body1"> <Typography variant="body1">
<strong>Station Location:</strong> <strong>Station Location:</strong>
<Typography variant="body2"> <Typography variant="body2">
{selectedManager.registeredAddress} {selectedManager.chargingStation
?.registeredAddress || "Not Available"}
</Typography> </Typography>
</Typography> </Typography>
</Grid> </Grid>
@ -118,7 +119,8 @@ export default function ManagerViewModal({ open, setViewModal, id }: Props) {
<Typography variant="body1"> <Typography variant="body1">
<strong>Station Name:</strong> <strong>Station Name:</strong>
<Typography variant="body2"> <Typography variant="body2">
{selectedManager.stationName} {selectedManager.chargingStation?.name ||
"Not Available"}
</Typography> </Typography>
</Typography> </Typography>
</Grid> </Grid>

View file

@ -50,12 +50,8 @@ const DashboardLayout: React.FC<LayoutProps> = ({ customStyles }) => {
...customStyles, ...customStyles,
mt: { xs: 8, md: 0 }, mt: { xs: 8, md: 0 },
padding: 0, padding: 0,
})} })}
> >
<Stack <Stack
spacing={2} spacing={2}
sx={{ sx={{

View file

@ -11,7 +11,6 @@ import {
} from "../../redux/slices/slotSlice"; } from "../../redux/slices/slotSlice";
import AddSlotModal from "../../components/AddSlotModal"; import AddSlotModal from "../../components/AddSlotModal";
import dayjs from "dayjs"; import dayjs from "dayjs";
import EditManagerModal from "../../components/EditManagerModal";
import EditSlotModal from "../../components/EditSlotModal"; import EditSlotModal from "../../components/EditSlotModal";
export default function EVSlotList() { export default function EVSlotList() {
const [addModalOpen, setAddModalOpen] = useState<boolean>(false); const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
@ -56,31 +55,35 @@ export default function EVSlotList() {
console.error("Error adding slot", error); console.error("Error adding slot", error);
} }
}; };
console.log("isAvailable type:", typeof isAvailable);
const handleUpdate = async ( const handleUpdate = async (
id: string, id: string,
date: string, startTime: string,
startTime: string, endTime: string,
endTime: string, isAvailable: boolean
isAvailable: boolean ) => {
) => { try {
try { // Convert times to 24-hour format if needed
// Update manager with stationId const formattedStartTime = dayjs(startTime, "HH:mm").format("HH:mm");
await dispatch( const formattedEndTime = dayjs(endTime, "HH:mm").format("HH:mm");
updateSlot({
id, await dispatch(
date, updateSlot({
startTime, id,
endTime, startTime: formattedStartTime, // Ensure it matches backend expectation
isAvailable, endTime: formattedEndTime, // Ensure it matches backend expectation
}) isAvailable,
); })
await dispatch(fetchAvailableSlots()); // Refresh the list after update ).unwrap(); // This helps catch and handle any errors
handleCloseModal(); // Close modal after update
} catch (error) { await dispatch(fetchAvailableSlots()); // Refresh the list
console.error("Update failed", error); handleCloseModal(); // Close the modal
} } catch (error) {
}; console.error("Update failed", error);
// Optionally add error toast or user notification
}
};
const slotColumns: Column[] = [ const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },
@ -95,25 +98,24 @@ export default function EVSlotList() {
// Make sure dayjs is imported // Make sure dayjs is imported
const slotRows = availableSlots?.length const slotRows = availableSlots?.length
? availableSlots.map((slot, index) => { ? availableSlots.map((slot, index) => {
const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD"); const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD");
const startTime = dayjs(slot?.startTime).format("HH:mm"); const startTime = dayjs(slot?.startTime).format("HH:mm");
const endTime = dayjs(slot?.endTime).format("HH:mm"); const endTime = dayjs(slot?.endTime).format("HH:mm");
console.log("availability", availableSlots.isAvailable);
console.log("first", startTime); return {
return { srno: index + 1,
srno: index + 1, id: slot?.id ?? "NA",
id: slot?.id ?? "NA", stationId: slot?.stationId ?? "NA",
stationId: slot?.stationId ?? "NA", name: slot?.ChargingStation?.name ?? "NA",
name: slot?.ChargingStation?.name ?? "NA", date: formattedDate ?? "NA",
date: formattedDate ?? "NA", startTime: startTime ?? "NA",
startTime: startTime ?? "NA", endTime: endTime ?? "NA",
endTime: endTime ?? "NA", isAvailable: slot?.isAvailable ? "Yes" : "No",
isAvailable: slot?.isAvailable ? "Yes" : "No", };
}; })
}) : [];
: [];
return ( return (
<> <>

View file

@ -47,7 +47,8 @@ export default function StationList() {
const handleAddStation = async (data: { const handleAddStation = async (data: {
name: string; name: string;
registeredAddress: string; registeredAddress: string;
totalSlots: string; totalSlots: number;
allowedCarIds: number[];
}) => { }) => {
try { try {
await dispatch(createStation(data)); // Dispatch action to add Station await dispatch(createStation(data)); // Dispatch action to add Station
@ -62,7 +63,7 @@ export default function StationList() {
id: string, id: string,
name: string, name: string,
registeredAddress: string, registeredAddress: string,
totalSlots: string, totalSlots: number,
allowedCarIds: number[] allowedCarIds: number[]
) => { ) => {
try { try {
@ -89,12 +90,13 @@ export default function StationList() {
}; };
const filterStations = Array.isArray(stations) const filterStations = Array.isArray(stations)
? stations.filter((station) => ? stations.filter((station) =>
station.name station.name
.toLocaleLowerCase() .toLocaleLowerCase()
.includes(searchTerm.toLowerCase()) .includes(searchTerm.toLowerCase())
) )
: []; : [];
// Mapping and formatting vehicles // Mapping and formatting vehicles
const categoryRows = filterStations?.length const categoryRows = filterStations?.length
@ -126,6 +128,7 @@ export default function StationList() {
}; };
}) })
: []; : [];
console.log("Rowssss",categoryRows)
const categoryColumns: Column[] = [ const categoryColumns: Column[] = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },

View file

@ -2,6 +2,7 @@ import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import http from "../../lib/https"; import http from "../../lib/https";
import { toast } from "sonner"; import { toast } from "sonner";
interface VehicleBrand { interface VehicleBrand {
name: string;
id: string; id: string;
company: string; company: string;
} }
@ -34,18 +35,23 @@ export const fetchVehicleBrands = createAsyncThunk<
>("vehicle/fetchVehicleBrands", async (_, { rejectWithValue }) => { >("vehicle/fetchVehicleBrands", async (_, { rejectWithValue }) => {
try { try {
const response = await http.get("/get-vehicle-brand"); const response = await http.get("/get-vehicle-brand");
if (!response.data || !Array.isArray(response.data.data)) { if (!response.data || !Array.isArray(response.data.data)) {
throw new Error("Expected array of vehicle brands"); throw new Error("Expected array of vehicle brands");
} }
console.log("beans", response.data.data) // Map the brand names (strings) to objects with 'id' and 'name' properties
// Assuming that 'data' contains an array of objects with a 'company' field return response.data.data.map((brand: string) => ({
return response.data.data; id: brand, // Use brand name as the ID
name: brand, // Use brand name as the label
}));
} catch (error: any) { } catch (error: any) {
return rejectWithValue("Failed to fetch vehicle brands"); return rejectWithValue("Failed to fetch vehicle brands");
} }
}); });
export const vehicleList = createAsyncThunk< export const vehicleList = createAsyncThunk<
Vehicle, Vehicle,
void, void,
@ -157,6 +163,7 @@ const vehicleSlice = createSlice({
state.vehicleBrands = action.payload; state.vehicleBrands = action.payload;
} }
) )
.addCase(fetchVehicleBrands.rejected, (state, action) => { .addCase(fetchVehicleBrands.rejected, (state, action) => {
state.loading = false; state.loading = false;
state.error = state.error =

View file

@ -60,7 +60,7 @@ export const getCarPorts = createAsyncThunk<
>("fetchCarPorts", async (_, { rejectWithValue }) => { >("fetchCarPorts", async (_, { rejectWithValue }) => {
try { try {
const response = await http.get("/get-vehicle-port-dropdown"); const response = await http.get("/get-vehicle-port-dropdown");
return response.data.data; // Adjust based on actual API response return response.data.data; // Adjust based on actual API response
} catch (error: any) { } catch (error: any) {
@ -121,7 +121,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({ const bookSlice = createSlice({
name: "booking", name: "booking",
initialState, initialState,
@ -169,7 +184,32 @@ const bookSlice = createSlice({
(state, action: PayloadAction<string[]>) => { (state, action: PayloadAction<string[]>) => {
state.carPorts = action.payload; state.carPorts = action.payload;
} }
); )
.addCase(deleteBooking.pending, (state) => {
state.loading = true;
})
.addCase(
deleteBooking.fulfilled,
(state, action: PayloadAction<string>) => {
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";
});
}, },
}); });

View file

@ -175,6 +175,9 @@ const managerSlice = createSlice({
}) })
.addCase(deleteManager.fulfilled, (state, action) => { .addCase(deleteManager.fulfilled, (state, action) => {
state.loading = false; state.loading = false;
state.managers = state.managers.filter(
(manager) => String(manager.id) !== String(action.payload)
);
}) })
.addCase(deleteManager.rejected, (state, action) => { .addCase(deleteManager.rejected, (state, action) => {
state.loading = false; state.loading = false;

View file

@ -5,7 +5,7 @@ import { toast } from "sonner";
// Define TypeScript types // Define TypeScript types
interface Slot { interface Slot {
id: string; id: string;
stationId:string; stationId: string;
date: string; date: string;
startTime: string; startTime: string;
endTime: string; endTime: string;
@ -88,16 +88,23 @@ export const createSlot = createAsyncThunk<
// Update Slot details // Update Slot details
export const updateSlot = createAsyncThunk< export const updateSlot = createAsyncThunk<
Slot, Slot,
{ id: number; startTime: string; endTime: string }, // Argument type (slot update data) {
id: string;
startTime: string;
endTime: string;
isAvailable: boolean;
},
{ rejectValue: string } { rejectValue: string }
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => { >("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
try { try {
const response = await http.patch( const response = await http.patch(`/update-availability/${id}`, {
`/update-availability/${id}`, ...slotData,
slotData // Ensure data matches exactly what backend expects
); startHour: slotData.startTime,
endHour: slotData.endTime,
});
toast.success("Slot updated successfully"); toast.success("Slot updated successfully");
return response.data.data; // Return updated slot data return response.data.data;
} catch (error: any) { } catch (error: any) {
toast.error("Error updating the slot: " + error?.message); toast.error("Error updating the slot: " + error?.message);
return rejectWithValue( return rejectWithValue(
@ -167,14 +174,15 @@ const slotSlice = createSlice({
(state, action: PayloadAction<Slot>) => { (state, action: PayloadAction<Slot>) => {
state.loading = false; state.loading = false;
// Update the slot in the state with the updated data // 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 (slot) => slot.id === action.payload.id
); );
if (index !== -1) { if (index !== -1) {
state.slots[index] = action.payload; state.availableSlots[index] = action.payload;
} }
} }
) )
.addCase(updateSlot.rejected, (state, action) => { .addCase(updateSlot.rejected, (state, action) => {
state.loading = false; state.loading = false;
state.error = action.payload || "Failed to update slot"; state.error = action.payload || "Failed to update slot";