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