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