Change /_lib/session_cookie.jst to /_lib/get_session.jst
[ndcode_site.git] / my_account / sign_up / index.html.jst
1 let XDate = require('xdate')
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 get_session = await _require('/_lib/get_session.jst')
9
10   // preload draft details if any
11   let transaction = await env.site.database.Transaction(), draft_details
12   try {
13     // initialize env.session_key, set cookie in env.response
14     let session = await get_session(env, transaction)
15
16     let sign_up_draft = await session.get('sign_up_draft')
17     draft_details =
18       sign_up_draft !== undefined &&
19         XDate.now() < await sign_up_draft.get_json('expires') ?
20         {
21           email: await sign_up_draft.get_json('email'),
22           given_names: await sign_up_draft.get_json('given_names'),
23           family_name: await sign_up_draft.get_json('family_name'),
24           contact_me: await sign_up_draft.get_json('contact_me')
25         } :
26         null
27
28     await transaction.commit()
29   }
30   catch (error) {
31     transaction.rollback()
32     throw error
33   }
34   console.log('draft_details', JSON.stringify(draft_details))
35
36   await navbar(
37     env,
38     // head
39     async _out => {},
40     // body
41     async _out => {
42       await breadcrumbs(env, _out)
43
44       p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
45
46       p {'Your given names are visible to other users if you comment on our blog. Your email and family name remain private. If your name is one word or does not fit given names/family name pattern, then please enter given names only.'}
47
48       div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
49         div.card#step-1 {
50           div.card-header#step-1-heading(role="tab") {
51             span#step-1-tick(style="display: none;") {
52               span.icon-color.pr-3 {_out.push(icon_tick)}
53             }
54             span#step-1-cross(style="display: none;") {
55               span.icon-color.pr-3 {_out.push(icon_cross)}
56             }
57             //span#step-1-spinner(style="display: none;") {
58             //  span.icon-color.pr-3 {
59             //    div.spinner-border(role="status") {
60             //      span.sr-only {'Loading...'}
61             //    }
62             //  }
63             //}
64             a.h5(data-toggle="collapse" data-parent="#accordion" href="#step-1-collapse" aria-expanded="true" aria-controls="step-1-collapse") {
65               'Your details'
66             }
67           }
68           div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
69             div.card-body {
70               div.row {
71                 div.col-md-6 {
72                   div.form-group {
73                     label.form-label(for="given-names") {'Given names *'}
74                     input.form-control#given-names(type="text" value=draft_details ? draft_details.given_names : '' placeholder="Your given names" required="required" maxlength=256) {}
75                   }
76                 }
77                 div.col-md-6 {
78                   div.form-group {
79                     label.form-label(for="family-name") {'Family name'}
80                     input.form-control#family-name(type="text" value=draft_details ? draft_details.family_name : '' placeholder="Your family name" maxlength=256) {}
81                   }
82                 }
83               }
84               div.row {
85                 div.col-md-6 {
86                   div.form-group {
87                     label.form-label(for="email") {'Email *'}
88                     input.form-control#email(type="email" value=draft_details ? draft_details.email : '' placeholder="Your email address" required="required" maxlength=256) {}
89                   }
90                 }
91                 div.col-md-6 {
92                   div.form-group {
93                     label.form-label(for="password") {'Password *'}
94                     input.form-control#password(type="password" placeholder="New password" required="required" minlength=8 maxlength=256) {}
95                   }
96                 }
97               }
98               div.row {
99                 div.col-md-12 {
100                   div.custom-control.custom-checkbox {
101                     if (!draft_details || draft_details.contact_me)
102                       input.custom-control-input#contact-me(type="checkbox" checked="checked") {}
103                     else
104                       input.custom-control-input#contact-me(type="checkbox") {}
105                     ' '
106                     label.custom-control-label(for="contact-me") {
107                       'Contact me by email with updates and special offers'
108                     }
109                   }
110                 }
111               }
112               div.row.align-items-center.mb-3 {
113                 div.'col-md-6' {
114                   div.form-group {
115                     label.form-label(for="verification-code") {'Verification code *'}
116                     input.form-control#verification-code(type="text" placeholder="Type the code shown to the right" required="required" minlength=6 maxlength=6) {}
117                   }
118                 }
119                 div.'col-md-4' {
120                   img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
121                 }
122                 div.'col-md-2'.my-auto.text-center {
123                   button.btn.btn-outline-secondary#'step-1-new-code'(type="button") {'New code'}
124                 }
125               }
126
127               button.btn.btn-success#step-1-continue(type="button") {'Continue'}
128
129               p.'mt-3'.mb-0 {'* These fields are required.'}
130             }
131           }
132         }
133         div.card#step-2 {
134           div.card-header#step-2-heading(role="tab") {
135             span#step-2-tick(style="display: none;") {
136               span.icon-color.pr-3 {_out.push(icon_tick)}
137             }
138             span#step-2-cross(style="display: none;") {
139               span.icon-color.pr-3 {_out.push(icon_cross)}
140             }
141             span#step-2-spinner(style="display: none;") {
142               span.icon-color.pr-3 {
143                 div.spinner-border(role="status") {
144                   span.sr-only {'Loading...'}
145                 }
146               }
147             }
148             a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-2-collapse" aria-expanded="false" aria-controls="step-2-collapse") {
149               'Create account'
150             }
151           }
152           div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
153             div.card-body {
154               p#step-2-message {'Please enter your details first.'}
155
156               button.btn.btn-outline-secondary#step-2-back(type="button") {'Back'}
157               button.btn.btn-outline-secondary.ml-3#step-2-continue(type="button") {'Continue'}
158             }
159           }
160         }
161         div.card#step-3 {
162           div.card-header#step-3-heading(role="tab") {
163             span#step-3-tick(style="display: none;") {
164               span.icon-color.pr-3 {_out.push(icon_tick)}
165             }
166             span#step-3-cross(style="display: none;") {
167               span.icon-color.pr-3 {_out.push(icon_cross)}
168             }
169             span#step-3-spinner(style="display: none;") {
170               span.icon-color.pr-3 {
171                 div.spinner-border(role="status") {
172                   span.sr-only {'Loading...'}
173                 }
174               }
175             }
176             a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-3-collapse" aria-expanded="false" aria-controls="step-3-collapse") {
177               'Send email verification link'
178             }
179           }
180           div#step-3-collapse.collapse(role="tabpanel" aria-labelledby="step-3-heading" data-parent="#accordion") {
181             div.card-body {
182               p#step-3-message {'Please create your account first.'}
183
184               button.btn.btn-outline-secondary#step-3-back(type="button") {'Back'}
185               button.btn.btn-outline-secondary.ml-3#step-3-resend-email(type="button") {'Re-send email'}
186             }
187           }
188         }
189       }
190     },
191     // scripts
192     async _out => {
193       //script(src="/js/api_call.js") {}
194
195       script {
196         let api_account_sign_up_create_account = async (...args) => api_call(
197           '/api/account/sign_up/create_account.json',
198           ...args
199         )
200         //let api_account_sign_up_get_draft = async (...args) => api_call(
201         //  '/api/account/sign_up/get_draft.json',
202         //  ...args
203         //)
204         let api_account_sign_up_set_draft = async (...args) => api_call(
205           '/api/account/sign_up/set_draft.json',
206           ...args
207         )
208         let api_account_sign_up_send_email_verification_link = async (...args) => api_call(
209           '/api/account/sign_up/send_email_verification_link.json',
210           ...args
211         )
212
213         let draft_timeout_running = false
214         let draft_timeout_handler = async () => {
215           draft_timeout_running = false
216           await api_account_sign_up_set_draft(
217             {
218               email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
219               given_names: document.getElementById('given-names').value.slice(0, 256),
220               family_name: document.getElementById('family-name').value.slice(0, 256),
221               contact_me: document.getElementById('contact-me').checked ? true : false
222             }
223           )
224           //console.log('draft', await api_account_sign_up_get_draft())
225         }
226         let draft_change_handler = () => {
227           if (!draft_timeout_running) {
228             draft_timeout_running = true
229             setTimeout(draft_timeout_handler, 5000)
230           }
231         }
232
233         let details
234         let step_1 = async () => {
235           if (
236             !document.getElementById('given-names').reportValidity() ||
237               !document.getElementById('family-name').reportValidity() ||
238               !document.getElementById('email').reportValidity() ||
239               !document.getElementById('password').reportValidity() ||
240               !document.getElementById('verification-code').reportValidity()
241           ) {
242             $('#step-1-tick').hide()
243             $('#step-1-cross').show()
244             //$('#step-1-spinner').hide()
245             return false
246           }
247           $('#step-1-tick').show()
248           $('#step-1-cross').hide()
249           //$('#step-1-spinner').hide()
250
251           details = {
252             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
253             given_names: document.getElementById('given-names').value.slice(0, 256),
254             family_name: document.getElementById('family-name').value.slice(0, 256),
255             password: document.getElementById('password').value.slice(0, 256),
256             contact_me: document.getElementById('contact-me').checked ? true : false
257           }
258           return true
259         }
260
261         let step_2 = async () => {
262           $('#step-2-tick').hide()
263           $('#step-2-cross').hide()
264           $('#step-2-spinner').show()
265           document.getElementById('step-2').scrollIntoView()
266
267           try {
268             await api_account_sign_up_create_account(
269               // verification_code
270               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
271               // details
272               details
273             )
274           }
275           catch (error) {
276             let problem =
277               error instanceof Problem ?
278                 error :
279                 new Problem(
280                   // title
281                   'Bad request',
282                   // detail
283                   (error.stack || error.message).toString()
284                   // status
285                   400
286                 )
287
288             $('#step-2-tick').hide()
289             $('#step-2-cross').show()
290             $('#step-2-spinner').hide()
291
292             document.getElementById('step-2-message').textContent = problem.detail
293             $('#step-2-collapse').collapse('show')
294             return false
295           }
296           $('#step-2-tick').show()
297           $('#step-2-cross').hide()
298           $('#step-2-spinner').hide()
299           document.getElementById('step-2-message').textContent = `Your account with email "${details.email}" has been created.`
300           return true
301         }
302
303         let step_3 = async () => {
304           $('#step-3-tick').hide()
305           $('#step-3-cross').hide()
306           $('#step-3-spinner').show()
307           document.getElementById('step-3').scrollIntoView()
308
309           try {
310             await api_account_sign_up_send_email_verification_link(details.email)
311           }
312           catch (error) {
313             let problem =
314               error instanceof Problem ?
315                 error :
316                 new Problem(
317                   // title
318                   'Bad request',
319                   // detail
320                   (error.stack || error.message).toString()
321                   // status
322                   400
323                 )
324
325             $('#step-3-tick').hide()
326             $('#step-3-cross').show()
327             $('#step-3-spinner').hide()
328
329             document.getElementById('step-3-message').textContent = problem.detail
330             $('#step-3-collapse').collapse('show')
331             return false
332           }
333           $('#step-3-tick').show()
334           $('#step-3-cross').hide()
335           $('#step-3-spinner').hide()
336
337           document.getElementById('step-3-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
338           return true
339         }
340
341         document.addEventListener(
342           'DOMContentLoaded',
343           () => {
344             document.getElementById('given-names').addEventListener(
345               'change',
346               draft_change_handler
347             )
348             document.getElementById('family-name').addEventListener(
349               'change',
350               draft_change_handler
351             )
352             document.getElementById('email').addEventListener(
353               'change',
354               draft_change_handler
355             )
356             document.getElementById('password').addEventListener(
357               'change',
358               draft_change_handler
359             )
360             document.getElementById('contact-me').addEventListener(
361               'change',
362               draft_change_handler
363             )
364
365             let image_seq = 1
366             document.getElementById('step-1-new-code').addEventListener(
367               'click',
368               () => {
369                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
370                 image_seq += 1
371               }
372             )
373
374             document.getElementById('step-1-continue').addEventListener(
375               'click',
376               async () => {
377                 if (await step_1() && await step_2() && await step_3())
378                   $('#step-3-collapse').collapse('show')
379               }
380             )
381
382             document.getElementById('step-2-back').addEventListener(
383               'click',
384               () => {$('#step-1-collapse').collapse('show')}
385             )
386
387             document.getElementById('step-2-continue').addEventListener(
388               'click',
389               async () => {
390                 if (await step_3())
391                   $('#step-3-collapse').collapse('show')
392               }
393             )
394
395             document.getElementById('step-3-back').addEventListener(
396               'click',
397               () => {$('#step-2-collapse').collapse('show')}
398             )
399
400             document.getElementById('step-3-resend-email').addEventListener(
401               'click',
402               async () => {
403                 if (await step_3())
404                   $('#step-3-collapse').collapse('show')
405               }
406             )
407           }
408         )
409       }
410     }
411   )
412 }