-let XDate = require('xdate')
-
return async env => {
let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
+ let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
+ let get_placeholder = await _require('/_lib/get_placeholder.jst')
let get_session = await _require('/_lib/get_session.jst')
let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
// preload draft details if any
let transaction = await env.site.database.Transaction()
- let draft_details
+ let placeholder
+ let contact_draft
try {
let root = await transaction.get({})
let session = await get_session(env, root)
- let contact_draft = await session.get('contact_draft')
- draft_details =
- contact_draft !== undefined &&
- XDate.now() < await contact_draft.get_json('expires') ?
- {
- email: await contact_draft.get_json('email'),
- given_names: await contact_draft.get_json('given_names'),
- family_name: await contact_draft.get_json('family_name'),
- company: await contact_draft.get_json('company'),
- email: await contact_draft.get_json('email'),
- message: await contact_draft.get_json('message')
- } :
- null
+ placeholder = await get_placeholder(env, session)
+
+ contact_draft = await session.get_json('contact_draft')
+ if (contact_draft === undefined || env.now >= contact_draft.expires)
+ contact_draft = null
+ transaction.commit()
}
- finally {
+ catch (error) {
transaction.rollback()
+ throw error
}
- console.log('draft_details', JSON.stringify(draft_details))
await navbar(
env,
p {'Do you require more information, or assistance with integrating the projects on this site? We’d love to hear from you.'}
- 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;") {
- span.icon-color.pr-3 {_out.push(icon_tick)}
- }
- span#step-1-cross(style="display: none;") {
- span.icon-color.pr-3 {_out.push(icon_cross)}
- }
- //span#step-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") {
- 'Enquiry details'
+ 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=contact_draft ? contact_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
+ div.invalid-feedback {'Please enter a name we can address you by.'}
}
}
- div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-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) {}
- }
- }
- 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.row {
- div.col-md-6 {
- div.form-group {
- label.form-label(for="company") {'Company'}
- input.form-control#company(type="company" value=draft_details ? draft_details.company : '' placeholder="Your company" maxlength=256) {}
- }
- }
- 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-12 {
- div.form-group {
- label.form-label(for="message") {'Message *'}
- textarea.form-control#message(placeholder="Your message" required="required" rows=6 maxlength=65536) {
- if (draft_details)
- `${draft_details.message}`
- }
- }
- }
- }
-
- button.btn.btn-success#step-1-continue(type="button") {'Continue'}
- p.'mt-3'.mb-0 {'* These fields are required.'}
+ div.col-md-6 {
+ div.form-group {
+ label.form-label(for="family-name") {'Family name'}
+ input.form-control#family-name(type="text" value=contact_draft ? contact_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
}
}
}
- 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)}
+ div.row {
+ div.col-md-6 {
+ div.form-group {
+ label.form-label(for="company") {'Company'}
+ input.form-control#company(type="company" value=contact_draft ? contact_draft.company : '' placeholder=placeholder.company maxlength=256) {}
}
- 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") {
- 'Send enquiry'
+ }
+ div.col-md-6 {
+ div.form-group {
+ label.form-label(for="email") {'Email *'}
+ input.form-control#email(type="email" value=contact_draft ? contact_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
+ div.invalid-feedback {'Please enter an email address we can contact you on.'}
}
}
- div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
- div.card-body {
- p#step-2-message {'Please enter enquiry details first.'}
-
- button.btn.btn-outline-secondary#step-2-back(type="button") {'Back'}
- button.btn.btn-outline-secondary.ml-3#step-2-resend-enquiry(type="button") {'Re-send enquiry'}
+ }
+ div.row {
+ div.col-md-12 {
+ div.form-group {
+ label.form-label(for="message1") {'Message *'}
+ textarea.form-control#message1(placeholder="I would like to..." required rows=6 maxlength=65536) {
+ if (contact_draft)
+ `${contact_draft.message}`
+ }
+ div.invalid-feedback {'Please let us know your application or question.'}
}
}
}
}
- },
- // scripts
- async _out => {
- //script(src="/js/utils.js") {}
- script {
- let draft_timeout_running = false
- let draft_timeout_handler = async () => {
- draft_timeout_running = false
- await api_call(
- '/api/contact/set_draft.json',
- {
- given_names: document.getElementById('given-names').value.slice(0, 256),
- family_name: document.getElementById('family-name').value.slice(0, 256),
- company: document.getElementById('company').value.slice(0, 256),
- email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
- message: document.getElementById('message').value.slice(0, 65536)
+ if (contact_draft !== null)
+ button.btn.btn-success#send-enquiry(type="button") {
+ div.icon24-outer.mr-2#icon {
+ div.icon24-inner {_out.push(fa_envelope)}
+ }
+ div.icon24-outer.mr-2#tick(hidden) {
+ div.icon24-inner {_out.push(icon_tick)}
+ }
+ div.icon24-outer.mr-2#cross(hidden) {
+ div.icon24-inner {_out.push(icon_cross)}
+ }
+ div.icon24-outer.mr-2#spinner(hidden) {
+ div.icon24-inner {
+ div.spinner-border.spinner-border-sm(role="status") {}
}
- )
- //console.log('draft', await api_call('/api/contact/get_draft.json'))
- }
- let draft_change_handler = () => {
- if (!draft_timeout_running) {
- draft_timeout_running = true
- setTimeout(draft_timeout_handler, 5000)
}
+ 'Send enquiry'
}
-
- let details
- let step_1 = async () => {
- if (
- !document.getElementById('given-names').reportValidity() ||
- !document.getElementById('family-name').reportValidity() ||
- !document.getElementById('company').reportValidity() ||
- !document.getElementById('email').reportValidity() ||
- !document.getElementById('message').reportValidity()
- ) {
- $('#step-1-tick').hide()
- $('#step-1-cross').show()
- //$('#step-1-spinner').hide()
- return false
+ else
+ button.btn.btn-success#send-enquiry(type="button" disabled) {
+ div.icon24-outer.mr-2#icon {
+ div.icon24-inner {_out.push(fa_envelope)}
}
- $('#step-1-tick').show()
- $('#step-1-cross').hide()
- //$('#step-1-spinner').hide()
-
- details = {
- given_names: document.getElementById('given-names').value.slice(0, 256),
- family_name: document.getElementById('family-name').value.slice(0, 256),
- company: document.getElementById('company').value.slice(0, 256),
- email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
- message: document.getElementById('message').value.slice(0, 65536)
+ div.icon24-outer.mr-2#tick(hidden) {
+ div.icon24-inner {_out.push(icon_tick)}
}
- 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(
- '/api/contact/send_enquiry.json',
- details
- )
+ div.icon24-outer.mr-2#cross(hidden) {
+ div.icon24-inner {_out.push(icon_cross)}
}
- catch (error) {
- let problem = Problem.from(error)
-
- $('#step-2-tick').hide()
- $('#step-2-cross').show()
- $('#step-2-spinner').hide()
-
- document.getElementById('step-2-message').textContent = problem.detail
- $('#step-2-collapse').collapse('show')
- return false
+ div.icon24-outer.mr-2#spinner(hidden) {
+ div.icon24-inner {
+ div.spinner-border.spinner-border-sm(role="status") {}
+ }
}
- $('#step-2-tick').show()
- $('#step-2-cross').hide()
- $('#step-2-spinner').hide()
-
- document.getElementById('step-2-message').textContent = 'We have received your enquiry. We will be in touch as soon as possible.'
- return true
+ 'Send enquiry'
}
+ p.'mt-3'.mb-0#message(hidden) {}
+
+ p.text-muted.mt-3 {'* These fields are required.'}
+ },
+ // scripts
+ async _out => {
+ //script(src="/js/utils.js") {}
+
+ script {
document.addEventListener(
'DOMContentLoaded',
() => {
- document.getElementById('given-names').addEventListener(
- 'change',
- draft_change_handler
- )
- document.getElementById('family-name').addEventListener(
- 'change',
- draft_change_handler
- )
- document.getElementById('company').addEventListener(
- 'change',
- draft_change_handler
- )
- document.getElementById('email').addEventListener(
- 'change',
- draft_change_handler
- )
- document.getElementById('message').addEventListener(
- 'change',
- draft_change_handler
- )
+ let id_company = document.getElementById('company')
+ let id_cross = document.getElementById('cross')
+ let id_email = document.getElementById('email')
+ let id_family_name = document.getElementById('family-name')
+ let id_form = document.getElementById('form')
+ let id_given_names = document.getElementById('given-names')
+ let id_icon = document.getElementById('icon')
+ let id_message = document.getElementById('message')
+ let id_message1 = document.getElementById('message1')
+ let id_send_enquiry = document.getElementById('send-enquiry')
+ let id_spinner = document.getElementById('spinner')
+ let id_tick = document.getElementById('tick')
- document.getElementById('step-1-continue').addEventListener(
- 'click',
+ let input_semaphore = new BinarySemaphore(false)
+ ;(
async () => {
- if (await step_1() && await step_2())
- $('#step-2-collapse').collapse('show')
+ while (true) {
+ await input_semaphore.acquire()
+ await new Promise(resolve => setTimeout(resolve, 3000))
+ input_semaphore.try_acquire()
+ await api_call(
+ '/api/contact/set_draft.json',
+ id_given_names.value.length === 0 &&
+ id_family_name.value.length === 0 &&
+ id_company.value.length === 0 &&
+ id_email.value.length === 0 &&
+ id_message1.value.length === 0 ?
+ null :
+ {
+ given_names: id_given_names.value.slice(0, 256),
+ family_name: id_family_name.value.slice(0, 256),
+ company: id_company.value.slice(0, 256),
+ email: id_email.value.slice(0, 256).toLowerCase(),
+ message: id_message1.value.slice(0, 65536)
+ }
+ )
+ }
}
- )
+ )() // ignore returned promise (start thread)
- document.getElementById('step-2-back').addEventListener(
- 'click',
- () => {$('#step-1-collapse').collapse('show')}
- )
+ let edited = () => {
+ input_semaphore.release()
- document.getElementById('step-2-resend-enquiry').addEventListener(
+ id_send_enquiry.disabled =
+ id_given_names.value.length === 0 &&
+ id_family_name.value.length === 0 &&
+ id_company.value.length === 0 &&
+ id_email.value.length === 0 &&
+ id_message1.value.length === 0
+ id_icon.hidden = false
+ id_tick.hidden = true
+ id_cross.hidden = true
+ id_spinner.hidden = true
+ id_message.hidden = true
+ }
+
+ id_given_names.addEventListener('input', edited)
+ id_family_name.addEventListener('input', edited)
+ id_company.addEventListener('input', edited)
+ id_email.addEventListener('input', edited)
+ id_message1.addEventListener('input', edited)
+
+ id_send_enquiry.addEventListener(
'click',
async () => {
- if (await step_2())
- $('#step-2-collapse').collapse('show')
+ id_icon.hidden = false
+ id_tick.hidden = true
+ id_cross.hidden = true
+ id_spinner.hidden = true
+ // the below causes an ugly flicker, so just keep the message
+ //id_message.hidden = true
+
+ if (!id_form.checkValidity()) {
+ id_form.classList.add('was-validated');
+
+ id_icon.hidden = true
+ id_cross.hidden = false
+ return
+ }
+ id_form.classList.remove('was-validated');
+
+ let details = {
+ given_names: id_given_names.value.slice(0, 256),
+ family_name: id_family_name.value.slice(0, 256),
+ company: id_company.value.slice(0, 256),
+ email: id_email.value.slice(0, 256).toLowerCase(),
+ message: id_message1.value.slice(0, 65536)
+ }
+
+ id_icon.hidden = true
+ id_spinner.hidden = false
+ try {
+ await api_call(
+ '/api/contact/send_enquiry.json',
+ details
+ )
+ }
+ catch (error) {
+ let problem = Problem.from(error)
+
+ id_cross.hidden = false
+ id_spinner.hidden = true
+
+ id_message.textContent = problem.detail
+ //id_message.classList.remove('text-success')
+ id_message.classList.add('text-danger')
+ id_message.hidden = false
+ return
+ }
+ id_tick.hidden = false
+ id_spinner.hidden = true
+ id_message.textContent = 'We have received your enquiry. We will be in touch as soon as possible.'
+ //id_message.classList.add('text-success')
+ id_message.classList.remove('text-danger')
+ id_message.hidden = false
}
)
}