From 0544ed15cfb1c37cfb781330f563f360cbadaced Mon Sep 17 00:00:00 2001 From: "Au Chen Xi, Gabriel" Date: Fri, 3 Nov 2023 14:29:43 +0800 Subject: [PATCH] Changes for a3 recording --- src/components/NavBar.tsx | 29 +++++++++++++++++++++----- src/components/UserDenied.tsx | 2 +- src/components/wrapper/AuthWrapper.tsx | 21 +++++++++++++++---- src/pages/judge/index.tsx | 5 ++++- src/pages/questions/index.tsx | 28 ++++++++++++++----------- 5 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 4c1b3e4..71dd1e4 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,12 +1,16 @@ +import { signOut, useSession } from "next-auth/react"; import Link from "next/link"; +import { useRouter } from "next/router"; import { useState } from "react"; +import toast from "react-hot-toast"; const NavBar = () => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); - + const router = useRouter(); const toggleDropdown = () => { setIsDropdownOpen(!isDropdownOpen); }; + const { data: session } = useSession(); const closeDropdown = () => setIsDropdownOpen(false); @@ -22,6 +26,13 @@ const NavBar = () => { {isDropdownOpen && (
+ + PeerPrep + { > CodeExecution - Judge0 - SignUp - - } + {!session && SignIn - + } + {session &&
{ + void signOut(); + }} + className="cursor-pointer flex items-center justify-center font-bold text-white no-underline transition hover:bg-white/20 rounded-md whitespace-nowrap bg-white/10 flex-[1_0_0%] px-4" + > + Log out +
}
)} diff --git a/src/components/UserDenied.tsx b/src/components/UserDenied.tsx index 7131314..3d26c1f 100644 --- a/src/components/UserDenied.tsx +++ b/src/components/UserDenied.tsx @@ -5,7 +5,7 @@ import { LoadingPage } from "./Loading"; export default function UserDenied() { return ( - + User Denied ) } diff --git a/src/components/wrapper/AuthWrapper.tsx b/src/components/wrapper/AuthWrapper.tsx index accf7d6..ee557ad 100644 --- a/src/components/wrapper/AuthWrapper.tsx +++ b/src/components/wrapper/AuthWrapper.tsx @@ -5,9 +5,12 @@ import { useRouter } from "next/router"; import UserDenied from "~/components/UserDenied"; import { LoadingPage } from "../Loading"; import { PageLayout } from "../Layout"; +import { useEffect } from "react"; +import toast from "react-hot-toast"; type Props = { children: React.ReactElement; + maintainerOnly?: boolean; }; /* @@ -19,13 +22,22 @@ type Props = { export default OrderDetail; */ -export const AuthWrapper = ({ children }: Props): JSX.Element => { - const { status: sessionStatus } = useSession({ required: true }); +export const AuthWrapper = ({ children, maintainerOnly }: Props): JSX.Element => { + const { status: sessionStatus, data: session } = useSession({ required: true }); const authorized = sessionStatus === "authenticated"; + const isMaintainer = session?.user.role === "MAINTAINER"; const loading = sessionStatus === "loading"; + const router = useRouter(); + + useEffect(() => { + if (session && maintainerOnly && !isMaintainer) { + toast.error("Maintainer only route"); + void router.back(); + } + }, [session]); // if the user refreshed the page or somehow navigated to the protected page - if (loading) { + if (loading || maintainerOnly && !isMaintainer) { return ( <> @@ -42,11 +54,12 @@ export const AuthWrapper = ({ children }: Props): JSX.Element => { export function WithAuthWrapper( Component: (props: Props) => JSX.Element, + maintainerOnly = false ) { // eslint-disable-next-line react/display-name return (pageProps: Props) => { return ( - + ); diff --git a/src/pages/judge/index.tsx b/src/pages/judge/index.tsx index fb2ebc6..9c6322a 100644 --- a/src/pages/judge/index.tsx +++ b/src/pages/judge/index.tsx @@ -13,9 +13,10 @@ import { PeerPrepRectLogo } from "~/assets/logo"; import { api } from "~/utils/api"; import { PageLayout } from "~/components/Layout"; import Head from "next/head"; +import { WithAuthWrapper } from "~/components/wrapper/AuthWrapper"; // import { useSession } from "next-auth/react"; -export default function Questions() { +function Questions() { const [output, setOutput] = useState(null); const [questionId, setQuestionId] = useState(null); const [environmentId, setEnvironmentId] = useState(null); @@ -269,3 +270,5 @@ export default function Questions() { ); } + +export default WithAuthWrapper(Questions, true); \ No newline at end of file diff --git a/src/pages/questions/index.tsx b/src/pages/questions/index.tsx index eaad77d..a996bbd 100644 --- a/src/pages/questions/index.tsx +++ b/src/pages/questions/index.tsx @@ -11,8 +11,9 @@ import { makeMap } from "../../utils/utils"; import { toast } from "react-hot-toast"; import { useSession } from "next-auth/react"; import WarningModal from "~/components/WarningModal"; +import { WithAuthWrapper } from "~/components/wrapper/AuthWrapper"; -export default function Questions() { +function Questions() { const { data: sessionData } = useSession(); const allowedToModify = sessionData?.user.role === "MAINTAINER"; const getAllQuery = api.useContext().question.getAll; @@ -21,18 +22,19 @@ export default function Questions() { const [changedQns, setChangedQns] = useState(new Set()); const [deletedQns, setDeletedQns] = useState(new Set()); + const questionsQuery = api.question.getAll.useQuery(undefined, { + onSuccess: (data) => { + const mappedData = data.map((q) => ({ + ...(changedQns.has(q.id) ? viewQns.get(q.id) ?? q : q), + })); + setViewQns(makeMap(mappedData, "id")); + }, + onError: (e) => { + toast.error("Failed to fetch questions"); + }, + }); const questions = makeMap( - api.question.getAll.useQuery(undefined, { - onSuccess: (data) => { - const mappedData = data.map((q) => ({ - ...(changedQns.has(q.id) ? viewQns.get(q.id) ?? q : q), - })); - setViewQns(makeMap(mappedData, "id")); - }, - onError: (e) => { - toast.error("Failed to fetch questions"); - }, - }).data ?? [], + questionsQuery.data ?? [], "id", ); @@ -225,3 +227,5 @@ export default function Questions() { ); } + +export default WithAuthWrapper(Questions);