|
| 1 | +import React from 'react'; |
| 2 | +import PropTypes from 'prop-types'; |
| 3 | +import { FormattedMessage } from 'react-intl'; |
| 4 | +import { reduxForm, Field, change } from 'redux-form'; |
| 5 | +import { Alert } from 'react-bootstrap'; |
| 6 | +import isEmail from 'validator/lib/isEmail'; |
| 7 | + |
| 8 | +import SubmitButton from '../SubmitButton'; |
| 9 | +import { validateRegistrationData } from '../../../redux/modules/users'; |
| 10 | +import { TextField, PasswordField, PasswordStrength } from '../Fields'; |
| 11 | + |
| 12 | +const CreateUserForm = ({ |
| 13 | + submitting, |
| 14 | + handleSubmit, |
| 15 | + onSubmit, |
| 16 | + dirty, |
| 17 | + submitFailed = false, |
| 18 | + submitSucceeded = false, |
| 19 | + asyncValidating, |
| 20 | + invalid, |
| 21 | + reset, |
| 22 | +}) => ( |
| 23 | + <div> |
| 24 | + <Field |
| 25 | + name="firstName" |
| 26 | + component={TextField} |
| 27 | + maxLength={100} |
| 28 | + required |
| 29 | + ignoreDirty |
| 30 | + label={<FormattedMessage id="app.editUserProfile.firstName" defaultMessage="Given Name:" />} |
| 31 | + /> |
| 32 | + |
| 33 | + <Field |
| 34 | + name="lastName" |
| 35 | + component={TextField} |
| 36 | + maxLength={255} |
| 37 | + required |
| 38 | + ignoreDirty |
| 39 | + label={<FormattedMessage id="app.editUserProfile.lastName" defaultMessage="Surname:" />} |
| 40 | + /> |
| 41 | + |
| 42 | + <Field |
| 43 | + name="email" |
| 44 | + component={TextField} |
| 45 | + autoComplete="off" |
| 46 | + maxLength={255} |
| 47 | + ignoreDirty |
| 48 | + label={<FormattedMessage id="app.changePasswordForm.email" defaultMessage="Email:" />} |
| 49 | + /> |
| 50 | + |
| 51 | + <Field |
| 52 | + name="password" |
| 53 | + component={PasswordField} |
| 54 | + autoComplete="off" |
| 55 | + ignoreDirty |
| 56 | + label={<FormattedMessage id="app.changePasswordForm.password" defaultMessage="New Password:" />} |
| 57 | + /> |
| 58 | + |
| 59 | + <Field |
| 60 | + name="passwordStrength" |
| 61 | + component={PasswordStrength} |
| 62 | + label={<FormattedMessage id="app.changePasswordForm.passwordStrength" defaultMessage="Password Strength:" />} |
| 63 | + /> |
| 64 | + |
| 65 | + <Field |
| 66 | + name="passwordConfirm" |
| 67 | + component={PasswordField} |
| 68 | + ignoreDirty |
| 69 | + label={<FormattedMessage id="app.changePasswordForm.passwordCheck" defaultMessage="New Password (again):" />} |
| 70 | + /> |
| 71 | + |
| 72 | + {submitFailed && ( |
| 73 | + <Alert bsStyle="danger"> |
| 74 | + <FormattedMessage id="generic.operationFailed" defaultMessage="Operation failed. Please try again later." /> |
| 75 | + </Alert> |
| 76 | + )} |
| 77 | + |
| 78 | + <div className="text-center"> |
| 79 | + <SubmitButton |
| 80 | + id="createUser" |
| 81 | + handleSubmit={handleSubmit(data => onSubmit(data).then(reset))} |
| 82 | + submitting={submitting} |
| 83 | + dirty={dirty} |
| 84 | + invalid={invalid} |
| 85 | + hasSucceeded={submitSucceeded} |
| 86 | + hasFailed={submitFailed} |
| 87 | + asyncValidating={asyncValidating} |
| 88 | + messages={{ |
| 89 | + submit: <FormattedMessage id="generic.create" defaultMessage="Create" />, |
| 90 | + submitting: <FormattedMessage id="generic.creating" defaultMessage="Creating..." />, |
| 91 | + success: <FormattedMessage id="generic.created" defaultMessage="Created" />, |
| 92 | + }} |
| 93 | + /> |
| 94 | + </div> |
| 95 | + </div> |
| 96 | +); |
| 97 | + |
| 98 | +CreateUserForm.propTypes = { |
| 99 | + handleSubmit: PropTypes.func.isRequired, |
| 100 | + onSubmit: PropTypes.func.isRequired, |
| 101 | + asyncValidate: PropTypes.func.isRequired, |
| 102 | + submitFailed: PropTypes.bool, |
| 103 | + submitSucceeded: PropTypes.bool, |
| 104 | + dirty: PropTypes.bool, |
| 105 | + submitting: PropTypes.bool, |
| 106 | + asyncValidating: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), |
| 107 | + invalid: PropTypes.bool, |
| 108 | + pristine: PropTypes.bool, |
| 109 | + reset: PropTypes.func, |
| 110 | +}; |
| 111 | + |
| 112 | +const validate = ({ firstName, lastName, email, password, passwordConfirm }) => { |
| 113 | + const errors = {}; |
| 114 | + |
| 115 | + if (!firstName) { |
| 116 | + errors.firstName = ( |
| 117 | + <FormattedMessage |
| 118 | + id="app.editUserProfile.validation.emptyFirstName" |
| 119 | + defaultMessage="First name cannot be empty." |
| 120 | + /> |
| 121 | + ); |
| 122 | + } |
| 123 | + |
| 124 | + if (firstName && firstName.length < 2) { |
| 125 | + errors.firstName = ( |
| 126 | + <FormattedMessage |
| 127 | + id="app.editUserProfile.validation.shortFirstName" |
| 128 | + defaultMessage="First name must contain at least 2 characters." |
| 129 | + /> |
| 130 | + ); |
| 131 | + } |
| 132 | + |
| 133 | + if (!lastName) { |
| 134 | + errors.lastName = ( |
| 135 | + <FormattedMessage id="app.editUserProfile.validation.emptyLastName" defaultMessage="Last name cannot be empty." /> |
| 136 | + ); |
| 137 | + } |
| 138 | + |
| 139 | + if (lastName && lastName.length < 2) { |
| 140 | + errors.lastName = ( |
| 141 | + <FormattedMessage |
| 142 | + id="app.editUserProfile.validation.shortLastName" |
| 143 | + defaultMessage="Last name must contain at least 2 characters." |
| 144 | + /> |
| 145 | + ); |
| 146 | + } |
| 147 | + |
| 148 | + if (email && isEmail(email) === false) { |
| 149 | + errors.email = ( |
| 150 | + <FormattedMessage |
| 151 | + id="app.editUserProfile.validation.emailNotValid" |
| 152 | + defaultMessage="E-mail address is not valid." |
| 153 | + /> |
| 154 | + ); |
| 155 | + } else if (!email) { |
| 156 | + errors.email = ( |
| 157 | + <FormattedMessage |
| 158 | + id="app.editUserProfile.validation.emptyEmail" |
| 159 | + defaultMessage="E-mail address cannot be empty." |
| 160 | + /> |
| 161 | + ); |
| 162 | + } |
| 163 | + |
| 164 | + if (!password) { |
| 165 | + errors.password = ( |
| 166 | + <FormattedMessage |
| 167 | + id="app.createUserForm.validation.emptyPassword" |
| 168 | + defaultMessage="The password cannot be empty." |
| 169 | + /> |
| 170 | + ); |
| 171 | + } |
| 172 | + |
| 173 | + if (!passwordConfirm) { |
| 174 | + errors.passwordConfirm = ( |
| 175 | + <FormattedMessage |
| 176 | + id="app.createUserForm.validation.emptyPassword" |
| 177 | + defaultMessage="The password cannot be empty." |
| 178 | + /> |
| 179 | + ); |
| 180 | + } |
| 181 | + |
| 182 | + if (password !== passwordConfirm) { |
| 183 | + errors.passwordConfirm = ( |
| 184 | + <FormattedMessage |
| 185 | + id="app.editUserProfile.validation.passwordsDontMatch" |
| 186 | + defaultMessage="Passwords do not match." |
| 187 | + /> |
| 188 | + ); |
| 189 | + } |
| 190 | + |
| 191 | + return errors; |
| 192 | +}; |
| 193 | + |
| 194 | +const asyncValidate = ({ email, password = '' }, dispatch) => { |
| 195 | + if (password === '') { |
| 196 | + dispatch(change('edit-user-profile', 'passwordStrength', null)); |
| 197 | + return Promise.resolve(); |
| 198 | + } |
| 199 | + |
| 200 | + return new Promise((resolve, reject) => |
| 201 | + dispatch(validateRegistrationData(email, password)) |
| 202 | + .then(res => res.value) |
| 203 | + .then(({ usernameIsFree, passwordScore }) => { |
| 204 | + var errors = {}; |
| 205 | + if (!usernameIsFree) { |
| 206 | + errors['email'] = ( |
| 207 | + <FormattedMessage |
| 208 | + id="app.createUserForm.validation.emailTaken" |
| 209 | + defaultMessage="This email address is already taken by someone else." |
| 210 | + /> |
| 211 | + ); |
| 212 | + } |
| 213 | + |
| 214 | + dispatch(change('edit-user-profile', 'passwordStrength', passwordScore)); |
| 215 | + |
| 216 | + if (Object.keys(errors).length > 0) { |
| 217 | + throw errors; |
| 218 | + } |
| 219 | + }) |
| 220 | + .then(resolve()) |
| 221 | + .catch(errors => reject(errors)) |
| 222 | + ); |
| 223 | +}; |
| 224 | + |
| 225 | +export default reduxForm({ |
| 226 | + form: 'edit-user-profile', |
| 227 | + validate, |
| 228 | + asyncValidate, |
| 229 | + asyncBlurFields: ['email', 'password', 'passwordConfirm'], |
| 230 | + enableReinitialize: true, |
| 231 | + keepDirtyOnReinitialize: false, |
| 232 | +})(CreateUserForm); |
0 commit comments