Files
JQC_features/static/js/demo-booking.js
T

96 lines
3.4 KiB
JavaScript

/* Demo booking form: keep the time picker in step with the chosen date.
*
* Pick a date -> ask /demo/slots what's still free that day -> rebuild the time
* dropdown from the answer, so a slot someone else has already booked is never
* offered. Weekends and out-of-range dates come back closed, with the reason.
*
* Progressive enhancement: without JS (or if the fetch fails) the select keeps
* the full server-rendered list and the POST handler still rejects a taken slot
* — the server is the authority either way, this just stops the customer
* choosing a time that was never going to be accepted.
*/
(function () {
'use strict';
var form = document.querySelector('[data-slots-url]');
if (!form) return;
var url = form.getAttribute('data-slots-url');
var dateEl = document.getElementById('preferred_date');
var timeEl = document.getElementById('preferred_time');
var noteEl = document.getElementById('slot-note');
if (!dateEl || !timeEl || !noteEl) return;
var placeholder = 'Select a time…';
var baseNote = noteEl.textContent; // e.g. "Eastern Time."
var request = 0; // drops out-of-order responses
function note(message, kind) {
noteEl.textContent = message;
noteEl.className = 'hint' + (kind ? ' hint--' + kind : '');
}
function fill(slots, keep) {
timeEl.innerHTML = '';
var first = document.createElement('option');
first.value = '';
first.textContent = slots.length ? placeholder : 'No times available';
timeEl.appendChild(first);
slots.forEach(function (slot) {
var option = document.createElement('option');
option.value = slot;
option.textContent = slot;
if (slot === keep) option.selected = true;
timeEl.appendChild(option);
});
timeEl.disabled = slots.length === 0;
}
function load() {
var value = dateEl.value;
if (!value) {
note(baseNote);
return;
}
var ticket = ++request;
var wanted = timeEl.value;
timeEl.disabled = true;
note('Checking availability…');
fetch(url + '?date=' + encodeURIComponent(value), {
headers: { 'Accept': 'application/json' }
}).then(function (res) {
return res.json().then(function (body) {
if (!res.ok) throw new Error(body.error || 'Could not load times.');
return body;
});
}).then(function (data) {
if (ticket !== request) return; // a newer date won the race
if (!data.open) {
fill([], null);
note(data.reason || 'That date is not available.', 'warn');
return;
}
fill(data.slots, wanted);
if (!data.slots.length) {
note('Every time on that day is booked — please choose another date.', 'warn');
} else if (wanted && data.slots.indexOf(wanted) === -1) {
// The time they had chosen went while they were filling the form in.
note('That time has just been booked. Please pick another.', 'warn');
} else {
note(baseNote);
}
}).catch(function () {
if (ticket !== request) return;
// Fail open: leave whatever is in the select and let the server decide.
timeEl.disabled = false;
note("Couldn't check availability — we'll confirm your time by email.", 'warn');
});
}
dateEl.addEventListener('change', load);
// A date can survive a failed submit or a browser restore.
if (dateEl.value) load();
})();