Skip to content
Snippets Groups Projects
Unverified Commit 44470c66 authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Migrate UserPasswordForm to formik (#26228)

parent bec0524a
Branches
Tags
No related merge requests found
import { t } from "ttag";
import { UserApi, UtilApi } from "metabase/services";
import { createThunkAction } from "metabase/lib/redux";
export const UPDATE_PASSWORD = "UPDATE_PASSWORD";
export const VALIDATE_PASSWORD = "VALIDATE_PASSWORD";
export const validatePassword = createThunkAction(
VALIDATE_PASSWORD,
password => async () =>
UtilApi.password_check({
password,
}),
);
export const updatePassword = createThunkAction(
UPDATE_PASSWORD,
(user_id, password, old_password) => async () => {
await UserApi.update_password({
id: user_id,
password,
old_password,
});
return {
success: true,
data: {
message: t`Password updated successfully!`,
},
};
},
);
import { getIn } from "icepick";
import { UserApi, UtilApi } from "metabase/services";
import MetabaseSettings from "metabase/lib/settings";
import { User } from "metabase-types/api";
import { UserPasswordData } from "./types";
export const validatePassword = async (password: string) => {
const error = MetabaseSettings.passwordComplexityDescription(password);
if (error) {
return error;
}
try {
await UtilApi.password_check({ password });
} catch (error) {
return getIn(error, ["data", "errors", "password"]);
}
};
export const updatePassword = async (user: User, data: UserPasswordData) => {
await UserApi.update_password({
id: user.id,
password: data.password,
old_password: data.old_password,
});
};
import React, { useCallback } from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
import User from "metabase/entities/users";
const propTypes = {
user: PropTypes.object,
validatePassword: PropTypes.func,
updatePassword: PropTypes.func,
};
const UserPasswordForm = ({ user, validatePassword, updatePassword }) => {
const handleAsyncValidate = useCallback(
async ({ password }) => {
try {
await validatePassword(password);
} catch (error) {
return error.data.errors;
}
},
[validatePassword],
);
const handleSubmit = useCallback(
async ({ password, old_password }) => {
await updatePassword(user.id, password, old_password);
},
[user, updatePassword],
);
return (
<User.Form
form={User.forms.password}
submitTitle={t`Save`}
asyncValidate={handleAsyncValidate}
asyncBlurFields={["password"]}
onSubmit={handleSubmit}
/>
);
};
UserPasswordForm.propTypes = propTypes;
export default UserPasswordForm;
import React, { useCallback, useMemo } from "react";
import { t } from "ttag";
import _ from "underscore";
import * as Yup from "yup";
import Form from "metabase/core/components/Form";
import FormProvider from "metabase/core/components/FormProvider";
import FormInput from "metabase/core/components/FormInput";
import FormSubmitButton from "metabase/core/components/FormSubmitButton";
import FormErrorMessage from "metabase/core/components/FormErrorMessage";
import { User } from "metabase-types/api";
import { UserPasswordData } from "../../types";
const UserPasswordSchema = Yup.object({
old_password: Yup.string().required(t`required`),
password: Yup.string()
.required(t`required`)
.test(async (value = "", context) => {
const error = await context.options.context?.onValidatePassword(value);
return error ? context.createError({ message: error }) : true;
}),
password_confirm: Yup.string()
.required(t`required`)
.oneOf([Yup.ref("password")], t`passwords do not match`),
});
export interface UserPasswordFormProps {
user: User;
onValidatePassword: (password: string) => Promise<string | undefined>;
onSubmit: (user: User, data: UserPasswordData) => void;
}
const UserPasswordForm = ({
user,
onValidatePassword,
onSubmit,
}: UserPasswordFormProps): JSX.Element => {
const initialValues = useMemo(
() => ({
old_password: "",
password: "",
password_confirm: "",
}),
[],
);
const validationContext = useMemo(
() => ({ onValidatePassword: _.memoize(onValidatePassword) }),
[onValidatePassword],
);
const handleSubmit = useCallback(
(data: UserPasswordData) => {
return onSubmit(user, data);
},
[user, onSubmit],
);
return (
<FormProvider
initialValues={initialValues}
validationSchema={UserPasswordSchema}
validationContext={validationContext}
onSubmit={handleSubmit}
>
<Form>
<FormInput
name="old_password"
type="password"
title={t`Current password`}
placeholder={t`Shhh...`}
autoComplete="current-password"
fullWidth
/>
<FormInput
name="password"
type="password"
title={t`Create a password`}
placeholder={t`Shhh...`}
autoComplete="new-password"
fullWidth
/>
<FormInput
name="password_confirm"
type="password"
title={t`Confirm your password`}
placeholder={t`Shhh... but one more time so we get it right`}
autoComplete="new-password"
fullWidth
/>
<FormSubmitButton title={t`Save`} primary />
<FormErrorMessage />
</Form>
</FormProvider>
);
};
export default UserPasswordForm;
import { connect } from "react-redux";
import { getUser } from "metabase/selectors/user";
import { State } from "metabase-types/store";
import { updatePassword, validatePassword } from "../../actions";
import UserPasswordForm from "../../components/UserPasswordForm";
const mapStateToProps = state => ({
const mapStateToProps = (state: State) => ({
user: getUser(state),
onValidatePassword: validatePassword,
onSubmit: updatePassword,
});
const mapDispatchToProps = {
validatePassword,
updatePassword,
};
export default connect(mapStateToProps, mapDispatchToProps)(UserPasswordForm);
export default connect(mapStateToProps)(UserPasswordForm);
export interface UserPasswordData {
old_password: string;
password: string;
password_confirm: string;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment