2026-05-12 16:28:47 -08:00
|
|
|
(function (drupalSettings) {
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
2026-06-03 00:37:13 -08:00
|
|
|
var el = document.getElementById('riverside-calendar');
|
2026-05-12 16:28:47 -08:00
|
|
|
if (!el) return;
|
|
|
|
|
|
2026-06-01 01:46:47 -08:00
|
|
|
requestAnimationFrame(function () {
|
2026-06-03 01:02:56 -08:00
|
|
|
var slotsWrap = document.getElementById('riverside-slots-wrap');
|
|
|
|
|
var slotsGrid = document.getElementById('riverside-booking-slots');
|
|
|
|
|
|
2026-06-03 00:37:13 -08:00
|
|
|
var selectedDate = null;
|
2026-06-03 01:02:56 -08:00
|
|
|
var initialized = false;
|
2026-06-03 18:39:35 -08:00
|
|
|
var currentService = 'diagnostic';
|
|
|
|
|
|
|
|
|
|
function buildEventsUrl(service) {
|
|
|
|
|
return drupalSettings.riversidePt.eventsUrl + '?service=' + service;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function localDateStr(d) {
|
|
|
|
|
return d.getFullYear() + "-" +
|
|
|
|
|
String(d.getMonth() + 1).padStart(2, "0") + "-" +
|
|
|
|
|
String(d.getDate()).padStart(2, "0");
|
|
|
|
|
}
|
2026-06-03 01:02:56 -08:00
|
|
|
|
|
|
|
|
function nextBusinessDay() {
|
|
|
|
|
var d = new Date();
|
|
|
|
|
d.setDate(d.getDate() + 1);
|
|
|
|
|
while (d.getDay() === 0 || d.getDay() === 6) d.setDate(d.getDate() + 1);
|
2026-06-03 18:39:35 -08:00
|
|
|
return localDateStr(d);
|
2026-06-03 01:02:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var initDate = nextBusinessDay();
|
|
|
|
|
|
|
|
|
|
function slotLabel(date) {
|
|
|
|
|
var h = date.getHours();
|
|
|
|
|
return (h % 12 || 12) + (h < 12 ? 'AM' : 'PM') + ' PST';
|
|
|
|
|
}
|
2026-06-01 01:46:47 -08:00
|
|
|
|
2026-06-03 01:02:56 -08:00
|
|
|
function renderSlots(dateStr, events) {
|
|
|
|
|
var dayEvents = events
|
|
|
|
|
.filter(function (e) { return e.startStr.startsWith(dateStr); })
|
|
|
|
|
.sort(function (a, b) { return a.start - b.start; });
|
2026-06-03 00:37:13 -08:00
|
|
|
|
2026-06-03 01:02:56 -08:00
|
|
|
if (!slotsWrap || !slotsGrid || dayEvents.length === 0) return;
|
|
|
|
|
|
|
|
|
|
slotsGrid.innerHTML = '';
|
|
|
|
|
dayEvents.forEach(function (event, idx) {
|
|
|
|
|
var btn = document.createElement('button');
|
|
|
|
|
btn.type = 'button';
|
|
|
|
|
btn.textContent = slotLabel(event.start);
|
|
|
|
|
btn.className = 'riverside-slot-btn' + (idx === 0 ? ' is-selected' : '');
|
|
|
|
|
btn.addEventListener('click', function () {
|
|
|
|
|
slotsGrid.querySelectorAll('.riverside-slot-btn').forEach(function (b) {
|
|
|
|
|
b.classList.remove('is-selected');
|
|
|
|
|
});
|
|
|
|
|
btn.classList.add('is-selected');
|
|
|
|
|
fetch(drupalSettings.riversidePt.storeSlotUrl, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
2026-06-03 18:39:35 -08:00
|
|
|
body: JSON.stringify({ start: event.startStr, end: event.endStr, service: currentService }),
|
2026-06-03 01:02:56 -08:00
|
|
|
}).then(function (res) {
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
window.location.href = drupalSettings.riversidePt.bookingUrl;
|
|
|
|
|
} else {
|
|
|
|
|
btn.textContent += ' (unavailable)';
|
|
|
|
|
btn.disabled = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
slotsGrid.appendChild(btn);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
slotsWrap.hidden = false;
|
2026-06-03 00:37:13 -08:00
|
|
|
}
|
|
|
|
|
|
2026-06-03 01:02:56 -08:00
|
|
|
function selectDay(dateStr, events) {
|
|
|
|
|
el.querySelectorAll('.fc-daygrid-day.is-selected').forEach(function (d) {
|
|
|
|
|
d.classList.remove('is-selected');
|
|
|
|
|
});
|
|
|
|
|
var dayEl = el.querySelector('.fc-daygrid-day[data-date="' + dateStr + '"]');
|
|
|
|
|
if (dayEl) dayEl.classList.add('is-selected');
|
|
|
|
|
selectedDate = dateStr;
|
|
|
|
|
renderSlots(dateStr, events);
|
2026-06-03 00:37:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var calendar = new FullCalendar.Calendar(el, {
|
|
|
|
|
initialView: 'dayGridMonth',
|
2026-06-03 01:02:56 -08:00
|
|
|
initialDate: initDate,
|
2026-06-03 00:37:13 -08:00
|
|
|
headerToolbar: { left: 'prev', center: 'title', right: 'next' },
|
|
|
|
|
titleFormat: { year: 'numeric', month: 'long' },
|
|
|
|
|
dayHeaderFormat: { weekday: 'narrow' },
|
|
|
|
|
validRange: function (now) {
|
|
|
|
|
return {
|
|
|
|
|
start: new Date(now.getFullYear(), now.getMonth(), 1),
|
|
|
|
|
end: new Date(now.getFullYear(), now.getMonth() + 7, 1),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
fixedWeekCount: false,
|
|
|
|
|
height: 'auto',
|
2026-06-03 18:39:35 -08:00
|
|
|
events: buildEventsUrl(currentService),
|
2026-06-03 00:37:13 -08:00
|
|
|
eventDisplay: 'none',
|
|
|
|
|
dayMaxEvents: false,
|
|
|
|
|
|
|
|
|
|
datesSet: function () {
|
|
|
|
|
el.querySelectorAll('.fc-daygrid-day.is-selected').forEach(function (d) {
|
|
|
|
|
d.classList.remove('is-selected');
|
|
|
|
|
});
|
|
|
|
|
selectedDate = null;
|
2026-06-03 01:02:56 -08:00
|
|
|
if (slotsWrap) slotsWrap.hidden = true;
|
2026-06-03 00:37:13 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
eventsSet: function (events) {
|
|
|
|
|
el.querySelectorAll('.fc-daygrid-day.has-availability').forEach(function (d) {
|
|
|
|
|
d.classList.remove('has-availability');
|
2026-05-13 14:26:33 -08:00
|
|
|
});
|
2026-06-03 00:37:13 -08:00
|
|
|
events.forEach(function (event) {
|
|
|
|
|
var dateStr = event.startStr.substring(0, 10);
|
|
|
|
|
var dayEl = el.querySelector('.fc-daygrid-day[data-date="' + dateStr + '"]');
|
|
|
|
|
if (dayEl) dayEl.classList.add('has-availability');
|
|
|
|
|
});
|
2026-06-03 01:02:56 -08:00
|
|
|
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
initialized = true;
|
|
|
|
|
var targetEl = el.querySelector('.fc-daygrid-day[data-date="' + initDate + '"]');
|
|
|
|
|
if (targetEl && targetEl.classList.contains('has-availability')) {
|
|
|
|
|
selectDay(initDate, events);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-03 00:37:13 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
dayCellClassNames: function (arg) {
|
|
|
|
|
var date = arg.date.toISOString().substring(0, 10);
|
|
|
|
|
if (drupalSettings.riversidePt.holidays[date]) return ['is-holiday'];
|
|
|
|
|
},
|
2026-05-12 17:33:59 -08:00
|
|
|
|
2026-06-03 00:37:13 -08:00
|
|
|
dateClick: function (arg) {
|
|
|
|
|
if (!arg.dayEl.classList.contains('has-availability')) return;
|
2026-06-03 01:02:56 -08:00
|
|
|
selectDay(arg.dateStr, calendar.getEvents());
|
2026-06-03 00:37:13 -08:00
|
|
|
},
|
|
|
|
|
});
|
2026-05-12 16:28:47 -08:00
|
|
|
|
2026-06-03 00:37:13 -08:00
|
|
|
calendar.render();
|
2026-06-03 01:02:56 -08:00
|
|
|
});
|
2026-05-12 16:28:47 -08:00
|
|
|
});
|
|
|
|
|
})(drupalSettings);
|