Confirm and password emails sending
This commit is contained in:
10
src/App.tsx
10
src/App.tsx
@@ -10,7 +10,9 @@ import RaidGroupPage from "./pages/protected/RaidGroupPage";
|
|||||||
import RaidGroupsPage from "./pages/protected/RaidGroupsPage";
|
import RaidGroupsPage from "./pages/protected/RaidGroupsPage";
|
||||||
import RaidInstancePage from "./pages/protected/RaidInstancePage";
|
import RaidInstancePage from "./pages/protected/RaidInstancePage";
|
||||||
import RaidLayoutPage from "./pages/protected/RaidLayoutPage";
|
import RaidLayoutPage from "./pages/protected/RaidLayoutPage";
|
||||||
|
import ConfirmPage from "./pages/public/ConfirmPage";
|
||||||
import ForgotPasswordPage from "./pages/public/ForgotPassword";
|
import ForgotPasswordPage from "./pages/public/ForgotPassword";
|
||||||
|
import ForgotTokenPage from "./pages/public/ForgotTokenPage";
|
||||||
import HomePage from "./pages/public/HomePage";
|
import HomePage from "./pages/public/HomePage";
|
||||||
import LoginPage from "./pages/public/LoginPage";
|
import LoginPage from "./pages/public/LoginPage";
|
||||||
import SignupPage from "./pages/public/SignupPage";
|
import SignupPage from "./pages/public/SignupPage";
|
||||||
@@ -41,6 +43,14 @@ const routes = createBrowserRouter([
|
|||||||
path: "/forgotPassword",
|
path: "/forgotPassword",
|
||||||
element: <ForgotPasswordPage/>
|
element: <ForgotPasswordPage/>
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/forgotPassword/:forgotToken",
|
||||||
|
element: <ForgotTokenPage/>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/confirm/:confirmToken",
|
||||||
|
element: <ConfirmPage/>
|
||||||
|
},
|
||||||
{
|
{
|
||||||
element: <ProtectedRoute/>,
|
element: <ProtectedRoute/>,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Account } from "@/interface/Account";
|
import { Account } from "@/interface/Account";
|
||||||
import { api } from "@/util/AxiosUtil";
|
import { api } from "@/util/AxiosUtil";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
|
|
||||||
export function useSignup(){
|
export function useSignup(){
|
||||||
@@ -18,3 +19,77 @@ export function useSignup(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function useConfirm(){
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["confirm"],
|
||||||
|
mutationFn: async (confirmToken: string) => {
|
||||||
|
try{
|
||||||
|
const response = await api.post(`/auth/confirm/${confirmToken}`);
|
||||||
|
|
||||||
|
if(response.data.errors){
|
||||||
|
throw new Error(response.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(error){
|
||||||
|
if(error instanceof AxiosError && error.response?.data.errors){
|
||||||
|
throw new Error(error.response?.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useForgotPassword(){
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["forgotPassword"],
|
||||||
|
mutationFn: async (username: string) => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("username", username);
|
||||||
|
|
||||||
|
|
||||||
|
try{
|
||||||
|
const response = await api.post(`/auth/forgot?${params}`);
|
||||||
|
|
||||||
|
if(response.data.errors){
|
||||||
|
throw new Error(response.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(error){
|
||||||
|
if(error instanceof AxiosError && error.response?.data.errors){
|
||||||
|
throw new Error(error.response?.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useForgotResetPassword(forgotToken: string){
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["forgotResetPassword"],
|
||||||
|
mutationFn: async (password: string) => {
|
||||||
|
try{
|
||||||
|
const response = await api.post(`/auth/forgot/${forgotToken}`, {password});
|
||||||
|
|
||||||
|
if(response.data.errors){
|
||||||
|
throw new Error(response.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(error){
|
||||||
|
if(error instanceof AxiosError && error.response?.data.errors){
|
||||||
|
throw new Error(error.response?.data.errors.join(", "));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
41
src/pages/public/ConfirmPage.tsx
Normal file
41
src/pages/public/ConfirmPage.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import DangerMessage from "@/components/message/DangerMessage";
|
||||||
|
import SuccessMessage from "@/components/message/SuccessMessage";
|
||||||
|
import { useConfirm } from "@/hooks/AuthHooks";
|
||||||
|
import { useTimedModal } from "@/providers/TimedModalProvider";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router";
|
||||||
|
|
||||||
|
|
||||||
|
export default function ConfirmPage(){
|
||||||
|
const { confirmToken } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { addErrorMessage, addSuccessMessage } = useTimedModal();
|
||||||
|
|
||||||
|
|
||||||
|
const { mutate: confirmQueryMutate, status: confirmQueryStatus, error: confirmQueryError, reset: confirmQueryReset } = useConfirm();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(confirmQueryStatus === "idle"){
|
||||||
|
confirmQueryMutate(confirmToken ?? "");
|
||||||
|
}
|
||||||
|
else if(confirmQueryStatus === "error"){
|
||||||
|
addErrorMessage(confirmQueryError.message);
|
||||||
|
confirmQueryReset();
|
||||||
|
}
|
||||||
|
else if(confirmQueryStatus === "success"){
|
||||||
|
addSuccessMessage("Email confirmed. Please Login.");
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
}, [ confirmToken, confirmQueryMutate, confirmQueryStatus, confirmQueryError, confirmQueryReset, navigate, addSuccessMessage, addErrorMessage ]);
|
||||||
|
|
||||||
|
|
||||||
|
if(confirmQueryStatus === "pending"){
|
||||||
|
return <main>Confirming...</main>;
|
||||||
|
}
|
||||||
|
else if(confirmQueryStatus === "error"){
|
||||||
|
return <main><DangerMessage>Error: {confirmQueryError.message}</DangerMessage></main>;
|
||||||
|
}
|
||||||
|
else if(confirmQueryStatus === "success"){
|
||||||
|
return <main><SuccessMessage>Confirmed</SuccessMessage></main>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,55 @@
|
|||||||
|
import PrimaryButton from "@/components/button/PrimaryButton";
|
||||||
|
import TextInput from "@/components/input/TextInput";
|
||||||
|
import { useForgotPassword } from "@/hooks/AuthHooks";
|
||||||
|
import { useTimedModal } from "@/providers/TimedModalProvider";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
|
|
||||||
export default function ForgotPasswordPage(){
|
export default function ForgotPasswordPage(){
|
||||||
return (
|
const { mutate: forgotEmailMutate, status: forgotEmailStatus, error: forgotEmailError, reset: forgotEmailReset } = useForgotPassword();
|
||||||
|
const { addErrorMessage, addSuccessMessage } = useTimedModal();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [ username, setUsername ] = useState("");
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(forgotEmailStatus === "success"){
|
||||||
|
addSuccessMessage("Email sent successfully");
|
||||||
|
forgotEmailReset();
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
else if(forgotEmailStatus === "error"){
|
||||||
|
addErrorMessage("Failed to send email: " + forgotEmailError?.message);
|
||||||
|
forgotEmailReset();
|
||||||
|
}
|
||||||
|
}, [addErrorMessage, addSuccessMessage, forgotEmailError, forgotEmailReset, forgotEmailStatus, navigate]);
|
||||||
|
|
||||||
|
const sendForgotEmail = () => {
|
||||||
|
forgotEmailMutate(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
<main>
|
<main>
|
||||||
Under Construction
|
<div
|
||||||
|
className="flex flex-col items-center justify-center gap-y-8"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<TextInput
|
||||||
|
id="forgotPasswordUsername"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={sendForgotEmail}
|
||||||
|
>
|
||||||
|
Send Recovery Email
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/pages/public/ForgotTokenPage.tsx
Normal file
52
src/pages/public/ForgotTokenPage.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import PrimaryButton from "@/components/button/PrimaryButton";
|
||||||
|
import PasswordInput from "@/components/input/PasswordInput";
|
||||||
|
import { useForgotResetPassword } from "@/hooks/AuthHooks";
|
||||||
|
import { useTimedModal } from "@/providers/TimedModalProvider";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router";
|
||||||
|
|
||||||
|
|
||||||
|
export default function ForgotTokenPage(){
|
||||||
|
const { forgotToken } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { addErrorMessage, addSuccessMessage } = useTimedModal();
|
||||||
|
|
||||||
|
const [ newPassword, setNewPassword ] = useState("");
|
||||||
|
|
||||||
|
|
||||||
|
const { mutate: forgotQueryMutate, status: forgotQueryStatus, error: forgotQueryError, reset: forgotQueryReset } = useForgotResetPassword(forgotToken ?? "");
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(forgotQueryStatus === "success"){
|
||||||
|
addSuccessMessage("Password reset. Please login.");
|
||||||
|
forgotQueryReset();
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
else if(forgotQueryStatus === "error"){
|
||||||
|
addErrorMessage("Error resetting password: " + forgotQueryError.message);
|
||||||
|
forgotQueryReset();
|
||||||
|
}
|
||||||
|
}, [addErrorMessage, addSuccessMessage, forgotQueryError, forgotQueryReset, forgotQueryStatus, navigate]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main
|
||||||
|
className="flex flex-col items-center justify-center gap-y-8"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<PasswordInput
|
||||||
|
id="newPassword"
|
||||||
|
placeholder="New Password"
|
||||||
|
value={newPassword}
|
||||||
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => forgotQueryMutate(newPassword)}
|
||||||
|
>
|
||||||
|
Reset Password
|
||||||
|
</PrimaryButton>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user