diff --git a/src/App.tsx b/src/App.tsx index 1ca9de4..e705983 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { createBrowserRouter, RouterProvider } from "react-router"; import NavBar from "./components/nav/NavBar"; +import AccountPage from "./pages/protected/AccountPage"; import AdminPage from "./pages/protected/AdminPage"; import GamePage from "./pages/protected/GamePage"; import GamesPage from "./pages/protected/GamesPage"; @@ -38,6 +39,10 @@ const routes = createBrowserRouter([ { element: , children: [ + { + path: "/account", + element: + }, { path: "/logout", element: diff --git a/src/components/nav/ProtectedNavLinks.tsx b/src/components/nav/ProtectedNavLinks.tsx index 78ff221..266f1bd 100644 --- a/src/components/nav/ProtectedNavLinks.tsx +++ b/src/components/nav/ProtectedNavLinks.tsx @@ -1,5 +1,6 @@ import { useAuth } from "@/providers/AuthProvider"; import { isSiteAdmin } from "@/util/PermissionUtil"; +import { BsFillPersonFill } from "react-icons/bs"; import { NavLink } from "react-router"; @@ -46,6 +47,16 @@ export default function ProtectedNavLinks(){ )) } + { + jwt && + + + + } ); } diff --git a/src/hooks/AccountHooks.ts b/src/hooks/AccountHooks.ts index b0ab92a..860cd8a 100644 --- a/src/hooks/AccountHooks.ts +++ b/src/hooks/AccountHooks.ts @@ -3,6 +3,7 @@ import { AccountTutorialStatus } from "@/interface/AccountTutorialStatus"; import { RaidGroupPermissionType } from "@/interface/RaidGroup"; import { api } from "@/util/AxiosUtil"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { AxiosError } from "axios"; export function useGetAccounts(page: number, pageSize: number, searchTerm?: string){ @@ -160,6 +161,28 @@ export function useUpdateTutorialsStatus(){ }); } +export function useUpdatePassword(){ + return useMutation({ + mutationKey: ["updatePassword"], + mutationFn: async ({currentPassword, newPassword}:{currentPassword: string; newPassword: string;}) => { + try{ + await api.post("/auth/resetPassword", { + currentPassword, + newPassword + }); + } + catch(error){ + if(error instanceof AxiosError && error.response?.data.errors){ + throw new Error(error.response?.data.errors.join(", ")); + } + else{ + throw error; + } + } + } + }); +} + export function useForcePasswordReset(accountId: string){ diff --git a/src/pages/protected/AccountPage.tsx b/src/pages/protected/AccountPage.tsx new file mode 100644 index 0000000..0090d18 --- /dev/null +++ b/src/pages/protected/AccountPage.tsx @@ -0,0 +1,29 @@ +import TabGroup, { Tab } from "@/components/tab/TabGroup"; +import PasswordResetDisplay from "@/ui/account/PasswordResetDisplay"; + + +export default function AccountPage(){ + const tabs: Tab[] = [ + { + tabId: "password", + tabHeader: "Password", + tabContent: + } + ]; + + + return ( +
+

+ My Account +

+ +
+ ); +} diff --git a/src/pages/public/SignupPage.tsx b/src/pages/public/SignupPage.tsx index 0dce6e9..decab24 100644 --- a/src/pages/public/SignupPage.tsx +++ b/src/pages/public/SignupPage.tsx @@ -22,7 +22,17 @@ export default function SignupPage(){ const signup = () => { - if(password !== secondPassword){ + if(username === ""){ + addErrorMessage("Username cannot be empty"); + return; + } + else if(email === ""){ + addErrorMessage("Email cannot be empty"); + } + else if(password === ""){ + addErrorMessage("Password cannot be empty"); + } + else if(password !== secondPassword){ addErrorMessage("Passwords do not match"); return; } diff --git a/src/ui/account/PasswordResetDisplay.tsx b/src/ui/account/PasswordResetDisplay.tsx new file mode 100644 index 0000000..52f25de --- /dev/null +++ b/src/ui/account/PasswordResetDisplay.tsx @@ -0,0 +1,90 @@ +import PrimaryButton from "@/components/button/PrimaryButton"; +import PasswordInput from "@/components/input/PasswordInput"; +import { useUpdatePassword } from "@/hooks/AccountHooks"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router"; + + +export default function PasswordResetDisplay(){ + const [ currentPassword, setCurrentPassword ] = useState(""); + const [ newPassword, setNewPassword ] = useState(""); + const [ confirmPassword, setConfirmPassword ] = useState(""); + + const navigate = useNavigate(); + const { addErrorMessage, addSuccessMessage } = useTimedModal(); + + const {mutate: updatePasswordMutate, status: updatePasswordStatus, error: updatePasswordError, reset: updatePasswordReset} = useUpdatePassword(); + + + const updatePassword = () => { + if(newPassword !== confirmPassword){ + addErrorMessage("Passwords do not match"); + return; + } + else if(newPassword === ""){ + addErrorMessage("Password cannot be empty"); + return; + } + else{ + updatePasswordMutate({ + currentPassword, + newPassword + }); + } + } + + + useEffect(() => { + if(updatePasswordStatus === "success"){ + addSuccessMessage("Password updated successfully"); + updatePasswordReset(); + setCurrentPassword(""); + setNewPassword(""); + setConfirmPassword(""); + } + else if(updatePasswordStatus === "error"){ + addErrorMessage("Failed to update password: " + updatePasswordError.message); + updatePasswordReset(); + } + }, [ updatePasswordMutate, updatePasswordStatus, updatePasswordError, updatePasswordReset, addSuccessMessage, addErrorMessage, navigate ]); + + + return ( +
+
+ setCurrentPassword(e.target.value)} + /> +
+
+ setNewPassword(e.target.value)} + /> +
+
+ setConfirmPassword(e.target.value)} + /> +
+
+ + Update Password + +
+
+ ); +}