diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index f590fce12..c48894cc5 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -1,14 +1,21 @@ -export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'; -export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'; -export const USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE'; -export const USER_FACEBOOK_LOGIN_REQUEST = 'USER_FACEBOOK_LOGIN_REQUEST'; -export const USER_FACEBOOK_SUCCESS = 'USER_FACEBOOK_SUCCESS'; -export const USER_FACEBOOK_FAILURE = 'USER_FACEBOOK_FAILURE'; -export const USER_LOGOUT_REQUEST = 'USER_LOGOUT_REQUEST'; -export const USER_LOGOUT_SUCCESS = 'USER_LOGOUT_SUCCESS'; -export const USER_LOGOUT_FAILURE = 'USER_LOGOUT_FAILURE'; +export const CHANGE_VIEW = 'CHANGE_VIEW'; +export const CLEAN_STATE = 'CLEAN_STATE'; export const SHOW_SIGNIN_DIALOG = 'SHOW_SIGNIN_DIALOG'; export const HIDE_SIGNIN_DIALOG = 'HIDE_SIGNIN_DIALOG'; -export const CHANGE_VIEW = 'CHANGE_VIEW'; +export const FETCH_SIGNUP_REQUEST = 'FETCH_SIGNUP_REQUEST'; +export const FETCH_SIGNUP_FAILURE = 'FETCH_SIGNUP_FAILURE'; +export const FETCH_SIGNUP_SUCCESS = 'FETCH_SIGNUP_SUCCESS'; + +export const FETCH_SIGNIN_REQUEST = 'FETCH_SIGNIN_REQUEST'; +export const FETCH_SIGNIN_FAILURE = 'FETCH_SIGNIN_FAILURE'; +export const FETCH_SIGNIN_SUCCESS = 'FETCH_SIGNIN_SUCCESS'; + +export const FETCH_SIGNIN_FACEBOOK_REQUEST = 'FETCH_SIGNIN_FACEBOOK_REQUEST'; +export const FETCH_SIGNIN_FACEBOOK_FAILURE = 'FETCH_SIGNIN_FACEBOOK_FAILURE'; +export const FETCH_SIGNIN_FACEBOOK_SUCCESS = 'FETCH_SIGNIN_FACEBOOK_SUCCESS'; + +export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST'; +export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS'; +export const FETCH_FORGOT_PASSWORD_FAILURE = 'FETCH_FORGOT_PASSWORD_FAILURE'; diff --git a/client/coral-framework/helpers/error.js b/client/coral-framework/helpers/error.js index 6ae79a996..2674bc55b 100644 --- a/client/coral-framework/helpers/error.js +++ b/client/coral-framework/helpers/error.js @@ -1,4 +1,6 @@ export default { email: 'Not a valid E-Mail', - password: 'Password must be at least 8 characters' + password: 'Password must be at least 8 characters', + username: 'Username is too short', + confirmPassword: 'Passwords do not match' }; diff --git a/client/coral-framework/helpers/validate.js b/client/coral-framework/helpers/validate.js index e69de29bb..9fda4b1c2 100644 --- a/client/coral-framework/helpers/validate.js +++ b/client/coral-framework/helpers/validate.js @@ -0,0 +1,6 @@ +export default { + email: email => (/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)), + password: pass => (/^(?=.{8,}).*$/.test(pass)), + confirmPassword: () => true, + username: username => (/^(?=.{3,}).*$/.test(username)) +}; diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 1cc67d192..5e54d70b6 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -3,11 +3,16 @@ import {Map} from 'immutable'; import { SHOW_SIGNIN_DIALOG, HIDE_SIGNIN_DIALOG, - CHANGE_VIEW + CHANGE_VIEW, + CLEAN_STATE, + FETCH_SIGNIN_REQUEST, + FETCH_SIGNIN_FAILURE, + FETCH_SIGNIN_SUCCESS } from '../constants/auth'; const initialState = Map({ auth: Map(), + isLoading: false, loggedIn: false, error: '', user: {}, @@ -15,17 +20,29 @@ const initialState = Map({ view: 'SIGNIN' }); -export default function community (state = initialState, action) { +export const getEmail = state => state.auth.formData.email; + +export default function auth (state = initialState, action) { switch (action.type) { case SHOW_SIGNIN_DIALOG : return state .set('showSignInDialog', true); case HIDE_SIGNIN_DIALOG : - return state - .set('showSignInDialog', false); + return initialState; case CHANGE_VIEW : return state .set('view', action.view); + case CLEAN_STATE: + return initialState; + case FETCH_SIGNIN_REQUEST: + return state + .set('isLoading', true); + case FETCH_SIGNIN_FAILURE: + return state + .set('isLoading', false); + case FETCH_SIGNIN_SUCCESS: + return state + .set('isLoading', false); default : return state; } diff --git a/client/coral-sign-in/components/Alert.js b/client/coral-sign-in/components/Alert.js index e69de29bb..019c3456a 100644 --- a/client/coral-sign-in/components/Alert.js +++ b/client/coral-sign-in/components/Alert.js @@ -0,0 +1,13 @@ +import React from 'react'; +import styles from './styles.css'; + +const Alert = ({cStyle = 'error', children, className, ...props}) => ( +
+ {children} +
+); + +export default Alert; diff --git a/client/coral-sign-in/components/ForgotContent.js b/client/coral-sign-in/components/ForgotContent.js index 9f32f6933..7214d2bb6 100644 --- a/client/coral-sign-in/components/ForgotContent.js +++ b/client/coral-sign-in/components/ForgotContent.js @@ -1,21 +1,21 @@ import React from 'react'; import styles from './styles.css'; -import Button from './Button'; +import Button from 'coral-ui/components/Button'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const ForgotContent = ({changeView}) => ( +const ForgotContent = ({changeView, ...props}) => (

{lang.t('signIn.recoverPassword')}

-
+ {e.preventDefault(); props.fetchForgotPassword();}}>
-
diff --git a/client/coral-sign-in/components/FormField.js b/client/coral-sign-in/components/FormField.js index e69de29bb..62e50e637 100644 --- a/client/coral-sign-in/components/FormField.js +++ b/client/coral-sign-in/components/FormField.js @@ -0,0 +1,18 @@ +import React from 'react'; +import styles from './styles.css'; + +const FormField = ({className, showErrors = false, errorMsg, label, ...props}) => ( +
+ + + {showErrors && errorMsg && {errorMsg}} +
+); + +export default FormField; diff --git a/client/coral-sign-in/components/SignDialog.js b/client/coral-sign-in/components/SignDialog.js index e5c6f0ea4..242b7443a 100644 --- a/client/coral-sign-in/components/SignDialog.js +++ b/client/coral-sign-in/components/SignDialog.js @@ -6,9 +6,9 @@ import SignInContent from './SignInContent'; import SingUpContent from './SignUpContent'; import ForgotContent from './ForgotContent'; -const SignDialog = ({open, view, onClose, ...props}) => ( +const SignDialog = ({open, view, handleClose, ...props}) => ( - × + × {view === 'SIGNIN' && } {view === 'SIGNUP' && } {view === 'FORGOT' && } diff --git a/client/coral-sign-in/components/SignInContent.js b/client/coral-sign-in/components/SignInContent.js index 4764f1cb2..6a1e726c5 100644 --- a/client/coral-sign-in/components/SignInContent.js +++ b/client/coral-sign-in/components/SignInContent.js @@ -1,39 +1,65 @@ import React from 'react'; import styles from './styles.css'; -import Button from './Button'; + +import Button from 'coral-ui/components/Button'; +import FormField from './FormField'; +import Alert from './Alert'; +import Spinner from 'coral-ui/components/Spinner'; + import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const SignInContent = ({openFacebookWindow, changeView}) => ( +const SignInContent = ({handleChange, formData, ...props}) => (
-

{lang.t('signIn.signIn')}

+

+ {lang.t('signIn.signIn')} +

-
-

{lang.t('signIn.or')}

+

+ {lang.t('signIn.or')} +

-
-
- - -
-
- - -
- + { props.message && {props.message} } + + + + { + !props.isLoading ? + + : + + }
); diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 70fb30d06..db005368f 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -1,46 +1,83 @@ import React from 'react'; import styles from './styles.css'; -import Button from './Button'; +import FormField from './FormField'; +import Button from 'coral-ui/components/Button'; +import Spinner from 'coral-ui/components/Spinner'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const SignUpContent = ({changeView, openFacebookWindow}) => ( +const SignUpContent = ({handleChange, formData, ...props}) => (
-

{lang.t('signIn.signUp')}

+

+ {lang.t('signIn.signUp')} +

-
-

{lang.t('signIn.or')}

+

+ {lang.t('signIn.or')} +

-
-
- - -
-
- - -
-
- - -
-
- - -
- + + + + + + + { + !props.isLoading ? + + : + + }
- {lang.t('signIn.alreadyHaveAnAccount')} changeView('SIGNIN')}>{lang.t('signIn.signIn')} + + {lang.t('signIn.alreadyHaveAnAccount')} + props.changeView('SIGNIN')}> + {lang.t('signIn.signIn')} + +
); diff --git a/client/coral-sign-in/components/styles.css b/client/coral-sign-in/components/styles.css index e7d77b85c..3800f53e1 100644 --- a/client/coral-sign-in/components/styles.css +++ b/client/coral-sign-in/components/styles.css @@ -1,53 +1,3 @@ -.button { - background: 0 0; - border: none; - border-radius: 2px; - color: #000; - display: block; - position: relative; - height: 36px; - min-width: 64px; - padding: 0 8px; - display: inline-block; - font-family: 'Roboto','Helvetica','Arial',sans-serif; - font-size: 14px; - font-weight: 500; - letter-spacing: 0; - overflow: hidden; - will-change: box-shadow,transform; - -webkit-transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1); - transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1); - outline: none; - cursor: pointer; - text-decoration: none; - text-align: center; - line-height: 36px; - vertical-align: middle; - width: 100%; - margin: 0; -} - -.type--black { - color: #E0E0E0; - background: #212121; -} - -.type--local { - background: #E0E0E0; - color: #212121; -} - -.type--facebook { - background-color: #4267b2; - border-color: #4267b2; - color: rgb(255, 255, 255); -} - -.type--facebook:hover { - background-color: #365899; - border-color: #365899; -} - .dialog { width: 400px; border: none; @@ -96,6 +46,7 @@ .footer a { color: #2c69b6; cursor: pointer; + margin: 0 5px; } .socialConnections { @@ -120,4 +71,32 @@ .close:hover { color: #6b6b6b; +} + +input.error{ + border: solid 1px #f44336; +} + +.errorMsg { + color: grey; + font-weight: 600; + padding: 3px 0 16px; +} + +.alert--success { + border: solid 1px #c0c4da; + background: #afb5c2; + color: #131314; +} + +.alert--success { + border: solid 1px #1ec00e; + background: #cbf1b8; + color: #006900; +} + +.alert--error { + border: solid 1px #f44336; + background: #F1A097; + color: #741818; } \ No newline at end of file diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js index e9e32d474..b4d8a14e2 100644 --- a/client/coral-sign-in/containers/SignInContainer.js +++ b/client/coral-sign-in/containers/SignInContainer.js @@ -2,28 +2,119 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import SignDialog from '../components/SignDialog'; -import Button from '../components/Button'; +import Button from 'coral-ui/components/Button'; + +import validate from 'coral-framework/helpers/validate'; +import error from 'coral-framework/helpers/error'; import { - loginFacebookCallback, - loginFacebook, - logout, - showSignInDialog, changeView, - hideSignInDialog + fetchSignUp, + fetchSignIn, + showSignInDialog, + hideSignInDialog, + fetchSignInFacebook, + fetchForgotPassword } from '../../coral-framework/actions/auth'; class SignInContainer extends Component { + initialState = { + formData: { + email: '', + username: '', + password: '', + confirmPassword: '' + }, + errors: {}, + showErrors: false + }; + constructor(props) { super(props); + this.state = this.initialState; + this.handleChange = this.handleChange.bind(this); + this.handleSignUp = this.handleSignUp.bind(this); + this.handleSignIn = this.handleSignIn.bind(this); + this.handleClose = this.handleClose.bind(this); + this.cleanState = this.cleanState.bind(this); + this.validation = this.validation.bind(this); } - componentDidMount() { - window.authCallback = this.props.loginFacebookCallback; + cleanState () { + this.setState(this.initialState); + } + + handleChange(e) { + const {name, value} = e.target; + this.validation(name, value, this.state.formData); + + this.setState(state => ({ + ...state, + formData: { + ...state.formData, + [name]: value + } + })); + } + + validation(name, value) { + if (!validate[name](value)) { + this.setState(state => ({ + errors: { + ...state.errors, + [name]: error[name] + } + })); + } else { + const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line + this.setState(state => ({ + ...state, + errors + })); + } + } + + isCompleted() { + const {formData} = this.state; + return !Object.keys(formData).filter(prop => !formData[prop].length).length; + } + + displayErrors(show = true) { + this.setState({ + showErrors: show + }); + } + + handleSignUp(e) { + e.preventDefault(); + this.displayErrors(); + if (this.isCompleted()) { + console.log('Is Completed!!'); + } else { + console.log('Is not Completed!!'); + } + this.props.fetchSignUp(this.state.formData); + } + + handleSignIn(e) { + e.preventDefault(); + this.displayErrors(); + this.props.fetchSignIn(this.state.formData); + } + + handleClose() { + this.cleanState(); + this.props.hideSignInDialog(); + } + + changeView(view) { + this.cleanState(); + this.props.changeView(view); } render() { const {auth, showSignInDialog} = this.props; + const {errors, showErrors, formData} = this.state; return (
@@ -42,12 +139,13 @@ class SignInContainer extends Component { const mapStateToProps = ({auth}) => ({auth}); const mapDispatchToProps = dispatch => ({ - loginFacebookCallback: () => dispatch(loginFacebookCallback()), - loginFacebook: () => dispatch(loginFacebook()), - logout: () => dispatch(logout()), + fetchSignUp: (formData) => dispatch(fetchSignUp(formData)), + fetchSignIn: (formData) => dispatch(fetchSignIn(formData)), + fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), + fetchForgotPassword: (formData) => dispatch(fetchForgotPassword(formData)), showSignInDialog: () => dispatch(showSignInDialog()), changeView: (view) => dispatch(changeView(view)), - onClose: () => dispatch(hideSignInDialog()) + handleClose: () => dispatch(hideSignInDialog()) }); export default connect( diff --git a/client/coral-ui/components/Button.css b/client/coral-ui/components/Button.css index e69de29bb..150f1df7b 100644 --- a/client/coral-ui/components/Button.css +++ b/client/coral-ui/components/Button.css @@ -0,0 +1,49 @@ +.button { + background: 0 0; + border: none; + border-radius: 2px; + color: #000; + display: block; + position: relative; + height: 36px; + min-width: 64px; + padding: 0 8px; + display: inline-block; + font-family: 'Roboto','Helvetica','Arial',sans-serif; + font-size: 14px; + font-weight: 500; + letter-spacing: 0; + overflow: hidden; + will-change: box-shadow,transform; + -webkit-transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1); + transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1); + outline: none; + cursor: pointer; + text-decoration: none; + text-align: center; + line-height: 36px; + vertical-align: middle; + width: 100%; + margin: 0; +} + +.type--black { + color: #E0E0E0; + background: #212121; +} + +.type--local { + background: #E0E0E0; + color: #212121; +} + +.type--facebook { + background-color: #4267b2; + border-color: #4267b2; + color: rgb(255, 255, 255); +} + +.type--facebook:hover { + background-color: #365899; + border-color: #365899; +} \ No newline at end of file diff --git a/client/coral-ui/components/Button.js b/client/coral-ui/components/Button.js index 9c0d239b7..13ea46349 100644 --- a/client/coral-ui/components/Button.js +++ b/client/coral-ui/components/Button.js @@ -1,5 +1,5 @@ import React from 'react'; -import styles from './styles.css'; +import styles from './Button.css'; const Button = ({cStyle = 'local', children, className, ...props}) => (