/* 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(); })();