dev-jaanvi #1
|
@ -26,7 +26,7 @@ import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddEditUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
import { stationList } from "../../redux/slices/stationSlice.ts";
|
import { getAllStations, stationList } from "../../redux/slices/stationSlice.ts";
|
||||||
import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
|
import { fetchAvailableSlots } from "../../redux/slices/slotSlice.ts";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export default function AddBookingModal({
|
||||||
console.log("first", availableSlots);
|
console.log("first", availableSlots);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchAvailableSlots());
|
dispatch(fetchAvailableSlots());
|
||||||
dispatch(stationList());
|
dispatch(getAllStations());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -74,7 +74,7 @@ export default function AddBookingModal({
|
||||||
// Fetch the bookings after this
|
// Fetch the bookings after this
|
||||||
dispatch(bookingList());
|
dispatch(bookingList());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
console.log("Car Ports: ", carPorts);
|
||||||
// Get today's date in yyyy-mm-dd format
|
// Get today's date in yyyy-mm-dd format
|
||||||
const today = new Date().toISOString().split("T")[0];
|
const today = new Date().toISOString().split("T")[0];
|
||||||
|
|
||||||
|
@ -266,9 +266,9 @@ export default function AddBookingModal({
|
||||||
{carPorts.map((port, index) => (
|
{carPorts.map((port, index) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={index}
|
key={index}
|
||||||
value={port.chargeType}
|
value={port}
|
||||||
>
|
>
|
||||||
{port.chargeType}
|
{port}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
@ -290,7 +290,6 @@ export default function AddBookingModal({
|
||||||
|
|
||||||
{/* Start Time and End Time */}
|
{/* Start Time and End Time */}
|
||||||
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
|
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Start Time
|
Start Time
|
||||||
|
@ -311,7 +310,6 @@ export default function AddBookingModal({
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
End Time
|
End Time
|
||||||
|
|
|
@ -172,14 +172,21 @@ useEffect(() => {
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
{stations.map((station) => (
|
{Array.isArray(stations) &&
|
||||||
|
stations.length > 0 ? (
|
||||||
|
stations.map((station) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={station.id}
|
key={station.id}
|
||||||
value={station.name}
|
value={station.name}
|
||||||
>
|
>
|
||||||
{station.name}
|
{station.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<MenuItem disabled>
|
||||||
|
No stations available
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
{errors.stationName && (
|
{errors.stationName && (
|
||||||
<Typography color="error" variant="body2">
|
<Typography color="error" variant="body2">
|
||||||
|
|
|
@ -46,58 +46,59 @@ export default function AddStationModal({
|
||||||
const vehicleBrands = useSelector(
|
const vehicleBrands = useSelector(
|
||||||
(state: RootState) => state.vehicleReducer.vehicleBrands
|
(state: RootState) => state.vehicleReducer.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[]>([]);
|
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
|
||||||
allowedCarIds: 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 (
|
||||||
|
@ -251,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={{
|
||||||
|
@ -261,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>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
@ -297,7 +305,8 @@ export default function AddStationModal({
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Vehicle Name Dropdown with Checkboxes */}
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -314,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(", ")
|
||||||
}
|
}
|
||||||
|
@ -337,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>
|
||||||
|
|
|
@ -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 */}
|
||||||
|
|
|
@ -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,8 +486,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
{/* Menu Actions */}
|
{/* Menu Actions */}
|
||||||
{open &&
|
{open && (
|
||||||
(
|
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
id="menu"
|
id="menu"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -52,7 +52,7 @@ 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(() => {
|
||||||
|
@ -68,6 +68,8 @@ 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;
|
||||||
|
@ -83,12 +85,14 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,10 +51,15 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
handleClose,
|
handleClose,
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
editRow,
|
editRow,
|
||||||
vehicles,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { control, handleSubmit, setValue, reset, watch } = useForm<FormData>(
|
const {
|
||||||
{
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
reset,
|
||||||
|
watch,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<FormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
registeredAddress: "",
|
registeredAddress: "",
|
||||||
|
@ -57,43 +67,66 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
status: 1,
|
status: 1,
|
||||||
allowedCarIds: [],
|
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
|
||||||
|
.filter((vehicle) => selectedVehicles.includes(vehicle.name))
|
||||||
|
.map((vehicle) => vehicle.id);
|
||||||
|
|
||||||
handleUpdate(
|
handleUpdate(
|
||||||
editRow.id,
|
editRow.id,
|
||||||
data.name,
|
data.name,
|
||||||
data.registeredAddress,
|
data.registeredAddress,
|
||||||
data.totalSlots,
|
data.totalSlots,
|
||||||
data.allowedCarIds
|
vehicleIds
|
||||||
);
|
);
|
||||||
}
|
handleClose();
|
||||||
handleClose(); // Close the modal after update
|
reset();
|
||||||
reset(); // Reset the form fields
|
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,13 +273,20 @@ 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 sx={{ display: "flex", gap: 2 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -243,48 +295,87 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Vehicle Names
|
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>
|
</Typography>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel>Choose Vehicles</InputLabel>
|
<InputLabel>Choose Vehicles</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
value={allowedCarIds || []} // Watch the allowedCarIds to reflect selected values
|
value={selectedVehicles}
|
||||||
onChange={(e) => {
|
onChange={handleCheckboxChange}
|
||||||
// Update the allowedCarIds when the selection changes
|
renderValue={(selected) =>
|
||||||
setValue(
|
(selected as string[]).join(", ")
|
||||||
"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) => (
|
{filteredVehicles.length > 0 ? (
|
||||||
|
filteredVehicles.map((vehicle) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={vehicle.id}
|
key={vehicle.id}
|
||||||
value={vehicle.id}
|
value={vehicle.name}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={allowedCarIds?.includes(
|
checked={selectedVehicles.includes(
|
||||||
vehicle.id
|
vehicle.name
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<ListItemText primary={vehicle.name} />
|
<ListItemText
|
||||||
|
primary={vehicle.name}
|
||||||
|
/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<MenuItem disabled>
|
||||||
|
No vehicles available for this brand
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{errors.allowedCarIds
|
||||||
|
? errors.allowedCarIds.message
|
||||||
|
: ""}
|
||||||
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<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>
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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);
|
||||||
|
@ -24,9 +25,7 @@ export default function Header() {
|
||||||
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());
|
||||||
|
@ -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"
|
||||||
|
|
|
@ -103,13 +103,13 @@ export default function MenuContent({ hidden }: PropType) {
|
||||||
<ListItem
|
<ListItem
|
||||||
key={index}
|
key={index}
|
||||||
disablePadding
|
disablePadding
|
||||||
sx={{ display: "block", py: 1 }}
|
sx={{ display: "block", py: 1, px: 0.9 }}
|
||||||
>
|
>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
component={Link}
|
component={Link}
|
||||||
to={item.url}
|
to={item.url}
|
||||||
selected={item.url === location.pathname}
|
selected={item.url === location.pathname}
|
||||||
sx={{ alignItems: "center", columnGap: 1 }}
|
sx={{ alignItems: "center", columnGap: 0.5 }}
|
||||||
>
|
>
|
||||||
<ListItemIcon
|
<ListItemIcon
|
||||||
sx={{
|
sx={{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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={{
|
||||||
|
|
|
@ -1,25 +1,35 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
// import { useHistory } from "react-router-dom";
|
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL: process.env.REACT_APP_BACKEND_URL,
|
baseURL: process.env.REACT_APP_BACKEND_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
http.interceptors.request.use((config) => {
|
http.interceptors.request.use((config) => {
|
||||||
const authToken = localStorage.getItem("authToken");
|
const authToken = localStorage.getItem("authToken");
|
||||||
if (authToken) {
|
if (authToken) {
|
||||||
config.headers.Authorization = `Bearer ${authToken}`;
|
config.headers.Authorization = `Bearer ${authToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
http.interceptors.response.use(
|
http.interceptors.response.use(
|
||||||
(response) => response,
|
(response) => response,
|
||||||
(error) => {
|
(error) => {
|
||||||
|
if (error.response) {
|
||||||
|
const status = error.response.status;
|
||||||
|
const requestUrl = error.config.url; // Get the API route
|
||||||
|
|
||||||
if (error.response && error.response.status === 403 ) {
|
// Handle token expiration (401) but NOT for login failures
|
||||||
|
if (status === 401 && !requestUrl.includes("/login")) {
|
||||||
|
localStorage.removeItem("authToken");
|
||||||
window.location.href = "/login";
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
|
||||||
// const history = useHistory();
|
// Handle forbidden access
|
||||||
// history.push("/login");
|
if (status === 403) {
|
||||||
|
localStorage.removeItem("authToken");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,10 +56,10 @@ 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
|
||||||
|
@ -69,7 +69,7 @@ export default function EVSlotList() {
|
||||||
await dispatch(
|
await dispatch(
|
||||||
updateSlot({
|
updateSlot({
|
||||||
id,
|
id,
|
||||||
date,
|
|
||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
isAvailable,
|
isAvailable,
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -88,9 +89,14 @@ export default function StationList() {
|
||||||
await dispatch(toggleStatus({ id, status: newStatus }));
|
await dispatch(toggleStatus({ id, status: newStatus }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterStations = stations?.filter((station) =>
|
const filterStations = Array.isArray(stations)
|
||||||
station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
? stations.filter((station) =>
|
||||||
);
|
station.name
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(searchTerm.toLowerCase())
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
|
||||||
// Mapping and formatting vehicles
|
// Mapping and formatting vehicles
|
||||||
const categoryRows = filterStations?.length
|
const categoryRows = filterStations?.length
|
||||||
|
@ -122,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" },
|
||||||
|
|
|
@ -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,19 +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");
|
||||||
}
|
}
|
||||||
// Assuming that 'data' contains an array of objects with a 'company' field
|
|
||||||
return response.data.data.map((item: any) => ({
|
// Map the brand names (strings) to objects with 'id' and 'name' properties
|
||||||
id: item.company, // You can use 'company' as the unique identifier
|
return response.data.data.map((brand: string) => ({
|
||||||
name: item.company, // The name field will be used in the dropdown
|
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,
|
||||||
|
@ -158,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 =
|
||||||
|
|
|
@ -60,6 +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) {
|
||||||
return rejectWithValue(
|
return rejectWithValue(
|
||||||
|
@ -119,7 +120,22 @@ export const addBooking = createAsyncThunk<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
export const deleteBooking = createAsyncThunk<
|
||||||
|
string, // Return type (id of deleted slot)
|
||||||
|
string,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("booking/deleteBooking", async (id, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const response = await http.delete(`/delete-booking/${id}`);
|
||||||
|
toast.success("Slot deleted successfully");
|
||||||
|
return id; // Return the id of the deleted slot
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error deleting the slot: " + error?.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
const bookSlice = createSlice({
|
const bookSlice = createSlice({
|
||||||
name: "booking",
|
name: "booking",
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -167,7 +183,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";
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,7 +88,7 @@ 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 }, // Argument type (slot update data)
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
|
>("slots/updateSlot", async ({ id, ...slotData }, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
|
@ -97,6 +97,7 @@ export const updateSlot = createAsyncThunk<
|
||||||
slotData
|
slotData
|
||||||
);
|
);
|
||||||
toast.success("Slot updated successfully");
|
toast.success("Slot updated successfully");
|
||||||
|
console.log("Slots",response.data.data)
|
||||||
return response.data.data; // Return updated slot data
|
return response.data.data; // Return updated slot data
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error("Error updating the slot: " + error?.message);
|
toast.error("Error updating the slot: " + error?.message);
|
||||||
|
@ -167,11 +168,11 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface Station {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
totalSlots: string;
|
totalSlots: number;
|
||||||
status: number;
|
status: number;
|
||||||
allowedCarIds: number[];
|
allowedCarIds: number[];
|
||||||
}
|
}
|
||||||
|
@ -47,14 +47,35 @@ export const stationList = createAsyncThunk<any, void, { rejectValue: string }>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
export const getAllStations = createAsyncThunk<
|
||||||
|
any,
|
||||||
|
void,
|
||||||
|
{ rejectValue: string }
|
||||||
|
>("getAllStations", async (_, { rejectWithValue }) => {
|
||||||
|
try {
|
||||||
|
const token = localStorage?.getItem("authToken");
|
||||||
|
if (!token) throw new Error("No token found");
|
||||||
|
|
||||||
|
const response = await http.get("/get-all-stations");
|
||||||
|
|
||||||
|
if (!response.data) throw new Error("Invalid API response");
|
||||||
|
|
||||||
|
// Return the full response to handle in the reducer
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Error Fetching Stations: " + error.message);
|
||||||
|
return rejectWithValue(
|
||||||
|
error?.response?.data?.message || "An error occurred"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
// Create Station
|
// Create Station
|
||||||
export const createStation = createAsyncThunk<
|
export const createStation = createAsyncThunk<
|
||||||
any,
|
any,
|
||||||
{
|
{
|
||||||
name: string;
|
name: string;
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
totalSlots: string;
|
totalSlots: number;
|
||||||
allowedCarIds: number[];
|
allowedCarIds: number[];
|
||||||
},
|
},
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
|
@ -62,7 +83,7 @@ export const createStation = createAsyncThunk<
|
||||||
try {
|
try {
|
||||||
const response = await http.post("/create-station", data);
|
const response = await http.post("/create-station", data);
|
||||||
toast.success("Station created successfully");
|
toast.success("Station created successfully");
|
||||||
return response.data;
|
return response.data; // Assuming the response contains the created station data
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(
|
toast.error(
|
||||||
"Failed to create Station: " +
|
"Failed to create Station: " +
|
||||||
|
@ -74,25 +95,31 @@ export const createStation = createAsyncThunk<
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update Station details
|
||||||
// Update Station details
|
// Update Station details
|
||||||
export const updateStation = createAsyncThunk(
|
export const updateStation = createAsyncThunk(
|
||||||
"updateStation",
|
"updateStation",
|
||||||
async ({ id, ...stationData }: Station, { rejectWithValue }) => {
|
async ({ id, ...stationData }: Station, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
|
// Exclude the `status` from the update payload
|
||||||
|
const { status, ...updateData } = stationData;
|
||||||
|
|
||||||
|
// Send the update request without the `status`
|
||||||
const response = await http.patch(
|
const response = await http.patch(
|
||||||
`/update-station/${id}`,
|
`/update-station/${id}`,
|
||||||
stationData
|
updateData
|
||||||
);
|
);
|
||||||
toast.success("Station Deatils updated successfully");
|
toast.success("Station Details updated successfully");
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error("Error updating the user: " + error);
|
toast.error("Error updating the station: " + error);
|
||||||
return rejectWithValue(
|
return rejectWithValue(
|
||||||
error.response?.data?.message || "An error occurred"
|
error.response?.data?.message || "An error occurred"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const deleteStation = createAsyncThunk<
|
export const deleteStation = createAsyncThunk<
|
||||||
string,
|
string,
|
||||||
string,
|
string,
|
||||||
|
@ -158,17 +185,30 @@ const stationSlice = createSlice({
|
||||||
stationList.fulfilled,
|
stationList.fulfilled,
|
||||||
(state, action: PayloadAction<any>) => {
|
(state, action: PayloadAction<any>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
// Properly extract stations from the response data structure
|
// Correct data extraction
|
||||||
state.stations =
|
state.stations = action.payload.data?.stations || [];
|
||||||
action.payload.data?.results ||
|
|
||||||
action.payload.data ||
|
|
||||||
[];
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(stationList.rejected, (state, action) => {
|
.addCase(stationList.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload || "Failed to fetch stations";
|
state.error = action.payload || "Failed to fetch stations";
|
||||||
})
|
})
|
||||||
|
.addCase(getAllStations.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
})
|
||||||
|
.addCase(
|
||||||
|
getAllStations.fulfilled,
|
||||||
|
(state, action: PayloadAction<any>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.stations = action.payload.data || [];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
.addCase(getAllStations.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload || "Failed to fetch stations";
|
||||||
|
})
|
||||||
.addCase(createStation.pending, (state) => {
|
.addCase(createStation.pending, (state) => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
})
|
})
|
||||||
|
@ -176,12 +216,12 @@ const stationSlice = createSlice({
|
||||||
createStation.fulfilled,
|
createStation.fulfilled,
|
||||||
(state, action: PayloadAction<any>) => {
|
(state, action: PayloadAction<any>) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
// Add the newly created station to the state if it exists in the response
|
|
||||||
if (action.payload.data) {
|
if (action.payload.data) {
|
||||||
state.stations.push(action.payload.data);
|
state.stations.push(action.payload.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
.addCase(
|
.addCase(
|
||||||
createStation.rejected,
|
createStation.rejected,
|
||||||
(state, action: PayloadAction<string | undefined>) => {
|
(state, action: PayloadAction<string | undefined>) => {
|
||||||
|
@ -225,8 +265,21 @@ const stationSlice = createSlice({
|
||||||
})
|
})
|
||||||
.addCase(updateStation.fulfilled, (state, action) => {
|
.addCase(updateStation.fulfilled, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload;
|
// If the station was updated, find and update it in the state
|
||||||
|
const updatedStation = action.payload;
|
||||||
|
const stationIndex = state.stations.findIndex(
|
||||||
|
(station) => station.id === updatedStation.id
|
||||||
|
);
|
||||||
|
if (stationIndex !== -1) {
|
||||||
|
// Here, merge the updated station with the existing one
|
||||||
|
// Ensure `status` is not overwritten if not explicitly updated
|
||||||
|
state.stations[stationIndex] = {
|
||||||
|
...state.stations[stationIndex],
|
||||||
|
...updatedStation,
|
||||||
|
};
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
.addCase(updateStation.rejected, (state) => {
|
.addCase(updateStation.rejected, (state) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue