vehicle list created and some bugs solved
This commit is contained in:
parent
d21f4f5d27
commit
d1612f9131
BIN
public/model-s-exterior-front-view.webp
Normal file
BIN
public/model-s-exterior-front-view.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
|
@ -8,15 +8,28 @@ import SearchIcon from "@mui/icons-material/Search";
|
|||
import Divider from "@mui/material/Divider";
|
||||
import MenuButton from "../MenuButton";
|
||||
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
||||
import ColorModeIconDropdown from "../../shared-theme/ColorModeIconDropdown";
|
||||
import NotificationsRoundedIcon from "@mui/icons-material/NotificationsRounded";
|
||||
import SideMenu from "../SideMenu";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch, RootState } from "../../redux/store/store";
|
||||
import { fetchAdminProfile } from "../../redux/slices/profileSlice";
|
||||
import OptionsMenu from "../OptionsMenu";
|
||||
|
||||
export default function Header() {
|
||||
const [showNotifications, setShowNotifications] = React.useState(false);
|
||||
const toggleNotifications = () => {
|
||||
setShowNotifications((prev) => !prev);
|
||||
};
|
||||
const [open, setOpen] = React.useState(true);
|
||||
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { user } = useSelector(
|
||||
(state: RootState) => state?.profileReducer
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchAdminProfile());
|
||||
}, [dispatch]);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -92,11 +105,11 @@ export default function Header() {
|
|||
sx={{ width: 36, height: 36 }}
|
||||
/>
|
||||
<Typography variant="body1" sx={{ color: "#202020" }}>
|
||||
Momah
|
||||
{user?.name || "No Admin"}
|
||||
</Typography>
|
||||
{/* Dropdown Icon */}
|
||||
<ArrowDropDownIcon
|
||||
sx={{ color: "#202020", width: 16, height: 16 }}
|
||||
|
||||
<OptionsMenu
|
||||
|
||||
/>
|
||||
</Stack>
|
||||
{/* <ColorModeIconDropdown /> */}
|
||||
|
|
|
@ -47,6 +47,11 @@ export default function MenuContent({ hidden }: PropType) {
|
|||
icon: <AnalyticsRoundedIcon />,
|
||||
url: "/panel/role-list",
|
||||
},
|
||||
userRole === "admin" && {
|
||||
text: "Vehicles",
|
||||
icon: <AnalyticsRoundedIcon />,
|
||||
url: "/panel/vehicle-list",
|
||||
},
|
||||
];
|
||||
|
||||
const filteredMenuItems = baseMenuItems.filter(Boolean);
|
||||
|
|
|
@ -13,6 +13,7 @@ import MenuButton from "../MenuButton";
|
|||
import { Avatar } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Logout from "../LogOutFunction/LogOutFunction";
|
||||
import { ArrowDropDownIcon } from "@mui/x-date-pickers";
|
||||
|
||||
const MenuItem = styled(MuiMenuItem)({
|
||||
margin: "2px 0",
|
||||
|
@ -46,7 +47,7 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
onClick={handleClick}
|
||||
sx={{ borderColor: "transparent" }}
|
||||
>
|
||||
{avatar ? (
|
||||
{/* {avatar ? (
|
||||
<MoreVertRoundedIcon />
|
||||
) : (
|
||||
<Avatar
|
||||
|
@ -55,7 +56,8 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
src="/static/images/avatar/7.jpg"
|
||||
sx={{ width: 36, height: 36 }}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
<ArrowDropDownIcon />
|
||||
</MenuButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
|
@ -78,11 +80,11 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
}}
|
||||
>
|
||||
<MenuItem onClick={handleProfile}>Profile</MenuItem>
|
||||
<MenuItem onClick={handleClose}>My account</MenuItem>
|
||||
{/* <MenuItem onClick={handleClose}>My account</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleClose}>Add another account</MenuItem>
|
||||
<MenuItem onClick={handleClose}>Settings</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleClose}>Settings</MenuItem> */}
|
||||
{/* <Divider /> */}
|
||||
<MenuItem
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
|
@ -92,15 +94,12 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
},
|
||||
}}
|
||||
>
|
||||
{/* //Eknoor singh and jaanvi
|
||||
//date:- 13-Feb-2025
|
||||
//Implemented logout functionality which was static previously */}
|
||||
|
||||
<ListItemText
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setLogoutModal(true);
|
||||
}}
|
||||
sx={{color:"red"}}
|
||||
>
|
||||
Logout
|
||||
</ListItemText>
|
||||
|
@ -109,9 +108,9 @@ export default function OptionsMenu({ avatar }: { avatar?: boolean }) {
|
|||
logoutModal={logoutModal}
|
||||
/>
|
||||
|
||||
<ListItemIcon>
|
||||
{/* <ListItemIcon>
|
||||
<LogoutRoundedIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
</ListItemIcon> */}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -29,10 +29,6 @@ const Drawer = styled(MuiDrawer)({
|
|||
export default function SideMenu() {
|
||||
const [open, setOpen] = React.useState(true);
|
||||
|
||||
//Eknoor singh
|
||||
//date:- 12-Feb-2025
|
||||
//Dispatch is called with user from Authstate Interface
|
||||
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { user } = useSelector((state: RootState) => state?.profileReducer);
|
||||
|
||||
|
@ -74,7 +70,7 @@ export default function SideMenu() {
|
|||
</Box>
|
||||
<MenuContent hidden={open} />
|
||||
{/* <CardAlert /> */}
|
||||
<Stack
|
||||
{/* <Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
p: 2,
|
||||
|
@ -104,8 +100,8 @@ export default function SideMenu() {
|
|||
{user?.email || "No Email"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<OptionsMenu avatar={open} />
|
||||
</Stack>
|
||||
<OptionsMenu avatar={open} />
|
||||
</Stack> */}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<ILoginForm>();
|
||||
formState: { errors, isValid },
|
||||
} = useForm<ILoginForm>({ mode: "onChange" });
|
||||
const dispatch = useDispatch();
|
||||
const router = useNavigate();
|
||||
|
||||
|
@ -228,6 +228,11 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
message:
|
||||
"Password must be at least 6 characters long.",
|
||||
},
|
||||
pattern: {
|
||||
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/,
|
||||
message:
|
||||
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.",
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ position: "relative" }}>
|
||||
|
@ -255,17 +260,22 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
sx={{
|
||||
paddingRight: "40px",
|
||||
height: "40px",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
right: "10px",
|
||||
transform:
|
||||
"translateY(-50%)",
|
||||
background: "none",
|
||||
borderColor:
|
||||
"transparent",
|
||||
transform:
|
||||
"translateY(-50%)",
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
"transparent",
|
||||
|
@ -295,6 +305,8 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
color: "white",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
|
@ -326,6 +338,7 @@ export default function Login(props: { disableCustomTheme?: boolean }) {
|
|||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
disabled={!isValid}
|
||||
sx={{
|
||||
color: "white",
|
||||
backgroundColor: "#52ACDF",
|
||||
|
|
227
src/pages/VehicleList/index.tsx
Normal file
227
src/pages/VehicleList/index.tsx
Normal file
|
@ -0,0 +1,227 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Box, Button, TextField, Typography } from "@mui/material";
|
||||
import AddEditCategoryModal from "../../components/AddEditCategoryModal";
|
||||
import { useForm } from "react-hook-form";
|
||||
import CustomTable, { Column } from "../../components/CustomTable";
|
||||
import DeleteModal from "../../components/Modals/DeleteModal";
|
||||
import { RootState } from "../../redux/reducers";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch } from "../../redux/store/store";
|
||||
import {
|
||||
addVehicle,
|
||||
updateVehicle,
|
||||
vehicleList,
|
||||
} from "../../redux/slices/VehicleSlice";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
const categoryRows = [
|
||||
{
|
||||
srno: 1,
|
||||
id: 1, // Using a number for 'id'
|
||||
name: "Tesla Model S",
|
||||
brand: "Tesla",
|
||||
imageUrl:
|
||||
"https://example.com/https://imgd-ct.aeplcdn.com/1056x660/n/cw/ec/93821/model-s-exterior-front-view.jpeg?q=80.jpg",
|
||||
},
|
||||
{
|
||||
srno: 2,
|
||||
id: 2,
|
||||
name: "BMW X5",
|
||||
brand: "BMW",
|
||||
imageUrl: "https://example.com/bmw_x5.jpg",
|
||||
},
|
||||
{
|
||||
srno: 3,
|
||||
id: 3,
|
||||
name: "Audi A6",
|
||||
brand: "Audi",
|
||||
imageUrl: "https://example.com/audi_a6.jpg",
|
||||
},
|
||||
{
|
||||
srno: 4,
|
||||
id: 4,
|
||||
name: "Mercedes-Benz S-Class",
|
||||
brand: "Mercedes-Benz",
|
||||
imageUrl: "https://example.com/mercedes_s_class.jpg",
|
||||
},
|
||||
{
|
||||
srno: 5,
|
||||
id: 5,
|
||||
name: "Ford Mustang",
|
||||
brand: "Ford",
|
||||
imageUrl: "https://example.com/ford_mustang.jpg",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export default function VehicleList() {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
const [editRow, setEditRow] = useState<any>(null);
|
||||
const { reset } = useForm();
|
||||
|
||||
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
|
||||
const [viewModal, setViewModal] = React.useState<boolean>(false);
|
||||
const [rowData, setRowData] = React.useState<any | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const vehicles = useSelector(
|
||||
(state: RootState) => state.vehicleReducer.vehicles
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(vehicleList());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setRowData(null); // Reset row data when opening for new admin
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setModalOpen(false);
|
||||
setRowData(null);
|
||||
reset();
|
||||
};
|
||||
|
||||
const handleCreate = async (data: {
|
||||
name: string;
|
||||
brand: string;
|
||||
imageUrl: string;
|
||||
}) => {
|
||||
try {
|
||||
await dispatch(addVehicle(data));
|
||||
await dispatch(vehicleList());
|
||||
handleCloseModal();
|
||||
} catch (error) {
|
||||
console.error("Creation failed", error);
|
||||
}
|
||||
};
|
||||
const handleUpdate = async (
|
||||
id: number,
|
||||
name: string,
|
||||
brand: string,
|
||||
imageUrl: string
|
||||
) => {
|
||||
try {
|
||||
await dispatch(
|
||||
updateVehicle({
|
||||
id,
|
||||
name,
|
||||
brand,
|
||||
imageUrl,
|
||||
})
|
||||
);
|
||||
await dispatch(vehicleList());
|
||||
} catch (error) {
|
||||
console.error("Update failed", error);
|
||||
}
|
||||
};
|
||||
const categoryColumns: Column[] = [
|
||||
{ id: "srno", label: "Sr No" },
|
||||
{ id: "name", label: "Vehicle Name" },
|
||||
{ id: "brand", label: "Brand" },
|
||||
{ id: "imageUrl", label: "Image" },
|
||||
{ id: "action", label: "Action", align: "center" },
|
||||
];
|
||||
|
||||
const filteredVehicles = vehicles?.filter(
|
||||
(vehicle) =>
|
||||
vehicle.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
vehicle.brand.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
// const categoryRows = filteredVehicles?.length
|
||||
// ? filteredVehicles?.map(
|
||||
// (
|
||||
// vehicle: {
|
||||
// id: number;
|
||||
// name: string;
|
||||
// brand: string;
|
||||
// imageUrl: string;
|
||||
// },
|
||||
// index: number
|
||||
// ) => ({
|
||||
// id: vehicle?.id,
|
||||
// srno: index + 1,
|
||||
// name: vehicle?.name,
|
||||
// brand: vehicle?.brand,
|
||||
// imageUrl: vehicle?.imageUrl,
|
||||
// })
|
||||
// )
|
||||
// : [];
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
{/* Title and Add Category button */}
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
sx={{ mt: 2, fontWeight: 600 }}
|
||||
>
|
||||
Vehicles
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: { xs: "column", sm: "row" },
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
mb: 2, // Add margin bottom for spacing
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
size="small"
|
||||
placeholder="Search..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "30%" },
|
||||
marginBottom: { xs: 2, sm: 0 },
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<SearchIcon
|
||||
sx={{ color: "#202020", marginRight: 1 }}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
width: { xs: "100%", sm: "auto" },
|
||||
}}
|
||||
onClick={handleClickOpen}
|
||||
>
|
||||
Add Vehicle
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
|
||||
<CustomTable
|
||||
columns={categoryColumns}
|
||||
rows={categoryRows}
|
||||
setDeleteModal={setDeleteModal}
|
||||
deleteModal={deleteModal}
|
||||
setViewModal={setViewModal}
|
||||
viewModal={viewModal}
|
||||
setRowData={setRowData}
|
||||
setModalOpen={setModalOpen}
|
||||
/>
|
||||
{/* <AddEditCategoryModal
|
||||
open={modalOpen}
|
||||
handleClose={handleCloseModal}
|
||||
editRow={rowData}
|
||||
/>
|
||||
<DeleteModal
|
||||
open={deleteModal}
|
||||
setDeleteModal={setDeleteModal}
|
||||
handleDelete={handleDelete}
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import AddEditCategoryModal from '../../components/AddEditCategoryModal';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import CustomTable from '../../components/CustomTable';
|
||||
import DeleteModal from '../../components/Modals/DeleteModal';
|
||||
|
||||
// Sample data for categories
|
||||
// const categoryRows = [
|
||||
// { srno: 1, name: 'Strength', date: '01/03/2025' },
|
||||
// {
|
||||
// srno: 2,
|
||||
// name: 'HIIT (High-Intensity Interval Training)',
|
||||
// date: '01/03/2025',
|
||||
// },
|
||||
// { srno: 3, name: 'Cardio', date: '01/03/2025' },
|
||||
// { srno: 4, name: 'Combat', date: '01/03/2025' },
|
||||
// { srno: 5, name: 'Yoga', date: '01/03/2025' },
|
||||
// ];
|
||||
|
||||
export default function Vehicles() {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
const [editRow, setEditRow] = useState<any>(null);
|
||||
const { reset } = useForm();
|
||||
|
||||
const [deleteModal, setDeleteModal] = React.useState<boolean>(false);
|
||||
const [rowData, setRowData] = React.useState<any | null>(null);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setModalOpen(true);
|
||||
setEditRow(null);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setModalOpen(false);
|
||||
reset();
|
||||
};
|
||||
|
||||
// const handleEdit = () => {
|
||||
// setEditRow(rowData);
|
||||
// };
|
||||
|
||||
const handleDelete = () => {
|
||||
console.log('Deleted row:', rowData);
|
||||
setDeleteModal(false);
|
||||
};
|
||||
|
||||
const categoryColumns = [
|
||||
{ id: 'srno', label: 'Sr No' },
|
||||
{ id: 'name', label: 'Category Name' },
|
||||
{ id: 'date', label: 'Date' },
|
||||
{ id: 'action', label: 'Action', align: 'center' },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
maxWidth: {
|
||||
sm: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* Title and Add Category button */}
|
||||
{/* <Typography component="h2" variant="h6" sx={{ mt: 2, fontWeight: 600 }}>
|
||||
Vehicles
|
||||
</Typography> */}
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
sx={{ textAlign: 'right' }}
|
||||
onClick={handleClickOpen}
|
||||
>
|
||||
Add Categorywewfw
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<CustomTable
|
||||
columns={categoryColumns}
|
||||
rows={categoryRows}
|
||||
editRow={editRow}
|
||||
setDeleteModal={setDeleteModal}
|
||||
setRowData={setRowData}
|
||||
setModalOpen={setModalOpen}
|
||||
/>
|
||||
<AddEditCategoryModal
|
||||
open={modalOpen}
|
||||
handleClose={handleCloseModal}
|
||||
editRow={rowData}
|
||||
/>
|
||||
<DeleteModal
|
||||
open={deleteModal}
|
||||
setDeleteModal={setDeleteModal}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -5,6 +5,8 @@ import adminReducer from "./slices/adminSlice";
|
|||
import profileReducer from "./slices/profileSlice";
|
||||
import userReducer from "./slices/userSlice.ts";
|
||||
import roleReducer from "./slices/roleSlice.ts";
|
||||
import vehicleReducer from "./slices/VehicleSlice.ts";
|
||||
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
authReducer,
|
||||
|
@ -12,6 +14,7 @@ const rootReducer = combineReducers({
|
|||
profileReducer,
|
||||
userReducer,
|
||||
roleReducer,
|
||||
vehicleReducer
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
|
135
src/redux/slices/VehicleSlice.ts
Normal file
135
src/redux/slices/VehicleSlice.ts
Normal file
|
@ -0,0 +1,135 @@
|
|||
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||
import http from "../../lib/https";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Vehicle {
|
||||
id:number;
|
||||
name:string;
|
||||
brand:string;
|
||||
imageUrl:string;
|
||||
|
||||
}
|
||||
interface VehicleState {
|
||||
vehicles:Vehicle[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
const initialState: VehicleState = {
|
||||
vehicles: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
export const vehicleList = createAsyncThunk<Vehicle, void, { rejectValue: string }>(
|
||||
"fetchVehicles",
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const token = localStorage?.getItem("authToken");
|
||||
if (!token) throw new Error("No token found");
|
||||
|
||||
const response = await http.get("/");
|
||||
|
||||
if (!response.data?.data) throw new Error("Invalid API response");
|
||||
|
||||
return response.data.data;
|
||||
} catch (error: any) {
|
||||
toast.error("Error Fetching Profile" + error);
|
||||
return rejectWithValue(
|
||||
error?.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//Add Vehicle
|
||||
export const addVehicle = createAsyncThunk<
|
||||
Vehicle,
|
||||
{
|
||||
name: string;
|
||||
brand: string;
|
||||
imageUrl: string;
|
||||
},
|
||||
{ rejectValue: string }
|
||||
>("/AddVehicle", async (data, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await http.post("/", data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(
|
||||
error.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Update Vehicle details
|
||||
export const updateVehicle = createAsyncThunk(
|
||||
"updateVehicle",
|
||||
async ({ id, ...vehicleData }: Vehicle, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await http.put(`/${id}`, vehicleData);
|
||||
toast.success("Vehicle Deatils updated successfully");
|
||||
return response?.data;
|
||||
} catch (error: any) {
|
||||
toast.error("Error updating the user: " + error);
|
||||
return rejectWithValue(
|
||||
error.response?.data?.message || "An error occurred"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
const vehicleSlice = createSlice({
|
||||
name: "vehicle",
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(vehicleList.pending, (state) => {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(
|
||||
vehicleList.fulfilled,
|
||||
(state, action: PayloadAction<Vehicle[]>) => {
|
||||
state.loading = false;
|
||||
state.vehicles = action.payload;
|
||||
}
|
||||
)
|
||||
.addCase(vehicleList.rejected, (state, action) => {
|
||||
state.loading = false;
|
||||
state.error = action.error.message || "Failed to fetch users";
|
||||
})
|
||||
.addCase(addVehicle.pending, (state) => {
|
||||
state.loading = true;
|
||||
// state.error = null;
|
||||
})
|
||||
.addCase(
|
||||
addVehicle.fulfilled,
|
||||
(state, action: PayloadAction<Vehicle>) => {
|
||||
state.loading = false;
|
||||
state.vehicles.push(action.payload);
|
||||
}
|
||||
)
|
||||
.addCase(
|
||||
addVehicle.rejected,
|
||||
(state, action: PayloadAction<string | undefined>) => {
|
||||
state.loading = false;
|
||||
}
|
||||
)
|
||||
.addCase(updateVehicle.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
.addCase(updateVehicle.fulfilled, (state, action) => {
|
||||
const updateVehicle = action.payload;
|
||||
state.vehicles = state?.vehicles?.map((vehicle) =>
|
||||
vehicle?.id === updateVehicle?.id ? updateVehicle : vehicle
|
||||
);
|
||||
state.loading = false;
|
||||
})
|
||||
.addCase(updateVehicle.rejected, (state) => {
|
||||
state.loading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default vehicleSlice.reducer;
|
|
@ -28,15 +28,6 @@ const initialState: UserState = {
|
|||
error: null,
|
||||
};
|
||||
|
||||
// Async thunk to fetch user list
|
||||
// export const userList = createAsyncThunk<User[]>("users/fetchUsers", async () => {
|
||||
// try {
|
||||
// const response = await axios.get<User[]>("/api/users"); // Adjust the API endpoint as needed
|
||||
// return response.data;
|
||||
// } catch (error: any) {
|
||||
// throw new Error(error.response?.data?.message || "Failed to fetch users");
|
||||
// }
|
||||
// });
|
||||
export const userList = createAsyncThunk<User, void, { rejectValue: string }>(
|
||||
"fetchUsers",
|
||||
async (_, { rejectWithValue }) => {
|
||||
|
|
|
@ -4,12 +4,13 @@ import LoadingComponent from "./components/Loading";
|
|||
import DashboardLayout from "./layouts/DashboardLayout";
|
||||
import RoleList from "./pages/RoleList";
|
||||
import AddEditRolePage from "./pages/AddEditRolePage";
|
||||
import VehicleList from "./pages/VehicleList";
|
||||
|
||||
// Page imports
|
||||
const Login = lazy(() => import("./pages/Auth/Login"));
|
||||
const SignUp = lazy(() => import("./pages/Auth/SignUp"));
|
||||
const Dashboard = lazy(() => import("./pages/Dashboard"));
|
||||
const Vehicles = lazy(() => import("./pages/Vehicles"));
|
||||
const Vehicles = lazy(() => import("./pages/VehicleList"));
|
||||
const AdminList = lazy(() => import("./pages/AdminList"));
|
||||
const ProfilePage = lazy(() => import("./pages/ProfilePage"));
|
||||
const NotFoundPage = lazy(() => import("./pages/NotFound"));
|
||||
|
@ -95,6 +96,15 @@ export default function AppRouter() {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="vehicle-list"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
caps={[]}
|
||||
component={<VehicleList />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="permissions"
|
||||
element={
|
||||
|
|
Loading…
Reference in a new issue