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