From 34a96a8445213a8e49d41c3adfa7e1d2373d0955 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Mon, 29 Jul 2024 17:59:04 +0800 Subject: [PATCH] Recover email from sign in --- client/assets/google-passkey.svg | 1 + client/src/App.tsx | 106 +++++++++++--------- client/src/components/SignInModule.tsx | 26 +++-- client/src/layouts/global.tsx | 21 ++++ client/src/layouts/restricted.tsx | 34 +++++++ client/src/pages/ForgotPasswordPage.tsx | 109 +++++++++++++++++++++ client/src/pages/ResetPasswordPage.tsx | 123 +++++++++--------------- 7 files changed, 290 insertions(+), 130 deletions(-) create mode 100644 client/assets/google-passkey.svg create mode 100644 client/src/layouts/global.tsx create mode 100644 client/src/layouts/restricted.tsx create mode 100644 client/src/pages/ForgotPasswordPage.tsx diff --git a/client/assets/google-passkey.svg b/client/assets/google-passkey.svg new file mode 100644 index 0000000..91e8659 --- /dev/null +++ b/client/assets/google-passkey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index 99b95ef..44e9f92 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -21,60 +21,72 @@ import DefaultLayout from "./layouts/default"; import AdministratorLayout from "./layouts/administrator"; import UsersManagement from "./pages/UsersManagement"; import ResetPasswordPage from "./pages/ResetPasswordPage"; +import ForgotPasswordPage from "./pages/ForgotPasswordPage"; +import RestrictedLayout from "./layouts/restricted"; function App() { return ( - {/* User Routes */} - - }> - {/* General Routes */} - } /> - } path="signup" /> - } path="signin" /> - } path="springboard" /> - } path="manage-account" /> + + {/* User Routes */} + + }> + {/* General Routes */} + } /> + } path="signup" /> + } path="signin" /> + } path="springboard" /> + } path="manage-account" /> - {/* Events Route */} + {/* Events Route */} + + } /> + + + {/* Karang Guni Schedules Route */} + + } /> + + + {/* Home Bill Contest Route */} + + } /> + } path="new-submission" /> + + + {/* Community Posts Route */} + + } /> + } path="create" /> + } path="post/:id" /> + } path="edit/:id" /> + + + + {/* Special (Restricted) Routes */} + }> + } path="forgot-password" /> + } + path="reset-password/:token" + /> + + + + {/* Admin Routes */} + }> + } /> + } /> + + } /> + + + {/* Events */} - } /> + } /> + } path="createEvent" /> + } path="editEvent/:id" /> - - {/* Karang Guni Schedules Route */} - - } /> - - - {/* Home Bill Contest Route */} - - } /> - } path="new-submission" /> - - - {/* Community Posts Route */} - - } /> - } path="create" /> - } path="post/:id" /> - } path="edit/:id" /> - - - } path="reset-password/:token" /> - - - {/* Admin Routes */} - }> - } /> - } /> - - } /> - - - {/* Events */} - - } /> - } path="createEvent" /> - } path="editEvent/:id" /> diff --git a/client/src/components/SignInModule.tsx b/client/src/components/SignInModule.tsx index ecc7bb1..3d810bc 100644 --- a/client/src/components/SignInModule.tsx +++ b/client/src/components/SignInModule.tsx @@ -1,7 +1,6 @@ import { Button, Link } from "@nextui-org/react"; import { Formik, Form } from "formik"; import * as Yup from "yup"; -import axios from "axios"; import config from "../config"; import NextUIFormikInput from "./NextUIFormikInput"; import { useNavigate } from "react-router-dom"; @@ -74,13 +73,24 @@ export default function SignInModule() { placeholder="johndoe@email.com" labelPlacement="outside" /> - +
+ + { + navigate("/forgot-password"); + }} + > + Forgot password? + +
+
+

·

+

+ © Copyright {new Date().getFullYear()}. All rights reserved. +

+
+ + + + + ); +} diff --git a/client/src/pages/ForgotPasswordPage.tsx b/client/src/pages/ForgotPasswordPage.tsx new file mode 100644 index 0000000..ed816c3 --- /dev/null +++ b/client/src/pages/ForgotPasswordPage.tsx @@ -0,0 +1,109 @@ +import { Button } from "@nextui-org/react"; +import { Formik, Form } from "formik"; +import * as Yup from "yup"; +import axios from "axios"; +import config from "../config"; +import NextUIFormikInput from "../components/NextUIFormikInput"; +import { popErrorToast, popToast } from "../utilities"; +import { ArrowUTurnLeftIcon } from "../icons"; +import { useNavigate } from "react-router-dom"; +import instance from "../security/http"; + +const validationSchema = Yup.object({ + email: Yup.string() + .trim() + .lowercase() + .min(5) + .max(69) + .email("Invalid email format") + .required("Email is required"), +}); + +export default function ForgotPasswordPage() { + const navigate = useNavigate(); + const initialValues = { + email: "", + }; + + const handleSubmit = (values: any): void => { + instance + .put( + `${ + config.serverAddress + }/users/request-reset-password/${encodeURIComponent(values.email)}` + ) + .then(() => { + console.log("Email sent successfully"); + popToast("Email sent to your mailbox!", 1); + setTimeout(() => { + navigate("/signin"); + }, 1500); + }) + .catch((error) => { + console.error("Failed to send email:", error); + popErrorToast("Failed to send email: " + error); + }); + }; + + return ( +
+
+ +
+ Google Passkey SVG +
+
+
+
+
+
+

Password Recovery

+

+ Enter your email address below, and we will send you a recovery + mail. +

+
+
+ + {({ isValid, dirty }) => ( +
+ + + + )} +
+
+
+ ); +} diff --git a/client/src/pages/ResetPasswordPage.tsx b/client/src/pages/ResetPasswordPage.tsx index 4a8ef7d..44afbdc 100644 --- a/client/src/pages/ResetPasswordPage.tsx +++ b/client/src/pages/ResetPasswordPage.tsx @@ -2,8 +2,7 @@ import { useParams, useNavigate } from "react-router-dom"; import instance from "../security/http"; import config from "../config"; import { useEffect, useState } from "react"; -import { Button, Card, CircularProgress } from "@nextui-org/react"; -import EcoconnectFullLogo from "../components/EcoconnectFullLogo"; +import { Button } from "@nextui-org/react"; import NextUIFormikInput from "../components/NextUIFormikInput"; import { Formik, Form } from "formik"; import * as Yup from "yup"; @@ -29,7 +28,6 @@ export default function ResetPasswordPage() { const navigate = useNavigate(); const [validationResult, setValidationResult] = useState(false); - const [pageLoading, setPageLoading] = useState(true); const validateToken = () => { instance @@ -39,9 +37,6 @@ export default function ResetPasswordPage() { }) .catch(() => { setValidationResult(false); - }) - .finally(() => { - setPageLoading(false); }); }; @@ -66,77 +61,55 @@ export default function ResetPasswordPage() { }; return ( -
-
- {pageLoading && ( -
- +
+ {validationResult && ( +
+
+

Password Reset

+

Enter a new password below.

- )} - {!pageLoading && ( -
- - {validationResult && ( -
-
-

Password Reset

-

Enter a new password below.

-
- - {({ isValid, dirty }) => ( -
- - - - - )} -
-
- )} - {!validationResult && ( -
-
-

- Reset portal has been closed. -

-

Please request for a password reset again.

-
-
- )} -
-
- -

·

-

- © Copyright {new Date().getFullYear()}. All rights reserved. -

-
+ + {({ isValid, dirty }) => ( +
+ + + + + )} +
+
+ )} + {!validationResult && ( +
+
+

Reset portal has been closed.

+

Please request for a password reset again.

- )} -
+
+ )}
); }