Skip to content

Commit

Permalink
Merge pull request #1282 from peterpardo/fix/password-validation-on-a…
Browse files Browse the repository at this point in the history
…ccount

fix: Password validation on account
  • Loading branch information
ajhollid authored Dec 5, 2024
2 parents cedc441 + 6fa26ff commit 95d832e
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 22 deletions.
54 changes: 33 additions & 21 deletions Client/src/Components/TabPanels/Account/PasswordPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import Alert from "../../Alert";
import { update } from "../../../Features/Auth/authSlice";
import { useDispatch, useSelector } from "react-redux";
import { createToast } from "../../../Utils/toastUtils";
import { getTouchedFieldErrors } from "../../../Validation/error";

const defaultPasswordsState = {
password: "",
newPassword: "",
confirm: "",
};

/**
* PasswordPanel component manages the form for editing password.
Expand All @@ -30,35 +37,40 @@ const PasswordPanel = () => {
"edit-confirm-password": "confirm",
};

const [localData, setLocalData] = useState({
password: "",
newPassword: "",
confirm: "",
const [localData, setLocalData] = useState(defaultPasswordsState);
const [errors, setErrors] = useState(defaultPasswordsState);
const [touchedFields, setTouchedFields] = useState({
password: false,
newPassword: false,
confirm: false,
});
const [errors, setErrors] = useState({});

const handleChange = (event) => {
const { value, id } = event.target;
const name = idToName[id];
setLocalData((prev) => ({
...prev,

const updatedData = {
...localData,
[name]: value,
}));
};
const updatedTouchedFields = {
...touchedFields,
[name]: true,
};

const validation = credentials.validate(
{ [name]: value },
{ abortEarly: false, context: { password: localData.newPassword } }
{ ...updatedData },
{ abortEarly: false, context: { password: updatedData.newPassword } }
);

setErrors((prev) => {
const updatedErrors = { ...prev };
const updatedErrors = getTouchedFieldErrors(validation, updatedTouchedFields);

if (validation.error) {
updatedErrors[name] = validation.error.details[0].message;
} else {
delete updatedErrors[name];
}
return updatedErrors;
});
if (!touchedFields[name]) {
setTouchedFields(updatedTouchedFields);
}

setLocalData(updatedData);
setErrors(updatedErrors);
};

const handleSubmit = async (event) => {
Expand Down Expand Up @@ -220,8 +232,8 @@ const PasswordPanel = () => {
loading={isLoading}
loadingIndicator="Saving..."
disabled={
Object.keys(errors).length !== 0 ||
Object.values(localData).filter((value) => value !== "").length === 0
Object.keys(errors).length > 0 ||
Object.values(localData).filter((value) => value === "").length > 0
}
sx={{
px: theme.spacing(12),
Expand Down
27 changes: 26 additions & 1 deletion Client/src/Validation/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,31 @@ const buildErrors = (prev, id, error) => {
return updatedErrors;
};

/**
* Processes Joi validation errors and returns a filtered object of error messages for fields that have been touched.
*
* @param {Object} validation - The Joi validation result object.
* @param {Object} validation.error - The error property of the validation result containing details of validation failures.
* @param {Object[]} validation.error.details - An array of error details from the Joi validation. Each item contains information about the path and the message.
* @param {Object} touchedErrors - An object representing which fields have been interacted with. Keys are field IDs (field names), and values are booleans indicating whether the field has been touched.
* @returns {Object} - An object where keys are the field IDs (if they exist in `touchedErrors` and are in the error details) and values are their corresponding error messages.
*/
const getTouchedFieldErrors = (validation, touchedErrors) => {
let newErrors = {};

if (validation?.error) {
newErrors = validation.error.details.reduce((errors, detail) => {
const fieldId = detail.path[0];
if (touchedErrors[fieldId] && !(fieldId in errors)) {
errors[fieldId] = detail.message;
}
return errors;
}, {});
}

return newErrors;
};

const hasValidationErrors = (form, validation, setErrors) => {
const { error } = validation.validate(form, {
abortEarly: false,
Expand Down Expand Up @@ -53,4 +78,4 @@ const hasValidationErrors = (form, validation, setErrors) => {
}
return false;
};
export { buildErrors, hasValidationErrors };
export { buildErrors, hasValidationErrors, getTouchedFieldErrors };

0 comments on commit 95d832e

Please sign in to comment.