Rework /my_account/sign_up/index.html.jst to use custom validation style, reduce...
[ndcode_site.git] / my_account / sign_up / index.html.jst
index 9ca59e2..339458e 100644 (file)
@@ -1,5 +1,3 @@
-let XDate = require('xdate')
-
 return async env => {
   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
   let get_session = await _require('/_lib/get_session.jst')
@@ -9,27 +7,17 @@ return async env => {
 
   // preload draft details if any
   let transaction = await env.site.database.Transaction()
-  let draft_details
+  let sign_up_draft
   try {
     let root = await transaction.get({})
     let session = await get_session(env, root)
-
-    let sign_up_draft = await session.get('sign_up_draft')
-    draft_details =
-      sign_up_draft !== undefined &&
-        XDate.now() < await sign_up_draft.get_json('expires') ?
-        {
-          email: await sign_up_draft.get_json('email'),
-          given_names: await sign_up_draft.get_json('given_names'),
-          family_name: await sign_up_draft.get_json('family_name'),
-          contact_me: await sign_up_draft.get_json('contact_me')
-        } :
-        null
+    sign_up_draft = await session.get_json('sign_up_draft')
+    if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
+      sign_up_draft = {}
   }
   finally {
     transaction.rollback()
   }
-  console.log('draft_details', JSON.stringify(draft_details))
 
   await navbar(
     env,
@@ -43,148 +31,126 @@ return async env => {
 
       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.'}
 
-      div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
-        div.card#step-1 {
-          div.card-header#step-1-heading(role="tab") {
-            span#step-1-tick(style="display: none;") {
+      div.accordion#accordion(role="tablist" aria-multiselectable="true") {
+        div.card#card-1 {
+          div.card-header#card-1-heading(role="tab") {
+            span#card-1-tick(style="display: none;") {
               span.icon-color.pr-3 {_out.push(icon_tick)}
             }
-            span#step-1-cross(style="display: none;") {
+            span#card-1-cross(style="display: none;") {
               span.icon-color.pr-3 {_out.push(icon_cross)}
             }
-            //span#step-1-spinner(style="display: none;") {
+            //span#card-1-spinner(style="display: none;") {
             //  span.icon-color.pr-3 {
             //    div.spinner-border(role="status") {
             //      span.sr-only {'Loading...'}
             //    }
             //  }
             //}
-            a.h5(data-toggle="collapse" data-parent="#accordion" href="#step-1-collapse" aria-expanded="true" aria-controls="step-1-collapse") {
-              'Your details'
+            a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
+              'Create account'
             }
           }
-          div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
+          div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
             div.card-body {
-              div.row {
-                div.col-md-6 {
-                  div.form-group {
-                    label.form-label(for="given-names") {'Given names *'}
-                    input.form-control#given-names(type="text" value=draft_details ? draft_details.given_names : '' placeholder="Your given names" required="required" maxlength=256) {}
+              form#form {
+                div.row {
+                  div.col-md-6 {
+                    div.form-group {
+                      label.form-label(for="given-names") {'Given names *'}
+                      input.form-control#given-names(type="text" value=sign_up_draft.given_names || '' placeholder="Miley" required maxlength=256) {}
+                      div.invalid-feedback {'Please enter a name we can address you by.'}
+                    }
                   }
-                }
-                div.col-md-6 {
-                  div.form-group {
-                    label.form-label(for="family-name") {'Family name'}
-                    input.form-control#family-name(type="text" value=draft_details ? draft_details.family_name : '' placeholder="Your family name" maxlength=256) {}
+                  div.col-md-6 {
+                    div.form-group {
+                      label.form-label(for="family-name") {'Family name'}
+                      input.form-control#family-name(type="text" value=sign_up_draft.family_name || '' placeholder="Chapman" maxlength=256) {}
+                    }
                   }
                 }
-              }
-              div.row {
-                div.col-md-6 {
-                  div.form-group {
-                    label.form-label(for="email") {'Email *'}
-                    input.form-control#email(type="email" value=draft_details ? draft_details.email : '' placeholder="Your email address" required="required" maxlength=256) {}
+                div.row {
+                  div.col-md-6 {
+                    div.form-group {
+                      label.form-label(for="email") {'Email *'}
+                      input.form-control#email(type="email" value=sign_up_draft.email || '' placeholder="mileychapman@email.com" required maxlength=256) {}
+                      div.invalid-feedback {'Please enter an email address we can contact you on.'}
+                    }
                   }
-                }
-                div.col-md-6 {
-                  div.form-group {
-                    label.form-label(for="password") {'Password *'}
-                    input.form-control#password(type="password" placeholder="New password" required="required" minlength=8 maxlength=256) {}
+                  div.col-md-6 {
+                    div.form-group {
+                      label.form-label(for="password") {'Password *'}
+                      input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
+                      div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
+                    }
                   }
                 }
-              }
-              div.row {
-                div.col-md-12 {
-                  div.custom-control.custom-checkbox {
-                    if (!draft_details || draft_details.contact_me)
-                      input.custom-control-input#contact-me(type="checkbox" checked="checked") {}
-                    else
-                      input.custom-control-input#contact-me(type="checkbox") {}
-                    ' '
-                    label.custom-control-label(for="contact-me") {
-                      'Contact me by email with updates and special offers'
+                div.row {
+                  div.col-md-12 {
+                    div.custom-control.custom-checkbox {
+                      if (sign_up_draft === undefined || sign_up_draft.contact_me)
+                        input.custom-control-input#contact-me(type="checkbox" checked) {}
+                      else
+                        input.custom-control-input#contact-me(type="checkbox") {}
+                      ' '
+                      label.custom-control-label(for="contact-me") {
+                        'Contact me by email with updates and special offers'
+                      }
                     }
                   }
                 }
-              }
-              div.row.align-items-center.mb-3 {
-                div.'col-md-6' {
-                  div.form-group {
-                    label.form-label(for="verification-code") {'Verification code *'}
-                    input.form-control#verification-code(type="text" placeholder="Type the code shown to the right" required="required" minlength=6 maxlength=6) {}
+                div.row.align-items-center {
+                  div.col-md-6 {
+                    div.form-group {
+                      label.form-label(for="verification-code") {'Verification code *'}
+                      input.form-control#verification-code(type="text" placeholder="ad7jb3" required minlength=6 maxlength=6) {}
+                      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.'}
+                    }
+                  }
+                  div.'col-md-6'.my-3 {
+                    img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
                   }
-                }
-                div.'col-md-4' {
-                  img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
-                }
-                div.'col-md-2'.my-auto.text-center {
-                  button.btn.btn-outline-secondary#'step-1-new-code'(type="button") {'New code'}
                 }
               }
 
-              button.btn.btn-success#step-1-continue(type="button") {'Continue'}
-
-              p.'mt-3'.mb-0 {'* These fields are required.'}
-            }
-          }
-        }
-        div.card#step-2 {
-          div.card-header#step-2-heading(role="tab") {
-            span#step-2-tick(style="display: none;") {
-              span.icon-color.pr-3 {_out.push(icon_tick)}
-            }
-            span#step-2-cross(style="display: none;") {
-              span.icon-color.pr-3 {_out.push(icon_cross)}
-            }
-            span#step-2-spinner(style="display: none;") {
-              span.icon-color.pr-3 {
-                div.spinner-border(role="status") {
-                  span.sr-only {'Loading...'}
-                }
-              }
-            }
-            a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-2-collapse" aria-expanded="false" aria-controls="step-2-collapse") {
-              'Create account'
-            }
-          }
-          div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
-            div.card-body {
-              p#step-2-message {'Please enter your details first.'}
+              button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {'New code'}
+              button.btn.btn-success.ml-3#card-1-create-account(type="button") {'Create account'}
 
-              button.btn.btn-outline-secondary#step-2-back(type="button") {'Back'}
-              button.btn.btn-outline-secondary.ml-3#step-2-continue(type="button") {'Continue'}
+              p.'mt-3'.mb-0#card-1-message(style="display: none;") {}
             }
           }
         }
-        div.card#step-3 {
-          div.card-header#step-3-heading(role="tab") {
-            span#step-3-tick(style="display: none;") {
+        div.card#card-2 {
+          div.card-header#card-2-heading(role="tab") {
+            span#card-2-tick(style="display: none;") {
               span.icon-color.pr-3 {_out.push(icon_tick)}
             }
-            span#step-3-cross(style="display: none;") {
+            span#card-2-cross(style="display: none;") {
               span.icon-color.pr-3 {_out.push(icon_cross)}
             }
-            span#step-3-spinner(style="display: none;") {
+            span#card-2-spinner(style="display: none;") {
               span.icon-color.pr-3 {
                 div.spinner-border(role="status") {
                   span.sr-only {'Loading...'}
                 }
               }
             }
-            a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-3-collapse" aria-expanded="false" aria-controls="step-3-collapse") {
+            a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
               'Send email verification link'
             }
           }
-          div#step-3-collapse.collapse(role="tabpanel" aria-labelledby="step-3-heading" data-parent="#accordion") {
+          div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
             div.card-body {
-              p#step-3-message {'Please create your account first.'}
+              button.btn.btn-outline-secondary#card-2-back(type="button") {'Back'}
+              button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button") {'Re-send email'}
 
-              button.btn.btn-outline-secondary#step-3-back(type="button") {'Back'}
-              button.btn.btn-outline-secondary.ml-3#step-3-resend-email(type="button") {'Re-send email'}
+              p.'mt-3'.mb-0#card-2-message(style="display: none;") {}
             }
           }
         }
       }
+
+      p.mt-3 {'* These fields are required.'}
     },
     // scripts
     async _out => {
@@ -213,22 +179,15 @@ return async env => {
         }
 
         let details
-        let step_1 = async () => {
-          if (
-            !document.getElementById('given-names').reportValidity() ||
-              !document.getElementById('family-name').reportValidity() ||
-              !document.getElementById('email').reportValidity() ||
-              !document.getElementById('password').reportValidity() ||
-              !document.getElementById('verification-code').reportValidity()
-          ) {
-            $('#step-1-tick').hide()
-            $('#step-1-cross').show()
-            //$('#step-1-spinner').hide()
+        let card_1 = async () => {
+          if (!document.getElementById('form').checkValidity()) {
+            document.getElementById('form').classList.add('was-validated');
+            $('#card-1-tick').hide()
+            $('#card-1-cross').show()
+            //$('#card-1-spinner').hide()
             return false
           }
-          $('#step-1-tick').show()
-          $('#step-1-cross').hide()
-          //$('#step-1-spinner').hide()
+          document.getElementById('form').classList.remove('was-validated');
 
           details = {
             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
@@ -237,14 +196,6 @@ return async env => {
             password: document.getElementById('password').value.slice(0, 256),
             contact_me: document.getElementById('contact-me').checked ? true : false
           }
-          return true
-        }
-
-        let step_2 = async () => {
-          $('#step-2-tick').hide()
-          $('#step-2-cross').hide()
-          $('#step-2-spinner').show()
-          document.getElementById('step-2').scrollIntoView()
 
           try {
             await api_call(
@@ -256,26 +207,33 @@ return async env => {
           catch (error) {
             let problem = Problem.from(error)
 
-            $('#step-2-tick').hide()
-            $('#step-2-cross').show()
-            $('#step-2-spinner').hide()
+            $('#card-1-tick').hide()
+            $('#card-1-cross').show()
+            $('#card-1-spinner').hide()
+
+            document.getElementById('card-1-message').textContent = problem.detail
+            //document.getElementById('card-1-message').classList.remove('text-success')
+            document.getElementById('card-1-message').classList.add('text-danger')
+            $('#card-1-message').show()
 
-            document.getElementById('step-2-message').textContent = problem.detail
-            $('#step-2-collapse').collapse('show')
+            $('#card-1-collapse').collapse('show')
             return false
           }
-          $('#step-2-tick').show()
-          $('#step-2-cross').hide()
-          $('#step-2-spinner').hide()
-          document.getElementById('step-2-message').textContent = `Your account with email "${details.email}" has been created.`
+          $('#card-1-tick').show()
+          $('#card-1-cross').hide()
+          $('#card-1-spinner').hide()
+          document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
+          //document.getElementById('card-1-message').classList.add('text-success')
+          document.getElementById('card-1-message').classList.remove('text-danger')
+          $('#card-1-message').show()
           return true
         }
 
-        let step_3 = async () => {
-          $('#step-3-tick').hide()
-          $('#step-3-cross').hide()
-          $('#step-3-spinner').show()
-          document.getElementById('step-3').scrollIntoView()
+        let card_2 = async () => {
+          $('#card-2-tick').hide()
+          $('#card-2-cross').hide()
+          $('#card-2-spinner').show()
+          document.getElementById('card-2').scrollIntoView()
 
           try {
             await api_call(
@@ -286,19 +244,26 @@ return async env => {
           catch (error) {
             let problem = Problem.from(error)
 
-            $('#step-3-tick').hide()
-            $('#step-3-cross').show()
-            $('#step-3-spinner').hide()
+            $('#card-2-tick').hide()
+            $('#card-2-cross').show()
+            $('#card-2-spinner').hide()
 
-            document.getElementById('step-3-message').textContent = problem.detail
-            $('#step-3-collapse').collapse('show')
+            document.getElementById('card-2-message').textContent = problem.detail
+            //document.getElementById('card-2-message').classList.remove('text-success')
+            document.getElementById('card-2-message').classList.add('text-danger')
+            $('#card-2-message').show()
+
+            $('#card-2-collapse').collapse('show')
             return false
           }
-          $('#step-3-tick').show()
-          $('#step-3-cross').hide()
-          $('#step-3-spinner').hide()
+          $('#card-2-tick').show()
+          $('#card-2-cross').hide()
+          $('#card-2-spinner').hide()
 
-          document.getElementById('step-3-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
+          document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
+          //document.getElementById('card-2-message').classList.add('text-success')
+          document.getElementById('card-2-message').classList.remove('text-danger')
+          $('#card-2-message').show()
           return true
         }
 
@@ -327,7 +292,7 @@ return async env => {
             )
 
             let image_seq = 1
-            document.getElementById('step-1-new-code').addEventListener(
+            document.getElementById('card-1-new-code').addEventListener(
               'click',
               () => {
                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
@@ -335,37 +300,24 @@ return async env => {
               }
             )
 
-            document.getElementById('step-1-continue').addEventListener(
-              'click',
-              async () => {
-                if (await step_1() && await step_2() && await step_3())
-                  $('#step-3-collapse').collapse('show')
-              }
-            )
-
-            document.getElementById('step-2-back').addEventListener(
-              'click',
-              () => {$('#step-1-collapse').collapse('show')}
-            )
-
-            document.getElementById('step-2-continue').addEventListener(
+            document.getElementById('card-1-create-account').addEventListener(
               'click',
               async () => {
-                if (await step_3())
-                  $('#step-3-collapse').collapse('show')
+                if (await card_1() && await card_2())
+                  $('#card-2-collapse').collapse('show')
               }
             )
 
-            document.getElementById('step-3-back').addEventListener(
+            document.getElementById('card-2-back').addEventListener(
               'click',
-              () => {$('#step-2-collapse').collapse('show')}
+              () => {$('#card-1-collapse').collapse('show')}
             )
 
-            document.getElementById('step-3-resend-email').addEventListener(
+            document.getElementById('card-2-resend-email').addEventListener(
               'click',
               async () => {
-                if (await step_3())
-                  $('#step-3-collapse').collapse('show')
+                if (await card_2())
+                  $('#card-2-collapse').collapse('show')
               }
             )
           }