Skip to content

Commit

Permalink
Validate that email is not repeated
Browse files Browse the repository at this point in the history
  • Loading branch information
javierbrea committed Feb 10, 2019
1 parent c736e6e commit b365c46
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 88 deletions.
155 changes: 91 additions & 64 deletions src/components/user/User.js
Expand Up @@ -18,8 +18,8 @@ export class User extends Component {
constructor(props) {
super(props);
this.state = {
submitSuccess: false,
isNew: props.isNew
submitSuccess: props.fromCreation,
fromCreation: props.fromCreation
};

this.handleNameChange = this.handleNameChange.bind(this);
Expand All @@ -39,7 +39,8 @@ export class User extends Component {
...state,
name,
submitSuccess: false,
nameValid: this.props.isValidUserName(name)
nameValid: this.props.isValidUserName(name),
nameError: null
}));
}

Expand All @@ -49,7 +50,8 @@ export class User extends Component {
...state,
email,
submitSuccess: false,
emailValid: this.props.isValidUserEmail(email)
emailValid: this.props.isValidUserEmail(email),
emailError: null
}));
}

Expand Down Expand Up @@ -98,63 +100,20 @@ export class User extends Component {
return false;
}

handleSubmit(event) {
event.preventDefault();
const { name, email, password, role } = this.state;
this.setState(state => ({
...state,
submitSuccess: false
}));

this.props
.onSubmit(
{
name,
email,
password,
role
},
this.props.user._id // Or this.state.newUserId (if new)
)
.then(() => {
// TODO, receive here new user ID, set as "newUserId" in state
this.setState(state => ({
...state,
name: null,
nameValid: false,
email: null,
emailValid: false,
role: null,
roleValid: null,
passwordsValid: null,
submitSuccess: true,
isNew: false
}));
})
.catch(() => {
console.error("Error sending user");
});
}

handleCancel() {
event.preventDefault();
this.props.onCancel();
}

hasChangedName() {
return this.state.name && this.state.name.length;
}

hasValidName() {
return this.props.isValidUserName(this.state.name);
return this.props.isValidUserName(this.state.name) && !this.state.nameError;
}

hasChangedEmail() {
return this.state.email && this.state.email.length;
}

hasValidEmail() {
return this.props.isValidUserEmail(this.state.email);
return this.props.isValidUserEmail(this.state.email) && !this.state.emailError;
}

hasChangedRole() {
Expand All @@ -170,7 +129,7 @@ export class User extends Component {
}

submitEnabled() {
if (!this.state.isNew) {
if (!this.props.isNew) {
return (this.hasChangedPassword() || this.state.role) && !this.repeatedPasswordError();
}
return (
Expand All @@ -187,11 +146,21 @@ export class User extends Component {
}

handleNameBlur() {
if (this.hasChangedName() && !this.hasValidName()) {
this.setState(state => ({
...state,
nameError: NAME_NOT_VALID
}));
if (this.hasChangedName()) {
if (!this.hasValidName()) {
this.setState(state => ({
...state,
nameError: NAME_NOT_VALID
}));
} else {
this.props.isUserNameRepeated(this.state.name).then(isRepeated => {
this.setState(state => ({
...state,
nameValid: !isRepeated,
nameError: isRepeated ? "Name is repeated" : null
}));
});
}
} else {
this.setState(state => ({
...state,
Expand All @@ -201,11 +170,21 @@ export class User extends Component {
}

handleEmailBlur() {
if (this.hasChangedEmail() && !this.hasValidEmail()) {
this.setState(state => ({
...state,
emailError: EMAIL_NOT_VALID
}));
if (this.hasChangedEmail()) {
if (!this.hasValidEmail()) {
this.setState(state => ({
...state,
emailError: EMAIL_NOT_VALID
}));
} else {
this.props.isUserEmailRepeated(this.state.email).then(isRepeated => {
this.setState(state => ({
...state,
emailValid: !isRepeated,
emailError: isRepeated ? "Email is repeated" : null
}));
});
}
} else {
this.setState(state => ({
...state,
Expand All @@ -214,6 +193,50 @@ export class User extends Component {
}
}

handleSubmit(event) {
event.preventDefault();
const { name, email, password, role } = this.state;
this.setState(state => ({
...state,
submitSuccess: false
}));

this.props
.onSubmit(
{
name,
email,
password,
role
},
this.props.user._id
)
.then(() => {
if (!this.props.isNew) {
this.setState(state => ({
...state,
name: null,
nameValid: false,
email: null,
emailValid: false,
role: null,
roleValid: null,
passwordsValid: null,
submitSuccess: true,
fromCreation: false
}));
}
})
.catch(() => {
console.error("Error sending user");
});
}

handleCancel() {
event.preventDefault();
this.props.onCancel();
}

render() {
const {
user = {},
Expand All @@ -222,7 +245,8 @@ export class User extends Component {
roles,
currentUserIsAdmin,
submitLoading,
submitError
submitError,
isNew
} = this.props;
const {
submitSuccess,
Expand All @@ -232,7 +256,7 @@ export class User extends Component {
emailValid,
roleValid,
passwordsValid,
isNew
fromCreation
} = this.state;

const repeatedPasswordErrorMessage = passwordsValid ? (
Expand Down Expand Up @@ -344,8 +368,8 @@ export class User extends Component {
/>
<Message
success
header={this.props.isNew ? "Created" : "Modified"}
content={`User was successfully ${this.props.isNew ? "created" : "modified"}`}
header={fromCreation ? "Created" : "Modified"}
content={`User was successfully ${fromCreation ? "created" : "modified"}`}
/>
{nameField}
{emailField}
Expand Down Expand Up @@ -380,7 +404,10 @@ export class User extends Component {

User.propTypes = {
currentUserIsAdmin: PropTypes.bool,
fromCreation: PropTypes.bool,
isNew: PropTypes.bool,
isUserEmailRepeated: PropTypes.func,
isUserNameRepeated: PropTypes.func,
isValidUserEmail: PropTypes.func,
isValidUserName: PropTypes.func,
onCancel: PropTypes.func,
Expand Down
26 changes: 26 additions & 0 deletions src/data-sources/users/collection.js
Expand Up @@ -43,6 +43,32 @@ export const usersCollectionWithExtraData = new Selector(
[]
);

const exactSearchBy = (usersResults, { email, name }) => {
return usersResults.filter(user => {
let matchKeys = 0;
let matches = 0;
if (email) {
matchKeys++;
if (user.email === email) {
matches++;
}
}
if (name) {
matchKeys++;
if (user.name === name) {
matches++;
}
}
return matchKeys === matches;
});
};

export const usersCollectionExactFiltered = new Selector(
usersCollectionWithExtraData,
exactSearchBy,
[]
);

const searchBy = (usersResults, { search, showSystem }) => {
return usersResults.filter(user => {
if (!showSystem && user.isSystemRole) {
Expand Down
8 changes: 6 additions & 2 deletions src/data-sources/users/index.js
@@ -1,5 +1,9 @@
import { userMe, userMeWithAvatar, userMeIsAdmin } from "./me";
import { usersCollection, usersCollectionFilteredAndSorted } from "./collection";
import {
usersCollection,
usersCollectionExactFiltered,
usersCollectionFilteredAndSorted
} from "./collection";
import {
usersModels,
usersModelsWithExtraData,
Expand All @@ -20,7 +24,7 @@ usersModels.onChangeAny(changeDetails => {
});

export { userMe, userMeWithAvatar, userMeIsAdmin };
export { usersCollection, usersCollectionFilteredAndSorted };
export { usersCollection, usersCollectionExactFiltered, usersCollectionFilteredAndSorted };
export {
usersModels,
usersModelsWithExtraData,
Expand Down
69 changes: 62 additions & 7 deletions src/modules/users/Layouts.js
@@ -1,13 +1,16 @@
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { plugins } from "reactive-data-source";
import { pickBy, identity } from "lodash";

import { UsersContainer } from "./views/UsersContainer";
import { UsersList } from "./views/UsersList";

import { Component as UsersListTogglable } from "src/components/users-list-togglable";
import { UpdateUser } from "./views/UpdateUser";
import { CreateUser } from "./views/CreateUser";
import { usersCollection, usersCollectionExactFiltered } from "src/data-sources/users";

// LIST USERS

Expand Down Expand Up @@ -67,15 +70,67 @@ export const UpdateUserLayout = withRouter(UpdateUserLayoutBase);

// CREATE USER

export const CreateUserLayoutBase = ({ history }) => {
const onCancel = () => {
history.goBack();
};
return <CreateUser onCancel={onCancel} />;
};
export class CreateUserLayoutBase extends Component {
constructor(props) {
super(props);
this.state = {
newUserId: false
};
this.createUser = this.createUser.bind(this);
this.onCancel = this.onCancel.bind(this);
}

createUser(userData) {
return usersCollection.create(pickBy(userData, identity)).then(() => {
return usersCollectionExactFiltered
.filter({
name: userData.name
})
.read()
.then(results => {
this.setState(state => ({
...state,
newUserId: results[0]._id
}));
});
});
}

onCancel() {
this.props.history.goBack();
}

render() {
const { newUserId } = this.state;

return newUserId ? (
<UpdateUser id={newUserId} onCancel={this.onCancel} fromCreation={true} />
) : (
<CreateUser
onCancel={this.onCancel}
onSubmit={this.createUser}
submitLoading={this.props.createLoading}
submitError={this.props.createError}
isNew={true}
user={{}}
/>
);
}
}

CreateUserLayoutBase.propTypes = {
createError: PropTypes.instanceOf(Error),
createLoading: PropTypes.bool,
history: PropTypes.any
};

export const CreateUserLayout = withRouter(CreateUserLayoutBase);
export const mapDataSourceToProps = () => {
return {
createLoading: usersCollection.create.getters.loading,
createError: usersCollection.create.getters.error
};
};

export const CreateUserLayout = withRouter(
plugins.connect(mapDataSourceToProps)(CreateUserLayoutBase)
);

0 comments on commit b365c46

Please sign in to comment.