Skip to content

Commit 1e04345

Browse files
authored
Merge pull request #3185 from llpingll/change-password
feat: Allow all users to change password (not just admins)
2 parents 174c3cd + 278ced2 commit 1e04345

File tree

5 files changed

+51
-12
lines changed

5 files changed

+51
-12
lines changed

client/src/Components/v1/TextLink/index.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import PropTypes from "prop-types";
55
import { useTheme } from "@emotion/react";
66
import { Link as RouterLink } from "react-router-dom";
77

8-
const TextLink = ({ text, linkText, href, target = "_self" }) => {
8+
const TextLink = ({ text, linkText, href, state, target = "_self" }) => {
99
const theme = useTheme();
1010

1111
return (
@@ -19,6 +19,7 @@ const TextLink = ({ text, linkText, href, target = "_self" }) => {
1919
to={href}
2020
component={RouterLink}
2121
target={target}
22+
state={state}
2223
>
2324
{linkText}
2425
</Link>
@@ -30,6 +31,7 @@ TextLink.propTypes = {
3031
text: PropTypes.string,
3132
linkText: PropTypes.string,
3233
href: PropTypes.string,
34+
state: PropTypes.object,
3335
target: PropTypes.string,
3436
};
3537

client/src/Pages/Account/components/PasswordPanel.jsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { useDispatch, useSelector } from "react-redux";
1111
import { createToast } from "../../../Utils/toastUtils.jsx";
1212
import { getTouchedFieldErrors } from "../../../Validation/error.js";
1313
import { useTranslation } from "react-i18next";
14+
import { useNavigate, useLocation } from "react-router-dom";
15+
import { clearAuthState } from "../../../Features/Auth/authSlice.js";
16+
import TextLink from "@/Components/v1/TextLink/index.jsx";
1417

1518
const defaultPasswordsState = {
1619
password: "",
@@ -28,11 +31,13 @@ const PasswordPanel = () => {
2831
const theme = useTheme();
2932
const dispatch = useDispatch();
3033
const { t } = useTranslation();
34+
const navigate = useNavigate();
35+
const location = useLocation();
3136

3237
const SPACING_GAP = theme.spacing(12);
3338

3439
//redux state
35-
const { isLoading } = useSelector((state) => state.auth);
40+
const { isLoading, authToken } = useSelector((state) => state.auth);
3641

3742
const idToName = {
3843
"edit-current-password": "password",
@@ -79,6 +84,16 @@ const PasswordPanel = () => {
7984
const handleSubmit = async (event) => {
8085
event.preventDefault();
8186

87+
// Check for expired/invalid token before submitting
88+
if (!authToken) {
89+
createToast({
90+
body: "Your session has expired. Please log in again.",
91+
});
92+
dispatch(clearAuthState());
93+
navigate("/login");
94+
return;
95+
}
96+
8297
const { error } = newOrChangedCredentials.validate(localData, {
8398
abortEarly: false,
8499
context: { password: localData.newPassword },
@@ -249,8 +264,15 @@ const PasswordPanel = () => {
249264
)}
250265
<Stack
251266
direction="row"
252-
justifyContent="flex-end"
267+
justifyContent="space-between"
268+
alignItems="center"
253269
>
270+
<TextLink
271+
text={"Forgot password?"}
272+
linkText={"Reset password"}
273+
state={{ from: location.pathname }}
274+
href={"/forgot-password"}
275+
/>
254276
<Button
255277
variant="contained"
256278
color="accent"
@@ -263,7 +285,6 @@ const PasswordPanel = () => {
263285
}
264286
sx={{
265287
px: theme.spacing(12),
266-
mt: theme.spacing(20),
267288
}}
268289
>
269290
{t("commonSave", "Save")}

client/src/Pages/Account/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const Account = ({ open = "profile" }) => {
8585
))}
8686
</CustomTabList>
8787
<ProfilePanel />
88-
{user.role.includes("superadmin") && <PasswordPanel />}
88+
<PasswordPanel />
8989
{!hideTeams && <TeamPanel />}
9090
</TabContext>
9191
</Box>

client/src/Pages/Auth/CheckEmail.jsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Box, Button, Stack, Typography } from "@mui/material";
22
import { useEffect, useState } from "react";
33
import { useTheme } from "@emotion/react";
44
import { useDispatch } from "react-redux";
5-
import { useNavigate } from "react-router-dom";
5+
import { useNavigate, useLocation } from "react-router-dom";
66
import { createToast } from "../../Utils/toastUtils.jsx";
77
import { forgotPassword } from "../../Features/Auth/authSlice.js";
88
import { Trans, useTranslation } from "react-i18next";
@@ -17,6 +17,7 @@ const CheckEmail = () => {
1717
const navigate = useNavigate();
1818
const dispatch = useDispatch();
1919
const { t } = useTranslation();
20+
const location = useLocation();
2021

2122
const [email, setEmail] = useState();
2223
const [disabled, setDisabled] = useState(false);
@@ -83,7 +84,9 @@ const CheckEmail = () => {
8384

8485
const handleNavigate = () => {
8586
sessionStorage.removeItem("email");
86-
navigate("/login");
87+
location.state?.from
88+
? navigate(location.state.from, { replace: true })
89+
: navigate("/login");
8790
};
8891

8992
return (
@@ -219,7 +222,11 @@ const CheckEmail = () => {
219222
>
220223
<Typography display="inline-block">
221224
<Trans
222-
i18nKey="auth.forgotPassword.links.login"
225+
i18nKey={
226+
location.state?.from === "/account/password"
227+
? "Go back to <a>Account Password</a>"
228+
: "auth.forgotPassword.links.login"
229+
}
223230
components={{
224231
a: (
225232
<Typography

client/src/Pages/Auth/ForgotPassword.jsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from "react-redux";
55
import { forgotPassword } from "../../Features/Auth/authSlice.js";
66
import { useEffect, useState } from "react";
77
import { newOrChangedCredentials } from "../../Validation/validation.js";
8-
import { useNavigate } from "react-router-dom";
8+
import { useNavigate, useLocation } from "react-router-dom";
99
import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
1010
import Logo from "@/assets/icons/checkmate-icon.svg?react";
1111
import Background from "@/assets/Images/background-grid.svg?react";
@@ -18,6 +18,7 @@ const ForgotPassword = () => {
1818
const navigate = useNavigate();
1919
const dispatch = useDispatch();
2020
const theme = useTheme();
21+
const location = useLocation();
2122

2223
const { isLoading } = useSelector((state) => state.auth);
2324
const [errors, setErrors] = useState({});
@@ -51,7 +52,9 @@ const ForgotPassword = () => {
5152
const action = await dispatch(forgotPassword(form));
5253
if (action.payload.success) {
5354
sessionStorage.setItem("email", form.email);
54-
navigate("/check-email");
55+
navigate("/check-email", {
56+
state: location.state || { from: "/login" }, // Preserve original from, fallback to login
57+
});
5558
createToast({
5659
body: t("auth.forgotPassword.toasts.sent").replace("<email/>", form.email),
5760
});
@@ -86,7 +89,9 @@ const ForgotPassword = () => {
8689

8790
const handleNavigate = () => {
8891
sessionStorage.removeItem("email");
89-
navigate("/login");
92+
location.state?.from
93+
? navigate(location.state.from, { replace: true })
94+
: navigate("/login");
9095
};
9196

9297
return (
@@ -212,7 +217,11 @@ const ForgotPassword = () => {
212217
>
213218
<Typography display="inline-block">
214219
<Trans
215-
i18nKey="auth.forgotPassword.links.login"
220+
i18nKey={
221+
location.state?.from
222+
? "Go back to <a>Account Password</a>"
223+
: "auth.forgotPassword.links.login"
224+
}
216225
components={{
217226
a: (
218227
<Typography

0 commit comments

Comments
 (0)