Upgrade to nick_site commit f981fa57, adds alerts and inactive sidebar option
[ndcode_site.git] / my_account / password_reset / index.html.jst
1 return async env => {
2   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3   let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
4   let get_placeholder = await _require('/_lib/get_placeholder.jst')
5   let get_session = await _require('/_lib/get_session.jst')
6   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
7   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
8   let navbar = await _require('/_lib/navbar.jst')
9
10   // preload placeholder
11   let transaction = await env.site.database.Transaction()
12   let placeholder
13   try {
14     let root = await transaction.get()
15     let session = await get_session(env, root)
16     placeholder = await get_placeholder(env, session)
17     await transaction.commit()
18   }
19   catch (error) {
20     transaction.rollback()
21     throw error
22   }
23
24   // preload URL details if any
25   let email =
26     Object.prototype.hasOwnProperty.call(
27       env.parsed_url.query,
28       'email'
29     ) ?
30       decodeURIComponent(env.parsed_url.query.email) :
31       ''
32
33    await navbar(
34     env,
35     // head
36     async _out => {},
37     // body
38     async _out => {
39       await breadcrumbs(env, _out)
40
41       p/*.mt-3*/ {'To reset your password, please enter new details below and we will send you a password reset link.'}
42
43       form#form {
44         div.row {
45           div.col-md-6 {
46             div.form-group {
47              label.form-label(for="email") {'Email *'}
48               input.form-control#email(type="email" value=email placeholder=placeholder.email required="required" maxlength=256) {}
49               div.invalid-feedback {'Please enter your account\'s email address.'}
50             }
51           }
52           div.col-md-6 {
53             div.form-group {
54               label.form-label(for="new-password") {'New password *'}
55               input.form-control#new-password(type="password" placeholder="Choose" required="required" minlength=8 maxlength=256) {}
56               div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
57             }
58           }
59         }
60       }
61
62       if (email.length)
63         button.btn.btn-success.mb-3#password-reset(type="button") {
64           div.icon24-outer.mr-2#icon {
65             div.icon24-inner {_out.push(fa_envelope)}
66           }
67           div.icon24-outer.mr-2#tick(hidden) {
68             div.icon24-inner {_out.push(icon_tick)}
69           }
70           div.icon24-outer.mr-2#cross(hidden) {
71             div.icon24-inner {_out.push(icon_cross)}
72           }
73           div.icon24-outer.mr-2#spinner(hidden) {
74             div.icon24-inner {
75               div.spinner-border.spinner-border-sm(role="status") {}
76             }
77           }
78           'Password reset'
79         }
80       else
81         button.btn.btn-success.mb-3#password-reset(type="button" disabled) {
82           div.icon24-outer.mr-2#icon {
83             div.icon24-inner {_out.push(fa_envelope)}
84           }
85           div.icon24-outer.mr-2#tick(hidden) {
86             div.icon24-inner {_out.push(icon_tick)}
87           }
88           div.icon24-outer.mr-2#cross(hidden) {
89             div.icon24-inner {_out.push(icon_cross)}
90           }
91           div.icon24-outer.mr-2#spinner(hidden) {
92             div.icon24-inner {
93               div.spinner-border.spinner-border-sm(role="status") {}
94             }
95           }
96           'Password reset'
97         }
98
99       div.alert#alert(hidden) {}
100
101       p.text-muted {'* These fields are required.'}
102     },
103     // scripts
104     async _out => {
105       //script(src="/js/utils.js") {}
106
107       script {
108         document.addEventListener(
109           'DOMContentLoaded',
110           () => {
111             let id_alert = document.getElementById('alert')
112             let id_cross = document.getElementById('cross')
113             let id_email = document.getElementById('email')
114             let id_form = document.getElementById('form')
115             let id_icon = document.getElementById('icon')
116             let id_new_password = document.getElementById('new-password')
117             let id_password_reset = document.getElementById('password-reset')
118             let id_spinner = document.getElementById('spinner')
119             let id_tick = document.getElementById('tick')
120
121             let edited = () => {
122               id_password_reset.disabled =
123                 id_email.value.length === 0 &&
124                   id_new_password.value.length === 0
125               id_icon.hidden = false
126               id_tick.hidden = true
127               id_cross.hidden = true
128               id_spinner.hidden = true
129               id_alert.hidden = true
130             }
131
132             id_email.addEventListener('input', edited)
133             id_new_password.addEventListener('input', edited)
134
135             id_password_reset.addEventListener(
136               'click',
137               async () => {
138                 id_icon.hidden = false
139                 id_tick.hidden = true
140                 id_cross.hidden = true
141                 id_spinner.hidden = true
142                 // the below causes an ugly flicker, so just keep the alert
143                 //id_alert.hidden = true
144
145                 if (!id_form.checkValidity()) {
146                   id_form.classList.add('was-validated');
147
148                   id_icon.hidden = true
149                   id_cross.hidden = false
150                   return
151                 }
152                 id_form.classList.remove('was-validated');
153
154                 let email = id_email.value.slice(0, 256).toLowerCase()
155                 let new_password = id_new_password.value.slice(0, 256)
156
157                 id_icon.hidden = true
158                 id_spinner.hidden = false
159                 try {
160                   await api_call(
161                     '/api/account/password_reset.json',
162                     email,
163                     new_password
164                   )
165                 }
166                 catch (error) {
167                   let problem = Problem.from(error)
168
169                   id_cross.hidden = false
170                   id_spinner.hidden = true
171
172                   id_alert.textContent = problem.detail
173                   id_alert.classList.remove('alert-success')
174                   id_alert.classList.add('alert-danger')
175                   id_alert.hidden = false
176                   return
177                 }
178                 id_tick.hidden = false
179                 id_spinner.hidden = true
180                 id_alert.textContent = `Password reset link has been sent to "${email}". Please check your email for next steps.`
181                 id_alert.classList.add('alert-success')
182                 id_alert.classList.remove('alert-danger')
183                 id_alert.hidden = false
184               }
185             )
186           }
187         )
188       }
189     }
190   )
191 }