From 4645bde0250d4a9cb476c01757d9f119a4cf6a35 Mon Sep 17 00:00:00 2001 From: llpingll <94099281+llpingll@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:19:21 +0000 Subject: [PATCH 1/9] Allow all users to change password and handle expired sessions --- client/package-lock.json | 2 +- .../Pages/Account/components/PasswordPanel.jsx | 16 ++++++++++++++-- client/src/Pages/Account/index.jsx | 2 +- client/src/locales/en.json | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 7f5397b7f..282ab2611 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -38,7 +38,7 @@ "react-toastify": "^10.0.5", "recharts": "2.15.2", "redux-persist": "6.0.0", - "swr": "2.3.6", + "swr": "^2.3.6", "vite-plugin-svgr": "^4.2.0", "zod": "4.1.11" }, diff --git a/client/src/Pages/Account/components/PasswordPanel.jsx b/client/src/Pages/Account/components/PasswordPanel.jsx index f54c61a16..f06555dcd 100644 --- a/client/src/Pages/Account/components/PasswordPanel.jsx +++ b/client/src/Pages/Account/components/PasswordPanel.jsx @@ -11,6 +11,8 @@ import { useDispatch, useSelector } from "react-redux"; import { createToast } from "../../../Utils/toastUtils.jsx"; import { getTouchedFieldErrors } from "../../../Validation/error.js"; import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { clearAuthState } from "../../../Features/Auth/authSlice.js"; const defaultPasswordsState = { password: "", @@ -28,11 +30,12 @@ const PasswordPanel = () => { const theme = useTheme(); const dispatch = useDispatch(); const { t } = useTranslation(); + const navigate = useNavigate(); const SPACING_GAP = theme.spacing(12); //redux state - const { isLoading } = useSelector((state) => state.auth); + const { isLoading, authToken } = useSelector((state) => state.auth); const idToName = { "edit-current-password": "password", @@ -79,6 +82,16 @@ const PasswordPanel = () => { const handleSubmit = async (event) => { event.preventDefault(); + // Check for expired/invalid token before submitting + if (!authToken) { + createToast({ + body: t("passwordPanel.sessionExpired", "Your session has expired. Please log in again."), + }); + dispatch(clearAuthState()); + navigate("/login"); + return; + } + const { error } = newOrChangedCredentials.validate(localData, { abortEarly: false, context: { password: localData.newPassword }, @@ -105,7 +118,6 @@ const PasswordPanel = () => { confirm: "", }); } else { - // TODO: Check for other errors? createToast({ body: t( "passwordPanel.passwordInputIncorrect", diff --git a/client/src/Pages/Account/index.jsx b/client/src/Pages/Account/index.jsx index ae16ff9d7..3bf480336 100644 --- a/client/src/Pages/Account/index.jsx +++ b/client/src/Pages/Account/index.jsx @@ -85,7 +85,7 @@ const Account = ({ open = "profile" }) => { ))} - {user.role.includes("superadmin") && } + {!hideTeams && } diff --git a/client/src/locales/en.json b/client/src/locales/en.json index de02815b1..7e5eb7092 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -555,7 +555,7 @@ "enterNewPassword": "Enter your new password", "confirmNewPassword": "Confirm new password", "passwordRequirements": "New password must contain at least 8 characters and must have at least one uppercase letter, one lowercase letter, one number and one special character (!?@#$%^&*()-_=+[]{}|;:'\",./\\~`).", - "saving": "Saving..." + "sessionExpired": "Your session has expired, please log in again to continue." }, "emailSent": "Email sent successfully", "failedToSendEmail": "Failed to send email", From 14a08a303be5bb8142df51d382c2d523663dd10f Mon Sep 17 00:00:00 2001 From: llpingll <94099281+llpingll@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:45:34 +0000 Subject: [PATCH 2/9] Fix helper text to correctly use translation to display message not key --- client/src/Pages/Account/components/PasswordPanel.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/client/src/Pages/Account/components/PasswordPanel.jsx b/client/src/Pages/Account/components/PasswordPanel.jsx index f06555dcd..427c263ea 100644 --- a/client/src/Pages/Account/components/PasswordPanel.jsx +++ b/client/src/Pages/Account/components/PasswordPanel.jsx @@ -85,7 +85,10 @@ const PasswordPanel = () => { // Check for expired/invalid token before submitting if (!authToken) { createToast({ - body: t("passwordPanel.sessionExpired", "Your session has expired. Please log in again."), + body: t( + "passwordPanel.sessionExpired", + "Your session has expired. Please log in again." + ), }); dispatch(clearAuthState()); navigate("/login"); @@ -179,7 +182,7 @@ const PasswordPanel = () => { value={localData.password} onChange={handleChange} error={errors[idToName["edit-current-password"]] ? true : false} - helperText={errors[idToName["edit-current-password"]]} + helperText={t(errors[idToName["edit-current-password"]])} endAdornment={} flex={1} /> @@ -205,7 +208,7 @@ const PasswordPanel = () => { value={localData.newPassword} onChange={handleChange} error={errors[idToName["edit-new-password"]] ? true : false} - helperText={errors[idToName["edit-new-password"]]} + helperText={t(errors[idToName["edit-new-password"]])} endAdornment={} flex={1} /> @@ -231,7 +234,7 @@ const PasswordPanel = () => { value={localData.confirm} onChange={handleChange} error={errors[idToName["edit-confirm-password"]] ? true : false} - helperText={errors[idToName["edit-confirm-password"]]} + helperText={t(errors[idToName["edit-confirm-password"]])} endAdornment={} flex={1} /> From f564636cb435e6076f50f58fbf16c59c2adaa92c Mon Sep 17 00:00:00 2001 From: llpingll <94099281+llpingll@users.noreply.github.com> Date: Sun, 25 Jan 2026 19:11:25 +0000 Subject: [PATCH 3/9] Fix missingKey warning with helper text --- .../Account/components/PasswordPanel.jsx | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/client/src/Pages/Account/components/PasswordPanel.jsx b/client/src/Pages/Account/components/PasswordPanel.jsx index 427c263ea..1c365804f 100644 --- a/client/src/Pages/Account/components/PasswordPanel.jsx +++ b/client/src/Pages/Account/components/PasswordPanel.jsx @@ -182,7 +182,12 @@ const PasswordPanel = () => { value={localData.password} onChange={handleChange} error={errors[idToName["edit-current-password"]] ? true : false} - helperText={t(errors[idToName["edit-current-password"]])} + helperText={ + //Only call t() if there is an error to prevent unnecessary localization calls + errors[idToName["edit-current-password"]] + ? t(errors[idToName["edit-current-password"]]) + : "" + } endAdornment={} flex={1} /> @@ -208,7 +213,12 @@ const PasswordPanel = () => { value={localData.newPassword} onChange={handleChange} error={errors[idToName["edit-new-password"]] ? true : false} - helperText={t(errors[idToName["edit-new-password"]])} + helperText={ + //Only call t() if there is an error to prevent unnecessary localization calls + errors[idToName["edit-new-password"]] + ? t(errors[idToName["edit-new-password"]]) + : "" + } endAdornment={} flex={1} /> @@ -234,7 +244,12 @@ const PasswordPanel = () => { value={localData.confirm} onChange={handleChange} error={errors[idToName["edit-confirm-password"]] ? true : false} - helperText={t(errors[idToName["edit-confirm-password"]])} + helperText={ + //Only call t() if there is an error to prevent unnecessary localization calls + errors[idToName["edit-confirm-password"]] + ? t(errors[idToName["edit-confirm-password"]]) + : "" + } endAdornment={} flex={1} /> From 4ce4a124d640bf5809577dc9e44ee20817d4c1ec Mon Sep 17 00:00:00 2001 From: llpingll <94099281+llpingll@users.noreply.github.com> Date: Sun, 25 Jan 2026 22:57:36 +0000 Subject: [PATCH 4/9] Refactor PasswordPanel to pass navigation context via state to ForgotPassword using TextLink | Update ForgotPassword and CheckEmail to handle navigation context for Back links, ensuring users return to the correct page after password reset | Fix TextLink prop usage to avoid warnings by passing state and href correctly | Ensure error messages and navigation links use translation keys for localization --- client/src/Components/v1/TextLink/index.jsx | 4 +++- .../Account/components/PasswordPanel.jsx | 13 +++++++++++-- client/src/Pages/Auth/CheckEmail.jsx | 13 ++++++++++--- client/src/Pages/Auth/ForgotPassword.jsx | 19 ++++++++++++++----- client/src/locales/en.json | 3 +++ 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/client/src/Components/v1/TextLink/index.jsx b/client/src/Components/v1/TextLink/index.jsx index 83ae251a5..e226250a6 100644 --- a/client/src/Components/v1/TextLink/index.jsx +++ b/client/src/Components/v1/TextLink/index.jsx @@ -5,7 +5,7 @@ import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; import { Link as RouterLink } from "react-router-dom"; -const TextLink = ({ text, linkText, href, target = "_self" }) => { +const TextLink = ({ text, linkText, href, state, target = "_self" }) => { const theme = useTheme(); return ( @@ -19,6 +19,7 @@ const TextLink = ({ text, linkText, href, target = "_self" }) => { to={href} component={RouterLink} target={target} + state={state} > {linkText} @@ -30,6 +31,7 @@ TextLink.propTypes = { text: PropTypes.string, linkText: PropTypes.string, href: PropTypes.string, + state: PropTypes.object, target: PropTypes.string, }; diff --git a/client/src/Pages/Account/components/PasswordPanel.jsx b/client/src/Pages/Account/components/PasswordPanel.jsx index 1c365804f..7deb6dc8c 100644 --- a/client/src/Pages/Account/components/PasswordPanel.jsx +++ b/client/src/Pages/Account/components/PasswordPanel.jsx @@ -13,6 +13,8 @@ import { getTouchedFieldErrors } from "../../../Validation/error.js"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { clearAuthState } from "../../../Features/Auth/authSlice.js"; +import TextLink from "@/Components/v1/TextLink/index.jsx"; +import { useLocation } from "react-router-dom"; const defaultPasswordsState = { password: "", @@ -31,6 +33,7 @@ const PasswordPanel = () => { const dispatch = useDispatch(); const { t } = useTranslation(); const navigate = useNavigate(); + const location = useLocation(); const SPACING_GAP = theme.spacing(12); @@ -267,8 +270,15 @@ const PasswordPanel = () => { )} +