From 2ac5b8e03eb8b365b0b638c19bbb3c38c2975183 Mon Sep 17 00:00:00 2001 From: Chanuka Lakshan Chandra Yapa <118423471+Chanuka-ChandraYapa@users.noreply.github.com> Date: Tue, 12 Sep 2023 02:35:00 +0530 Subject: [PATCH] Implemented useContext and private Routes --- src/App.js | 43 +++- src/adminroutes.js | 201 ++++++++++++++++++ src/examples/Navbars/DashboardNavbar/index.js | 15 +- src/index.js | 14 +- src/layouts/TBI/tbi.js | 21 ++ src/layouts/advertisement_map/map.js | 74 ++++--- src/layouts/authentication/sign-in/index.js | 3 + src/layouts/authentication/sign-up/index.js | 3 + .../dashboard/components/Projects/index.js | 4 +- .../components/SearchBar/searchBar.js | 4 +- src/routes.js | 38 +++- src/utils/privateRoutes.js | 19 ++ src/utils/userContext.js | 35 +++ 13 files changed, 408 insertions(+), 66 deletions(-) create mode 100644 src/adminroutes.js create mode 100644 src/layouts/TBI/tbi.js create mode 100644 src/utils/privateRoutes.js create mode 100644 src/utils/userContext.js diff --git a/src/App.js b/src/App.js index 159abf7..ca2a9af 100644 --- a/src/App.js +++ b/src/App.js @@ -38,6 +38,7 @@ import themeDark from "assets/theme-dark"; // Material Dashboard 2 React routes import routes from "routes"; +import adminRoutes from "adminroutes"; // Material Dashboard 2 React contexts import { useMaterialUIController, setMiniSidenav, setOpenConfigurator } from "context"; @@ -47,6 +48,7 @@ import brandWhite from "assets/images/brand-dark.png"; import brandDark from "assets/images/brand-light.png"; import Background from "examples/LayoutContainers/background"; import bgImage from "layouts/landing/Assets/images/background3.png"; +import { useUser } from "utils/userContext"; export default function App() { const [controller, dispatch] = useMaterialUIController(); @@ -63,6 +65,7 @@ export default function App() { const [onMouseEnter, setOnMouseEnter] = useState(false); const { pathname } = useLocation(); + const { user } = useUser(); // Open sidenav when mouse enter on mini sidenav const handleOnMouseEnter = () => { @@ -137,21 +140,41 @@ export default function App() { {layout === "dashboard" && ( <> - - - {configsButton} + {user?.role === "user" && ( + <> + + + {configsButton} + + )} + {user?.role === "admin" && ( + <> + + + {configsButton} + + )} )} {layout === "vr" && } + {getRoutes(routes)} + {getRoutes(adminRoutes)} } /> diff --git a/src/adminroutes.js b/src/adminroutes.js new file mode 100644 index 0000000..be9958e --- /dev/null +++ b/src/adminroutes.js @@ -0,0 +1,201 @@ +/** +========================================================= +* Material Dashboard 2 React - v2.2.0 +========================================================= + +* Product Page: https://www.creative-tim.com/product/material-dashboard-react +* Copyright 2023 Creative Tim (https://www.creative-tim.com) + +Coded by www.creative-tim.com + + ========================================================= + +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +*/ + +/** + All of the routes for the Material Dashboard 2 React are added here, + You can add a new route, customize the routes and delete the routes here. + + Once you add a new route on this file it will be visible automatically on + the Sidenav. + + For adding a new route you can follow the existing routes in the routes array. + 1. The `type` key with the `collapse` value is used for a route. + 2. The `type` key with the `title` value is used for a title inside the Sidenav. + 3. The `type` key with the `divider` value is used for a divider between Sidenav items. + 4. The `name` key is used for the name of the route on the Sidenav. + 5. The `key` key is used for the key of the route (It will help you with the key prop inside a loop). + 6. The `icon` key is used for the icon of the route on the Sidenav, you have to add a node. + 7. The `collapse` key is used for making a collapsible item on the Sidenav that has other routes + inside (nested routes), you need to pass the nested routes inside an array as a value for the `collapse` key. + 8. The `route` key is used to store the route location which is used for the react router. + 9. The `href` key is used to store the external links location. + 10. The `title` key is only for the item with the type of `title` and its used for the title text on the Sidenav. + 10. The `component` key is used to store the component of its route. +*/ + +// Material Dashboard 2 React layouts +import Dashboard from "layouts/dashboard"; + +import Billing from "layouts/billing"; +import Notifications from "layouts/notifications"; +import Profile from "layouts/profile"; +import SignIn from "layouts/authentication/sign-in"; +import SignUp from "layouts/authentication/sign-up"; + +import TBH from "layouts/TBI/tbi"; + +// @mui icons +import Icon from "@mui/material/Icon"; + +import AdvertisementDetail from "layouts/advertisement"; + +import GraphReport from "layouts/reports/graphs"; +import App from "layouts/landing/App"; +// import PrivateRoute from "utils/privateRoutes"; +import { useUser } from "utils/userContext"; +import PropTypes from "prop-types"; + +// ... + +// Create a wrapper component for the Dashboard +const Wrapper = ({ component: Component }) => { + const { isAuthenticated } = useUser(); + + if (isAuthenticated) { + return ; + } else { + return ; + } +}; + +const adminRoutes = [ + { + type: "title", + title: "Admin Panel", + }, + { type: "divider" }, + { + type: "collapse", + name: "Dashboard", + key: "dashboard", + icon: dashboard, + route: "/dashboard", + component: , + }, + { + type: "collapse", + name: "Manage Advertisements", + key: "admanage", + icon: search, + route: "/manageadvertisements", + component: , + }, + { + type: "collapse", + name: "Manage User Accounts", + key: "usermanage", + icon: newspaper, + route: "/manageuser", + component: , + }, + { + type: "collapse", + name: "User Feedback", + key: "feedback", + icon: equalizer, + route: "/managefeedback", + component: , + }, + { + type: "collapse", + name: "Content Approval", + key: "content", + icon: map, + route: "/managecontent", + component: , + }, + { + type: "collapse", + name: "Configure Graphs", + key: "configGraph", + icon: report, + route: "/managegraphs", + component: , + }, + { + type: "collapse", + name: "Reports", + key: "reportsview", + icon: report, + route: "/managereports", + component: , + }, + { + type: "collapse", + name: "User Support", + key: "support", + icon: table_view, + route: "/managesupport", + component: , + }, + { + type: "collapse", + name: "Billing", + key: "billing", + icon: receipt_long, + route: "/billing", + component: , + }, + { + route: "/advertisement/:id", + component: , + }, + { + route: "/landing", + component: , + }, + { + route: "/reports/:title", + component: , + }, + { + type: "collapse", + name: "Notifications", + key: "notifications", + icon: notifications, + route: "/notifications", + component: , + }, + { + type: "collapse", + name: "Profile", + key: "profile", + icon: person, + route: "/profile", + component: , + }, + { + type: "collapse", + name: "Sign In", + key: "sign-in", + icon: login, + route: "/authentication/sign-in", + component: , + }, + { + type: "collapse", + name: "Sign Up", + key: "sign-up", + icon: assignment, + route: "/authentication/sign-up", + component: , + }, +]; + +Wrapper.propTypes = { + component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired, +}; + +export default adminRoutes; diff --git a/src/examples/Navbars/DashboardNavbar/index.js b/src/examples/Navbars/DashboardNavbar/index.js index e0a18c1..06488fd 100644 --- a/src/examples/Navbars/DashboardNavbar/index.js +++ b/src/examples/Navbars/DashboardNavbar/index.js @@ -51,13 +51,17 @@ import { setMiniSidenav, setOpenConfigurator, } from "context"; +import { useUser } from "utils/userContext"; +import MDTypography from "components/MDTypography"; function DashboardNavbar({ absolute, light, isMini }) { const [navbarType, setNavbarType] = useState(); const [controller, dispatch] = useMaterialUIController(); const { miniSidenav, transparentNavbar, fixedNavbar, openConfigurator, darkMode } = controller; const [openMenu, setOpenMenu] = useState(false); + const { user } = useUser(); const route = useLocation().pathname.split("/").slice(1); + const { logout } = useUser(); useEffect(() => { // Setting the navbar type @@ -87,7 +91,7 @@ function DashboardNavbar({ absolute, light, isMini }) { const handleMiniSidenav = () => setMiniSidenav(dispatch, !miniSidenav); const handleConfiguratorOpen = () => setOpenConfigurator(dispatch, !openConfigurator); - const handleOpenMenu = (event) => setOpenMenu(event.currentTarget); + const handleLogOut = () => logout(); const handleCloseMenu = () => setOpenMenu(false); // Render the notifications menu @@ -131,6 +135,9 @@ function DashboardNavbar({ absolute, light, isMini }) { navbarContainer(theme)}> navbarRow(theme, { isMini })}> + + Welcome {user?.name} + {isMini ? null : ( navbarRow(theme, { isMini })}> @@ -165,12 +172,10 @@ function DashboardNavbar({ absolute, light, isMini }) { disableRipple color="inherit" sx={navbarIconButton} - aria-controls="notification-menu" - aria-haspopup="true" variant="contained" - onClick={handleOpenMenu} + onClick={handleLogOut} > - notifications + logout {renderMenu()} diff --git a/src/index.js b/src/index.js index 6b6df4b..cc85b16 100644 --- a/src/index.js +++ b/src/index.js @@ -17,19 +17,19 @@ import React from "react"; import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import App from "App"; -import { AuthProvider } from "context/auth"; // Material Dashboard 2 React Context Provider import { MaterialUIControllerProvider } from "context"; +import UserProvider from "utils/userContext"; const container = document.getElementById("app"); const root = createRoot(container); root.render( - - - + + + - - - + + + ); diff --git a/src/layouts/TBI/tbi.js b/src/layouts/TBI/tbi.js new file mode 100644 index 0000000..e6fc8e7 --- /dev/null +++ b/src/layouts/TBI/tbi.js @@ -0,0 +1,21 @@ +import { Typography } from "@mui/material"; +import Loading from "components/Loading"; +import Footer from "examples/Footer"; +import DashboardLayout from "examples/LayoutContainers/DashboardLayout"; +import DashboardNavbar from "examples/Navbars/DashboardNavbar"; +import React from "react"; + +export default function TBH() { + return ( +
+ + + + Still in progress of Implementation + + +
+ +
+ ); +} diff --git a/src/layouts/advertisement_map/map.js b/src/layouts/advertisement_map/map.js index 668a16f..7d7d18f 100644 --- a/src/layouts/advertisement_map/map.js +++ b/src/layouts/advertisement_map/map.js @@ -10,15 +10,14 @@ import MDBox from "components/MDBox"; import MDTypography from "components/MDTypography"; import Card from "@mui/material/Card"; -import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; -import Icon from "@mui/material/Icon"; import customMarkerIcon1 from "../../assets/images/map_markers/house.png"; import customMarkerIcon2 from "../../assets/images/map_markers/land.png"; import customMarkerIcon3 from "../../assets/images/map_markers/marriage.png"; import { getRecentAdLocation } from "api/advertisementMap/advertisementLocation"; +import MDInput from "components/MDInput"; const MapComponent = () => { const location = useLocation(); @@ -28,11 +27,8 @@ const MapComponent = () => { const mapCenter = [7.8731, 80.7718]; const [markers, setMarkers] = useState([]); const [selectedData, setSelectedData] = useState("LandSale"); + const [selectedTime, setSelectedTime] = useState("Overall"); const [test, setTest] = useState(true); - const [menu, setMenu] = useState(null); - - const openMenu = ({ currentTarget }) => setMenu(currentTarget); - const closeMenu = () => setMenu(null); useEffect(() => { const fetchData = async () => { @@ -130,32 +126,14 @@ const MapComponent = () => { }), }; - const handleMenuItemClick = (dataKey) => { + const handleMenuItemClick = (event) => { + const dataKey = event.target.value; setSelectedData(dataKey); - - closeMenu(); }; - - const renderMenu = ( - - handleMenuItemClick("LandSale")}>Land Sale - handleMenuItemClick("HouseSale")}>House Sale - handleMenuItemClick("MarriageProp")}>Marriage Proposals - - ); + const handleTimeItemClick = (event) => { + const dataKey = event.target.value; + setSelectedTime(dataKey); + }; const renderContent = (marker) => { switch (selectedData) { @@ -218,7 +196,39 @@ const MapComponent = () => { Top Locations in {selectedData} - + + + + Land Sale + House Sale + Marriage Proposals + + + + Overall + Today + Yesterday + Last Week + Last Month + + + {/* { more_vert - {renderMenu} + {renderMenu} */} diff --git a/src/layouts/authentication/sign-in/index.js b/src/layouts/authentication/sign-in/index.js index 7383bca..82495f8 100644 --- a/src/layouts/authentication/sign-in/index.js +++ b/src/layouts/authentication/sign-in/index.js @@ -38,8 +38,10 @@ import GoogleButton from "react-google-button"; import { auth, provider } from "layouts/authentication/sign-in/config"; import { signInWithPopup } from "firebase/auth"; import { useNavigate } from "react-router-dom"; +import { useUser } from "utils/userContext"; function Basic() { + const { login } = useUser(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); @@ -82,6 +84,7 @@ function Basic() { .then((data) => { console.log(data); // Display a success message if applicable + login({ name: email, role: "user" }); navigate("/dashboard"); }) .catch((status) => { diff --git a/src/layouts/authentication/sign-up/index.js b/src/layouts/authentication/sign-up/index.js index 3922afd..1a6fd9e 100644 --- a/src/layouts/authentication/sign-up/index.js +++ b/src/layouts/authentication/sign-up/index.js @@ -38,6 +38,7 @@ import bgImage from "assets/images/bg-sign-up-cover.jpeg"; import VerificationDialog from "./VerificationDialog"; import { useNavigate } from "react-router-dom"; +import { useUser } from "utils/userContext"; function Cover() { const [email, setEmail] = useState(""); @@ -45,6 +46,7 @@ function Cover() { const [name, setName] = useState(""); const navigate = useNavigate(); const [showSuccessAlert, setShowSuccessAlert] = useState(false); + const { login } = useUser(); const [verificationOpen, setVerificationOpen] = useState(false); // State to manage the dialog @@ -202,6 +204,7 @@ function Cover() { setShowSuccessAlert(true); setTimeout(() => { setShowSuccessAlert(false); + login({ name: email, role: "user" }); navigate("/dashboard"); }, 1000); }} diff --git a/src/layouts/dashboard/components/Projects/index.js b/src/layouts/dashboard/components/Projects/index.js index 00b7f2e..c79e7df 100644 --- a/src/layouts/dashboard/components/Projects/index.js +++ b/src/layouts/dashboard/components/Projects/index.js @@ -69,7 +69,7 @@ function Projects() { - Recent Advertisements + Advertisements -  15 Advertisements Found this month +  15 Advertisements Found in your database diff --git a/src/layouts/dashboard/components/SearchBar/searchBar.js b/src/layouts/dashboard/components/SearchBar/searchBar.js index a45a5c4..996be88 100644 --- a/src/layouts/dashboard/components/SearchBar/searchBar.js +++ b/src/layouts/dashboard/components/SearchBar/searchBar.js @@ -47,12 +47,14 @@ const AdvertisementSearch = () => { }; const [selectedData, setSelectedData] = useState([]); + const [currentPage, setCurrentPage] = useState(0); const adsPerPage = 20; const handleSearch = async () => { try { // Implement your search logic here, fetching data based on the selected option and query // For example, you can use the getRecentAd function from the API file + setCurrentPage(0); setLoading(true); const searchData = await getAdbyFilter( selectedOption, @@ -73,7 +75,7 @@ const AdvertisementSearch = () => { const openCategoryMenu = ({ currentTarget }) => setCategoryMenu(currentTarget); const closeCategoryMenu = () => setCategoryMenu(null); - const [currentPage, setCurrentPage] = useState(0); + const openMenu = ({ currentTarget }) => setMenu(currentTarget); const closeMenu = () => setMenu(null); const handleMenuItemClick = (dataKey) => { diff --git a/src/routes.js b/src/routes.js index 1f7e1d3..e3c917a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -55,6 +55,22 @@ import Ad from "layouts/advertisement/ad"; import Report from "layouts/reports"; import GraphReport from "layouts/reports/graphs"; import App from "layouts/landing/App"; +// import PrivateRoute from "utils/privateRoutes"; +import { useUser } from "utils/userContext"; +import PropTypes from "prop-types"; + +// ... + +// Create a wrapper component for the Dashboard +const Wrapper = ({ component: Component }) => { + const { isAuthenticated } = useUser(); + + if (isAuthenticated) { + return ; + } else { + return ; + } +}; const routes = [ { @@ -63,7 +79,7 @@ const routes = [ key: "dashboard", icon: dashboard, route: "/dashboard", - component: , + component: , }, { type: "collapse", @@ -71,7 +87,7 @@ const routes = [ key: "adsearch", icon: search, route: "/advertisement", - component: , + component: , }, { type: "collapse", @@ -79,7 +95,7 @@ const routes = [ key: "upload", icon: newspaper, route: "/upload", - component: , + component: , }, { type: "collapse", @@ -87,7 +103,7 @@ const routes = [ key: "graph", icon: equalizer, route: "/graphs", - component: , + component: , }, { type: "collapse", @@ -95,7 +111,7 @@ const routes = [ key: "A_map", icon: map, route: "/advertisement_map", - component: , + component: , }, { type: "collapse", @@ -103,7 +119,7 @@ const routes = [ key: "reports", icon: report, route: "/reports", - component: , + component: , }, { type: "collapse", @@ -131,7 +147,7 @@ const routes = [ }, { route: "/advertisement/:id", - component: , + component: , }, { route: "/landing", @@ -139,7 +155,7 @@ const routes = [ }, { route: "/reports/:title", - component: , + component: , }, { type: "collapse", @@ -155,7 +171,7 @@ const routes = [ key: "profile", icon: person, route: "/profile", - component: , + component: , }, { type: "collapse", @@ -175,4 +191,8 @@ const routes = [ }, ]; +Wrapper.propTypes = { + component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired, +}; + export default routes; diff --git a/src/utils/privateRoutes.js b/src/utils/privateRoutes.js new file mode 100644 index 0000000..98f5810 --- /dev/null +++ b/src/utils/privateRoutes.js @@ -0,0 +1,19 @@ +import React from "react"; +import { Route, Navigate } from "react-router-dom"; +import PropTypes from "prop-types"; + +const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => ( + + isAuthenticated ? : + } + /> +); + +PrivateRoute.propTypes = { + component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired, + isAuthenticated: PropTypes.bool.isRequired, +}; + +export default PrivateRoute; diff --git a/src/utils/userContext.js b/src/utils/userContext.js new file mode 100644 index 0000000..97699f4 --- /dev/null +++ b/src/utils/userContext.js @@ -0,0 +1,35 @@ +import React, { createContext, useContext, useState } from "react"; +import PropTypes from "prop-types"; + +const UserContext = createContext(); + +export const useUser = () => { + return useContext(UserContext); +}; + +export const UserProvider = ({ children }) => { + const [user, setUser] = useState(null); + const [isAuthenticated, setIsAuthenticated] = useState(false); + + const login = (userData) => { + setIsAuthenticated(true); + setUser(userData); + }; + + const logout = () => { + setIsAuthenticated(false); + setUser(null); + }; + + return ( + + {children} + + ); +}; + +UserProvider.propTypes = { + children: PropTypes.node, +}; + +export default UserProvider;