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
);
console.log("vehicle Brands", vehicleBrands);
const [selectedBrand, setSelectedBrand] = useState<string | "">("");
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<string[]>([]);
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
// 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
allowedCars: vehicleIds, // Pass the vehicle ids to the backend
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 (
@ -253,7 +252,7 @@ export default function AddStationModal({
</Box>
</Box>
{/* Vehicle Name Dropdown with Checkboxes */}
{/* Vehicle Brand Dropdown with Checkboxes */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
@ -263,26 +262,33 @@ export default function AddStationModal({
}}
>
<Typography variant="body2" fontWeight={500}>
Select Vehicle Brand
Select Vehicle Brands
</Typography>
<FormControl fullWidth>
<InputLabel>Choose Brand</InputLabel>
<InputLabel>Choose Brands</InputLabel>
<Select
value={selectedBrand}
onChange={(e) =>
setSelectedBrand(e.target.value)
multiple
value={selectedBrands}
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) => (
<MenuItem
key={brand.id}
value={brand.id}
>
{brand.name}{" "}
{/* Make sure 'brand.id' is set as value */}
<Checkbox
checked={selectedBrands.includes(
brand.id
)}
/>
<ListItemText
primary={brand.name}
/>
</MenuItem>
))
) : (
@ -298,8 +304,9 @@ export default function AddStationModal({
</FormHelperText>
</FormControl>
</Box>
{/* Vehicle Name Dropdown with Checkboxes */}
<Box
sx={{
display: "flex",
@ -316,7 +323,7 @@ export default function AddStationModal({
<Select
multiple
value={selectedVehicles}
onChange={handleCheckboxChange}
onChange={handleVehicleChange}
renderValue={(selected) =>
(selected as string[]).join(", ")
}
@ -339,8 +346,8 @@ export default function AddStationModal({
))
) : (
<MenuItem disabled>
No vehicles available for this
brand
No vehicles available for the
selected brands
</MenuItem>
)}
</Select>

View file

@ -83,6 +83,36 @@ export default function AddVehicleModal({
>
{/* First Row - Two Inputs */}
<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
sx={{
display: "flex",
@ -121,37 +151,6 @@ export default function AddVehicleModal({
})}
/>
</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>
{/* Second Row - Two Inputs */}

View file

@ -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<CustomTableProps> = ({
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<CustomTableProps> = ({
/>
</Box>
{/* Menu Actions */}
{open &&
(
<Menu
anchorEl={anchorEl}
id="menu"
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{
horizontal: "right",
vertical: "top",
}}
anchorOrigin={{
horizontal: "right",
vertical: "bottom",
{open && (
<Menu
anchorEl={anchorEl}
id="menu"
open={open}
onClose={handleClose}
onClick={handleClose}
transformOrigin={{
horizontal: "right",
vertical: "top",
}}
anchorOrigin={{
horizontal: "right",
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={{
[`& .${paperClasses.root}`]: {
padding: 0,
},
"& .MuiList-root": {
background: "#272727", // Remove any divider under menu items
},
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
color: "#52ACDF",
}}
>
<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={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
// setSelectedRow(row);
setViewModal(true);
}}
color="primary"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
color: "#52ACDF",
}}
>
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}
/>
)}
{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={() => 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={{
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>
)}
<Button
variant="text"
onClick={(e) => {
e.stopPropagation();
setDeleteModal(true);
}}
color="error"
sx={{
justifyContent: "flex-start",
py: 0,
fontWeight: "bold",
}}
>
Delete
</Button>
</Box>
</Menu>
)}
{/* Modals */}
{deleteModal && (
<DeleteModal

View file

@ -74,9 +74,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
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

View file

@ -52,12 +52,12 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const [loading, setLoading] = useState(false);
const [isAvailable, setIsAvailable] = useState<boolean>(
editRow?.isAvailable || false
); // Default to editRow availability
);
// 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
@ -68,27 +68,28 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const onSubmit = async (data: FormData) => {
if (editRow) {
setLoading(true); // Start loading
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,
},
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
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
}
@ -143,7 +144,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
{/* Input Fields */}
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 2 }}>
{/* Date */}
<Box sx={{ flex: "1 1 100%" }}>
{/* <Box sx={{ flex: "1 1 100%" }}>
<Typography variant="body2" fontWeight={500} mb={0.5}>
Date
</Typography>
@ -162,7 +163,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
/>
)}
/>
</Box>
</Box> */}
{/* Start Time */}
<Box sx={{ flex: "1 1 48%" }}>

View file

@ -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<EditStationModalProps> = ({
@ -46,54 +51,82 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
handleClose,
handleUpdate,
editRow,
vehicles,
}) => {
const { control, handleSubmit, setValue, reset, watch } = useForm<FormData>(
{
defaultValues: {
name: "",
registeredAddress: "",
totalSlots: 0,
status: 1,
allowedCarIds: [],
},
}
const {
control,
handleSubmit,
setValue,
reset,
watch,
formState: { errors },
} = 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 allowedCarIds = watch("allowedCarIds");
const [selectedBrand, setSelectedBrand] = useState<string | "">("");
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
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<EditStationModalProps> = ({
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<EditStationModalProps> = ({
{/* Horizontal Line */}
<Box sx={{ borderBottom: "1px solid #ddd", my: 2 }} />
{/* Input Fields */}
{/* Form */}
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{/* First Row - Two Inputs */}
{/* Station Name and Address */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
@ -168,11 +201,16 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth
placeholder="Enter Station Name"
size="small"
error={!!errors.name}
helperText={
errors.name
? errors.name.message
: ""
}
/>
)}
/>
</Box>
<Box
sx={{
display: "flex",
@ -196,13 +234,20 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
fullWidth
placeholder="Enter Registered Address"
size="small"
error={!!errors.registeredAddress}
helperText={
errors.registeredAddress
? errors.registeredAddress
.message
: ""
}
/>
)}
/>
</Box>
</Box>
{/* Second Row - Total Slots */}
{/* Total Slots */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
@ -228,61 +273,107 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
placeholder="Enter Total Slots"
size="small"
type="number"
error={!!errors.totalSlots}
helperText={
errors.totalSlots
? errors.totalSlots.message
: ""
}
/>
)}
/>
</Box>
</Box>
{/* Vehicle Dropdown with Checkboxes */}
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Vehicle Names
</Typography>
<FormControl fullWidth>
<InputLabel>Choose Vehicles</InputLabel>
<Select
multiple
value={allowedCarIds || []} // Watch the allowedCarIds to reflect selected values
onChange={(e) => {
// Update the allowedCarIds when the selection changes
setValue(
"allowedCarIds",
e.target.value as string[]
); // Update allowedCarIds on change
}}
renderValue={(selected) => {
return (selected as string[])
.map((id) => {
const vehicle = vehicles?.find(
(vehicle) => vehicle.id === id
);
return vehicle ? vehicle.name : "";
})
.join(", ");
}}
>
{vehicles?.map((vehicle) => (
<MenuItem
key={vehicle.id}
value={vehicle.id}
>
<Checkbox
checked={allowedCarIds?.includes(
vehicle.id
)}
/>
<ListItemText primary={vehicle.name} />
</MenuItem>
))}
</Select>
</FormControl>
{/* Vehicle Brand Selection */}
<Box sx={{ display: "flex", gap: 2 }}>
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
Select Vehicle Brand
</Typography>
<FormControl fullWidth>
<InputLabel>Choose Brand</InputLabel>
<Select
value={selectedBrand}
onChange={(e) =>
setSelectedBrand(e.target.value)
}
label="Choose Brand"
>
{vehicleBrands.length > 0 ? (
vehicleBrands.map((brand) => (
<MenuItem
key={brand.id}
value={brand.id}
>
{brand.name}
</MenuItem>
))
) : (
<MenuItem disabled>
No vehicle brands available
</MenuItem>
)}
</Select>
</FormControl>
</Box>
{/* Vehicle Selection with Checkboxes */}
<Box
sx={{
display: "flex",
flexDirection: "column",
width: "100%",
}}
>
<Typography variant="body2" fontWeight={500}>
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>
@ -300,7 +391,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
"&:hover": { backgroundColor: "#439BC1" },
}}
>
Update Charging Station
Update Station
</Button>
</Box>
</Box>

View file

@ -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<AppDispatch>();
const { user } = useSelector(
(state: RootState) => state?.profileReducer
);
const dispatch = useDispatch<AppDispatch>();
const { user } = useSelector((state: RootState) => state?.profileReducer);
React.useEffect(() => {
dispatch(fetchAdminProfile());
}, [dispatch]);
React.useEffect(() => {
dispatch(fetchAdminProfile());
}, [dispatch]);
return (
<Box
sx={{
@ -89,7 +88,6 @@ export default function Header() {
display: { xs: "none", sm: "flex" }, // Hide on mobile, show on larger screens
}}
>
<NotificationsNoneIcon onClick={toggleNotifications} />
<Avatar
alt="User Avatar"

View file

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

View file

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

View file

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

View file

@ -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<boolean>(false);
@ -56,31 +55,35 @@ 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 handleUpdate = async (
id: string,
startTime: string,
endTime: string,
isAvailable: boolean
) => {
try {
// Convert times to 24-hour format if needed
const formattedStartTime = dayjs(startTime, "HH:mm").format("HH:mm");
const formattedEndTime = dayjs(endTime, "HH:mm").format("HH:mm");
await dispatch(
updateSlot({
id,
startTime: formattedStartTime, // Ensure it matches backend expectation
endTime: formattedEndTime, // Ensure it matches backend expectation
isAvailable,
})
).unwrap(); // This helps catch and handle any errors
await dispatch(fetchAvailableSlots()); // Refresh the list
handleCloseModal(); // Close the modal
} catch (error) {
console.error("Update failed", error);
// Optionally add error toast or user notification
}
};
const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" },
@ -95,25 +98,24 @@ export default function EVSlotList() {
// 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("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",
};
})
: [];
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");
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",
};
})
: [];
return (
<>

View file

@ -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 {
@ -89,12 +90,13 @@ export default function StationList() {
};
const filterStations = Array.isArray(stations)
? stations.filter((station) =>
station.name
.toLocaleLowerCase()
.includes(searchTerm.toLowerCase())
)
: [];
? stations.filter((station) =>
station.name
.toLocaleLowerCase()
.includes(searchTerm.toLowerCase())
)
: [];
// Mapping and formatting vehicles
const categoryRows = filterStations?.length
@ -126,6 +128,7 @@ export default function StationList() {
};
})
: [];
console.log("Rowssss",categoryRows)
const categoryColumns: Column[] = [
{ id: "srno", label: "Sr No" },

View file

@ -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,18 +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");
}
console.log("beans", response.data.data)
// Assuming that 'data' contains an array of objects with a 'company' field
return response.data.data;
// 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,
@ -157,6 +163,7 @@ const vehicleSlice = createSlice({
state.vehicleBrands = action.payload;
}
)
.addCase(fetchVehicleBrands.rejected, (state, action) => {
state.loading = false;
state.error =

View file

@ -60,7 +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) {
@ -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({
name: "booking",
initialState,
@ -169,7 +184,32 @@ const bookSlice = createSlice({
(state, action: PayloadAction<string[]>) => {
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) => {
state.loading = false;
state.managers = state.managers.filter(
(manager) => String(manager.id) !== String(action.payload)
);
})
.addCase(deleteManager.rejected, (state, action) => {
state.loading = false;

View file

@ -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,16 +88,23 @@ 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;
isAvailable: boolean;
},
{ rejectValue: string }
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
try {
const response = await http.patch(
`/update-availability/${id}`,
slotData
);
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");
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(
@ -167,14 +174,15 @@ const slotSlice = createSlice({
(state, action: PayloadAction<Slot>) => {
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;
}
}
)
.addCase(updateSlot.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "Failed to update slot";