From 5833ec486945ed112f1b0c7c49df9fa4c1302e07 Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Mon, 24 Jan 2022 17:15:32 +1100 Subject: [PATCH] Rework /my_account/sign_up/index.html.jst to use custom validation style, reduce to 2 steps (former steps 1 and 2 are now done together and resulting message is displayed in red underneath the step 1 buttons), and improve placeholder text --- css/bootstrap/_card.scss | 6 +- css/bootstrap/_variables.scss | 10 +- my_account/sign_up/index.html.jst | 304 +++++++++++++----------------- 3 files changed, 141 insertions(+), 179 deletions(-) diff --git a/css/bootstrap/_card.scss b/css/bootstrap/_card.scss index a974163..6affdf6 100644 --- a/css/bootstrap/_card.scss +++ b/css/bootstrap/_card.scss @@ -81,7 +81,8 @@ // .card-header { - padding: $card-spacer-y $card-spacer-x; + //padding: $card-spacer-y $card-spacer-x; + padding: $card-spacer-yt $card-spacer-x $card-spacer-yb $card-spacer-x; // Nick margin-bottom: 0; // Removes the default margin-bottom of color: $card-cap-color; background-color: $card-cap-bg; @@ -93,7 +94,8 @@ } .card-footer { - padding: $card-spacer-y $card-spacer-x; + //padding: $card-spacer-y $card-spacer-x; + padding: $card-spacer-yt $card-spacer-x $card-spacer-yb $card-spacer-x; // Nick color: $card-cap-color; background-color: $card-cap-bg; border-top: $card-border-width solid $card-border-color; diff --git a/css/bootstrap/_variables.scss b/css/bootstrap/_variables.scss index a162bd0..a6ba2ed 100644 --- a/css/bootstrap/_variables.scss +++ b/css/bootstrap/_variables.scss @@ -491,7 +491,7 @@ $input-focus-color: $input-color !default; $input-focus-width: $input-btn-focus-width !default; $input-focus-box-shadow: $input-btn-focus-box-shadow !default; -$input-placeholder-color: $gray-600 !default; +$input-placeholder-color: $gray-500 !default; //$gray-600 !default; $input-plaintext-color: $body-color !default; $input-height-border: $input-border-width * 2 !default; @@ -840,6 +840,14 @@ $jumbotron-bg: $gray-200 !default; // Cards +// Nick +// the card header/footer looks slightly wonky because the centring is +// applied after room for ascender and descender, but don't want room +// for descender, therefore set the bottom spacing to be slightly less; +// try to allow enough room overall for a 32px icon to show and hide +$card-spacer-yt: .8rem !default; +$card-spacer-yb: .7rem !default; + $card-spacer-y: .75rem !default; $card-spacer-x: 1.25rem !default; $card-border-width: $border-width !default; diff --git a/my_account/sign_up/index.html.jst b/my_account/sign_up/index.html.jst index 9ca59e2..339458e 100644 --- a/my_account/sign_up/index.html.jst +++ b/my_account/sign_up/index.html.jst @@ -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') } ) } -- 2.34.1