From 6f38e94fbafec314db9b3f8fc6d258ecc2fdd75b Mon Sep 17 00:00:00 2001 From: Galav Date: Sun, 23 Mar 2025 20:21:06 -0400 Subject: [PATCH 1/2] Added page for Forgot Password --- package-lock.json | 45 +++++++++++++++-- src/App.tsx | 2 + src/pages/Authentication/ForgotPassword.tsx | 55 +++++++++++++++++++++ 3 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 src/pages/Authentication/ForgotPassword.tsx diff --git a/package-lock.json b/package-lock.json index 3ef4561e..ec5213eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.4.0", "bootstrap": "^5.3.3", - "chart.js": "^3.7.0", + "chart.js": "^4.1.1", "formik": "^2.2.9", "jquery": "^3.7.1", "jwt-decode": "^3.1.2", @@ -38,7 +38,7 @@ "react-redux": "^8.0.5", "react-router-dom": "^6.11.1", "react-scripts": "^5.0.1", - "recharts": "^2.12.3", + "recharts": "^2.0.0", "redux-persist": "^6.0.0", "sass": "^1.62.1", "save": "^2.9.0", @@ -3045,6 +3045,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -5760,9 +5766,16 @@ } }, "node_modules/chart.js": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", - "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } }, "node_modules/check-types": { "version": "11.2.3", @@ -16603,6 +16616,28 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", diff --git a/src/App.tsx b/src/App.tsx index 27736ba3..5a248435 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { createBrowserRouter, Navigate, RouterProvider } from "react-router-dom" import AdministratorLayout from "./layout/Administrator"; import ManageUserTypes, { loader as loadUsers } from "./pages/Administrator/ManageUserTypes"; import Login from "./pages/Authentication/Login"; +import ForgotPassword from "./pages/Authentication/ForgotPassword"; import Logout from "./pages/Authentication/Logout"; import InstitutionEditor, { loadInstitution } from "./pages/Institutions/InstitutionEditor"; import Institutions, { loadInstitutions } from "./pages/Institutions/Institutions"; @@ -49,6 +50,7 @@ function App() { children: [ { index: true, element: } /> }, { path: "login", element: }, + { path: "forgot-password", element: }, { path: "logout", element: } /> }, // Add the ViewTeamGrades route { diff --git a/src/pages/Authentication/ForgotPassword.tsx b/src/pages/Authentication/ForgotPassword.tsx new file mode 100644 index 00000000..401e01fe --- /dev/null +++ b/src/pages/Authentication/ForgotPassword.tsx @@ -0,0 +1,55 @@ +import React, {useState} from "react"; +import {useNavigate} from "react-router-dom"; +import axios from "axios"; +import { alertActions } from "../../store/slices/alertSlice"; +import { useDispatch } from "react-redux"; + +const ForgotPassword = () =>{ + + const [email, setEmail] = useState(''); + const dispatch = useDispatch(); + + const handleSubmit = async (e: React.MouseEvent) => { + + try { + console.log(email); + await axios.post("http://localhost:3002/password_resets", { email }); + dispatch( + alertActions.showAlert({ + variant: "success", + message: `A link to reset your password has been sent to your e-mail address.`, + }) + ); + } catch (error) { + dispatch( + alertActions.showAlert({ + variant: "danger", + message: `No account is associated with the e-mail address: "${email}". Please try again.`, + }) + ); + } + }; + + + return ( +
+
+

Forgotten Your Password?

+
+
+ Enter the e-mail address associated with your account +
+ setEmail(e.target.value)} + required + style={{marginTop: '5px', marginBottom: '5px', height: '20px', border: '1px solid black'}} + /> +
+ +
+ ); + +}; + +export default ForgotPassword; \ No newline at end of file From c160dd82cfd0e62e482a5ef87ca4947b32712dd3 Mon Sep 17 00:00:00 2001 From: galav12 Date: Mon, 24 Mar 2025 04:59:09 -0400 Subject: [PATCH 2/2] Added Reset Password Page --- src/App.tsx | 2 + src/pages/Authentication/ForgotPassword.tsx | 24 +++-- src/pages/Authentication/ResetPassword.tsx | 100 ++++++++++++++++++++ 3 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 src/pages/Authentication/ResetPassword.tsx diff --git a/src/App.tsx b/src/App.tsx index 5a248435..25f3c0e5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import AdministratorLayout from "./layout/Administrator"; import ManageUserTypes, { loader as loadUsers } from "./pages/Administrator/ManageUserTypes"; import Login from "./pages/Authentication/Login"; import ForgotPassword from "./pages/Authentication/ForgotPassword"; +import ResetPassword from "./pages/Authentication/ResetPassword"; import Logout from "./pages/Authentication/Logout"; import InstitutionEditor, { loadInstitution } from "./pages/Institutions/InstitutionEditor"; import Institutions, { loadInstitutions } from "./pages/Institutions/Institutions"; @@ -51,6 +52,7 @@ function App() { { index: true, element: } /> }, { path: "login", element: }, { path: "forgot-password", element: }, + { path: "password_edit/check_reset_url", element: }, { path: "logout", element: } /> }, // Add the ViewTeamGrades route { diff --git a/src/pages/Authentication/ForgotPassword.tsx b/src/pages/Authentication/ForgotPassword.tsx index 401e01fe..3ffff8d7 100644 --- a/src/pages/Authentication/ForgotPassword.tsx +++ b/src/pages/Authentication/ForgotPassword.tsx @@ -1,6 +1,5 @@ import React, {useState} from "react"; -import {useNavigate} from "react-router-dom"; -import axios from "axios"; +import axios, { AxiosError } from 'axios'; import { alertActions } from "../../store/slices/alertSlice"; import { useDispatch } from "react-redux"; @@ -12,8 +11,8 @@ const ForgotPassword = () =>{ const handleSubmit = async (e: React.MouseEvent) => { try { - console.log(email); - await axios.post("http://localhost:3002/password_resets", { email }); + await axios.post("http://localhost:3002/api/v1/password_resets", { email }); + dispatch( alertActions.showAlert({ variant: "success", @@ -21,12 +20,17 @@ const ForgotPassword = () =>{ }) ); } catch (error) { - dispatch( - alertActions.showAlert({ - variant: "danger", - message: `No account is associated with the e-mail address: "${email}". Please try again.`, - }) - ); + if (error instanceof AxiosError && error.response && error.response.data) { + const { error: errorMessage} = error.response.data; + if (errorMessage) { + dispatch( + alertActions.showAlert({ + variant: "danger", + message: errorMessage, + }) + ); + } + } } }; diff --git a/src/pages/Authentication/ResetPassword.tsx b/src/pages/Authentication/ResetPassword.tsx new file mode 100644 index 00000000..04123d4f --- /dev/null +++ b/src/pages/Authentication/ResetPassword.tsx @@ -0,0 +1,100 @@ +import React, { useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import axios, { AxiosError } from "axios"; +import { alertActions } from "../../store/slices/alertSlice"; +import { useDispatch } from "react-redux"; + +const ResetPassword = () => { + const location = useLocation(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const queryParams = new URLSearchParams(location.search); + const token = queryParams.get("token"); + + // Handle form submission + const handleSubmit = async (e: React.MouseEvent) => { + + + if (password.length < 6) { + dispatch( + alertActions.showAlert({ + variant: "danger", + message: "Password should be at least 6 letters long." + }) + ); + return; + } + else if (password !== confirmPassword) { + dispatch( + alertActions.showAlert({ + variant: "danger", + message: "Passwords do not match." + }) + ); + return; + } + else { + try { + // Send password reset request to the backend + await axios.put(`http://localhost:3002/api/v1/password_resets/${token}`, { + user: { password }, + }); + + navigate("/login") + + dispatch( + alertActions.showAlert({ + variant: "success", + message: `Password Successfully Updated`, + }) + ); + + } catch (error) { + if (error instanceof AxiosError && error.response && error.response.data) { + const { error: errorMessage} = error.response.data; + if (errorMessage) { + dispatch( + alertActions.showAlert({ + variant: "danger", + message: errorMessage, + }) + ); + } + } + } + } + }; + + return ( +
+
+

Reset Your Password

+
+ Password: +
+ setPassword(e.target.value)} + required + style={{marginTop: '5px', marginBottom: '5px', height: '20px', border: '1px solid black'}} + /> +
+ Confirm Password: +
+ setConfirmPassword(e.target.value)} + required + style={{marginTop: '5px', marginBottom: '5px', height: '20px', border: '1px solid black'}} + /> +
+ +
+ ); +}; + +export default ResetPassword;