dev-jaanvi #1

Open
jaanvi wants to merge 155 commits from dev-jaanvi into main
16 changed files with 239 additions and 251 deletions
Showing only changes of commit 2da2eab6bf - Show all commits

4
public/Group 14.svg Normal file
View file

@ -0,0 +1,4 @@
<svg width="535" height="226" viewBox="0 0 535 226" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M43.561 68.1C55.753 68.1 65.913 72.3757 74.041 80.927C82.2537 89.4783 86.36 100.019 86.36 112.55C86.36 125.081 82.2537 135.622 74.041 144.173C65.913 152.724 55.753 157 43.561 157H8.255V68.1H43.561ZM43.561 140.236C51.2657 140.236 57.531 137.696 62.357 132.616C67.183 127.451 69.596 120.763 69.596 112.55C69.596 104.337 67.183 97.691 62.357 92.611C57.531 87.4463 51.2657 84.864 43.561 84.864H25.781V140.236H43.561ZM98.7921 68.1H116.318V157H98.7921V68.1ZM220.024 109.248V116.106C220.024 128.806 215.96 139.093 207.832 146.967C199.704 154.841 189.205 158.778 176.336 158.778C162.62 158.778 151.232 154.333 142.173 145.443C133.198 136.468 128.711 125.546 128.711 112.677C128.711 99.723 133.156 88.7587 142.046 79.784C151.02 70.8093 162.112 66.322 175.32 66.322C183.617 66.322 191.195 68.227 198.053 72.037C204.911 75.7623 210.245 80.7577 214.055 87.023L199.069 95.659C196.952 92.0183 193.735 89.055 189.417 86.769C185.183 84.483 180.442 83.34 175.193 83.34C166.726 83.34 159.741 86.134 154.238 91.722C148.819 97.2253 146.11 104.21 146.11 112.677C146.11 121.059 148.861 128.002 154.365 133.505C159.953 138.924 167.319 141.633 176.463 141.633C183.236 141.633 188.824 140.151 193.227 137.188C197.714 134.14 200.762 129.991 202.371 124.742H175.447V109.248H220.024ZM232.489 68.1H250.015V157H232.489V68.1ZM410.998 140.236H448.463V157H393.472V68.1H447.828V84.864H410.998V103.787H444.653V120.297H410.998V140.236ZM482.788 157L452.943 68.1H471.993L493.71 136.426L515.3 68.1H534.477L504.505 157H482.788Z" fill="white"/>
<path d="M374.268 108.459L324.874 94.3623L347.782 14.0969L264.907 117.311L314.302 131.408L291.394 211.673L374.268 108.459Z" fill="#52ACDF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/evLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -3,7 +3,7 @@ import { IconButton, TextField } from "@mui/material";
export const CustomIconButton = styled(IconButton)({ export const CustomIconButton = styled(IconButton)({
backgroundColor: "transparent", backgroundColor: "transparent",
"&:hover": { "&:hover": {
backgroundColor: "#272727", backgroundColor: "transparent",
}, },
"*:where([data-mui-color-scheme='dark']) &": { "*:where([data-mui-color-scheme='dark']) &": {
backgroundColor: "transparent", backgroundColor: "transparent",
@ -20,7 +20,4 @@ export const CustomTextField = styled(TextField)({
"& .MuiInputBase-root.Mui-focused .MuiInputBase-input::placeholder": { "& .MuiInputBase-root.Mui-focused .MuiInputBase-input::placeholder": {
color: "darkgray", color: "darkgray",
}, },
}); });

View file

@ -341,7 +341,7 @@ export default function AddStationModal({
/> />
<ListItemText <ListItemText
primary={vehicle.name} primary={vehicle.name}
/> />
</MenuItem> </MenuItem>
)) ))
) : ( ) : (

View file

@ -83,10 +83,9 @@ interface CustomTableProps {
viewModal: boolean; viewModal: boolean;
setViewModal: Function; setViewModal: Function;
deleteModal: boolean; deleteModal: boolean;
handleStatusToggle: (id: string, currentStatus: number) => void; handleStatusToggle?: (id: string, currentStatus: number) => void;
tableType: string; // Adding tableType prop to change header text dynamically tableType: string; // Adding tableType prop to change header text dynamically
handleClickOpen: () => void; handleClickOpen: () => void;
//handleDeleteButton: (id: string | number | undefined) => void;
} }
const CustomTable: React.FC<CustomTableProps> = ({ const CustomTable: React.FC<CustomTableProps> = ({
@ -108,11 +107,8 @@ const CustomTable: React.FC<CustomTableProps> = ({
const [searchQuery, setSearchQuery] = React.useState(""); const [searchQuery, setSearchQuery] = React.useState("");
const [currentPage, setCurrentPage] = React.useState(1); const [currentPage, setCurrentPage] = React.useState(1);
const usersPerPage = 10; const usersPerPage = 10;
const { user, isLoading } = useSelector( const { user } = useSelector((state: RootState) => state?.profileReducer);
(state: RootState) => state?.profileReducer
);
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
console.log("Rows", rows);
const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => { const handleClick = (event: React.MouseEvent<HTMLElement>, row: Row) => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
setSelectedRow(row); setSelectedRow(row);
@ -199,7 +195,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
if (selectedRow) { if (selectedRow) {
// Toggle the opposite of current status // Toggle the opposite of current status
const newStatus = selectedRow.statusValue === 1 ? 0 : 1; const newStatus = selectedRow.statusValue === 1 ? 0 : 1;
handleStatusToggle(selectedRow.id, newStatus); handleStatusToggle?.(selectedRow.id, newStatus);
} }
handleClose(); handleClose();
}; };
@ -216,7 +212,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow); const currentRows = filteredRows.slice(indexOfFirstRow, indexOfLastRow);
const handlePageChange = ( const handlePageChange = (
event: React.ChangeEvent<unknown>, _event: React.ChangeEvent<unknown>,
value: number value: number
) => { ) => {
setCurrentPage(value); setCurrentPage(value);
@ -241,24 +237,30 @@ const CustomTable: React.FC<CustomTableProps> = ({
}} }}
> >
{/* Dynamic title based on the page type */} {/* Dynamic title based on the page type */}
{tableType === "admin" {(() => {
? "Admin" switch (tableType) {
: tableType === "role" case "admin":
? "Roles" return "Admin";
: tableType === "user" case "role":
? "Users" return "Roles";
: tableType === "manager" case "user":
? "Managers" return "Users";
: tableType === "vehicle" case "manager":
? "Vehicles" return "Managers";
: tableType === "station" case "vehicle":
? "Charging Station" return "Vehicles";
: tableType === "booking" case "station":
? "Booking" return "Charging Station";
: tableType === "slots" case "booking":
? "Slot" return "Booking";
: "List"} case "slots":
return "Slot";
default:
return "List";
}
})()}
</Typography> </Typography>
{/* Search & Buttons Section */} {/* Search & Buttons Section */}
<Box <Box
sx={{ sx={{
@ -324,23 +326,28 @@ const CustomTable: React.FC<CustomTableProps> = ({
onClick={() => handleClickOpen()} onClick={() => handleClickOpen()}
> >
Add{" "} Add{" "}
{tableType === "admin" {(() => {
? "Admin" switch (tableType) {
: tableType === "role" case "admin":
? "Role" return "Admin";
: tableType === "user" case "role":
? "User" return "Role";
: tableType === "manager" case "user":
? "Manager" return "User";
: tableType === "vehicle" case "manager":
? "Vehicle" return "Manager";
: tableType === "station" case "vehicle":
? "Charging Station" return "Vehicle";
: tableType === "booking" case "station":
? "Booking" return "Charging Station";
: tableType === "slots" case "booking":
? "Slot" return "Booking";
: "Item"} case "slots":
return "Slot";
default:
return "Item";
}
})()}
</Button> </Button>
)} )}
</Box> </Box>

View file

@ -14,11 +14,19 @@ import {
CustomIconButton, CustomIconButton,
CustomTextField, CustomTextField,
} from "../AddEditUserModel/styled.css.tsx"; // Custom styled components } from "../AddEditUserModel/styled.css.tsx"; // Custom styled components
import { AppDispatch } from "../../redux/store/store.ts";
interface EditManagerModalProps { interface EditManagerModalProps {
open: boolean; open: boolean;
handleClose: () => void; handleClose: () => void;
editRow: any; // Manager data including id handleUpdate: (
id: string,
name: string,
email: string,
phone: string,
stationId: string
) => Promise<void>;
editRow: any;
} }
interface FormData { interface FormData {
@ -33,7 +41,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
handleClose, handleClose,
editRow, editRow,
}) => { }) => {
const dispatch = useDispatch(); // Use dispatch to send Redux actions const dispatch = useDispatch<AppDispatch>();
const { const {
control, control,
handleSubmit, handleSubmit,
@ -74,7 +82,7 @@ const EditManagerModal: React.FC<EditManagerModalProps> = ({
email: data.email, email: data.email,
phone: data.phone, phone: data.phone,
stationId: data.stationId, stationId: data.stationId,
}, },
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).unwrap(); // Ensure that it throws an error if the update fails

View file

@ -14,10 +14,17 @@ import {
CustomIconButton, CustomIconButton,
CustomTextField, CustomTextField,
} from "../AddEditUserModel/styled.css.tsx"; // Custom styled components } from "../AddEditUserModel/styled.css.tsx"; // Custom styled components
import { AppDispatch } from "../../redux/store/store.ts";
interface EditSlotModalProps { interface EditSlotModalProps {
open: boolean; open: boolean;
handleClose: () => void; handleClose: () => void;
handleUpdate: (
id: string,
startTime: string,
endTime: string,
isAvailable: boolean
) => Promise<void>;
editRow: any; // Slot data including id editRow: any; // Slot data including id
} }
@ -33,7 +40,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
handleClose, handleClose,
editRow, editRow,
}) => { }) => {
const dispatch = useDispatch(); // Use dispatch to send Redux actions const dispatch = useDispatch<AppDispatch>();
const { const {
control, control,
handleSubmit, handleSubmit,
@ -54,13 +61,11 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
editRow?.isAvailable || false editRow?.isAvailable || false
); );
// Set values if editRow is provided
useEffect(() => { useEffect(() => {
if (editRow) { if (editRow) {
// setValue("date", editRow.date);
setValue("startTime", editRow.startTime); setValue("startTime", editRow.startTime);
setValue("endTime", editRow.endTime); setValue("endTime", editRow.endTime);
setIsAvailable(editRow.isAvailable); // Set the initial availability correctly setIsAvailable(editRow.isAvailable);
} else { } else {
reset(); reset();
} }
@ -68,10 +73,10 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
const onSubmit = async (data: FormData) => { const onSubmit = async (data: FormData) => {
if (editRow) { if (editRow) {
setLoading(true); // Start loading setLoading(true);
try { try {
// Convert boolean availability to 1/0
const availabilityStatus = isAvailable ? true : false; const availabilityStatus = isAvailable ? true : false;
await dispatch( await dispatch(
@ -81,9 +86,7 @@ const EditSlotModal: React.FC<EditSlotModalProps> = ({
endTime: data.endTime, endTime: data.endTime,
isAvailable: availabilityStatus, isAvailable: availabilityStatus,
}) })
).unwrap(); // Ensure that it throws an error if the update fails ).unwrap();
// Refresh the list after updating the slot
dispatch(fetchAvailableSlots()); dispatch(fetchAvailableSlots());
handleClose(); // Close modal on success handleClose(); // Close modal on success
reset(); // Reset form fields after submit reset(); // Reset form fields after submit

View file

@ -76,7 +76,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
(state: RootState) => state.vehicleReducer.vehicleBrands (state: RootState) => state.vehicleReducer.vehicleBrands
); );
const [selectedBrand, setSelectedBrand] = useState<string | "">(""); const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]); const [selectedVehicles, setSelectedVehicles] = useState<string[]>([]);
useEffect(() => { useEffect(() => {
@ -88,8 +88,9 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
if (editRow) { if (editRow) {
// Determine the brand based on the first vehicle's company // Determine the brand based on the first vehicle's company
const firstVehicle = vehicles.find( const firstVehicle = vehicles.find(
(vehicle) => editRow.allowedCarIds && (vehicle) =>
editRow.allowedCarIds.includes(vehicle.name) editRow.allowedCarIds &&
editRow.allowedCarIds.includes(vehicle.name)
); );
const brandId = firstVehicle ? firstVehicle.company : ""; const brandId = firstVehicle ? firstVehicle.company : "";
@ -97,9 +98,10 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
// Populate selected vehicles // Populate selected vehicles
const vehicleNames = vehicles const vehicleNames = vehicles
.filter((vehicle) => .filter(
editRow.allowedCarIds && (vehicle) =>
editRow.allowedCarIds.includes(vehicle.name) editRow.allowedCarIds &&
editRow.allowedCarIds.includes(vehicle.name)
) )
.map((vehicle) => vehicle.name); .map((vehicle) => vehicle.name);
@ -113,15 +115,24 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
setValue("allowedCarIds", vehicleNames); setValue("allowedCarIds", vehicleNames);
} else { } else {
reset(); reset();
setSelectedBrand(""); // setSelectedBrand("");
setSelectedVehicles([]); setSelectedVehicles([]);
} }
}, [editRow, vehicles, setValue, reset]); }, [editRow, setValue, reset]);
const filteredVehicles = vehicles.filter( // Filter vehicles based on selected brands
(vehicle) => vehicle.company === selectedBrand const filteredVehicles = vehicles.filter((vehicle) =>
selectedBrands.includes(vehicle.company)
); );
// Handle changes in vehicle brand selection
const handleBrandChange = (
event: React.ChangeEvent<{ value: unknown }>
) => {
setSelectedBrands(event.target.value as string[]);
};
// Handle changes in vehicle selection
const handleCheckboxChange = ( const handleCheckboxChange = (
event: React.ChangeEvent<{ value: unknown }> event: React.ChangeEvent<{ value: unknown }>
) => { ) => {
@ -144,8 +155,8 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
); );
handleClose(); handleClose();
reset(); reset();
setSelectedBrand(""); setSelectedBrands([]); // Reset brands after submit
setSelectedVehicles([]); setSelectedVehicles([]); // Reset selected vehicles
}; };
return ( return (
@ -304,7 +315,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
</Box> </Box>
</Box> </Box>
{/* Vehicle Brand Selection */} {/* Vehicle Brand Selection with Checkboxes */}
<Box sx={{ display: "flex", gap: 2 }}> <Box sx={{ display: "flex", gap: 2 }}>
<Box <Box
sx={{ sx={{
@ -314,16 +325,15 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
}} }}
> >
<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 Brand</InputLabel>
<Select <Select
value={selectedBrand} multiple
onChange={(e) => value={selectedBrands}
setSelectedBrand(e.target.value) onChange={handleBrandChange}
} label="Choose Brands"
label="Choose Brand"
> >
{vehicleBrands.length > 0 ? ( {vehicleBrands.length > 0 ? (
vehicleBrands.map((brand) => ( vehicleBrands.map((brand) => (
@ -331,7 +341,14 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
key={brand.id} key={brand.id}
value={brand.id} value={brand.id}
> >
{brand.name} <Checkbox
checked={selectedBrands.includes(
brand.id
)}
/>
<ListItemText
primary={brand.name}
/>
</MenuItem> </MenuItem>
)) ))
) : ( ) : (
@ -342,8 +359,6 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
</Select> </Select>
</FormControl> </FormControl>
</Box> </Box>
{/* Vehicle Selection with Checkboxes */}
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
@ -407,7 +422,7 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
color: "white", color: "white",
borderRadius: "8px", borderRadius: "8px",
width: "117px", width: "117px",
"&:hover": { backgroundColor: "#439BC1" }, "&:hover": { backgroundColor: "#439BC1" },
}} }}
> >
Update Station Update Station
@ -418,4 +433,4 @@ const EditStationModal: React.FC<EditStationModalProps> = ({
); );
}; };
export default EditStationModal; export default EditStationModal;

View file

@ -7,6 +7,7 @@ import CloseIcon from "@mui/icons-material/Close";
type Props = { type Props = {
open: boolean; open: boolean;
setViewModal: Function; setViewModal: Function;
handleView: (id: string | undefined) => void;
id?: string; id?: string;
}; };

View file

@ -64,15 +64,25 @@ export default function SideMenu() {
pl: 2, pl: 2,
}} }}
> >
<Avatar <img
src="/evLogo.png"
alt="Logo" alt="Logo"
src="/Digilogo.png" style={{
sx={{ justifyContent: "center",
width: "50px", width: open ? "200px" : "60px", // Adjust width depending on open state
height: "50px", height: "auto",
transition: "width 0.5s ease", // Smooth transition for width change
}} }}
/> />
<Box {/* <Avatar
alt="Logo"
src="/evLogo.png"
sx={{
width: "100%",
height: "100%",
}}
/> */}
{/* <Box
sx={{ sx={{
display: open ? "flex" : "none", display: open ? "flex" : "none",
flexDirection: "column", flexDirection: "column",
@ -81,7 +91,7 @@ export default function SideMenu() {
textAlign: "center", textAlign: "center",
}} }}
> >
{/* Digi EV Text Section */}
<Typography <Typography
variant="body2" variant="body2"
color="#D9D8D8" color="#D9D8D8"
@ -95,7 +105,7 @@ export default function SideMenu() {
<Typography variant="subtitle2" color="#D9D8D8"> <Typography variant="subtitle2" color="#D9D8D8">
{user?.userType || "N/A"} {user?.userType || "N/A"}
</Typography> </Typography>
</Box> </Box> */}
</Box> </Box>
<Box <Box

View file

@ -9,7 +9,6 @@ import {
TextField, TextField,
Typography, Typography,
Grid, Grid,
Link, Link,
InputAdornment, InputAdornment,
} from "@mui/material"; } from "@mui/material";
@ -23,6 +22,7 @@ import { useNavigate } from "react-router-dom";
import { Visibility, VisibilityOff } from "@mui/icons-material"; import { Visibility, VisibilityOff } from "@mui/icons-material";
import { Card, SignInContainer } from "./styled.css.tsx"; import { Card, SignInContainer } from "./styled.css.tsx";
import { CustomIconButton } from "../../../components/AddEditUserModel/styled.css.tsx"; import { CustomIconButton } from "../../../components/AddEditUserModel/styled.css.tsx";
import { AppDispatch } from "../../../redux/store/store.ts";
interface ILoginForm { interface ILoginForm {
email: string; email: string;
password: string; password: string;
@ -37,7 +37,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
handleSubmit, handleSubmit,
formState: { errors, isValid }, formState: { errors, isValid },
} = useForm<ILoginForm>({ mode: "onChange" }); } = useForm<ILoginForm>({ mode: "onChange" });
const dispatch = useDispatch(); const dispatch = useDispatch<AppDispatch>();
const router = useNavigate(); const router = useNavigate();
const handleClickOpen = () => { const handleClickOpen = () => {
@ -47,12 +47,12 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
}; };
const togglePasswordVisibility = (e: React.MouseEvent) => { const togglePasswordVisibility = (e: React.MouseEvent) => {
e.preventDefault(); // Prevent focus loss e.preventDefault(); // Prevent focus loss
setShowPassword((prev) => !prev); setShowPassword((prev) => !prev);
}; };
const onSubmit: SubmitHandler<ILoginForm> = async (data: ILoginForm) => { const onSubmit: SubmitHandler<ILoginForm> = async (data: ILoginForm) => {
try { try {
const response = await dispatch(loginUser(data)).unwrap(); const response = await dispatch(loginUser(data)).unwrap();
@ -100,15 +100,15 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
<Box <Box
sx={{ sx={{
textAlign: "center", textAlign: "center",
marginBottom: "1rem", marginBottom: "1rem",
}} }}
> >
<img <img
src="/DigiEVLogo.png" src="/evLogo.png"
alt="Logo" alt="Logo"
style={{ style={{
justifyContent: "center", justifyContent: "center",
width: "250px", width: "180px",
height: "auto", height: "auto",
}} }}
/> />
@ -329,9 +329,6 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
? "error" ? "error"
: "primary" : "primary"
} }
onMouseDown={
togglePasswordVisibility
}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end"> <InputAdornment position="end">
@ -339,7 +336,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
aria-label="toggle password visibility" aria-label="toggle password visibility"
onClick={ onClick={
togglePasswordVisibility togglePasswordVisibility
} } // Only the button triggers visibility toggle
edge="end" edge="end"
> >
{showPassword ? ( {showPassword ? (

View file

@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Box, Button, TextField, Typography } from "@mui/material";
import CustomTable, { Column } from "../../components/CustomTable"; import CustomTable, { Column } from "../../components/CustomTable";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "../../redux/store/store"; import { RootState, AppDispatch } from "../../redux/store/store";
@ -15,12 +14,10 @@ import EditSlotModal from "../../components/EditSlotModal";
export default function EVSlotList() { export default function EVSlotList() {
const [addModalOpen, setAddModalOpen] = useState<boolean>(false); const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
const [editModalOpen, setEditModalOpen] = useState<boolean>(false); const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
const [editRow, setEditRow] = useState<any>(null);
const { reset } = useForm(); const { reset } = useForm();
const [deleteModal, setDeleteModal] = useState<boolean>(false); const [deleteModal, setDeleteModal] = useState<boolean>(false);
const [viewModal, setViewModal] = useState<boolean>(false); const [viewModal, setViewModal] = useState<boolean>(false);
const [rowData, setRowData] = useState<any | null>(null); const [rowData, setRowData] = useState<any | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const availableSlots = useSelector( const availableSlots = useSelector(
(state: RootState) => state?.slotReducer.availableSlots (state: RootState) => state?.slotReducer.availableSlots
@ -55,35 +52,34 @@ export default function EVSlotList() {
console.error("Error adding slot", error); console.error("Error adding slot", error);
} }
}; };
const handleUpdate = async (
id: string,
startTime: string,
endTime: string,
isAvailable: boolean
) => {
try {
// Convert times to 24-hour format if needed
const formattedStartTime = dayjs(startTime, "HH:mm").format("HH:mm");
const formattedEndTime = dayjs(endTime, "HH:mm").format("HH:mm");
await dispatch( const handleUpdate = async (
updateSlot({ id: string,
id, startTime: string,
startTime: formattedStartTime, // Ensure it matches backend expectation endTime: string,
endTime: formattedEndTime, // Ensure it matches backend expectation isAvailable: boolean
isAvailable, ) => {
}) try {
).unwrap(); // This helps catch and handle any errors const formattedStartTime = dayjs(startTime, "HH:mm").format(
"HH:mm"
);
const formattedEndTime = dayjs(endTime, "HH:mm").format("HH:mm");
await dispatch(fetchAvailableSlots()); // Refresh the list await dispatch(
handleCloseModal(); // Close the modal updateSlot({
} catch (error) { id,
console.error("Update failed", error); startTime: formattedStartTime,
// Optionally add error toast or user notification endTime: formattedEndTime,
} isAvailable,
}; })
).unwrap();
await dispatch(fetchAvailableSlots());
handleCloseModal();
} catch (error) {
console.error("Update failed", error);
}
};
const slotColumns: Column[] = [ const slotColumns: Column[] = [
{ id: "srno", label: "Sr No" }, { id: "srno", label: "Sr No" },
@ -96,26 +92,24 @@ export default function EVSlotList() {
{ id: "action", label: "Action", align: "center" }, { id: "action", label: "Action", align: "center" },
]; ];
// Make sure dayjs is imported const slotRows = availableSlots?.length
? availableSlots.map((slot, index) => {
const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD");
const startTime = dayjs(slot?.startTime).format("HH:mm");
const endTime = dayjs(slot?.endTime).format("HH:mm");
const slotRows = availableSlots?.length return {
? availableSlots.map((slot, index) => { srno: index + 1,
const formattedDate = dayjs(slot?.date).format("YYYY-MM-DD"); id: slot?.id ?? "NA",
const startTime = dayjs(slot?.startTime).format("HH:mm"); stationId: slot?.stationId ?? "NA",
const endTime = dayjs(slot?.endTime).format("HH:mm"); name: slot?.ChargingStation?.name ?? "NA",
date: formattedDate ?? "NA",
return { startTime: startTime ?? "NA",
srno: index + 1, endTime: endTime ?? "NA",
id: slot?.id ?? "NA", isAvailable: slot?.isAvailable ? "Yes" : "No",
stationId: slot?.stationId ?? "NA", };
name: slot?.ChargingStation?.name ?? "NA", })
date: formattedDate ?? "NA", : [];
startTime: startTime ?? "NA",
endTime: endTime ?? "NA",
isAvailable: slot?.isAvailable ? "Yes" : "No",
};
})
: [];
return ( return (
<> <>

View file

@ -9,11 +9,9 @@ import {
managerList, managerList,
addManager, addManager,
updateManager, updateManager,
deleteManager,
} from "../../redux/slices/managerSlice"; } from "../../redux/slices/managerSlice";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
export default function ManagerList() { export default function ManagerList() {
const [addModalOpen, setAddModalOpen] = useState<boolean>(false); const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
const [editModalOpen, setEditModalOpen] = useState<boolean>(false); const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
@ -67,7 +65,7 @@ export default function ManagerList() {
// Handle updating an existing manager // Handle updating an existing manager
const handleUpdate = async ( const handleUpdate = async (
id: number, id: string,
name: string, name: string,
email: string, email: string,
phone: string, phone: string,
@ -101,65 +99,29 @@ export default function ManagerList() {
{ id: "registeredAddress", label: "Station Location" }, { id: "registeredAddress", label: "Station Location" },
{ id: "action", label: "Action", align: "center" }, { id: "action", label: "Action", align: "center" },
]; ];
// const categoryColumns: Column[] = [
// { id: "srno", label: "Sr No" },
// { id: "name", label: "Name" },
// { id: "email", label: "Email" },
// { id: "phone", label: "Phone" },
// { id: "stationName", label: "Station Name" }, // Added station name column
// { id: "stationAddress", label: "Station Location" }, // Added station address column
// { id: "action", label: "Action", align: "center" },
// ];
// Filter managers based on search term // Filter managers based on search term
const filteredManagers = managers?.filter( // const filteredManagers = managers?.filter(
(manager) => // (manager) =>
manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) || // manager.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) || // manager.email?.toLowerCase().includes(searchTerm.toLowerCase()) ||
manager.phone?.toLowerCase().includes(searchTerm.toLowerCase()) // manager.phone?.toLowerCase().includes(searchTerm.toLowerCase())
// );
);
// Format rows to display manager details
// const categoryRows = filteredManagers?.length
// ? filteredManagers?.map(
// (
// manager: {
// id: number;
// name: string;
// email: string;
// phone: string;
// },
// index: number
// ) => ({
// id: manager?.id,
// srno: index + 1,
// name: manager?.name,
// email: manager?.email,
// phone: manager.phone ?? "NA",
// })
// )
// : [];
const categoryRows = filteredManagers?.length
? filteredManagers.map((manager, index) => {
const station = manager?.chargingStation; // Correct access to the ChargingStation data
return {
id: manager.id,
srno: index + 1,
name: manager.name,
email: manager.email,
phone: manager.phone ?? "NA",
stationName: station?.name ?? "NA", // Corrected station name
registeredAddress: station?.registeredAddress ?? "NA", // Corrected station address
};
})
: [];
const categoryRows = managers?.length
? managers.map((manager, index) => {
const station = manager?.chargingStation; // Correct access to the ChargingStation data
return {
id: manager.id,
srno: index + 1,
name: manager.name,
email: manager.email,
phone: manager.phone ?? "NA",
stationName: station?.name ?? "NA", // Corrected station name
registeredAddress: station?.registeredAddress ?? "NA", // Corrected station address
};
})
: [];
return ( return (
<> <>

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import CustomTable, { Column } from "../../components/CustomTable"; import CustomTable, { Column } from "../../components/CustomTable";
import { RootState } from "../../redux/reducers"; import { RootState } from "../../redux/reducers";
@ -15,13 +15,11 @@ import EditVehicleModal from "../../components/EditVehicleModal";
export default function VehicleList() { export default function VehicleList() {
const [addModalOpen, setAddModalOpen] = useState<boolean>(false); const [addModalOpen, setAddModalOpen] = useState<boolean>(false);
const [editModalOpen, setEditModalOpen] = useState<boolean>(false); const [editModalOpen, setEditModalOpen] = useState<boolean>(false);
const [editRow, setEditRow] = useState<any>(null);
const { reset } = useForm(); const { reset } = useForm();
const [deleteModal, setDeleteModal] = useState<boolean>(false); const [deleteModal, setDeleteModal] = useState<boolean>(false);
const [viewModal, setViewModal] = useState<boolean>(false); const [viewModal, setViewModal] = useState<boolean>(false);
const [rowData, setRowData] = useState<any | null>(null); const [rowData, setRowData] = useState<any | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const vehicles = useSelector( const vehicles = useSelector(
(state: RootState) => state.vehicleReducer.vehicles (state: RootState) => state.vehicleReducer.vehicles
@ -32,7 +30,7 @@ export default function VehicleList() {
}, [dispatch]); }, [dispatch]);
const handleClickOpen = () => { const handleClickOpen = () => {
setRowData(null); // Reset row data when opening for new admin setRowData(null);
setAddModalOpen(true); setAddModalOpen(true);
}; };
@ -44,7 +42,7 @@ export default function VehicleList() {
}; };
const handleAddVehicle = async (data: { const handleAddVehicle = async (data: {
vehicleName: string; name: string;
company: string; company: string;
modelName: string; modelName: string;
chargeType: string; chargeType: string;
@ -60,7 +58,7 @@ export default function VehicleList() {
}; };
const handleUpdate = async ( const handleUpdate = async (
id: number, id: string,
name: string, name: string,
company: string, company: string,
modelName: string, modelName: string,
@ -95,24 +93,24 @@ export default function VehicleList() {
{ id: "action", label: "Action", align: "center" }, { id: "action", label: "Action", align: "center" },
]; ];
const filteredVehicles = vehicles?.filter( // const filteredVehicles = vehicles?.filter(
(vehicle) => // (vehicle) =>
vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) || // vehicle.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) || // vehicle.company?.toLowerCase().includes(searchTerm.toLowerCase()) ||
vehicle.modelName // vehicle.modelName
?.toLowerCase() // ?.toLowerCase()
.includes(searchTerm.toLowerCase()) || // .includes(searchTerm.toLowerCase()) ||
vehicle.chargeType // vehicle.chargeType
?.toLowerCase() // ?.toLowerCase()
.includes(searchTerm.toLowerCase()) || // .includes(searchTerm.toLowerCase()) ||
vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase()) // vehicle.imageUrl?.toLowerCase().includes(searchTerm.toLowerCase())
); // );
const categoryRows = filteredVehicles?.length const categoryRows = vehicles?.length
? filteredVehicles?.map( ? vehicles?.map(
( (
vehicle: { vehicle: {
id: number; id: string;
name: string; name: string;
company: string; company: string;
modelName: string; modelName: string;
@ -132,8 +130,6 @@ export default function VehicleList() {
) )
: []; : [];
return ( return (
<> <>
<CustomTable <CustomTable
@ -159,7 +155,6 @@ export default function VehicleList() {
handleUpdate={handleUpdate} handleUpdate={handleUpdate}
editRow={rowData} editRow={rowData}
/> />
</> </>
); );
} }

View file

@ -8,7 +8,7 @@ interface VehicleBrand {
} }
interface Vehicle { interface Vehicle {
brandId: string; brandId?: string;
id: string; id: string;
name: string; name: string;
company: string; company: string;
@ -50,10 +50,8 @@ export const fetchVehicleBrands = createAsyncThunk<
} }
}); });
export const vehicleList = createAsyncThunk< export const vehicleList = createAsyncThunk<
Vehicle, Vehicle[],
void, void,
{ rejectValue: string } { rejectValue: string }
>("fetchVehicles", async (_, { rejectWithValue }) => { >("fetchVehicles", async (_, { rejectWithValue }) => {
@ -180,12 +178,9 @@ const vehicleSlice = createSlice({
state.vehicles.push(action.payload); state.vehicles.push(action.payload);
} }
) )
.addCase( .addCase(addVehicle.rejected, (state) => {
addVehicle.rejected, state.loading = false;
(state, action: PayloadAction<string | undefined>) => { })
state.loading = false;
}
)
.addCase(updateVehicle.pending, (state) => { .addCase(updateVehicle.pending, (state) => {
state.loading = true; state.loading = true;
}) })

View file

@ -6,12 +6,12 @@ import { toast } from "sonner";
interface Manager { interface Manager {
Manager: any; Manager: any;
id: number; id: string;
name: string; name: string;
email: string; email: string;
phone: string; phone: string;
stationId: string; stationId: string;
chargingStation:string; chargingStation:{name:string, registeredAddress:string};
} }
interface ManagerState { interface ManagerState {
@ -74,7 +74,7 @@ export const addManager = createAsyncThunk<
// Update Manager (Async Thunk) // Update Manager (Async Thunk)
export const updateManager = createAsyncThunk< export const updateManager = createAsyncThunk<
Manager, Manager,
{ id: number; managerData: Manager }, { id: string; managerData: Manager },
{ rejectValue: string } { rejectValue: string }
>("updateManager", async ({ id, managerData }, { rejectWithValue }) => { >("updateManager", async ({ id, managerData }, { rejectWithValue }) => {
if (!id) { if (!id) {