060cee52c92eaff4381cbc3ec46a9b2dea69a245
[ndcode_site.git] / my_account / sign_up / index.html.jst
1 return async env => {
2   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3   let get_session = await _require('/_lib/get_session.jst')
4   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
5   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
6   let navbar = await _require('/_lib/navbar.jst')
7
8   // preload draft details if any
9   let transaction = await env.site.database.Transaction()
10   let sign_up_draft
11   try {
12     let root = await transaction.get({})
13     let session = await get_session(env, root)
14     sign_up_draft = await session.get_json('sign_up_draft')
15     if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
16       sign_up_draft = {}
17   }
18   finally {
19     transaction.rollback()
20   }
21
22   await navbar(
23     env,
24     // head
25     async _out => {},
26     // body
27     async _out => {
28       await breadcrumbs(env, _out)
29
30       p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
31
32       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.'}
33
34       div.accordion#accordion(role="tablist" aria-multiselectable="true") {
35         div.card#card-1 {
36           div.card-header#card-1-heading(role="tab") {
37             span#card-1-tick(style="display: none;") {
38               span.icon-color.pr-3 {_out.push(icon_tick)}
39             }
40             span#card-1-cross(style="display: none;") {
41               span.icon-color.pr-3 {_out.push(icon_cross)}
42             }
43             //span#card-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="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
51               'Create account'
52             }
53           }
54           div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
55             div.card-body {
56               form#form {
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=sign_up_draft.given_names || '' placeholder="Miley" required maxlength=256) {}
62                       div.invalid-feedback {'Please enter a name we can address you by.'}
63                     }
64                   }
65                   div.col-md-6 {
66                     div.form-group {
67                       label.form-label(for="family-name") {'Family name'}
68                       input.form-control#family-name(type="text" value=sign_up_draft.family_name || '' placeholder="Chapman" maxlength=256) {}
69                     }
70                   }
71                 }
72                 div.row {
73                   div.col-md-6 {
74                     div.form-group {
75                       label.form-label(for="email") {'Email *'}
76                       input.form-control#email(type="email" value=sign_up_draft.email || '' placeholder="mileychapman@email.com" required maxlength=256) {}
77                       div.invalid-feedback {'Please enter an email address we can contact you on.'}
78                     }
79                   }
80                   div.col-md-6 {
81                     div.form-group {
82                       label.form-label(for="password") {'Password *'}
83                       input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
84                       div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
85                     }
86                   }
87                 }
88                 div.row {
89                   div.col-md-12 {
90                     div.custom-control.custom-checkbox {
91                       if (sign_up_draft === undefined || sign_up_draft.contact_me)
92                         input.custom-control-input#contact-me(type="checkbox" checked) {}
93                       else
94                         input.custom-control-input#contact-me(type="checkbox") {}
95                       ' '
96                       label.custom-control-label(for="contact-me") {
97                         'Contact me by email with updates and special offers'
98                       }
99                     }
100                   }
101                 }
102                 div.row.align-items-center {
103                   div.col-md-6 {
104                     div.form-group {
105                       label.form-label(for="verification-code") {'Verification code *'}
106                       input.form-control#verification-code(type="text" placeholder="ad7jb3" required minlength=6 maxlength=6) {}
107                       div.invalid-feedback {'Please enter the 6 characters from the verification image to right or below. We need this to protect us from spam and bots.'}
108                     }
109                   }
110                   div.'col-md-6'.my-3 {
111                     img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
112                   }
113                 }
114               }
115
116               button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {'New code'}
117               button.btn.btn-success.ml-3#card-1-create-account(type="button") {'Create account'}
118
119               p.'mt-3'.mb-0#card-1-message(style="display: none;") {}
120             }
121           }
122         }
123         div.card#card-2 {
124           div.card-header#card-2-heading(role="tab") {
125             span#card-2-tick(style="display: none;") {
126               span.icon-color.pr-3 {_out.push(icon_tick)}
127             }
128             span#card-2-cross(style="display: none;") {
129               span.icon-color.pr-3 {_out.push(icon_cross)}
130             }
131             span#card-2-spinner(style="display: none;") {
132               span.icon-color.pr-3 {
133                 div.spinner-border(role="status") {
134                   span.sr-only {'Loading...'}
135                 }
136               }
137             }
138             a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
139               'Send email verification link'
140             }
141           }
142           div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
143             div.card-body {
144               button.btn.btn-outline-secondary#card-2-back(type="button") {'Back'}
145               button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button") {'Re-send email'}
146
147               p.'mt-3'.mb-0#card-2-message(style="display: none;") {}
148             }
149           }
150         }
151       }
152
153       p.mt-3 {'* These fields are required.'}
154     },
155     // scripts
156     async _out => {
157       //script(src="/js/utils.js") {}
158
159       script {
160         let input_semaphore = new BinarySemaphore(false)
161         ;(
162           async () => {
163             while (true) {
164               await input_semaphore.acquire()
165               await new Promise(resolve => setTimeout(resolve, 3000))
166               input_semaphore.try_acquire()
167               await api_call(
168                 '/api/account/sign_up/set_draft.json',
169                 {
170                   email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
171                   given_names: document.getElementById('given-names').value.slice(0, 256),
172                   family_name: document.getElementById('family-name').value.slice(0, 256),
173                   contact_me: document.getElementById('contact-me').checked ? true : false
174                 }
175               )
176             }
177           }
178         )() // ignore returned promise (start thread)
179
180         let details
181         let card_1 = async () => {
182           if (!document.getElementById('form').checkValidity()) {
183             document.getElementById('form').classList.add('was-validated');
184             $('#card-1-tick').hide()
185             $('#card-1-cross').show()
186             //$('#card-1-spinner').hide()
187             return false
188           }
189           document.getElementById('form').classList.remove('was-validated');
190
191           details = {
192             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
193             given_names: document.getElementById('given-names').value.slice(0, 256),
194             family_name: document.getElementById('family-name').value.slice(0, 256),
195             password: document.getElementById('password').value.slice(0, 256),
196             contact_me: document.getElementById('contact-me').checked ? true : false
197           }
198
199           try {
200             await api_call(
201               '/api/account/sign_up/create_account.json',
202               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
203               details
204             )
205           }
206           catch (error) {
207             let problem = Problem.from(error)
208
209             $('#card-1-tick').hide()
210             $('#card-1-cross').show()
211             $('#card-1-spinner').hide()
212
213             document.getElementById('card-1-message').textContent = problem.detail
214             //document.getElementById('card-1-message').classList.remove('text-success')
215             document.getElementById('card-1-message').classList.add('text-danger')
216             $('#card-1-message').show()
217
218             $('#card-1-collapse').collapse('show')
219             return false
220           }
221           $('#card-1-tick').show()
222           $('#card-1-cross').hide()
223           $('#card-1-spinner').hide()
224           document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
225           //document.getElementById('card-1-message').classList.add('text-success')
226           document.getElementById('card-1-message').classList.remove('text-danger')
227           $('#card-1-message').show()
228           return true
229         }
230
231         let card_2 = async () => {
232           $('#card-2-tick').hide()
233           $('#card-2-cross').hide()
234           $('#card-2-spinner').show()
235           document.getElementById('card-2').scrollIntoView()
236
237           try {
238             await api_call(
239               '/api/account/sign_up/send_email_verification_link.json',
240               details.email
241             )
242           }
243           catch (error) {
244             let problem = Problem.from(error)
245
246             $('#card-2-tick').hide()
247             $('#card-2-cross').show()
248             $('#card-2-spinner').hide()
249
250             document.getElementById('card-2-message').textContent = problem.detail
251             //document.getElementById('card-2-message').classList.remove('text-success')
252             document.getElementById('card-2-message').classList.add('text-danger')
253             $('#card-2-message').show()
254
255             $('#card-2-collapse').collapse('show')
256             return false
257           }
258           $('#card-2-tick').show()
259           $('#card-2-cross').hide()
260           $('#card-2-spinner').hide()
261
262           document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
263           //document.getElementById('card-2-message').classList.add('text-success')
264           document.getElementById('card-2-message').classList.remove('text-danger')
265           $('#card-2-message').show()
266           return true
267         }
268
269         document.addEventListener(
270           'DOMContentLoaded',
271           () => {
272             document.getElementById('given-names').addEventListener(
273               'input',
274               () => {input_semaphore.release()}
275             )
276             document.getElementById('family-name').addEventListener(
277               'input',
278               () => {input_semaphore.release()}
279             )
280             document.getElementById('email').addEventListener(
281               'input',
282               () => {input_semaphore.release()}
283             )
284             //document.getElementById('password').addEventListener(
285             //  'input',
286             //  () => {input_semaphore.release()}
287             //)
288             document.getElementById('contact-me').addEventListener(
289               'input',
290               () => {input_semaphore.release()}
291             )
292             //document.getElementById('verification-code').addEventListener(
293             //  'input',
294             //  () => {input_semaphore.release()}
295             //)
296
297             let image_seq = 1
298             document.getElementById('card-1-new-code').addEventListener(
299               'click',
300               () => {
301                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
302                 image_seq += 1
303               }
304             )
305
306             document.getElementById('card-1-create-account').addEventListener(
307               'click',
308               async () => {
309                 if (await card_1() && await card_2())
310                   $('#card-2-collapse').collapse('show')
311               }
312             )
313
314             document.getElementById('card-2-back').addEventListener(
315               'click',
316               () => {$('#card-1-collapse').collapse('show')}
317             )
318
319             document.getElementById('card-2-resend-email').addEventListener(
320               'click',
321               async () => {
322                 if (await card_2())
323                   $('#card-2-collapse').collapse('show')
324               }
325             )
326           }
327         )
328       }
329     }
330   )
331 }