Add per-embed lesson-type and section options to the booking block
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 2m56s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 2m56s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
Three sidebar options on the Lesson Booking block, all mirrored as shortcode attributes and carried to the front end as data attributes on #us-booking-app (or as omitted containers): - Lesson type (lessonTypeId / lesson_type) pins the calendar to a single private-lesson type: only the times bookable as it are listed, and it is the only type bookable there, auto-selected on the registration form. A pinned type that is no longer offered says so instead of showing an empty calendar. - Show the lesson-type filter (showTypeFilter / show_filter) drops the "Show Only" control for studios that do not want it. - Sections (displayMode / show) embeds one half of the page — the booking calendar or the student's upcoming lessons — so the two can live on different pages. The script skips the work belonging to a missing half: no availability or catalog request for an upcoming-only embed, no bookings request for a booking-only one. An unrecognised value renders the whole page. The editor preview follows the same setting. Also fixes the expanded filter's first lesson type sharing a line with the "Lesson type" heading — the choices now sit in their own row beneath it. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+41
-8
@@ -11,6 +11,11 @@
|
||||
const errorBox = document.getElementById('us-booking-error');
|
||||
const { restUrl, nonce } = usScheduler;
|
||||
|
||||
// Per-instance options from the block/shortcode: pin the page to a single
|
||||
// lesson type, and whether the "Show Only" filter is offered at all.
|
||||
const pinnedTypeId = Number(app.dataset.lessonType) || 0;
|
||||
const filterEnabled = app.dataset.typeFilter !== '0';
|
||||
|
||||
function apiFetch(path, options = {}) {
|
||||
return fetch(restUrl + path, {
|
||||
...options,
|
||||
@@ -153,7 +158,7 @@
|
||||
// Nothing to filter with a single bookable type, so the control only
|
||||
// appears once there is a choice to make.
|
||||
function filterToggleHtml() {
|
||||
if (catalog.length < 2) return '';
|
||||
if (!filterEnabled || catalog.length < 2) return '';
|
||||
|
||||
const count = filterActive() ? ` (${selectedTypeIds.size})` : '';
|
||||
|
||||
@@ -176,7 +181,7 @@
|
||||
// The lesson-type list itself — collapsed until the student opens it, and
|
||||
// rendered between the control row and the calendar.
|
||||
function filterHtml() {
|
||||
if (catalog.length < 2 || !filterOpen) return '';
|
||||
if (!filterEnabled || catalog.length < 2 || !filterOpen) return '';
|
||||
|
||||
const choices = catalog.map((o) => `
|
||||
<label class="us-type-filter-choice">
|
||||
@@ -188,8 +193,10 @@
|
||||
return `
|
||||
<div class="us-type-filter" id="us-type-filter" role="group" aria-label="Filter by lesson type">
|
||||
<span class="us-type-filter-heading">Lesson type</span>
|
||||
${choices}
|
||||
${filterActive() ? '<button type="button" id="us-type-filter-clear" class="us-type-filter-clear">Show all types</button>' : ''}
|
||||
<div class="us-type-filter-choices">
|
||||
${choices}
|
||||
${filterActive() ? '<button type="button" id="us-type-filter-clear" class="us-type-filter-clear">Show all types</button>' : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -240,6 +247,13 @@
|
||||
function render() {
|
||||
const slots = visibleSlots();
|
||||
|
||||
// The pinned lesson type is no longer on offer (deactivated or
|
||||
// deleted), so this page has nothing it is allowed to book.
|
||||
if (pinnedTypeId && !catalog.length) {
|
||||
slotList.innerHTML = '<p>This lesson type is not available for booking right now.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Nothing open at all: there is nothing for the controls to act on.
|
||||
if (!allSlots.length) {
|
||||
slotList.innerHTML = '<p>No available lesson slots at this time.</p>';
|
||||
@@ -247,7 +261,11 @@
|
||||
}
|
||||
|
||||
if (!slots.length) {
|
||||
slotList.innerHTML = controlsHtml() + filterHtml() + '<p>No open times match the selected lesson types.</p>';
|
||||
const message = pinnedTypeId
|
||||
? '<p>No open times for this lesson type right now.</p>'
|
||||
: '<p>No open times match the selected lesson types.</p>';
|
||||
|
||||
slotList.innerHTML = controlsHtml() + filterHtml() + message;
|
||||
wireControlEvents();
|
||||
return;
|
||||
}
|
||||
@@ -596,19 +614,34 @@
|
||||
// The private-lesson catalog drives both the filter and the registration
|
||||
// form's lesson-type picker, and it does not change while the student
|
||||
// browses — so it is fetched once and kept.
|
||||
let catalogLoaded = false;
|
||||
|
||||
function loadCatalog() {
|
||||
if (catalog.length) return Promise.resolve(catalog);
|
||||
if (catalogLoaded) return Promise.resolve(catalog);
|
||||
return apiFetch('offerings?kind=private_lesson').then((list) => {
|
||||
catalog = list;
|
||||
// A pinned lesson type is the only one this page may book, so the
|
||||
// catalog is narrowed to it and the filter is fixed on it. With a
|
||||
// single type left the "Show Only" control hides itself.
|
||||
catalog = pinnedTypeId
|
||||
? list.filter((o) => Number(o.id) === pinnedTypeId)
|
||||
: list;
|
||||
|
||||
if (pinnedTypeId) selectedTypeIds.add(pinnedTypeId);
|
||||
|
||||
catalogLoaded = true;
|
||||
return catalog;
|
||||
});
|
||||
}
|
||||
|
||||
function loadSlots() {
|
||||
clearError();
|
||||
loadMyLessons();
|
||||
|
||||
// An upcoming-lessons-only embed has no calendar to fill.
|
||||
if (!slotList) return;
|
||||
|
||||
slotList.style.display = 'block';
|
||||
confirm.style.display = 'none';
|
||||
loadMyLessons();
|
||||
Promise.all([apiFetch('availability'), loadCatalog()])
|
||||
.then(([slots]) => {
|
||||
allSlots = slots;
|
||||
|
||||
Reference in New Issue
Block a user