Change arguments to args everywhere, as arguments is a reserved word in strict
[ndcode_site.git] / my_account / index.html.jst
1 let logjson = (await import('@ndcode/logjson')).default
2 let XDate = require('xdate')
3
4 return async env => {
5   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
6   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
7   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
8   let menu = await env.site.get_menu('/my_account/_menu.json')
9   let navbar = await _require('/_lib/navbar.jst')
10   let session_cookie = await _require('/_lib/session_cookie.jst')
11
12   // see whether signed in, if so preload details, and draft details if any
13   let
14     transaction = await env.site.database.Transaction(),
15     details,
16     draft_details
17   try {
18     // initialize env.session_key, set cookie in env.response
19     let session = await session_cookie(env, transaction)
20     if (env.signed_in_as) {
21       account = await (
22         await (
23           await transaction.get({})
24         ).get('accounts')
25       ).get(env.signed_in_as)
26       details = {
27         given_names: await logjson.logjson_to_json(
28           await account.get('given_names')
29         ),
30         family_name: await logjson.logjson_to_json(
31           await account.get('family_name')
32         ),
33         contact_me: await logjson.logjson_to_json(
34           await account.get('contact_me')
35         )
36       }
37
38       let change_details_draft = await session.get('change_details_draft')
39       draft_details =
40         change_details_draft !== undefined &&
41           XDate.now() < await logjson.logjson_to_json(
42             await change_details_draft.get('expires')
43           ) ? {
44             given_names: await logjson.logjson_to_json(
45               await change_details_draft.get('given_names')
46             ),
47             family_name: await logjson.logjson_to_json(
48               await change_details_draft.get('family_name')
49             ),
50             contact_me: await logjson.logjson_to_json(
51               await change_details_draft.get('contact_me')
52             )
53           } : null
54     }
55     await transaction.commit()
56   }
57   catch (error) {
58     transaction.rollback()
59     throw error
60   }
61   console.log(
62     'details',
63     JSON.stringify(details),
64     'draft_details',
65     JSON.stringify(draft_details)
66   )
67
68   await navbar(
69     env,
70     // head
71     async _out => {},
72     // body
73     async _out => {
74       await breadcrumbs(env, _out)
75
76       if (env.signed_in_as) {
77         // signed in
78         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.'}
79
80         div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
81           div.card#step-1 {
82             div.card-header#step-1-heading(role="tab") {
83               span#step-1-tick(style="display: none;") {
84                 span.icon-color.pr-3 {_out.push(icon_tick)}
85               }
86               span#step-1-cross(style="display: none;") {
87                 span.icon-color.pr-3 {_out.push(icon_cross)}
88               }
89               //span#step-1-spinner(style="display: none;") {
90               //  span.icon-color.pr-3 {
91               //    div.spinner-border(role="status") {
92               //      span.sr-only {'Loading...'}
93               //    }
94               //  }
95               //}
96               a.h5(data-toggle="collapse" data-parent="#accordion" href="#step-1-collapse" aria-expanded="true" aria-controls="step-1-collapse") {
97                 'Change details'
98               }
99             }
100             div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
101               div.card-body {
102                 div.row {
103                   div.col-md-6 {
104                     div.form-group {
105                       label.form-label(for="given-names") {'Given names *'}
106                       input.form-control#given-names(type="text" value=draft_details ? draft_details.given_names : details.given_names placeholder="Your given names" required="required" maxlength=256) {}
107                     }
108                   }
109                   div.col-md-6 {
110                     div.form-group {
111                       label.form-label(for="family-name") {'Family name'}
112                       input.form-control#family-name(type="text" value=draft_details ? draft_details.family_name : details.family_name placeholder="Your family name" maxlength=256) {}
113                     }
114                   }
115                 }
116                 div.row.mb-3 {
117                   div.col-md-12 {
118                     div.custom-control.custom-checkbox {
119                       if (draft_details ? draft_details.contact_me : details.contact_me)
120                         input.custom-control-input#contact-me(type="checkbox" checked="checked") {}
121                       else
122                         input.custom-control-input#contact-me(type="checkbox") {}
123                       ' '
124                       label.custom-control-label(for="contact-me") {
125                         'Contact me by email with updates and special offers'
126                       }
127                     }
128                   }
129                 }
130
131                 if (draft_details)
132                   button.btn.btn-outline-secondary#step-1-revert(type="button") {'Revert'}
133                 else
134                   button.btn.btn-outline-secondary#step-1-revert(type="button" disabled="disabled") {'Revert'}
135                 if (draft_details)
136                   button.btn.btn-success.ml-3#step-1-save(type="button") {'Save'}
137                 else
138                   button.btn.btn-success.ml-3#step-1-save(type="button" disabled="disabled") {'Save'}
139
140                 p.'mt-3'.mb-0 {'* These fields are required.'}
141               }
142             }
143           }
144           div.card#step-2 {
145             div.card-header#step-2-heading(role="tab") {
146               span#step-2-tick(style="display: none;") {
147                 span.icon-color.pr-3 {_out.push(icon_tick)}
148               }
149               span#step-2-cross(style="display: none;") {
150                 span.icon-color.pr-3 {_out.push(icon_cross)}
151               }
152               span#step-2-spinner(style="display: none;") {
153                 span.icon-color.pr-3 {
154                   div.spinner-border(role="status") {
155                     span.sr-only {'Loading...'}
156                   }
157                 }
158               }
159               a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-2-collapse" aria-expanded="false" aria-controls="step-2-collapse") {
160                 'Change password'
161               }
162             }
163             div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
164               div.card-body {
165                 div.row.mb-3 {
166                   div.col-md-6 {
167                     div.form-group {
168                       label.form-label(for="old-password") {'Old password *'}
169                       input.form-control#old-password(type="password" placeholder="Old password" required="required" minlength=8 maxlength=256) {}
170                     }
171                   }
172                   div.col-md-6 {
173                     div.form-group {
174                       label.form-label(for="new-password") {'New password *'}
175                       input.form-control#'new-password'(type="password" placeholder="New password" required="required" minlength=8 maxlength=256) {}
176                     }
177                   }
178                 }
179
180                 button.btn.btn-outline-secondary#step-2-clear(type="button" disabled="disabled") {'Clear'}
181                 button.btn.btn-success.ml-3#step-2-save(type="button" disabled="disabled") {'Save'}
182
183                 p.'mt-3'.mb-0 {'* These fields are required.'}
184               }
185             }
186           }
187         }
188       }
189       else {
190         // signed out
191         p {'For account maintenance, please click on one of the options below.'}
192
193         ul.nav.flex-column {
194           let entries = menu.entries
195           for (let i = 0; i < entries.length; ++i) {
196             let entry = entries[i]
197             if (Object.prototype.hasOwnProperty.call(entry, 'icon'))
198               li.nav-item {
199                 a.nav-link(href=`${entry.dir}/index.html`) {
200                   table.icon-and-text {
201                     tr {
202                       td {
203                         _out.push(await env.site.get_min_svg(entry.icon))
204                       }
205                       td {
206                         span.h2{`${entry.name}`}
207                       }
208                     }
209                   }
210                 }
211               }
212           }
213         }
214       }
215     },
216     // scripts
217     async _out => {
218       script {
219         // this will be called by navbar logic after sign in/out
220         function sign_in_out(status) {
221           window.location.reload()
222         }
223       }
224
225       if (env.signed_in_as) {
226         //script(src="/js/api_call.js") {}
227
228         script {
229           let api_account_change_details_get = async (...args) => api_call(
230             '/api/account/change_details/get.json',
231             ...args
232           )
233           let api_account_change_details_set = async (...args) => api_call(
234             '/api/account/change_details/set.json',
235             ...args
236           )
237           //let api_account_change_details_get_draft = async (...args) => api_call(
238           //  '/api/account/change_details/get_draft.json',
239           //  ...args
240           //)
241           let api_account_change_details_set_draft = async (...args) => api_call(
242             '/api/account/change_details/set_draft.json',
243             ...args
244           )
245           let api_account_change_password = async (...args) => api_call(
246             '/api/account/change_password.json',
247             ...args
248           )
249
250           let step_1_dirty = ${JSON.stringify(draft_details !== null)}
251           let draft_timeout_running = false
252           let draft_timeout_handler = async () => {
253             draft_timeout_running = false
254             await api_account_change_details_set_draft(
255               step_1_dirty ? {
256                 given_names: document.getElementById('given-names').value.slice(0, 256),
257                 family_name: document.getElementById('family-name').value.slice(0, 256),
258                 contact_me: document.getElementById('contact-me').checked ? true : false
259               } : null
260             )
261             //console.log('draft', await api_account_change_details_get_draft())
262           }
263
264           document.addEventListener(
265             'DOMContentLoaded',
266             () => {
267               let step_1_change_handler = () => {
268                 step_1_dirty = true
269                 document.getElementById('step-1-revert').disabled = false
270                 document.getElementById('step-1-save').disabled = false
271     
272                 if (!draft_timeout_running) {
273                   draft_timeout_running = true
274                   setTimeout(draft_timeout_handler, 5000)
275                 }
276               }
277               document.getElementById('given-names').addEventListener(
278                 'change',
279                 step_1_change_handler
280               )
281               document.getElementById('family-name').addEventListener(
282                 'change',
283                 step_1_change_handler
284               )
285               document.getElementById('contact-me').addEventListener(
286                 'change',
287                 step_1_change_handler
288               )
289
290               document.getElementById('step-1-revert').addEventListener(
291                 'click',
292                 async () => {
293                   $('#step-1-tick').hide()
294                   $('#step-1-cross').hide()
295                   $('#step-1-spinner').show()
296
297                   let details      
298                   try {
299                     details = await api_account_change_details_get()
300                   }
301                   catch (error) {
302                     let problem =
303                       error instanceof Problem ?
304                         error :
305                         new Problem(
306                           // title
307                           'Bad request',
308                           // detail
309                           (error.stack || error.message).toString()
310                           // status
311                           400
312                         )
313                     console.log(problem.detail)
314       
315                     $('#step-1-tick').hide()
316                     $('#step-1-cross').show()
317                     $('#step-1-spinner').hide()
318                     return
319                   }
320                   $('#step-1-tick').hide()
321                   $('#step-1-cross').hide()
322                   $('#step-1-spinner').hide()
323
324                   step_1_dirty = false
325                   document.getElementById('step-1-revert').disabled = true
326                   document.getElementById('step-1-save').disabled = true
327
328                   document.getElementById('given-names').value = details.given_names
329                   document.getElementById('family-name').value = details.family_name
330                   document.getElementById('contact-me').checked = details.contact_me
331
332                   if (!draft_timeout_running) {
333                     draft_timeout_running = true
334                     setTimeout(draft_timeout_handler, 5000)
335                   }
336                 }
337               )
338
339               document.getElementById('step-1-save').addEventListener(
340                 'click',
341                 async () => {
342                   if (
343                     !document.getElementById('given-names').reportValidity() ||
344                       !document.getElementById('family-name').reportValidity()
345                   ) {
346                     $('#step-1-tick').hide()
347                     $('#step-1-cross').show()
348                     $('#step-1-spinner').hide()
349                     return false
350                   }
351                   $('#step-1-tick').hide()
352                   $('#step-1-cross').hide()
353                   $('#step-1-spinner').show()
354       
355                   try {
356                     await api_account_change_details_set(
357                       {
358                         given_names: document.getElementById('given-names').value.slice(0, 256),
359                         family_name: document.getElementById('family-name').value.slice(0, 256),
360                         contact_me: document.getElementById('contact-me').checked ? true : false
361                       }
362                     )
363                   }
364                   catch (error) {
365                     let problem =
366                       error instanceof Problem ?
367                         error :
368                         new Problem(
369                           // title
370                           'Bad request',
371                           // detail
372                           (error.stack || error.message).toString()
373                           // status
374                           400
375                         )
376                     console.log(problem.detail)
377       
378                     $('#step-1-tick').hide()
379                     $('#step-1-cross').show()
380                     $('#step-1-spinner').hide()
381                     return
382                   }
383                   $('#step-1-tick').show()
384                   $('#step-1-cross').hide()
385                   $('#step-1-spinner').hide()
386
387                   step_1_dirty = false
388                   document.getElementById('step-1-revert').disabled = true
389                   document.getElementById('step-1-save').disabled = true
390
391                   // SHOULD execute immediately here
392                   // (because user is likely to leave the page after save)
393                   if (!draft_timeout_running) {
394                     draft_timeout_running = true
395                     setTimeout(draft_timeout_handler, 5000)
396                   }
397                 }
398               )
399
400               let step_2_change_handler = () => {
401                 document.getElementById('step-2-clear').disabled = false
402                 document.getElementById('step-2-save').disabled = false
403               }
404               document.getElementById('old-password').addEventListener(
405                 'change',
406                 step_2_change_handler
407               )
408               document.getElementById('new-password').addEventListener(
409                 'change',
410                 step_2_change_handler
411               )
412
413               document.getElementById('step-2-clear').addEventListener(
414                 'click',
415                 () => {
416                   document.getElementById('step-2-clear').disabled = true
417                   document.getElementById('step-2-save').disabled = true
418
419                   document.getElementById('old-password').value = ''
420                   document.getElementById('new-password').value = ''
421                 }
422               )
423
424               document.getElementById('step-2-save').addEventListener(
425                 'click',
426                 async () => {
427                   if (
428                     !document.getElementById('old-password').reportValidity() ||
429                       !document.getElementById('new-password').reportValidity()
430                   ) {
431                     $('#step-2-tick').hide()
432                     $('#step-2-cross').show()
433                     $('#step-2-spinner').hide()
434                     return false
435                   }
436                   $('#step-2-tick').hide()
437                   $('#step-2-cross').hide()
438                   $('#step-2-spinner').show()
439       
440                   try {
441                     await api_account_change_password(
442                       // old_password
443                       document.getElementById('old-password').value.slice(0, 256),
444                       // new_password
445                       document.getElementById('new-password').value.slice(0, 256)
446                     )
447                   }
448                   catch (error) {
449                     let problem =
450                       error instanceof Problem ?
451                         error :
452                         new Problem(
453                           // title
454                           'Bad request',
455                           // detail
456                           (error.stack || error.message).toString()
457                           // status
458                           400
459                         )
460                     console.log(problem.detail)
461       
462                     $('#step-2-tick').hide()
463                     $('#step-2-cross').show()
464                     $('#step-2-spinner').hide()
465                     return
466                   }
467                   $('#step-2-tick').show()
468                   $('#step-2-cross').hide()
469                   $('#step-2-spinner').hide()
470
471                   document.getElementById('step-2-clear').disabled = true
472                   document.getElementById('step-2-save').disabled = true
473
474                   document.getElementById('old-password').value = ''
475                   document.getElementById('new-password').value = ''
476                 }
477               )
478             }
479           )
480         }
481       }
482     }
483   )
484 }