Upgrade to nick_site commit f981fa57, adds alerts and inactive sidebar option
[ndcode_site.git] / contact / index.html.jst
1 return async env => {
2   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3   let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
4   let get_placeholder = await _require('/_lib/get_placeholder.jst')
5   let get_session = await _require('/_lib/get_session.jst')
6   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
7   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
8   let navbar = await _require('/_lib/navbar.jst')
9
10   // preload draft details if any
11   let transaction = await env.site.database.Transaction()
12   let placeholder
13   let contact_draft
14   try {
15     let root = await transaction.get()
16     let session = await get_session(env, root)
17
18     placeholder = await get_placeholder(env, session)
19
20     contact_draft = await session.get_json('contact_draft')
21     if (contact_draft === undefined || env.now >= contact_draft.expires)
22       contact_draft = null
23
24     await transaction.commit()
25   }
26   catch (error) {
27     transaction.rollback()
28     throw error
29   }
30
31   await navbar(
32     env,
33     // head
34     async _out => {},
35     // body
36     async _out => {
37       await breadcrumbs(env, _out)
38
39       p/*.mt-3*/ {'Do you require more information, or assistance with integrating the projects on this site? We’d love to hear from you.'}
40
41       form#form {
42         div.row {
43           div.col-md-6 {
44             div.form-group {
45               label.form-label(for="given-names") {'Given names *'}
46               input.form-control#given-names(type="text" value=contact_draft ? contact_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
47               div.invalid-feedback {'Please enter a name we can address you by.'}
48             }
49           }
50           div.col-md-6 {
51             div.form-group {
52               label.form-label(for="family-name") {'Family name'}
53               input.form-control#family-name(type="text" value=contact_draft ? contact_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
54             }
55           }
56         }
57         div.row {
58           div.col-md-6 {
59             div.form-group {
60              label.form-label(for="company") {'Company'}
61               input.form-control#company(type="company" value=contact_draft ? contact_draft.company : '' placeholder=placeholder.company maxlength=256) {}
62             }
63           }
64           div.col-md-6 {
65             div.form-group {
66              label.form-label(for="email") {'Email *'}
67               input.form-control#email(type="email" value=contact_draft ? contact_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
68               div.invalid-feedback {'Please enter an email address we can contact you on.'}
69             }
70           }
71         }
72         div.row {
73           div.col-md-12 {
74             div.form-group {
75               label.form-label(for="message") {'Message *'}
76               textarea.form-control#message(placeholder="I am interested in..." required rows=6 maxlength=65536) {
77                 if (contact_draft)
78                   `${contact_draft.message}`
79               }
80               div.invalid-feedback {'Please let us know your application or question.'}
81             }
82           }
83         }
84       }
85
86       if (contact_draft !== null)
87         button.btn.btn-success.mb-3#send-enquiry(type="button") {
88           div.icon24-outer.mr-2#icon {
89             div.icon24-inner {_out.push(fa_envelope)}
90           }
91           div.icon24-outer.mr-2#tick(hidden) {
92             div.icon24-inner {_out.push(icon_tick)}
93           }
94           div.icon24-outer.mr-2#cross(hidden) {
95             div.icon24-inner {_out.push(icon_cross)}
96           }
97           div.icon24-outer.mr-2#spinner(hidden) {
98             div.icon24-inner {
99               div.spinner-border.spinner-border-sm(role="status") {}
100             }
101           }
102           'Send enquiry'
103         }
104       else
105         button.btn.btn-success.mb-3#send-enquiry(type="button" disabled) {
106           div.icon24-outer.mr-2#icon {
107             div.icon24-inner {_out.push(fa_envelope)}
108           }
109           div.icon24-outer.mr-2#tick(hidden) {
110             div.icon24-inner {_out.push(icon_tick)}
111           }
112           div.icon24-outer.mr-2#cross(hidden) {
113             div.icon24-inner {_out.push(icon_cross)}
114           }
115           div.icon24-outer.mr-2#spinner(hidden) {
116             div.icon24-inner {
117               div.spinner-border.spinner-border-sm(role="status") {}
118             }
119           }
120           'Send enquiry'
121         }
122
123       div.alert#alert(hidden) {}
124
125       p.text-muted {'* These fields are required.'}
126     },
127     // scripts
128     async _out => {
129       //script(src="/js/utils.js") {}
130
131       script {
132         document.addEventListener(
133           'DOMContentLoaded',
134           () => {
135             let id_alert = document.getElementById('alert')
136             let id_company = document.getElementById('company')
137             let id_cross = document.getElementById('cross')
138             let id_email = document.getElementById('email')
139             let id_family_name = document.getElementById('family-name')
140             let id_form = document.getElementById('form')
141             let id_given_names = document.getElementById('given-names')
142             let id_icon = document.getElementById('icon')
143             let id_message = document.getElementById('message')
144             let id_send_enquiry = document.getElementById('send-enquiry')
145             let id_spinner = document.getElementById('spinner')
146             let id_tick = document.getElementById('tick')
147
148             let input_semaphore = new BinarySemaphore(false)
149             ;(
150               async () => {
151                 while (true) {
152                   await input_semaphore.acquire()
153                   await new Promise(resolve => setTimeout(resolve, 3000))
154                   input_semaphore.try_acquire()
155                   await api_call(
156                     '/api/contact/set_draft.json',
157                     id_given_names.value.length === 0 &&
158                       id_family_name.value.length === 0 &&
159                       id_company.value.length === 0 &&
160                       id_email.value.length === 0 &&
161                       id_message.value.length === 0 ?
162                       null :
163                       {
164                         given_names: id_given_names.value.slice(0, 256),
165                         family_name: id_family_name.value.slice(0, 256),
166                         company: id_company.value.slice(0, 256),
167                         email: id_email.value.slice(0, 256).toLowerCase(),
168                         message: id_message.value.slice(0, 65536)
169                       }
170                   )
171                 }
172               }
173             )() // ignore returned promise (start thread)
174
175             let edited = () => {
176               input_semaphore.release()
177
178               id_send_enquiry.disabled =
179                 id_given_names.value.length === 0 &&
180                   id_family_name.value.length === 0 &&
181                   id_company.value.length === 0 &&
182                   id_email.value.length === 0 &&
183                   id_message.value.length === 0
184               id_icon.hidden = false
185               id_tick.hidden = true
186               id_cross.hidden = true
187               id_spinner.hidden = true
188               id_alert.hidden = true
189             }
190
191             id_given_names.addEventListener('input', edited)
192             id_family_name.addEventListener('input', edited)
193             id_company.addEventListener('input', edited)
194             id_email.addEventListener('input', edited)
195             id_message.addEventListener('input', edited)
196
197             id_send_enquiry.addEventListener(
198               'click',
199               async () => {
200                 id_icon.hidden = false
201                 id_tick.hidden = true
202                 id_cross.hidden = true
203                 id_spinner.hidden = true
204                 // the below causes an ugly flicker, so just keep the alert
205                 //id_alert.hidden = true
206
207                 if (!id_form.checkValidity()) {
208                   id_form.classList.add('was-validated');
209
210                   id_icon.hidden = true
211                   id_cross.hidden = false
212                   return
213                 }
214                 id_form.classList.remove('was-validated');
215
216                 id_icon.hidden = true
217                 id_spinner.hidden = false
218                 try {
219                   await api_call(
220                     '/api/contact/send_enquiry.json',
221                     {
222                       given_names: id_given_names.value.slice(0, 256),
223                       family_name: id_family_name.value.slice(0, 256),
224                       company: id_company.value.slice(0, 256),
225                       email: id_email.value.slice(0, 256).toLowerCase(),
226                       message: id_message.value.slice(0, 65536)
227                     }
228                   )
229                 }
230                 catch (error) {
231                   let problem = Problem.from(error)
232
233                   id_cross.hidden = false
234                   id_spinner.hidden = true
235
236                   id_alert.textContent = problem.detail
237                   id_alert.classList.remove('alert-success')
238                   id_alert.classList.add('alert-danger')
239                   id_alert.hidden = false
240                   return
241                 }
242                 id_tick.hidden = false
243                 id_spinner.hidden = true
244                 id_alert.textContent = 'We have received your enquiry. We will be in touch as soon as possible.'
245                 id_alert.classList.add('alert-success')
246                 id_alert.classList.remove('alert-danger')
247                 id_alert.hidden = false
248               }
249             )
250           }
251         )
252       }
253     }
254   )
255 }