dev-jaanvi #1
|
@ -13,19 +13,20 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
addBooking,
|
addBooking,
|
||||||
bookingList,
|
bookingList,
|
||||||
getCarNames,
|
getCarNames,
|
||||||
getCarPorts,
|
getCarPorts,
|
||||||
} from "../../redux/slices/bookSlice";
|
} from "../../redux/slices/bookSlice";
|
||||||
import { AppDispatch } from "../../redux/store/store";
|
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddEditUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
|
import { stationList } from "../../redux/slices/stationSlice.ts";
|
||||||
|
|
||||||
export default function AddBookingModal({
|
export default function AddBookingModal({
|
||||||
open,
|
open,
|
||||||
|
@ -44,6 +45,13 @@ export default function AddBookingModal({
|
||||||
} = useForm();
|
} = useForm();
|
||||||
const [carNames, setCarNames] = useState<any[]>([]); // To hold the car names
|
const [carNames, setCarNames] = useState<any[]>([]); // To hold the car names
|
||||||
const [carPorts, setCarPorts] = useState<any[]>([]); // To hold the car ports
|
const [carPorts, setCarPorts] = useState<any[]>([]); // To hold the car ports
|
||||||
|
const stations = useSelector(
|
||||||
|
(state: RootState) => state?.stationReducer.stations
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(stationList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch car names and car ports
|
// Fetch car names and car ports
|
||||||
|
@ -66,7 +74,7 @@ export default function AddBookingModal({
|
||||||
|
|
||||||
const onSubmit = async (data: any) => {
|
const onSubmit = async (data: any) => {
|
||||||
const bookingData = {
|
const bookingData = {
|
||||||
stationId: data.stationId,
|
stationId: data.stationId, // Using stationId here for the backend
|
||||||
date: data.date,
|
date: data.date,
|
||||||
startTime: data.startTime,
|
startTime: data.startTime,
|
||||||
endTime: data.endTime,
|
endTime: data.endTime,
|
||||||
|
@ -130,23 +138,34 @@ export default function AddBookingModal({
|
||||||
{/* Station ID */}
|
{/* Station ID */}
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Station ID
|
Select Station
|
||||||
</Typography>
|
</Typography>
|
||||||
<CustomTextField
|
<FormControl fullWidth error={!!errors.stationName}>
|
||||||
fullWidth
|
<InputLabel>Select Station</InputLabel>
|
||||||
placeholder="Enter Station Id"
|
<Select
|
||||||
size="small"
|
{...register("stationId", {
|
||||||
error={!!errors.stationId}
|
required: "Station Name is required", // Change to stationId
|
||||||
helperText={
|
})}
|
||||||
errors.stationId
|
defaultValue=""
|
||||||
? errors.stationId.message
|
size="small"
|
||||||
: ""
|
>
|
||||||
}
|
{stations.map((station) => (
|
||||||
{...register("stationId", {
|
<MenuItem
|
||||||
required: "Station ID is required",
|
key={station.id}
|
||||||
})}
|
value={station.id}
|
||||||
/>
|
>
|
||||||
|
{station.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
{errors.stationName && (
|
||||||
|
<Typography color="error" variant="body2">
|
||||||
|
{errors.stationName.message}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Date
|
Date
|
||||||
|
@ -190,7 +209,6 @@ export default function AddBookingModal({
|
||||||
size="small"
|
size="small"
|
||||||
error={!!errors.carName}
|
error={!!errors.carName}
|
||||||
>
|
>
|
||||||
<InputLabel>Car Name</InputLabel>
|
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
label="Car Name"
|
label="Car Name"
|
||||||
|
|
|
@ -6,21 +6,28 @@ import {
|
||||||
Modal,
|
Modal,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
InputLabel,
|
||||||
|
FormControl,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { addManager, managerList } from "../../redux/slices/managerSlice";
|
import { addManager, managerList } from "../../redux/slices/managerSlice";
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddEditUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
import React, { useState, useRef } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { RootState } from "../../redux/reducers.ts";
|
||||||
|
import { stationList } from "../../redux/slices/stationSlice.ts";
|
||||||
|
|
||||||
export default function AddManagerModal({
|
export default function AddManagerModal({
|
||||||
open,
|
open,
|
||||||
handleClose,
|
handleClose,
|
||||||
handleAddManager,
|
handleAddManager,
|
||||||
|
// Assume the stations prop contains a list of station objects with 'id' and 'name'
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const {
|
const {
|
||||||
|
@ -31,16 +38,26 @@ export default function AddManagerModal({
|
||||||
reset,
|
reset,
|
||||||
} = useForm();
|
} = useForm();
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const stations = useSelector(
|
||||||
|
(state: RootState) => state?.stationReducer.stations
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(stationList());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
const onSubmit = async (data: any) => {
|
const onSubmit = async (data: any) => {
|
||||||
|
// Retrieve the stationId based on the stationName selected
|
||||||
|
const selectedStation = stations.find(
|
||||||
|
(station) => station.name === data.stationName
|
||||||
|
);
|
||||||
const managerData = {
|
const managerData = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
phone: data.phone,
|
phone: data.phone,
|
||||||
password: data.password,
|
password: data.password,
|
||||||
stationId: data.stationId, // Send stationId here
|
stationId: selectedStation?.id, // Send stationId here
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -131,33 +148,48 @@ export default function AddManagerModal({
|
||||||
"Maximum 30 characters allowed",
|
"Maximum 30 characters allowed",
|
||||||
},
|
},
|
||||||
pattern: {
|
pattern: {
|
||||||
value: /^[A-Za-z\s]+$/, // Only letters and spaces are allowed
|
value: /^[A-Za-z\s]+$/,
|
||||||
message:
|
message:
|
||||||
"Manager Name must only contain letters and spaces",
|
"Manager Name must only contain letters and spaces",
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Station Dropdown */}
|
||||||
|
<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}>
|
||||||
Station Id
|
Select Station
|
||||||
</Typography>
|
</Typography>
|
||||||
<CustomTextField
|
<FormControl fullWidth error={!!errors.stationName}>
|
||||||
fullWidth
|
<InputLabel>Select Station</InputLabel>
|
||||||
placeholder="Enter Station Id"
|
<Select
|
||||||
size="small"
|
{...register("stationName", {
|
||||||
error={!!errors.stationId}
|
required: "Station Name is required",
|
||||||
helperText={
|
})}
|
||||||
errors.stationId
|
defaultValue=""
|
||||||
? errors.stationId.message
|
size="small"
|
||||||
: ""
|
>
|
||||||
}
|
{stations.map((station) => (
|
||||||
{...register("stationId", {
|
<MenuItem
|
||||||
required: "Station Id is required",
|
key={station.id}
|
||||||
})}
|
value={station.name}
|
||||||
/>
|
>
|
||||||
|
{station.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
{errors.stationName && (
|
||||||
|
<Typography color="error" variant="body2">
|
||||||
|
{errors.stationName.message}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Email and Password */}
|
{/* Email and Password */}
|
||||||
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
{/* Email */}
|
{/* Email */}
|
||||||
|
@ -219,7 +251,6 @@ export default function AddManagerModal({
|
||||||
: "primary"
|
: "primary"
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
// onMouseDown={togglePasswordVisibility}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
|
@ -247,9 +278,8 @@ export default function AddManagerModal({
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Phone and Registered Address */}
|
{/* Phone Number */}
|
||||||
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
<Box sx={{ display: "flex", gap: 2, mb: 2 }}>
|
||||||
{/* Phone */}
|
|
||||||
<Box sx={{ flex: 1 }}>
|
<Box sx={{ flex: 1 }}>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Typography variant="body2" fontWeight={500}>
|
||||||
Phone Number
|
Phone Number
|
||||||
|
@ -272,7 +302,6 @@ export default function AddManagerModal({
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { Box, Button, Typography, Modal } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Modal,
|
||||||
|
FormControl,
|
||||||
|
FormHelperText,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
Checkbox,
|
||||||
|
ListItemText,
|
||||||
|
InputLabel,
|
||||||
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { RootState } from "../../redux/reducers.ts";
|
||||||
|
import { vehicleList } from "../../redux/slices/VehicleSlice"; // Adjust this import path accordingly
|
||||||
import {
|
import {
|
||||||
CustomIconButton,
|
CustomIconButton,
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
|
@ -18,10 +34,49 @@ export default function AddStationModal({
|
||||||
reset,
|
reset,
|
||||||
} = useForm();
|
} = useForm();
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
// Get vehicle names from the Redux store
|
||||||
|
const vehicles = useSelector(
|
||||||
|
(state: RootState) => state.vehicleReducer.vehicles
|
||||||
|
); // Adjust according to your actual state structure
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(vehicleList()); // Fetch vehicles when the component mounts
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
|
||||||
|
|
||||||
|
// Handle the change in selected vehicles (checkboxes)
|
||||||
|
const handleCheckboxChange = (
|
||||||
|
event: React.ChangeEvent<{ value: unknown }>
|
||||||
|
) => {
|
||||||
|
const value = event.target.value as string[];
|
||||||
|
setSelectedVehicles(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to map selected vehicle names to corresponding vehicle ids
|
||||||
|
const getVehicleIds = () => {
|
||||||
|
return vehicles
|
||||||
|
.filter((vehicle) => selectedVehicles.includes(vehicle.name))
|
||||||
|
.map((vehicle) => vehicle.id); // Return an array of ids based on the selected names
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = (data: any) => {
|
const onSubmit = (data: any) => {
|
||||||
handleAddStation(data); // Add station to the list
|
const vehicleIds = getVehicleIds(); // Get the ids of the selected vehicles
|
||||||
|
|
||||||
|
// Prepare the data to be sent to the backend
|
||||||
|
const payload = {
|
||||||
|
...data,
|
||||||
|
status: 1, // Default status, can be adjusted if needed
|
||||||
|
allowedCarIds: vehicleIds, // Pass the vehicle ids to the backend
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle adding the station with the constructed payload
|
||||||
|
handleAddStation(payload);
|
||||||
handleClose(); // Close modal after adding
|
handleClose(); // Close modal after adding
|
||||||
reset();
|
reset();
|
||||||
|
setSelectedVehicles([]); // Reset selected vehicles after submission
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -77,7 +132,6 @@ export default function AddStationModal({
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* First Row - Two Inputs */}
|
|
||||||
<Box sx={{ display: "flex", gap: 2 }}>
|
<Box sx={{ display: "flex", gap: 2 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -175,6 +229,53 @@ export default function AddStationModal({
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Vehicle Name Dropdown with Checkboxes */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" fontWeight={500}>
|
||||||
|
Vehicle Name
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* Dropdown with Checkboxes */}
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel>Choose Vehicles</InputLabel>
|
||||||
|
<Select
|
||||||
|
multiple
|
||||||
|
value={selectedVehicles}
|
||||||
|
onChange={handleCheckboxChange}
|
||||||
|
renderValue={(selected) =>
|
||||||
|
(selected as string[]).join(", ")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{vehicles?.map((vehicle) => (
|
||||||
|
<MenuItem
|
||||||
|
key={vehicle.id}
|
||||||
|
value={vehicle.name}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={selectedVehicles.includes(
|
||||||
|
vehicle.name
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={vehicle.name}
|
||||||
|
/>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<FormHelperText>
|
||||||
|
{errors.vehicleName
|
||||||
|
? errors.vehicleName.message
|
||||||
|
: ""}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
|
@ -202,4 +303,4 @@ export default function AddStationModal({
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -145,7 +145,7 @@ export default function AddVehicleModal({
|
||||||
{...register("company", {
|
{...register("company", {
|
||||||
required: "Company is required",
|
required: "Company is required",
|
||||||
minLength: {
|
minLength: {
|
||||||
value: 5,
|
value: 3,
|
||||||
message:
|
message:
|
||||||
"Company must be at least 5 characters long",
|
"Company must be at least 5 characters long",
|
||||||
},
|
},
|
||||||
|
|
108
src/components/AvailableSlotModal/index.tsx
Normal file
108
src/components/AvailableSlotModal/index.tsx
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { styled } from "@mui/system";
|
||||||
|
import {
|
||||||
|
fetchAvailableSlots,
|
||||||
|
availableSlots,
|
||||||
|
} from "../../redux/slices/slotSlice"; // Update with the correct import path
|
||||||
|
|
||||||
|
const SlotButton = styled(Button)<{ selected: boolean }>(({ selected }) => ({
|
||||||
|
backgroundColor: selected ? "#1976d2" : "#333",
|
||||||
|
color: "#fff",
|
||||||
|
border: "1px solid #555",
|
||||||
|
width: "120px",
|
||||||
|
height: "40px",
|
||||||
|
margin: "5px",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: selected ? "#1565c0" : "#444",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const AvailableSlotsModal = ({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
// Fetch slots from the Redux store
|
||||||
|
const { availableSlots, loading, error } = useSelector(
|
||||||
|
(state: any) => state.slotReducer.availableSlots
|
||||||
|
); // Adjust selector path
|
||||||
|
const [selectedSlot, setSelectedSlot] = useState<availableSlots | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
// Fetch available slots when the modal opens
|
||||||
|
dispatch(fetchAvailableSlots());
|
||||||
|
}
|
||||||
|
}, [open, dispatch]);
|
||||||
|
|
||||||
|
const handleSlotSelect = (slot: availableSlots) => {
|
||||||
|
setSelectedSlot(slot); // Update the selected slot
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
// Save the selected slot (you can dispatch an action here if needed)
|
||||||
|
console.log("Selected Slot: ", selectedSlot);
|
||||||
|
onClose(); // Close the modal after saving
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose}>
|
||||||
|
<DialogTitle sx={{ color: "#fff", background: "#222" }}>
|
||||||
|
Available Slots
|
||||||
|
</DialogTitle>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
background: "#111",
|
||||||
|
padding: "20px",
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<CircularProgress color="primary" />
|
||||||
|
) : error ? (
|
||||||
|
<div style={{ color: "red" }}>Error: {error}</div>
|
||||||
|
) : Array.isArray(availableSlots) &&
|
||||||
|
availableSlots.length > 0 ? (
|
||||||
|
availableSlots.map((slot: availableSlots) => (
|
||||||
|
<SlotButton
|
||||||
|
key={slot?.id}
|
||||||
|
onClick={() => handleSlotSelect(slot)}
|
||||||
|
selected={selectedSlot?.id === slot?.id}
|
||||||
|
>
|
||||||
|
{`${slot?.startTime} - ${slot?.endTime}`}
|
||||||
|
</SlotButton>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div>No available slots</div>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
sx={{ marginTop: "10px" }}
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={!selectedSlot}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AvailableSlotsModal;
|
|
@ -1,5 +1,16 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Box, Button, Typography, Modal } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Modal,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
Checkbox,
|
||||||
|
ListItemText,
|
||||||
|
} from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
|
@ -7,6 +18,15 @@ import {
|
||||||
CustomTextField,
|
CustomTextField,
|
||||||
} from "../AddEditUserModel/styled.css.tsx";
|
} from "../AddEditUserModel/styled.css.tsx";
|
||||||
|
|
||||||
|
// Define the types for your form data
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
registeredAddress: string;
|
||||||
|
totalSlots: number;
|
||||||
|
status: number;
|
||||||
|
allowedCarIds: string[]; // Assuming allowedCarIds are the vehicle IDs
|
||||||
|
}
|
||||||
|
|
||||||
interface EditStationModalProps {
|
interface EditStationModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
|
@ -15,16 +35,10 @@ interface EditStationModalProps {
|
||||||
name: string,
|
name: string,
|
||||||
registeredAddress: string,
|
registeredAddress: string,
|
||||||
totalSlots: number,
|
totalSlots: number,
|
||||||
status: number
|
allowedCarIds: string[]
|
||||||
) => void;
|
) => void;
|
||||||
editRow: any;
|
editRow?: any;
|
||||||
}
|
vehicles: { id: string; name: string }[];
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
name: string;
|
|
||||||
registeredAddress: string;
|
|
||||||
totalSlots: number;
|
|
||||||
status: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditStationModal: React.FC<EditStationModalProps> = ({
|
const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
|
@ -32,34 +46,42 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
handleClose,
|
handleClose,
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
editRow,
|
editRow,
|
||||||
|
vehicles,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { control, handleSubmit, setValue, reset, watch } = useForm<FormData>(
|
||||||
control,
|
{
|
||||||
handleSubmit,
|
defaultValues: {
|
||||||
formState: { errors },
|
name: "",
|
||||||
setValue,
|
registeredAddress: "",
|
||||||
reset,
|
totalSlots: 0,
|
||||||
} = useForm<FormData>({
|
status: 1,
|
||||||
defaultValues: {
|
allowedCarIds: [],
|
||||||
name: "",
|
},
|
||||||
registeredAddress: "",
|
}
|
||||||
totalSlots: 0,
|
);
|
||||||
status: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set values if editRow is provided
|
// Watch allowedCarIds from the form state
|
||||||
|
const allowedCarIds = watch("allowedCarIds");
|
||||||
|
|
||||||
|
// 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.number);
|
setValue("status", editRow.status);
|
||||||
|
|
||||||
|
// Set the allowedCarIds with the previously selected vehicle IDs
|
||||||
|
setValue(
|
||||||
|
"allowedCarIds",
|
||||||
|
editRow.allowedCarIds?.map((car: any) => car.id) || []
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}, [editRow, setValue, reset]);
|
}, [editRow, setValue, reset]);
|
||||||
|
|
||||||
|
// Handle form submit
|
||||||
const onSubmit = (data: FormData) => {
|
const onSubmit = (data: FormData) => {
|
||||||
if (editRow) {
|
if (editRow) {
|
||||||
handleUpdate(
|
handleUpdate(
|
||||||
|
@ -67,10 +89,10 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
data.name,
|
data.name,
|
||||||
data.registeredAddress,
|
data.registeredAddress,
|
||||||
data.totalSlots,
|
data.totalSlots,
|
||||||
data.status
|
data.allowedCarIds
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handleClose(); // Close the modal
|
handleClose(); // Close the modal after update
|
||||||
reset(); // Reset the form fields
|
reset(); // Reset the form fields
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,27 +162,12 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
<Controller
|
<Controller
|
||||||
name="name"
|
name="name"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
|
||||||
required: "Station Name is required",
|
|
||||||
minLength: {
|
|
||||||
value: 3,
|
|
||||||
message:
|
|
||||||
"Minimum 3 characters required",
|
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 30,
|
|
||||||
message:
|
|
||||||
"Maximum 30 characters allowed",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
{...field}
|
{...field}
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder="Enter Station Name"
|
placeholder="Enter Station Name"
|
||||||
size="small"
|
size="small"
|
||||||
error={!!errors.name}
|
|
||||||
helperText={errors.name?.message}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -183,19 +190,12 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
<Controller
|
<Controller
|
||||||
name="registeredAddress"
|
name="registeredAddress"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
|
||||||
required: "Registered Address is required",
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
{...field}
|
{...field}
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder="Enter Registered Address"
|
placeholder="Enter Registered Address"
|
||||||
size="small"
|
size="small"
|
||||||
error={!!errors.registeredAddress}
|
|
||||||
helperText={
|
|
||||||
errors.registeredAddress?.message
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -221,13 +221,6 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
|
||||||
<Controller
|
<Controller
|
||||||
name="totalSlots"
|
name="totalSlots"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{
|
|
||||||
required: "Total Slots are required",
|
|
||||||
min: {
|
|
||||||
value: 1,
|
|
||||||
message: "At least 1 slot is required",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<CustomTextField
|
<CustomTextField
|
||||||
{...field}
|
{...field}
|
||||||
|
@ -235,14 +228,62 @@ 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?.message}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</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>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
|
|
|
@ -16,9 +16,6 @@ http.interceptors.response.use(
|
||||||
(error) => {
|
(error) => {
|
||||||
|
|
||||||
if (error.response && error.response.status === 401) {
|
if (error.response && error.response.status === 401) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// window.location.href = "/login";
|
// window.location.href = "/login";
|
||||||
|
|
||||||
// const history = useHistory();
|
// const history = useHistory();
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import { Chip } from "@mui/material";
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import CustomTable, { Column } from "../../components/CustomTable";
|
|
||||||
import { RootState } from "../../redux/reducers";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import { AppDispatch } from "../../redux/store/store";
|
|
||||||
import AddStationModal from "../../components/AddStationModal";
|
import AddStationModal from "../../components/AddStationModal";
|
||||||
|
import CustomTable, { Column } from "../../components/CustomTable";
|
||||||
import EditStationModal from "../../components/EditStationModal";
|
import EditStationModal from "../../components/EditStationModal";
|
||||||
import {
|
import { createStation, stationList, toggleStatus, updateStation } from "../../redux/slices/stationSlice";
|
||||||
createStation,
|
import { useEffect, useState } from "react";
|
||||||
stationList,
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
toggleStatus,
|
import { RootState } from "../../redux/reducers";
|
||||||
updateStation,
|
import { AppDispatch } from "../../redux/store/store";
|
||||||
} from "../../redux/slices/stationSlice";
|
import { useForm } from "react-hook-form";
|
||||||
import { Chip, Switch } from "@mui/material";
|
|
||||||
|
|
||||||
export default function StationList() {
|
export default function StationList() {
|
||||||
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
|
||||||
|
@ -28,6 +23,10 @@ export default function StationList() {
|
||||||
const stations = useSelector(
|
const stations = useSelector(
|
||||||
(state: RootState) => state.stationReducer.stations
|
(state: RootState) => state.stationReducer.stations
|
||||||
);
|
);
|
||||||
|
const vehicles = useSelector(
|
||||||
|
(state: RootState) => state.vehicleReducer.vehicles
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(stationList());
|
dispatch(stationList());
|
||||||
|
@ -63,7 +62,8 @@ export default function StationList() {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
registeredAddress: string,
|
registeredAddress: string,
|
||||||
totalSlots: string
|
totalSlots: string,
|
||||||
|
allowedCarIds: number[]
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
await dispatch(
|
await dispatch(
|
||||||
|
@ -72,7 +72,8 @@ export default function StationList() {
|
||||||
name,
|
name,
|
||||||
registeredAddress,
|
registeredAddress,
|
||||||
totalSlots,
|
totalSlots,
|
||||||
|
status: 0,
|
||||||
|
allowedCarIds, // Pass the updated allowedCarIds
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await dispatch(stationList());
|
await dispatch(stationList());
|
||||||
|
@ -82,56 +83,44 @@ export default function StationList() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStatusToggle = async(id: string, newStatus: number) => {
|
|
||||||
await dispatch(toggleStatus({ id, status: newStatus }));
|
const handleStatusToggle = async (id: string, newStatus: number) => {
|
||||||
|
await dispatch(toggleStatus({ id, status: newStatus }));
|
||||||
};
|
};
|
||||||
// Toggle station status
|
|
||||||
// const handleStatusToggle = async (id: string, currentStatus: number) => {
|
|
||||||
// try {
|
|
||||||
// const newStatus = currentStatus === 1 ? 0 : 1; // Toggle between Active(1) and Inactive(0)
|
|
||||||
// await dispatch(
|
|
||||||
// updateStation({
|
|
||||||
// id,
|
|
||||||
// status: newStatus,
|
|
||||||
// name: stations.name,
|
|
||||||
// registeredAddress: stations.registeredAddress,
|
|
||||||
// totalSlots: stations.totalSlots,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// await dispatch(stationList());
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error toggling status", error);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
const filterStations = stations?.filter((station) =>
|
const filterStations = stations?.filter((station) =>
|
||||||
station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Mapping and formatting vehicles
|
||||||
const categoryRows = filterStations?.length
|
const categoryRows = filterStations?.length
|
||||||
? filterStations?.map((station: Station, index: number) => ({
|
? filterStations?.map((station: any, index: number) => {
|
||||||
id: station.id,
|
// Format the selected vehicles from the allowedCars array
|
||||||
srno: index + 1,
|
const formattedVehicles = station.allowedCars?.map(
|
||||||
name: station.name,
|
(car: any) => car.name
|
||||||
registeredAddress:station.registeredAddress,
|
);
|
||||||
totalSlots:station.totalSlots,
|
|
||||||
status: (
|
// Format the vehicle list like "Tata Punch Electric, Royal" or similar
|
||||||
<Chip
|
const vehicleDisplay = formattedVehicles
|
||||||
label={
|
? formattedVehicles.length > 1
|
||||||
station.status === 1 ? "Available" : "Not Available"
|
? `${formattedVehicles.slice(0, 2).join(", ")} + ${
|
||||||
}
|
formattedVehicles.length - 2
|
||||||
color={station.status === 1 ? "primary" : "default"}
|
}`
|
||||||
variant="outlined"
|
: formattedVehicles.join(", ")
|
||||||
sx={{
|
: "No vehicles"; // In case there are no vehicles selected
|
||||||
fontWeight: 600,
|
|
||||||
width: "80px",
|
return {
|
||||||
textAlign: "center",
|
id: station.id,
|
||||||
borderRadius: "4px",
|
srno: index + 1,
|
||||||
}}
|
name: station.name,
|
||||||
/>
|
registeredAddress: station.registeredAddress,
|
||||||
),
|
totalSlots: station.totalSlots,
|
||||||
statusValue: station.status,
|
vehicles: vehicleDisplay, // Add the formatted vehicle display here
|
||||||
}))
|
status:
|
||||||
|
station.status === 1 ? "Available" : "Not Available", // Format status text
|
||||||
|
statusValue: station.status, // Status value for toggling
|
||||||
|
};
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const categoryColumns: Column[] = [
|
const categoryColumns: Column[] = [
|
||||||
|
@ -140,35 +129,11 @@ export default function StationList() {
|
||||||
{ id: "name", label: "Station Name" },
|
{ id: "name", label: "Station Name" },
|
||||||
{ id: "registeredAddress", label: "Station Location" },
|
{ id: "registeredAddress", label: "Station Location" },
|
||||||
{ id: "totalSlots", label: "Total Slots" },
|
{ id: "totalSlots", label: "Total Slots" },
|
||||||
//{ id: "status", label: "Status" },
|
{ id: "vehicles", label: "Vehicles" }, // Add Vehicles column here
|
||||||
|
{ id: "status", label: "Status" }, // Add Status column here
|
||||||
{ id: "action", label: "Action", align: "center" },
|
{ id: "action", label: "Action", align: "center" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Filter stations based on search term
|
|
||||||
// const filterStations = stations?.filter((station) =>
|
|
||||||
// station.name.toLocaleLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Prepare categoryRows with toggle switch for status
|
|
||||||
// const categoryRows = filterStations?.length
|
|
||||||
// ? filterStations?.map((station: any, index: number) => ({
|
|
||||||
// id: station.id,
|
|
||||||
// srno: index + 1,
|
|
||||||
// name: station.name,
|
|
||||||
// status: (
|
|
||||||
// <Switch
|
|
||||||
// checked={station.status === 1}
|
|
||||||
// onChange={() =>
|
|
||||||
// handleStatusToggle(station.id, station.status)
|
|
||||||
// }
|
|
||||||
// color="primary"
|
|
||||||
// inputProps={{ "aria-label": "station-status-toggle" }}
|
|
||||||
// />
|
|
||||||
// ),
|
|
||||||
// statusValue: station.status,
|
|
||||||
// }))
|
|
||||||
// : [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CustomTable
|
<CustomTable
|
||||||
|
@ -194,6 +159,7 @@ export default function StationList() {
|
||||||
handleClose={handleCloseModal}
|
handleClose={handleCloseModal}
|
||||||
handleUpdate={handleUpdate}
|
handleUpdate={handleUpdate}
|
||||||
editRow={rowData}
|
editRow={rowData}
|
||||||
|
vehicles={vehicles}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,6 +10,7 @@ interface Station {
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
totalSlots: string;
|
totalSlots: string;
|
||||||
status: number;
|
status: number;
|
||||||
|
allowedCarIds: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StationState {
|
interface StationState {
|
||||||
|
@ -54,6 +55,7 @@ export const createStation = createAsyncThunk<
|
||||||
name: string;
|
name: string;
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
totalSlots: string;
|
totalSlots: string;
|
||||||
|
allowedCarIds: number[];
|
||||||
},
|
},
|
||||||
{ rejectValue: string }
|
{ rejectValue: string }
|
||||||
>("Station/createStation", async (data, { rejectWithValue }) => {
|
>("Station/createStation", async (data, { rejectWithValue }) => {
|
||||||
|
|
Loading…
Reference in a new issue