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