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