diff options
Diffstat (limited to 'web-ui/src')
| -rw-r--r-- | web-ui/src/js/index.js | 223 | 
1 files changed, 223 insertions, 0 deletions
| diff --git a/web-ui/src/js/index.js b/web-ui/src/js/index.js new file mode 100644 index 00000000..35de6407 --- /dev/null +++ b/web-ui/src/js/index.js @@ -0,0 +1,223 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {createStore} from 'redux'; +import {Map} from 'immutable'; +import 'whatwg-fetch'; + + +class PixelatedComponent extends React.Component { +  _updateStateFromStore() { +    this.setState(this.props.store.getState().toJS()); +  } + +  componentWillMount() { +    this.unsubscribe = this.props.store.subscribe(() => this._updateStateFromStore()); +    this._updateStateFromStore(); +  } + +  componentWillUnmount() { +    this.unsubscribe() +  } +} + + +class PixelatedForm extends PixelatedComponent { +  _fetchAndDispatch(url, actionProperties) { +    const immutableActionProperties = new Map(actionProperties); +    this.props.store.dispatch(immutableActionProperties.merge({status: 'STARTED'}).toJS()); +    fetch(url).then((response) => { +      console.debug('got a reply', response); +      return response.json() +    }).then((json) => { +      console.debug('got json', json); +      setTimeout(() => { +        this.props.store.dispatch(immutableActionProperties.merge({status: 'SUCCESS', json: json}).toJS()); +      }, 3000); +    }).catch((error) => { +      console.error('something went wrong', error); +      this.props.store.dispatch(immutableActionProperties.merge({status: 'ERROR', error: error}).toJS()); +    }); +  } +} + + +class InviteCodeForm extends PixelatedForm { +  render() { +    return ( +      <form onSubmit={this._handleClick.bind(this)}> +        <div className="field-group"> +          <input type="text" name="invite-code" className="invite-code" required/> +          <label className="animated-label" htmlFor="invite-code">invite code</label> +        </div> +        <input type="submit" value="Get Started" className="blue-button validation-link" /> +      </form> +    ); +  } + +  _handleClick(event) { +    event.stopPropagation(); +    event.preventDefault(); +    this.props.store.dispatch({type: 'SUBMIT_INVITE_CODE', inviteCode: event.target['invite-code'].value}); +  } +} + + +class CreateAccountForm extends PixelatedForm { +  render() { +    return ( +      <form onSubmit={this._handleClick.bind(this)}> +        <span className="domain-label"> @domain.com </span> +        <div className="field-group"> +          <input type="text" name="username" className="username" required/> +          <label className="animated-label" htmlFor="username">username</label> +        </div> + +        <div className="field-group"> +          <input type="password" name="password" className="password" required/> +          <label className="animated-label" htmlFor="password">password</label> +        </div> + +        <input type="submit" value="Create my account" className="blue-button validation-link" /> +      </form> +    ); +  } + +  _handleClick(event) { +    event.stopPropagation(); +    event.preventDefault(); +    this.props.store.dispatch({type: 'SUBMIT_CREATE_ACCOUNT', username: event.target['username'].value, password: event.target['password'].value}); +  } +} + + +class BackupEmailForm extends PixelatedForm { +  render() { +    return ( +      <form onSubmit={this._handleClick.bind(this)}> +        <div className="field-group"> +          <input type="text" name="backup-email" required/> +          <label className="animated-label" htmlFor="password">type your backup email</label> +        </div> + +        <input type="submit" value="Send Email" className="blue-button validation-link" /> +        <p className="link-message"> +          <a href="#" className="validation-link">I didn't receive anything. Send the email again</a> +        </p> +      </form> +    ); +  } + +  _handleClick(event) { +    event.stopPropagation(); +    event.preventDefault(); +    this._fetchAndDispatch('dummy.json', {type: 'SUBMIT_BACKUP_EMAIL', backupEmail: event.target['backup-email'].value}); +  } +} + + +class BackupEmailSentForm extends PixelatedForm { +  render() { +    return ( +      <form onSubmit={this._handleClick.bind(this)}> +        {this.state.isFetching || <a href="/" className="blue-button">I received the codes. <br/>Go to my inbox</a>} +        <p className="link-message"> +          <a href="#">I didn't receive anything. Send the email again</a> +        </p> +      </form> +    ); +  } + +  _handleClick(event) { +    event.stopPropagation(); +    event.preventDefault(); +  } +} + + +class SignUp extends PixelatedComponent { +  render() { +    return ( +      <div> +        <div className="message"> +          <h1>{this.state.header}</h1> +          {this.state.icon} +          <p>{this.state.summary}</p> +        </div> +        <div className="form-container"> +          {this._form()} +        </div> +      </div> +    ); +  } + +  _form() { +    switch(this.state.form) { +      case 'invite_code': return <InviteCodeForm store={store} />; +      case 'create_account': return <CreateAccountForm store={store} />; +      case 'backup_email': return <BackupEmailForm store={store} />; +      case 'backup_email_sent': return <BackupEmailSentForm store={store} />; +      default: throw Error('TODO'); +    } +  } +} + + +const initialState = new Map({ +  isFetching: false, +  form: 'invite_code', +  header: 'Welcome', +  icon: null, +  summary: ['Do you have an invite code?', <br key='br1' />, 'Type it below'], +}); + + +const store = createStore((state=initialState, action) => { +  switch (action.type) { +  case 'SUBMIT_INVITE_CODE': +    return state.merge({ +      inviteCode: action.inviteCode, +      form: 'create_account', +      header: 'Create your account', +      summary: 'Choose your username, and be careful about your password, it must be strong and easy to remember. If you have a password manager, we strongly advise you to use one.', +    }); +  case 'SUBMIT_CREATE_ACCOUNT': +    return state.merge({ +      username: action.username, +      password: action.password, +      form: 'backup_email', +      header: 'In case you lose your password...', +      summary: 'Set up a backup email account. You\'ll receive an email with a code so you can recover your account in the future, other will be sent to your account administrator.', +    }); +  case 'SUBMIT_BACKUP_EMAIL': +    switch (action.status) { +    case 'STARTED': +      return state.merge({ +        isFetching: true, +        backupEmail: action.backupEmail, +        form: 'backup_email_sent', +        icon: <p><img key="img1" src="images/sent_email.png" className="sent-email-icon"/></p>, +        summary: 'An email was sent to the email you provided. Check your spam folder, just in case.', +      }); +    case 'SUCCESS': +      return state.merge({ +        isFetching: false, +      }); +    case 'ERROR': +      return state.merge({ +        isFetching: false, +      }); +    default: +      return state; +    } +  case 'SUBMIT_BACKUP_EMAIL_SENT': +    return state.merge({}); +  default: +    return state; +  } +}); + + +ReactDOM.render( +  <SignUp store={store}/>, +  document.getElementById('app') +); | 
