github.com/resonatecoop/id@v1.1.0-43/frontend/src/components/forms/passwordReset.js (about)

     1  /* global fetch */
     2  
     3  const html = require('choo/html')
     4  const Component = require('choo/component')
     5  const nanostate = require('nanostate')
     6  const Form = require('./generic')
     7  const isEmail = require('validator/lib/isEmail')
     8  const isEmpty = require('validator/lib/isEmpty')
     9  const validateFormdata = require('validate-formdata')
    10  
    11  class PasswordReset extends Component {
    12    constructor (id, state, emit) {
    13      super(id)
    14  
    15      this.emit = emit
    16      this.state = state
    17  
    18      this.local = state.components[id] = Object.create({
    19        machine: nanostate.parallel({
    20          request: nanostate('idle', {
    21            idle: { start: 'loading' },
    22            loading: { resolve: 'data', reject: 'error', reset: 'idle' },
    23            data: { reset: 'idle', start: 'loading' },
    24            error: { reset: 'idle', start: 'loading' }
    25          }),
    26          loader: nanostate('off', {
    27            on: { toggle: 'off' },
    28            off: { toggle: 'on' }
    29          })
    30        })
    31      })
    32  
    33      this.local.error = {}
    34      this.local.success = {}
    35  
    36      this.local.machine.on('request:error', () => {
    37        if (this.element) this.rerender()
    38      })
    39  
    40      this.local.machine.on('request:loading', () => {
    41        if (this.element) this.rerender()
    42      })
    43  
    44      this.local.machine.on('loader:toggle', () => {
    45        if (this.element) this.rerender()
    46      })
    47  
    48      this.local.machine.transitions.request.event('error', nanostate('error', {
    49        error: { start: 'loading' }
    50      }))
    51  
    52      this.local.machine.on('request:noResults', () => {
    53        if (this.element) this.rerender()
    54      })
    55  
    56      this.local.machine.transitions.request.event('noResults', nanostate('noResults', {
    57        noResults: { start: 'loading' }
    58      }))
    59  
    60      this.validator = validateFormdata()
    61      this.form = this.validator.state
    62    }
    63  
    64    createElement (props) {
    65      const message = {
    66        loading: html`<p class="status w-100 pa1">Loading...</p>`,
    67        error: html`<p class="status bg-yellow w-100 black pa1">${this.local.error.message}</p>`,
    68        data: html`<p class="w-100 pa1">${this.local.success.message}</p>`,
    69        noResults: html`<p class="status bg-yellow w-100 black pa1">An error occured.</p>`
    70      }[this.local.machine.state.request]
    71  
    72      return html`
    73        <div class="flex flex-column flex-auto">
    74          ${message}
    75          ${this.state.cache(Form, 'password-reset-form').render({
    76            id: 'password-reset',
    77            method: 'POST',
    78            action: '',
    79            buttonText: 'Reset my password',
    80            validate: (props) => {
    81              this.validator.validate(props.name, props.value)
    82              this.rerender()
    83            },
    84            form: this.form || {
    85              changed: false,
    86              valid: true,
    87              pristine: {},
    88              required: {},
    89              values: {},
    90              errors: {}
    91            },
    92            fields: [
    93              {
    94                type: 'email',
    95                label: 'To reset your password, please enter your email address below',
    96                placeholder: 'Enter your email address'
    97              }
    98            ],
    99            submit: async (data) => {
   100              if (this.local.machine.state === 'loading') {
   101                return
   102              }
   103  
   104              const loaderTimeout = setTimeout(() => {
   105                this.local.machine.emit('loader:toggle')
   106              }, 1000)
   107  
   108              try {
   109                this.local.machine.emit('request:start')
   110  
   111                let response = await fetch('')
   112  
   113                const csrfToken = response.headers.get('X-CSRF-Token')
   114  
   115                const payload = { email: data.email.value }
   116  
   117                response = await fetch('', {
   118                  method: 'POST',
   119                  credentials: 'include',
   120                  headers: {
   121                    Accept: 'application/json',
   122                    'X-CSRF-Token': csrfToken
   123                  },
   124                  body: new URLSearchParams(payload)
   125                })
   126  
   127                const isRedirected = response.redirected
   128  
   129                if (isRedirected) {
   130                  window.location.href = response.url
   131                }
   132  
   133                this.local.machine.state.loader === 'on' && this.local.machine.emit('loader:toggle')
   134  
   135                const status = response.status
   136                const contentType = response.headers.get('content-type')
   137  
   138                if (status >= 400 && contentType && contentType.indexOf('application/json') !== -1) {
   139                  const { error } = await response.json()
   140                  this.local.error.message = error
   141                  this.local.machine.emit('request:error')
   142                } else {
   143                  const { message } = await response.json()
   144  
   145                  this.local.success.message = message
   146  
   147                  this.local.machine.emit('request:resolve')
   148  
   149                  this.rerender()
   150                }
   151              } catch (err) {
   152                this.local.error.message = err.message
   153                this.local.machine.emit('request:reject')
   154                this.emit('error', err)
   155              } finally {
   156                clearTimeout(loaderTimeout)
   157              }
   158            }
   159          })}
   160        </div>
   161      `
   162    }
   163  
   164    load () {
   165      this.validator.field('email', (data) => {
   166        if (isEmpty(data)) return new Error('Please enter your email address')
   167        if (!(isEmail(data))) return new Error('This is not a valid email address')
   168      })
   169    }
   170  
   171    update () {
   172      return false
   173    }
   174  }
   175  
   176  module.exports = PasswordReset