Connected password form to redux

This commit is contained in:
Daniel Cojocea committed 2024-07-10 18:26:05 -04:00
1 parent 9277a37559
commit 682b7b4d06
4 files changed
+40 -24

No files matched your search

@@ -6,6 +6,8 @@ import ButtonSpinner from "../../ButtonSpinner";
import PasswordTextField from "../../TextFields/Password/PasswordTextField";
import { editPasswordValidation } from "../../../Validation/validation";
import Alert from "../../Alert";
import { update } from "../../../Features/Auth/authSlice";
import { useDispatch, useSelector } from "react-redux";
/**
* PasswordPanel component manages the form for editing password.
@@ -15,24 +17,24 @@ import Alert from "../../Alert";
const PasswordPanel = () => {
const theme = useTheme();
//TODO - use redux loading state
//!! - currently all loading buttons are tied to the same state
const [isLoading, setIsLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const dispatch = useDispatch();
//redux state
const { authToken, isLoading } = useSelector((state) => state.auth);
//for testing, will tweak when I implement redux slice
const idToName = {
"edit-current-password": "password",
//TBD - form field naming
"edit-new-password": "newpassword",
"edit-new-password": "newPassword",
"edit-confirm-password": "confirm",
};
const [localData, setLocalData] = useState({
password: "",
newpassword: "",
newPassword: "",
confirm: "",
});
const [errors, setErrors] = useState({});
const [showPassword, setShowPassword] = useState(false); // password visibility
const handleChange = (event) => {
const { value, id } = event.target;
@@ -44,7 +46,7 @@ const PasswordPanel = () => {
const validation = editPasswordValidation.validate(
{ [name]: value },
{ abortEarly: false, context: { newpassword: localData.newpassword } }
{ abortEarly: false, context: { newPassword: localData.newPassword } }
);
setErrors((prev) => {
@@ -59,13 +61,12 @@ const PasswordPanel = () => {
});
};
const handleSubmit = (event) => {
const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
const { error } = editPasswordValidation.validate(localData, {
abortEarly: false,
context: { newpassword: localData.newpassword },
context: { newPassword: localData.newPassword },
});
if (error) {
@@ -74,14 +75,21 @@ const PasswordPanel = () => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
setIsLoading(false);
} else {
//TODO - submit logic
const action = await dispatch(update({ authToken, localData }));
if (action.payload.success) {
// TODO: Add toast/notification for password update success
setLocalData({
password: "",
newPassword: "",
confirm: "",
});
} else {
// TODO: Add toast/notification for password update rejected
// TODO: Check for other errors?
setErrors({ password: "*" + action.payload.msg + "." });
}
}
setTimeout(() => {
setIsLoading(false);
}, 2000);
};
return (
@@ -111,6 +119,7 @@ const PasswordPanel = () => {
autoComplete="current-password"
visibility={showPassword}
setVisibility={setShowPassword}
value={localData.password}
onChange={handleChange}
error={errors[idToName["edit-current-password"]] ? true : false}
/>
@@ -135,6 +144,7 @@ const PasswordPanel = () => {
autoComplete="new-password"
visibility={showPassword}
setVisibility={setShowPassword}
value={localData.newPassword}
onChange={handleChange}
error={errors[idToName["edit-new-password"]] ? true : false}
/>
@@ -159,6 +169,7 @@ const PasswordPanel = () => {
autoComplete="new-password"
visibility={showPassword}
setVisibility={setShowPassword}
value={localData.confirm}
onChange={handleChange}
error={errors[idToName["edit-confirm-password"]] ? true : false}
/>
@@ -191,7 +202,7 @@ const PasswordPanel = () => {
loadingText="Saving..."
disabled={Object.keys(errors).length !== 0 && true}
sx={{
paddingX: "40px",
paddingX: theme.gap.xl,
}}
/>
</Box>
@@ -10,6 +10,7 @@ import Visibility from "@mui/icons-material/Visibility";
/**
* @component
* @param {Object} props
* @param {string} props.value - The current value of the text field
* @param {function} props.onChange - The function to call when the text field changes (optional)
* @param {string} props.id - Unique ID for the text field (optional)
* @param {string} props.label - The label text displayed above the text field (optional)
@@ -23,6 +24,7 @@ import Visibility from "@mui/icons-material/Visibility";
* @returns {JSX.Element} - Renders the password text field component with dynamic icon display
*/
const PasswordTextField = ({
value,
onChange,
id,
label = "Password",
@@ -53,6 +55,7 @@ const PasswordTextField = ({
<div className="password-text-field-title">{label}</div>
<div className="password-text-field">
<TextField
value={value}
onChange={onChange}
type={visibility ? "text" : "password"}
autoComplete={autoComplete}
+4 -2
View File
@@ -47,8 +47,10 @@ export const update = createAsyncThunk(
await new Promise((resolve) => setTimeout(resolve, 1500));
const fd = new FormData();
fd.append("firstname", form.firstname);
fd.append("lastname", form.lastname);
form.firstname && fd.append("firstname", form.firstname);
form.lastname && fd.append("lastname", form.lastname);
form.password && fd.append("password", form.password);
form.newPassword && fd.append("newPassword", form.newPassword);
if (form.file && form.file !== "") {
const imageResult = await axiosInstance.get(form.file, {
responseType: "blob",
+3 -3
View File
@@ -119,7 +119,7 @@ const passwordSchema = joi
const editPasswordValidation = joi.object({
// TBD - validation for current password ?
password: passwordSchema,
newpassword: passwordSchema,
newPassword: passwordSchema,
confirm: joi
.string()
.trim()
@@ -127,8 +127,8 @@ const editPasswordValidation = joi.object({
"string.empty": "*Password confirmation is required.",
})
.custom((value, helpers) => {
const { newpassword } = helpers.prefs.context;
if (value !== newpassword) {
const { newPassword } = helpers.prefs.context;
if (value !== newPassword) {
return helpers.message("*Passwords do not match.");
}
return value;