/** * Availability form: keep the lesson-length choices honest. * * A window is stored as consecutive lesson-length slots, so one shorter than the * chosen lesson length holds no slots at all and saves nothing. Picking 5:30–6:00 * PM while the length select sat on its default of 60 minutes used to do exactly * that, silently. The server now rejects it with a message; this narrows the * choices first so the mistake is hard to make. * * This is a convenience only — AvailabilityController and the REST endpoint both * validate the same window server-side regardless of what happens here. */ (function () { 'use strict'; const form = document.getElementById('usc-add-availability'); if (!form) return; const startEl = document.getElementById('start_dt'); const endEl = document.getElementById('end_dt'); const durationEl = document.getElementById('duration_minutes'); const warningEl = document.getElementById('usc-duration-warning'); const submitEl = form.querySelector('input[type="submit"], button[type="submit"]'); if (!startEl || !endEl || !durationEl) return; /** * Minutes between the two datetime-local inputs, or 0 when the pair is not a * usable window yet — empty, unparseable, backwards, or spanning two days * (which the server rejects on its own terms, with its own message). */ function windowMinutes() { const start = new Date(startEl.value); const end = new Date(endEl.value); if (!startEl.value || !endEl.value || isNaN(start) || isNaN(end)) return 0; if (end <= start) return 0; if (startEl.value.slice(0, 10) !== endEl.value.slice(0, 10)) return 0; return Math.round((end - start) / 60000); } function refresh() { const minutes = windowMinutes(); const options = Array.from(durationEl.options); // No usable window yet: leave every choice alone rather than fighting // someone part-way through typing a date. if (minutes === 0) { options.forEach((option) => { option.hidden = false; option.disabled = false; }); setBlocked(false); return; } let fits = []; options.forEach((option) => { const tooLong = Number(option.value) > minutes; option.hidden = tooLong; option.disabled = tooLong; if (!tooLong) fits.push(option); }); if (fits.length === 0) { // Nothing bookable fits, so the form cannot produce a single slot. setBlocked(true); return; } setBlocked(false); // The selection may have just been hidden — fall back to the longest // length that still fits, which is what the instructor most likely wants. if (durationEl.selectedOptions[0] && durationEl.selectedOptions[0].disabled) { durationEl.value = fits.reduce( (longest, option) => (Number(option.value) > Number(longest.value) ? option : longest), fits[0] ).value; } } function setBlocked(blocked) { if (warningEl) warningEl.hidden = !blocked; if (submitEl) submitEl.disabled = blocked; } startEl.addEventListener('change', refresh); startEl.addEventListener('input', refresh); endEl.addEventListener('change', refresh); endEl.addEventListener('input', refresh); refresh(); }());