diff --git a/public/Bell.jpg b/public/Bell.jpg new file mode 100644 index 0000000..a5c21ac Binary files /dev/null and b/public/Bell.jpg differ diff --git a/public/avatar.png b/public/avatar.png new file mode 100644 index 0000000..8c87604 Binary files /dev/null and b/public/avatar.png differ diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 4724de2..46ab0ad 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,63 +1,89 @@ import * as React from "react"; import Stack from "@mui/material/Stack"; -import NotificationsRoundedIcon from "@mui/icons-material/NotificationsRounded"; -import CustomDatePicker from "../CustomDatePicker"; -import NavbarBreadcrumbs from "../NavbarBreadcrumbs"; -import MenuButton from "../MenuButton"; -import ColorModeIconDropdown from "../../shared-theme/ColorModeIconDropdown"; +import Avatar from "@mui/material/Avatar"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import Search from "../Search"; +import InputBase from "@mui/material/InputBase"; +import SearchIcon from "@mui/icons-material/Search"; +import Divider from "@mui/material/Divider"; +import MenuButton from "../MenuButton"; +import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; export default function Header() { const [showNotifications, setShowNotifications] = React.useState(false); const toggleNotifications = () => { setShowNotifications((prev) => !prev); }; + return ( - - - - - - - - - - - {showNotifications && ( + + + {/* Search Bar */} - - No notifications yet - + + - )} - + + {/* Notification and Profile Section */} + + + {/* Custom Bell Icon */} + + + + + + + Momah + + {/* Dropdown Icon */} + + + + + ); } diff --git a/src/components/MenuContent/index.tsx b/src/components/MenuContent/index.tsx index 7b91f6a..cc6c53b 100644 --- a/src/components/MenuContent/index.tsx +++ b/src/components/MenuContent/index.tsx @@ -22,7 +22,11 @@ const baseMenuItems = [ icon: , url: "/panel/admin-list", }, - + { + text: "Users", + icon: , + url: "/panel/user-list", + }, ]; //Eknoor singh and Jaanvi diff --git a/src/pages/AdminList/index.tsx b/src/pages/AdminList/index.tsx index 0cb75cf..e095411 100644 --- a/src/pages/AdminList/index.tsx +++ b/src/pages/AdminList/index.tsx @@ -1,159 +1,355 @@ -import React, { useEffect, useState } from "react"; -import { Box, Button, Typography } from "@mui/material"; -import AddEditCategoryModal from "../../components/AddEditCategoryModal"; -import { useForm } from "react-hook-form"; -import CustomTable, { Column } from "../../components/CustomTable"; -import { useDispatch, useSelector } from "react-redux"; +import { useEffect, useState } from "react"; import { - adminList, - updateAdmin, - createAdmin, -} from "../../redux/slices/adminSlice"; + Box, + Button, + Typography, + TextField, + InputAdornment, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Pagination, + IconButton, +} from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; +import TuneIcon from "@mui/icons-material/Tune"; +import { useDispatch, useSelector } from "react-redux"; +import { adminList } from "../../redux/slices/adminSlice"; import { AppDispatch, RootState } from "../../redux/store/store"; export default function AdminList() { - const [modalOpen, setModalOpen] = useState(false); - const { reset } = useForm(); - - const [deleteModal, setDeleteModal] = React.useState(false); - const [viewModal, setViewModal] = React.useState(false); - const [rowData, setRowData] = React.useState(null); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const adminsPerPage = 10; const dispatch = useDispatch(); - const admins = useSelector((state: RootState) => state.adminReducer.admins); useEffect(() => { dispatch(adminList()); }, [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; - email: string; - phone: string; - registeredAddress: string; - }) => { - try { - await dispatch(createAdmin(data)); - await dispatch(adminList()); // Refresh the list after creation - handleCloseModal(); - } catch (error) { - console.error("Creation failed", error); - } - }; - - const handleUpdate = async ( - id: string, - name: string, - email: string, - phone: string, - registeredAddress: string - ) => { - try { - await dispatch( - updateAdmin({ - id, - name, - email, - phone, - registeredAddress, - }) - ); - await dispatch(adminList()); - } catch (error) { - console.error("Update failed", error); - } - }; - - const categoryColumns: Column[] = [ - { id: "srno", label: "Sr No" }, - { id: "name", label: "Name" }, - { id: "email", label: "Email" }, - { id: "phone", label: "Phone" }, - { id: "registeredAddress", label: "Address" }, - { id: "action", label: "Action", align: "center" }, + const staticAdmins = [ + { + name: "John Doe", + location: "New York", + managerAssigned: "Alice Johnson", + vehicle: "Tesla Model S", + phone: "+1 234 567 8901", + }, + { + name: "Jane Smith", + location: "Los Angeles", + managerAssigned: "Bob Brown", + vehicle: "Ford F-150", + phone: "+1 987 654 3210", + }, + { + name: "Michael Brown", + location: "Chicago", + managerAssigned: "Sarah Lee", + vehicle: "Chevrolet Bolt", + phone: "+1 312 555 7890", + }, + { + name: "Emily Davis", + location: "Houston", + managerAssigned: "Tom Wilson", + vehicle: "Nissan Leaf", + phone: "+1 713 444 5678", + }, + { + name: "Daniel Martinez", + location: "Phoenix", + managerAssigned: "Jessica White", + vehicle: "BMW i3", + phone: "+1 602 999 4321", + }, + { + name: "Sophia Miller", + location: "Philadelphia", + managerAssigned: "Mark Adams", + vehicle: "Audi e-tron", + phone: "+1 215 777 6543", + }, + { + name: "James Anderson", + location: "San Antonio", + managerAssigned: "Emma Thomas", + vehicle: "Hyundai Kona EV", + phone: "+1 210 321 8765", + }, + { + name: "James Anderson", + location: "San Antonio", + managerAssigned: "Emma Thomas", + vehicle: "Hyundai Kona EV", + phone: "+1 210 321 8765", + }, ]; - const categoryRows = admins?.length - ? admins?.map( - ( - admin: { - id: string; - name: string; - email: string; - phone: string; - registeredAddress: string; - }, - index: number - ) => ({ - id: admin?.id, - srno: index + 1, - name: admin?.name, - email: admin?.email, - phone: admin?.phone, - registeredAddress: admin?.registeredAddress, - }) - ) - : []; + const adminData = admins.length ? admins : staticAdmins; + + const filteredAdmins = adminData.filter((admin) => + admin.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const indexOfLastAdmin = currentPage * adminsPerPage; + const indexOfFirstAdmin = indexOfLastAdmin - adminsPerPage; + const currentAdmins = filteredAdmins.slice( + indexOfFirstAdmin, + indexOfLastAdmin + ); + + const handlePageChange = (event, value) => { + setCurrentPage(value); + }; return ( - <> + + + Charge stations + + + {/* Search & Buttons Section */} + + + + ), + }} + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + /> + + + + + + + + + + {/* Table Section */} + + + + + {[ + "Name", + "Location", + "Manager Assigned", + "Vehicle", + "Phone Number", + "Action", + ].map((header) => ( + + {header} + + ))} + + + + {currentAdmins.map((admin, index) => ( + + + {admin.name} + + + {admin.location || "N/A"} + + + {admin.managerAssigned || "N/A"} + + + {admin.vehicle || "N/A"}{" "} + + +6 more + + + + {admin.phone} + + + + + + + + ))} + +
+
+ + {/* Pagination */} + - Admins + Page Number : - + - - - - +
); } diff --git a/src/pages/UserList/index.tsx b/src/pages/UserList/index.tsx new file mode 100644 index 0000000..bddda89 --- /dev/null +++ b/src/pages/UserList/index.tsx @@ -0,0 +1,313 @@ +import { useEffect, useState } from "react"; +import { + Box, + Button, + Typography, + TextField, + InputAdornment, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Pagination, + IconButton, +} from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; +import TuneIcon from "@mui/icons-material/Tune"; +import { useDispatch, useSelector } from "react-redux"; +import { userList } from "../../redux/slices/userSlice"; // Make sure userSlice exists +import { AppDispatch, RootState } from "../../redux/store/store"; + +export default function UserList() { + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const usersPerPage = 10; + + const dispatch = useDispatch(); + const users = useSelector((state: RootState) => state.userReducer.users); + + useEffect(() => { + dispatch(userList()); + }, [dispatch]); + + const staticUsers = [ + { + name: "Alice Johnson", + email: "alice@example.com", + role: "User", + phone: "+1 234 567 8901", + }, + { + name: "Bob Brown", + email: "bob@example.com", + role: "Admin", + phone: "+1 987 654 3210", + }, + { + name: "Charlie Davis", + email: "charlie@example.com", + role: "User", + phone: "+1 312 555 7890", + }, + { + name: "Alice Johnson", + email: "alice@example.com", + role: "User", + phone: "+1 234 567 8901", + }, + { + name: "Bob Brown", + email: "bob@example.com", + role: "Admin", + phone: "+1 987 654 3210", + }, + { + name: "Charlie Davis", + email: "charlie@example.com", + role: "User", + phone: "+1 312 555 7890", + }, + { + name: "Bob Brown", + email: "bob@example.com", + role: "Admin", + phone: "+1 987 654 3210", + }, + { + name: "Charlie Davis", + email: "charlie@example.com", + role: "User", + phone: "+1 312 555 7890", + }, + ]; + + const userData = users.length ? users : staticUsers; + + const filteredUsers = userData.filter((user) => + user.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const indexOfLastUser = currentPage * usersPerPage; + const indexOfFirstUser = indexOfLastUser - usersPerPage; + const currentUsers = filteredUsers.slice(indexOfFirstUser, indexOfLastUser); + + const handlePageChange = (event, value) => { + setCurrentPage(value); + }; + + return ( + + + User List + + + {/* Search & Buttons Section */} + + + + + ), + }} + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + /> + + + + + + + + + + {/* Table Section */} + + + + + {["Name", "Email", "Role", "Phone", "Action"].map( + (header) => ( + + {header} + + ) + )} + + + + {currentUsers.map((user, index) => ( + + + {user.name} + + + {user.email} + + + {user.role} + + + {user.phone} + + + + + + + + ))} + +
+
+ + {/* Pagination */} + + + Page Number : + + + +
+ ); +} diff --git a/src/redux/reducers.ts b/src/redux/reducers.ts index 03caa21..4ad7387 100644 --- a/src/redux/reducers.ts +++ b/src/redux/reducers.ts @@ -3,11 +3,13 @@ import { combineReducers } from "@reduxjs/toolkit"; import authReducer from "./slices/authSlice"; import adminReducer from "./slices/adminSlice"; import profileReducer from "./slices/profileSlice"; +import userReducer from "./slices/userSlice.ts"; const rootReducer = combineReducers({ authReducer, adminReducer, - profileReducer + profileReducer, + userReducer, }); export type RootState = ReturnType; diff --git a/src/redux/slices/userSlice.ts b/src/redux/slices/userSlice.ts new file mode 100644 index 0000000..d9242fd --- /dev/null +++ b/src/redux/slices/userSlice.ts @@ -0,0 +1,59 @@ +import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; +import axios from "axios"; + +// Define TypeScript types +interface User { + id: number; + name: string; + email: string; + phone?: string; + location?: string; + managerAssigned?: string; + vehicle?: string; +} + +interface UserState { + users: User[]; + loading: boolean; + error: string | null; +} + +// Initial state +const initialState: UserState = { + users: [], + loading: false, + error: null, +}; + +// Async thunk to fetch user list +export const userList = createAsyncThunk("users/fetchUsers", async () => { + try { + const response = await axios.get("/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"); + } +}); + +const userSlice = createSlice({ + name: "users", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(userList.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase(userList.fulfilled, (state, action: PayloadAction) => { + state.loading = false; + state.users = action.payload; + }) + .addCase(userList.rejected, (state, action) => { + state.loading = false; + state.error = action.error.message || "Failed to fetch users"; + }); + }, +}); + +export default userSlice.reducer; diff --git a/src/router.tsx b/src/router.tsx index 06b35b6..34590b2 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -1,8 +1,4 @@ -import { - Routes as BaseRoutes, - Navigate, - Route, -} from "react-router-dom"; +import { Routes as BaseRoutes, Navigate, Route } from "react-router-dom"; import React, { lazy, Suspense } from "react"; import LoadingComponent from "./components/Loading"; import DashboardLayout from "./layouts/DashboardLayout"; @@ -15,6 +11,7 @@ const Vehicles = lazy(() => import("./pages/Vehicles")); const AdminList = lazy(() => import("./pages/AdminList")); const ProfilePage = lazy(() => import("./pages/ProfilePage")); const NotFoundPage = lazy(() => import("./pages/NotFound")); +const UserList = lazy(() => import("./pages/UserList")); interface ProtectedRouteProps { caps: string[]; @@ -77,6 +74,15 @@ export default function AppRouter() { /> } /> + } + /> + } + /> {/* Catch-all Route */} - } /> + } /> );