Add /api/sign_up/send_verification_email.json endpoint, add step 3 of sign up process...
[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 {
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                   input.btn.btn-outline-secondary#'new-code'(type="button" value="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               input.btn.btn-success#step-1-continue(type="button" value="Continue") {}
117               p.'mt-3'.mb-0 {'* These fields are required.'}
118             }
119           }
120         }
121         div.card {
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               input.btn.btn-outline-secondary.mr-3#step-2-back(type="button" value="Back") {}
145               input.btn.btn-outline-secondary#step-2-continue(type="button" value="Continue") {}
146             }
147           }
148         }
149         div.card {
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 verification email'
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               input.btn.btn-outline-secondary.mr-3#step-3-back(type="button" value="Back") {}
173               input.btn.btn-outline-secondary#step-3-resend-email(type="button" value="Resend 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_verification_email = async (...arguments) => api_call(
197           '/api/sign_up/send_verification_email.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_3_email = ''
244         let step_2 = async () => {
245           $('#step-2-tick').hide()
246           $('#step-2-cross').hide()
247           $('#step-2-spinner').show()
248           try {
249             let details = coerce_details()
250             await sign_up_create_account(
251               // verification_code
252               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
253               // details
254               details
255             )
256             step_3_email = details.email
257           }
258           catch (e) {
259             $('#step-2-tick').hide()
260             $('#step-2-cross').show()
261             $('#step-2-spinner').hide()
262
263             document.getElementById('step-2-message').textContent = e.message
264             $('#step-2-collapse').collapse('show')
265             return false
266           }
267           $('#step-2-tick').show()
268           $('#step-2-cross').hide()
269           $('#step-2-spinner').hide()
270
271           document.getElementById('step-2-message').textContent = `Your account with email "${document.getElementById('email').value}" has been created.`
272           return true
273         }
274
275         let step_3 = async () => {
276           $('#step-3-tick').hide()
277           $('#step-3-cross').hide()
278           $('#step-3-spinner').show()
279           try {
280             await sign_up_send_verification_email(step_3_email)
281           }
282           catch (e) {
283             $('#step-3-tick').hide()
284             $('#step-3-cross').show()
285             $('#step-3-spinner').hide()
286
287             document.getElementById('step-3-message').textContent = e.message
288             $('#step-3-collapse').collapse('show')
289             return false
290           }
291           $('#step-3-tick').show()
292           $('#step-3-cross').hide()
293           $('#step-3-spinner').hide()
294
295           document.getElementById('step-3-message').textContent = 'Verification email has been sent. Please check your email for next steps.'
296           return true
297         }
298  
299         document.addEventListener(
300           'DOMContentLoaded', 
301           () => {
302             document.getElementById('given-names').addEventListener(
303               'change',
304               draft_change_handler
305             )
306             document.getElementById('family-name').addEventListener(
307               'change',
308               draft_change_handler
309             )
310             document.getElementById('email').addEventListener(
311               'change',
312               draft_change_handler
313             )
314             document.getElementById('password').addEventListener(
315               'change',
316               draft_change_handler
317             )
318             document.getElementById('contact-me').addEventListener(
319               'change',
320               draft_change_handler
321             )
322
323             document.getElementById('step-1-continue').addEventListener(
324               'click',
325               async () => {
326                 if (await step_1() && await step_2() && await step_3())
327                   $('#step-3-collapse').collapse('show')
328               }
329             )
330
331             document.getElementById('step-2-back').addEventListener(
332               'click',
333               () => {$('#step-1-collapse').collapse('show')}
334             )
335
336             document.getElementById('step-2-continue').addEventListener(
337               'click',
338               async () => {
339                 if (await step_3())
340                   $('#step-3-collapse').collapse('show')
341               }
342            )
343
344             document.getElementById('step-3-back').addEventListener(
345               'click',
346               () => {$('#step-2-collapse').collapse('show')}
347             )
348
349             document.getElementById('step-3-resend-email').addEventListener(
350               'click',
351               async () => {
352                 if (await step_3())
353                   $('#step-3-collapse').collapse('show')
354               }
355             )
356
357             let image_seq = 1
358             $('#new-code').click(
359               () => {
360                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
361                 image_seq += 1
362               }
363             ) 
364           }
365         )
366       }
367     }
368   )
369 }