refactor password change form

This commit is contained in:
f0x 2023-01-04 21:12:57 +00:00
parent f6369ccae1
commit ceba7bc3bc
7 changed files with 55 additions and 57 deletions

View file

@ -93,14 +93,14 @@ function Checkbox({label, field, ...inputProps}) {
} }
function Select({label, field, options, ...inputProps}) { function Select({label, field, options, ...inputProps}) {
const {onChange, value} = field; const {onChange, value, ref} = field;
return ( return (
<div className="form-field select"> <div className="form-field select">
<label> <label>
{label} {label}
<select <select
{...{onChange, value}} {...{onChange, value, ref}}
{...inputProps} {...inputProps}
> >
{options} {options}

View file

@ -23,8 +23,6 @@ const React = require("react");
module.exports = function useBoolInput({name, Name}, {defaultValue=false} = {}) { module.exports = function useBoolInput({name, Name}, {defaultValue=false} = {}) {
const [value, setValue] = React.useState(defaultValue); const [value, setValue] = React.useState(defaultValue);
console.log("bool", name, value, defaultValue);
function onChange(e) { function onChange(e) {
setValue(e.target.checked); setValue(e.target.checked);
} }

View file

@ -39,7 +39,6 @@ module.exports = function useTextInput({name, Name}, {validator, defaultValue=""
let res = validator(text); let res = validator(text);
setValid(res == ""); setValid(res == "");
textRef.current.setCustomValidity(res); textRef.current.setCustomValidity(res);
textRef.current.reportValidity();
} }
}, [text, textRef, validator]); }, [text, textRef, validator]);

View file

@ -18,6 +18,8 @@
"use strict"; "use strict";
const base = require("./base");
module.exports = { module.exports = {
unwrapRes(res) { unwrapRes(res) {
if (res.error != undefined) { if (res.error != undefined) {
@ -25,5 +27,17 @@ module.exports = {
} else { } else {
return res.data; return res.data;
} }
},
updateCacheOnMutation(queryName, arg = undefined) {
// https://redux-toolkit.js.org/rtk-query/usage/manual-cache-updates#pessimistic-updates
return {
onQueryStarted: (_, { dispatch, queryFulfilled}) => {
queryFulfilled.then(({data: newData}) => {
dispatch(base.util.updateQueryData(queryName, arg, (draft) => {
Object.assign(draft, newData);
}));
});
}
};
} }
}; };

View file

@ -18,17 +18,14 @@
"use strict"; "use strict";
// const Promise = require("bluebird"); const { updateCacheOnMutation } = require("./lib");
// const { unwrapRes } = require("./lib");
const base = require("./base"); const base = require("./base");
const endpoints = (build) => ({ const endpoints = (build) => ({
verifyCredentials: build.query({ verifyCredentials: build.query({
query: () => ({ query: () => ({
url: `/api/v1/accounts/verify_credentials` url: `/api/v1/accounts/verify_credentials`
}), })
providesTags: [{type: "User", id: "SELF"}]
}), }),
updateCredentials: build.mutation({ updateCredentials: build.mutation({
query: (formData) => ({ query: (formData) => ({
@ -37,7 +34,14 @@ const endpoints = (build) => ({
asForm: true, asForm: true,
body: formData body: formData
}), }),
invalidatesTags: [{type: "User", id: "SELF"}] ...updateCacheOnMutation("verifyCredentials")
}),
passwordChange: build.mutation({
query: (data) => ({
method: "POST",
url: `/api/v1/user/password_change`,
body: data
})
}) })
}); });

View file

@ -337,6 +337,12 @@ section.with-sidebar > div, section.with-sidebar > form {
} }
} }
form {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-field label { .form-field label {
font-weight: bold; font-weight: bold;
} }
@ -346,7 +352,6 @@ section.with-sidebar > div, section.with-sidebar > form {
display: flex; display: flex;
} }
span.form-info { span.form-info {
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;

View file

@ -112,56 +112,34 @@ function UserSettingsForm({source}) {
} }
function PasswordChange() { function PasswordChange() {
const dispatch = Redux.useDispatch(); const form = {
oldPassword: useTextInput("old_password"),
newPassword: useTextInput("old_password", {validator(val) {
if (val != "" && val == form.oldPassword.value) {
return "New password same as old password";
}
return "";
}})
};
const [errorMsg, setError] = React.useState(""); const verifyNewPassword = useTextInput("verifyNewPassword", {
const [statusMsg, setStatus] = React.useState(""); validator(val) {
if (val != "" && val != form.newPassword.value) {
const [oldPassword, setOldPassword] = React.useState(""); return "Passwords do not match";
const [newPassword, setNewPassword] = React.useState(""); }
const [newPasswordConfirm, setNewPasswordConfirm] = React.useState(""); return "";
function changePassword() {
if (newPassword !== newPasswordConfirm) {
setError("New password and confirm new password did not match!");
return;
} }
});
setStatus("PATCHing"); const [result, submitForm] = useFormSubmit(form, query.usePasswordChangeMutation());
setError("");
return Promise.try(() => {
let data = {
old_password: oldPassword,
new_password: newPassword
};
return dispatch(api.apiCall("POST", "/api/v1/user/password_change", data, "form"));
}).then(() => {
setStatus("Saved!");
setOldPassword("");
setNewPassword("");
setNewPasswordConfirm("");
}).catch((e) => {
setError(e.message);
setStatus("");
});
}
return ( return (
<> <form className="change-password" onSubmit={submitForm}>
<h1>Change password</h1> <h1>Change password</h1>
<div className="labelinput"> <TextInput type="password" field={form.oldPassword} label="Current password"/>
<label htmlFor="password">Current password</label> <TextInput type="password" field={form.newPassword} label="New password"/>
<input name="password" id="password" type="password" autoComplete="current-password" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} /> <TextInput type="password" field={verifyNewPassword} label="Confirm new password"/>
</div> <MutationButton text="Change password" result={result}/>
<div className="labelinput"> </form>
<label htmlFor="new-password">New password</label>
<input name="new-password" id="new-password" type="password" autoComplete="new-password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} />
</div>
<div className="labelinput">
<label htmlFor="confirm-new-password">Confirm new password</label>
<input name="confirm-new-password" id="confirm-new-password" type="password" autoComplete="new-password" value={newPasswordConfirm} onChange={(e) => setNewPasswordConfirm(e.target.value)} />
</div>
<Submit onClick={changePassword} label="Save new password" errorMsg={errorMsg} statusMsg={statusMsg}/>
</>
); );
} }