Rework /my_account/sign_up/index.html.jst to use custom validation style, reduce...
[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/api_call.js") {}
158
159       script {
160         let draft_timeout_running = false
161         let draft_timeout_handler = async () => {
162           draft_timeout_running = false
163           await api_call(
164             '/api/account/sign_up/set_draft.json',
165             {
166               email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
167               given_names: document.getElementById('given-names').value.slice(0, 256),
168               family_name: document.getElementById('family-name').value.slice(0, 256),
169               contact_me: document.getElementById('contact-me').checked ? true : false
170             }
171           )
172           //console.log('draft', await api_call('/api/account/sign_up/get_draft.json'))
173         }
174         let draft_change_handler = () => {
175           if (!draft_timeout_running) {
176             draft_timeout_running = true
177             setTimeout(draft_timeout_handler, 5000)
178           }
179         }
180
181         let details
182         let card_1 = async () => {
183           if (!document.getElementById('form').checkValidity()) {
184             document.getElementById('form').classList.add('was-validated');
185             $('#card-1-tick').hide()
186             $('#card-1-cross').show()
187             //$('#card-1-spinner').hide()
188             return false
189           }
190           document.getElementById('form').classList.remove('was-validated');
191
192           details = {
193             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
194             given_names: document.getElementById('given-names').value.slice(0, 256),
195             family_name: document.getElementById('family-name').value.slice(0, 256),
196             password: document.getElementById('password').value.slice(0, 256),
197             contact_me: document.getElementById('contact-me').checked ? true : false
198           }
199
200           try {
201             await api_call(
202               '/api/account/sign_up/create_account.json',
203               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
204               details
205             )
206           }
207           catch (error) {
208             let problem = Problem.from(error)
209
210             $('#card-1-tick').hide()
211             $('#card-1-cross').show()
212             $('#card-1-spinner').hide()
213
214             document.getElementById('card-1-message').textContent = problem.detail
215             //document.getElementById('card-1-message').classList.remove('text-success')
216             document.getElementById('card-1-message').classList.add('text-danger')
217             $('#card-1-message').show()
218
219             $('#card-1-collapse').collapse('show')
220             return false
221           }
222           $('#card-1-tick').show()
223           $('#card-1-cross').hide()
224           $('#card-1-spinner').hide()
225           document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
226           //document.getElementById('card-1-message').classList.add('text-success')
227           document.getElementById('card-1-message').classList.remove('text-danger')
228           $('#card-1-message').show()
229           return true
230         }
231
232         let card_2 = async () => {
233           $('#card-2-tick').hide()
234           $('#card-2-cross').hide()
235           $('#card-2-spinner').show()
236           document.getElementById('card-2').scrollIntoView()
237
238           try {
239             await api_call(
240               '/api/account/sign_up/send_email_verification_link.json',
241               details.email
242             )
243           }
244           catch (error) {
245             let problem = Problem.from(error)
246
247             $('#card-2-tick').hide()
248             $('#card-2-cross').show()
249             $('#card-2-spinner').hide()
250
251             document.getElementById('card-2-message').textContent = problem.detail
252             //document.getElementById('card-2-message').classList.remove('text-success')
253             document.getElementById('card-2-message').classList.add('text-danger')
254             $('#card-2-message').show()
255
256             $('#card-2-collapse').collapse('show')
257             return false
258           }
259           $('#card-2-tick').show()
260           $('#card-2-cross').hide()
261           $('#card-2-spinner').hide()
262
263           document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
264           //document.getElementById('card-2-message').classList.add('text-success')
265           document.getElementById('card-2-message').classList.remove('text-danger')
266           $('#card-2-message').show()
267           return true
268         }
269
270         document.addEventListener(
271           'DOMContentLoaded',
272           () => {
273             document.getElementById('given-names').addEventListener(
274               'change',
275               draft_change_handler
276             )
277             document.getElementById('family-name').addEventListener(
278               'change',
279               draft_change_handler
280             )
281             document.getElementById('email').addEventListener(
282               'change',
283               draft_change_handler
284             )
285             document.getElementById('password').addEventListener(
286               'change',
287               draft_change_handler
288             )
289             document.getElementById('contact-me').addEventListener(
290               'change',
291               draft_change_handler
292             )
293
294             let image_seq = 1
295             document.getElementById('card-1-new-code').addEventListener(
296               'click',
297               () => {
298                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
299                 image_seq += 1
300               }
301             )
302
303             document.getElementById('card-1-create-account').addEventListener(
304               'click',
305               async () => {
306                 if (await card_1() && await card_2())
307                   $('#card-2-collapse').collapse('show')
308               }
309             )
310
311             document.getElementById('card-2-back').addEventListener(
312               'click',
313               () => {$('#card-1-collapse').collapse('show')}
314             )
315
316             document.getElementById('card-2-resend-email').addEventListener(
317               'click',
318               async () => {
319                 if (await card_2())
320                   $('#card-2-collapse').collapse('show')
321               }
322             )
323           }
324         )
325       }
326     }
327   )
328 }