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