11import React from 'react' ;
22import PropTypes from 'prop-types' ;
33import { FormattedMessage } from 'react-intl' ;
4+ import { Table } from 'react-bootstrap' ;
45import { reduxForm , Field , change } from 'redux-form' ;
56import isEmail from 'validator/lib/isEmail.js' ;
67
78import SubmitButton from '../SubmitButton' ;
89import Callout from '../../widgets/Callout' ;
10+ import Explanation from '../../widgets/Explanation' ;
11+ import UsersName from '../../Users/UsersName' ;
12+ import { WarningIcon } from '../../icons' ;
913import { validateRegistrationData } from '../../../redux/modules/users.js' ;
10- import { TextField , PasswordField , PasswordStrength } from '../Fields' ;
14+ import { TextField , PasswordField , PasswordStrength , CheckboxField } from '../Fields' ;
1115
1216const CreateUserForm = ( {
17+ matchingUsers,
1318 submitting,
1419 handleSubmit,
1520 onSubmit,
@@ -19,6 +24,7 @@ const CreateUserForm = ({
1924 asyncValidating,
2025 invalid,
2126 reset,
27+ change,
2228} ) => (
2329 < div >
2430 < Field
@@ -69,6 +75,54 @@ const CreateUserForm = ({
6975 label = { < FormattedMessage id = "app.changePasswordForm.passwordCheck" defaultMessage = "New Password (again):" /> }
7076 />
7177
78+ { matchingUsers && matchingUsers . length > 0 && (
79+ < >
80+ < hr />
81+ < h5 >
82+ < WarningIcon className = "text-warning" gapRight = { 2 } />
83+ < FormattedMessage
84+ id = "app.inviteUserForm.matchingUsers"
85+ defaultMessage = "There are existing users of the same name"
86+ />
87+ :
88+ </ h5 >
89+
90+ < Table bordered >
91+ < tbody >
92+ { matchingUsers . map ( user => (
93+ < tr key = { user . id } >
94+ < td >
95+ < UsersName { ...user } showEmail = "full" showExternalIdentifiers showRoleIcon />
96+ </ td >
97+ </ tr >
98+ ) ) }
99+ </ tbody >
100+ </ Table >
101+
102+ < Field
103+ name = "ignoreNameCollision"
104+ component = { CheckboxField }
105+ onOff
106+ label = {
107+ < span >
108+ < FormattedMessage
109+ id = "app.inviteUserForm.ignoreNameCollision"
110+ defaultMessage = "The user I am inviting does not match any of the existing users"
111+ />
112+ < Explanation id = "ignoreNameCollisionExplanation" >
113+ < FormattedMessage
114+ id = "app.inviteUserForm.ignoreNameCollisionExplanation"
115+ defaultMessage = "Please, make sure the listed students are not the same person as the one you are inviting to prevent duplicate accounts in the system."
116+ />
117+ </ Explanation >
118+ </ span >
119+ }
120+ />
121+ </ >
122+ ) }
123+
124+ < hr />
125+
72126 { submitFailed && (
73127 < Callout variant = "danger" >
74128 < FormattedMessage id = "generic.operationFailed" defaultMessage = "Operation failed. Please try again later." />
@@ -78,7 +132,17 @@ const CreateUserForm = ({
78132 < div className = "text-center" >
79133 < SubmitButton
80134 id = "createUser"
81- handleSubmit = { handleSubmit ( data => onSubmit ( data ) . then ( reset ) ) }
135+ handleSubmit = { handleSubmit ( data =>
136+ onSubmit ( data ) . then ( success => {
137+ if ( success ) {
138+ reset ( ) ;
139+ } else {
140+ // a hack so the change takes place after the whole submit process is completed
141+ window . setTimeout ( ( ) => change ( 'ignoreNameCollision' , false ) , 0 ) ;
142+ }
143+ } )
144+ ) }
145+ resetTimeout = { 0 }
82146 submitting = { submitting }
83147 dirty = { dirty }
84148 invalid = { invalid }
@@ -88,14 +152,14 @@ const CreateUserForm = ({
88152 messages = { {
89153 submit : < FormattedMessage id = "generic.create" defaultMessage = "Create" /> ,
90154 submitting : < FormattedMessage id = "generic.creating" defaultMessage = "Creating..." /> ,
91- success : < FormattedMessage id = "generic.created" defaultMessage = "Created" /> ,
92155 } }
93156 />
94157 </ div >
95158 </ div >
96159) ;
97160
98161CreateUserForm . propTypes = {
162+ matchingUsers : PropTypes . array ,
99163 handleSubmit : PropTypes . func . isRequired ,
100164 onSubmit : PropTypes . func . isRequired ,
101165 asyncValidate : PropTypes . func . isRequired ,
@@ -107,9 +171,13 @@ CreateUserForm.propTypes = {
107171 invalid : PropTypes . bool ,
108172 pristine : PropTypes . bool ,
109173 reset : PropTypes . func ,
174+ change : PropTypes . func ,
110175} ;
111176
112- const validate = ( { firstName, lastName, email, password, passwordConfirm } ) => {
177+ const validate = (
178+ { firstName, lastName, email, password, passwordConfirm, ignoreNameCollision } ,
179+ { matchingUsers }
180+ ) => {
113181 const errors = { } ;
114182
115183 if ( ! firstName ) {
@@ -188,40 +256,40 @@ const validate = ({ firstName, lastName, email, password, passwordConfirm }) =>
188256 ) ;
189257 }
190258
259+ if ( matchingUsers && matchingUsers . length > 0 && ! ignoreNameCollision ) {
260+ errors . ignoreNameCollision = (
261+ < FormattedMessage
262+ id = "app.inviteUserForm.validation.ignoreNameCollision"
263+ defaultMessage = "Please check the list of existing users and confirm that the invited user is a new user."
264+ />
265+ ) ;
266+ }
191267 return errors ;
192268} ;
193269
194270const asyncValidate = ( { email, password = '' } , dispatch ) => {
195- if ( password === '' ) {
196- dispatch ( change ( 'create-user' , 'passwordStrength' , null ) ) ;
197- return Promise . resolve ( ) ;
271+ if ( ! password ) {
272+ dispatch ( change ( 'create-user' , 'passwordStrength' , undefined ) ) ;
198273 }
199274
200- return new Promise ( ( resolve , reject ) =>
201- dispatch ( validateRegistrationData ( email , password ) )
202- . then ( res => res . value )
203- . then ( ( { usernameIsFree, passwordScore } ) => {
204- const errors = { } ;
205- if ( ! usernameIsFree ) {
206- errors . email = (
275+ return dispatch ( validateRegistrationData ( email , password ) )
276+ . then ( res => res . value )
277+ . then ( ( { usernameIsFree, passwordScore } ) => {
278+ dispatch ( change ( 'create-user' , 'passwordStrength' , passwordScore ) ) ;
279+
280+ if ( ! usernameIsFree ) {
281+ const errors = {
282+ email : (
207283 < FormattedMessage
208284 id = "app.createUserForm.validation.emailTaken"
209285 defaultMessage = "This email address is already taken by someone else."
210286 />
211- ) ;
212- }
213-
214- dispatch ( change ( 'create-user' , 'passwordStrength' , passwordScore ) ) ;
215-
216- if ( Object . keys ( errors ) . length > 0 ) {
217- throw errors ;
218- }
219- } )
220- . then ( resolve ( ) )
221- . catch ( errors => reject ( errors ) )
222- ) ;
287+ ) ,
288+ } ;
289+ throw errors ;
290+ }
291+ } ) ;
223292} ;
224-
225293export default reduxForm ( {
226294 form : 'create-user' ,
227295 validate,
0 commit comments