Major refactoring of get_session(), get_account(), get_nodemailer(), introduces ...
[ndcode_site.git] / my_account / password_reset / index.html.jst
1 let logjson = (await import('@ndcode/logjson')).default
2
3 return async env => {
4   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
5   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
6   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
7   let navbar = await _require('/_lib/navbar.jst')
8
9   // preload draft details if any
10   let details = {}
11   if (Object.prototype.hasOwnProperty.call(env.parsed_url.query, 'email'))
12     details.email = decodeURIComponent(env.parsed_url.query.email)
13   console.log('details', JSON.stringify(details))
14
15   await navbar(
16     env,
17     // head
18     async _out => {},
19     // body
20     async _out => {
21       await breadcrumbs(env, _out)
22
23       p {'To reset your password, please enter new details below and we will send you a password reset link.'}
24
25       div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
26         div.card#step-1 {
27           div.card-header#step-1-heading(role="tab") {
28             span#step-1-tick(style="display: none;") {
29               span.icon-color.pr-3 {_out.push(icon_tick)}
30             }
31             span#step-1-cross(style="display: none;") {
32               span.icon-color.pr-3 {_out.push(icon_cross)}
33             }
34             //span#step-1-spinner(style="display: none;") {
35             //  span.icon-color.pr-3 {
36             //    div.spinner-border(role="status") {
37             //      span.sr-only {'Loading...'}
38             //    }
39             //  }
40             //}
41             a.h5(data-toggle="collapse" data-parent="#accordion" href="#step-1-collapse" aria-expanded="true" aria-controls="step-1-collapse") {
42               'Account details'
43             }
44           }
45           div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
46             div.card-body {
47               div.row {
48                 div.col-md-6 {
49                   div.form-group {
50                    label.form-label(for="email") {'Email *'}
51                     input.form-control#email(type="email" value=details.email || '' placeholder="Account email address" required="required" maxlength=256) {}
52                   }
53                 }
54                 div.col-md-6 {
55                   div.form-group {
56                    label.form-label(for="password") {'Password *'}
57                     input.form-control#password(type="password" placeholder="New password" required="required" minlength=8 maxlength=256) {}
58                   }
59                 }
60               }
61
62               button.btn.btn-success#step-1-continue(type="button") {'Continue'}
63               p.'mt-3'.mb-0 {'* This field is required.'}
64             }
65           }
66         }
67         div.card#step-2 {
68           div.card-header#step-2-heading(role="tab") {
69             span#step-2-tick(style="display: none;") {
70               span.icon-color.pr-3 {_out.push(icon_tick)}
71             }
72             span#step-2-cross(style="display: none;") {
73               span.icon-color.pr-3 {_out.push(icon_cross)}
74             }
75             span#step-2-spinner(style="display: none;") {
76               span.icon-color.pr-3 {
77                 div.spinner-border(role="status") {
78                   span.sr-only {'Loading...'}
79                 }
80               }
81             }
82             a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-2-collapse" aria-expanded="false" aria-controls="step-2-collapse") {
83               'Send password reset link'
84             }
85           }
86           div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
87             div.card-body {
88               p#step-2-message {'Please enter account details first.'}
89
90               button.btn.btn-outline-secondary#step-2-back(type="button") {'Back'}
91               button.btn.btn-outline-secondary.ml-3#step-2-resend-email(type="button") {'Re-send email'}
92             }
93           }
94         }
95       }
96     },
97     // scripts
98     async _out => {
99       //script(src="/js/api_call.js") {}
100
101       script {
102         let api_account_password_reset = async (...args) => api_call(
103           '/api/account/password_reset.json',
104           ...args
105         )
106
107         let step_1 = async () => {
108           if (
109             !document.getElementById('email').reportValidity() ||
110               !document.getElementById('password').reportValidity()
111           ) {
112             $('#step-1-tick').hide()
113             $('#step-1-cross').show()
114             //$('#step-1-spinner').hide()
115             return false
116           }
117           $('#step-1-tick').show()
118           $('#step-1-cross').hide()
119           //$('#step-1-spinner').hide()
120           return true
121         }
122
123         let step_2 = async () => {
124           $('#step-2-tick').hide()
125           $('#step-2-cross').hide()
126           $('#step-2-spinner').show()
127           document.getElementById('step-1').scrollIntoView()
128
129           let email
130           try {
131             email = document.getElementById('email').value.slice(0, 256).toLowerCase()
132             await api_account_password_reset(
133               email,
134               document.getElementById('password').value.slice(0, 256)
135             )
136           }
137           catch (error) {
138             let problem =
139               error instanceof Problem ?
140                 error :
141                 new Problem(
142                   // title
143                   'Bad request',
144                   // details
145                   (error.stack || error.message).toString()
146                   // status
147                   400
148                 )
149
150             $('#step-2-tick').hide()
151             $('#step-2-cross').show()
152             $('#step-2-spinner').hide()
153
154             document.getElementById('step-2-message').textContent = problem.detail
155             $('#step-2-collapse').collapse('show')
156             return false
157           }
158           $('#step-2-tick').show()
159           $('#step-2-cross').hide()
160           $('#step-2-spinner').hide()
161
162           document.getElementById('step-2-message').textContent = `Password reset link has been sent to "${email}". Please check your email for next steps.`
163           return true
164         }
165
166         document.addEventListener(
167           'DOMContentLoaded',
168           () => {
169             document.getElementById('step-1-continue').addEventListener(
170               'click',
171               async () => {
172                 if (await step_1() && await step_2())
173                   $('#step-2-collapse').collapse('show')
174               }
175             )
176
177             document.getElementById('step-2-back').addEventListener(
178               'click',
179               () => {$('#step-1-collapse').collapse('show')}
180             )
181
182             document.getElementById('step-2-resend-email').addEventListener(
183               'click',
184               async () => {
185                 if (await step_2())
186                   $('#step-3-collapse').collapse('show')
187               }
188             )
189           }
190         )
191       }
192     }
193   )
194 }