From 35ede7d6900861b0db35320bccdb95fa4f506d40 Mon Sep 17 00:00:00 2001 From: Ethan Lajeunesse Date: Thu, 7 Mar 2024 00:09:06 -0500 Subject: [PATCH] Basic admin UI --- client/src/App.tsx | 40 +++++++++++++++++----- client/src/components/Navbar.tsx | 1 + client/src/components/UserRouteLayout.tsx | 11 ++++++ client/src/pages/admin/Admin.tsx | 41 +++++++++++++++++++++++ server/src/middleware/verifyIsAdmin.ts | 8 +++++ 5 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 client/src/components/UserRouteLayout.tsx create mode 100644 client/src/pages/admin/Admin.tsx create mode 100644 server/src/middleware/verifyIsAdmin.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 7147400..6d83c24 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,5 @@ import { Layout } from "antd"; -import { Routes, Route } from "react-router-dom"; +import { Routes, Route, Navigate, Outlet } from "react-router-dom"; import Navbar from "./components/Navbar"; import ChallengesView from "./pages/ChallengesView"; import Home from "./pages/Home"; @@ -11,6 +11,8 @@ import useInterceptor from "./hooks/useInterceptor"; import Loading from "./components/Loading"; import Leaderboard from "./pages/Leaderboard"; import Badges from "./pages/Badges"; +import AdminLayout from "./pages/admin/Admin"; +import UserRouteLayout from "./components/UserRouteLayout"; const { Content } = Layout; @@ -30,14 +32,36 @@ const App = () => { return ( - + - } /> - } /> - } /> - } /> - } /> - } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + + }> + } /> + Users} /> + Challenges} /> + +
Submissions
+ + + } + > + All Submissions} /> + Manual Review} /> + Correct Submissions} /> + Incorrect Submissions} /> + + Settings} /> +
diff --git a/client/src/components/Navbar.tsx b/client/src/components/Navbar.tsx index b4a704c..d92016c 100644 --- a/client/src/components/Navbar.tsx +++ b/client/src/components/Navbar.tsx @@ -15,6 +15,7 @@ const Navbar = () => { "/challenges": "Challenges", "/leaderboard": "Leaderboard", "/badges": "Badges", + "/admin": "Admin", }; const isLoggedIn = user != null; diff --git a/client/src/components/UserRouteLayout.tsx b/client/src/components/UserRouteLayout.tsx new file mode 100644 index 0000000..bf878b9 --- /dev/null +++ b/client/src/components/UserRouteLayout.tsx @@ -0,0 +1,11 @@ +import { Outlet } from "react-router-dom"; + +const UserRouteLayout = () => { + return ( +
+ +
+ ); +}; + +export default UserRouteLayout; diff --git a/client/src/pages/admin/Admin.tsx b/client/src/pages/admin/Admin.tsx new file mode 100644 index 0000000..82e5a43 --- /dev/null +++ b/client/src/pages/admin/Admin.tsx @@ -0,0 +1,41 @@ +import { Header } from "antd/es/layout/layout"; +import { Link, Outlet, useLocation } from "react-router-dom"; + +const AdminLayout = () => { + const location = useLocation(); + const selectedKey = location.pathname; + + const adminNavLinks = { + "/admin/users": "Users", + "/admin/challenges": "Challenges", + "/admin/submissions/all": "Submissions", + "/admin/settings": "Settings", + }; + + return ( +
+
+
+ {Object.entries(adminNavLinks).map(([link, text]) => ( + + {text} + + ))} +
+
+
+ +
+
+ ); +}; + +export default AdminLayout; diff --git a/server/src/middleware/verifyIsAdmin.ts b/server/src/middleware/verifyIsAdmin.ts new file mode 100644 index 0000000..43c0a5d --- /dev/null +++ b/server/src/middleware/verifyIsAdmin.ts @@ -0,0 +1,8 @@ +import { Response, NextFunction, Request } from "express"; + +export function verifyIsAdmin(req: Request, res: Response, next: NextFunction) { + if (!req.payload) + throw new Error("This middleware should be used after verifyAccess middleware"); + + return req.payload.isAdmin ? next() : res.status(403).send("Forbidden"); +} \ No newline at end of file