228b5b0453c8c7347149e8fafd980ff9bf45ff46
[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" 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/account/sign_up/create_account.json',
186           ...arguments
187         )
188         //let sign_up_get_draft = async (...arguments) => api_call(
189         //  '/api/account/sign_up/get_draft.json',
190         //  ...arguments
191         //)
192         let sign_up_set_draft = async (...arguments) => api_call(
193           '/api/account/sign_up/set_draft.json',
194           ...arguments
195         )
196         let sign_up_send_email_verification_link = async (...arguments) => api_call(
197           '/api/account/sign_up/send_email_verification_link.json',
198           ...arguments
199         )
200
201         let draft_timeout_running = false
202         let draft_timeout_handler = async () => {
203           draft_timeout_running = false
204           await sign_up_set_draft(
205             {
206               email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
207               given_names: document.getElementById('given-names').value.slice(0, 256),
208               family_name: document.getElementById('family-name').value.slice(0, 256),
209               contact_me: document.getElementById('contact-me').checked ? true : false
210             }
211           )
212           //console.log('draft', await sign_up_get_draft())
213         }
214         let draft_change_handler = () => {
215           if (!draft_timeout_running) {
216             draft_timeout_running = true
217             setTimeout(draft_timeout_handler, 5000)
218           }
219         }
220
221         let details
222         let step_1 = async () => {
223           if (
224             !document.getElementById('given-names').reportValidity() ||
225               !document.getElementById('family-name').reportValidity() ||
226               !document.getElementById('email').reportValidity() ||
227               !document.getElementById('password').reportValidity() ||
228               !document.getElementById('verification-code').reportValidity()
229           ) {
230             $('#step-1-tick').hide()
231             $('#step-1-cross').show()
232             //$('#step-1-spinner').hide()
233             return false
234           }
235           $('#step-1-tick').show()
236           $('#step-1-cross').hide()
237           //$('#step-1-spinner').hide()
238
239           details = {
240             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
241             given_names: document.getElementById('given-names').value.slice(0, 256),
242             family_name: document.getElementById('family-name').value.slice(0, 256),
243             password: document.getElementById('password').value.slice(0, 256),
244             contact_me: document.getElementById('contact-me').checked ? true : false
245           }
246           return true
247         }
248
249         let step_2 = async () => {
250           $('#step-2-tick').hide()
251           $('#step-2-cross').hide()
252           $('#step-2-spinner').show()
253           document.getElementById('step-2').scrollIntoView()
254
255           try {
256             await sign_up_create_account(
257               // verification_code
258               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
259               // details
260               details
261             )
262           }
263           catch (error) {
264             let problem =
265               error instanceof Problem ?
266                 error :
267                 new Problem(
268                   // title
269                   'Bad request',
270                   // details
271                   (error.stack || error.message).toString()
272                   // status
273                   400
274                 )
275
276             $('#step-2-tick').hide()
277             $('#step-2-cross').show()
278             $('#step-2-spinner').hide()
279
280             document.getElementById('step-2-message').textContent = problem.detail
281             $('#step-2-collapse').collapse('show')
282             return false
283           }
284           $('#step-2-tick').show()
285           $('#step-2-cross').hide()
286           $('#step-2-spinner').hide()
287           document.getElementById('step-2-message').textContent = `Your account with email "${details.email}" has been created.`
288           return true
289         }
290
291         let step_3 = async () => {
292           $('#step-3-tick').hide()
293           $('#step-3-cross').hide()
294           $('#step-3-spinner').show()
295           document.getElementById('step-3').scrollIntoView()
296
297           try {
298             await sign_up_send_email_verification_link(details.email)
299           }
300           catch (error) {
301             let problem =
302               error instanceof Problem ?
303                 error :
304                 new Problem(
305                   // title
306                   'Bad request',
307                   // details
308                   (error.stack || error.message).toString()
309                   // status
310                   400
311                 )
312
313             $('#step-3-tick').hide()
314             $('#step-3-cross').show()
315             $('#step-3-spinner').hide()
316
317             document.getElementById('step-3-message').textContent = problem.detail
318             $('#step-3-collapse').collapse('show')
319             return false
320           }
321           $('#step-3-tick').show()
322           $('#step-3-cross').hide()
323           $('#step-3-spinner').hide()
324
325           document.getElementById('step-3-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
326           return true
327         }
328
329         document.addEventListener(
330           'DOMContentLoaded',
331           () => {
332             document.getElementById('given-names').addEventListener(
333               'change',
334               draft_change_handler
335             )
336             document.getElementById('family-name').addEventListener(
337               'change',
338               draft_change_handler
339             )
340             document.getElementById('email').addEventListener(
341               'change',
342               draft_change_handler
343             )
344             document.getElementById('password').addEventListener(
345               'change',
346               draft_change_handler
347             )
348             document.getElementById('contact-me').addEventListener(
349               'change',
350               draft_change_handler
351             )
352
353             let image_seq = 1
354             document.getElementById('step-1-new-code').addEventListener(
355               'click',
356               () => {
357                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
358                 image_seq += 1
359               }
360             )
361
362             document.getElementById('step-1-continue').addEventListener(
363               'click',
364               async () => {
365                 if (await step_1() && await step_2() && await step_3())
366                   $('#step-3-collapse').collapse('show')
367               }
368             )
369
370             document.getElementById('step-2-back').addEventListener(
371               'click',
372               () => {$('#step-1-collapse').collapse('show')}
373             )
374
375             document.getElementById('step-2-continue').addEventListener(
376               'click',
377               async () => {
378                 if (await step_3())
379                   $('#step-3-collapse').collapse('show')
380               }
381             )
382
383             document.getElementById('step-3-back').addEventListener(
384               'click',
385               () => {$('#step-2-collapse').collapse('show')}
386             )
387
388             document.getElementById('step-3-resend-email').addEventListener(
389               'click',
390               async () => {
391                 if (await step_3())
392                   $('#step-3-collapse').collapse('show')
393               }
394             )
395           }
396         )
397       }
398     }
399   )
400 }