/* global usScheduler */ (function () { 'use strict'; const app = document.getElementById('us-group-app'); if (!app) return; const list = document.getElementById('us-group-list'); const confirm = document.getElementById('us-group-confirmation'); const errorBox = document.getElementById('us-group-error'); const { restUrl, nonce } = usScheduler; // When the shortcode/block pins a single offering, only that class is // shown, so the page can be embedded alongside a full class description. const singleOfferingId = Number(app.dataset.offering || 0); function apiFetch(path, options = {}) { return fetch(restUrl + path, { ...options, headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce, ...(options.headers || {}), }, }).then(async (res) => { const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Request failed'); return data; }); } function showError(message) { errorBox.textContent = message; errorBox.style.display = 'block'; } function clearError() { errorBox.style.display = 'none'; } function escHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function questionField(q) { const name = `q_${q.id}`; const required = q.is_required ? 'required' : ''; let input; if (q.field_type === 'textarea') { input = ``; } else if (q.field_type === 'select') { const opts = (q.options || []).map((o) => ``).join(''); input = ``; } else if (q.field_type === 'checkbox') { input = ``; } else { input = ``; } return `

`; } function policyField(p) { return `

${escHtml(p.title)}

${p.body || ''}
`; } // Parse a Y-m-d date into local time; new Date('Y-m-d') would parse as // UTC midnight and can display as the previous day in western timezones. function formatDate(ymd) { const [y, m, d] = ymd.split('-').map(Number); return new Date(y, m - 1, d).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); } function termLabel(o) { if (!o.term_start) return ''; if (!o.term_end || o.term_end === o.term_start) { return formatDate(o.term_start); } const weekMs = 7 * 24 * 60 * 60 * 1000; const sessions = Math.round((new Date(o.term_end) - new Date(o.term_start)) / weekMs) + 1; return `${formatDate(o.term_start)} – ${formatDate(o.term_end)} (${sessions} weekly sessions)`; } function renderClasses(offerings) { let groups = offerings.filter((o) => o.kind === 'group_class'); if (singleOfferingId) { groups = groups.filter((o) => Number(o.id) === singleOfferingId); } if (!groups.length) { list.innerHTML = singleOfferingId ? '

This class is not open for enrolment right now.

' : '

No group classes are open for enrolment right now.

'; return; } list.innerHTML = groups.map((o) => `

${escHtml(o.title)}

${termLabel(o) ? `

${escHtml(termLabel(o))}

` : ''} ${o.schedule_note ? `

${escHtml(o.schedule_note)}

` : ''} ${o.description ? `

${escHtml(o.description)}

` : ''}

${escHtml(Number(o.price).toFixed(2))} ${escHtml(o.currency)}

`).join(''); list.querySelectorAll('.us-enrol-btn').forEach((btn) => { const offering = groups.find((o) => String(o.id) === btn.dataset.offeringId); btn.addEventListener('click', () => openEnrolment(offering)); }); } function openEnrolment(offering) { clearError(); Promise.all([ apiFetch(`offerings/${offering.id}/questions`), apiFetch('policies?scope=booking'), ]) .then(([questions, policies]) => renderEnrolment(offering, questions, policies)) .catch((err) => showError(err.message)); } function renderEnrolment(offering, questions, policies) { list.innerHTML = `

${escHtml(offering.title)}

${questions.map(questionField).join('')} ${policies.map(policyField).join('')}

`; document.getElementById('us-group-cancel').addEventListener('click', loadClasses); document.getElementById('us-enrol-form').addEventListener('submit', (e) => { e.preventDefault(); submitEnrolment(e.target, offering, questions); }); } function submitEnrolment(form, offering, questions) { clearError(); const answers = {}; questions.forEach((q) => { const field = form.elements[`q_${q.id}`]; if (!field) return; answers[q.id] = field.type === 'checkbox' ? (field.checked ? '1' : '0') : field.value; }); const accepted = [...form.querySelectorAll('.us-policy-accept:checked')].map((c) => Number(c.value)); apiFetch('enrollments', { method: 'POST', body: JSON.stringify({ offering_id: offering.id, answers, accepted_policy_version_ids: accepted, }), }) // An enrolment with nothing owed has no payment, so there is no // payment step to run. .then((res) => (res.payment ? window.usPayment.collect('enrollment', res.id, list) : null)) .then((result) => showConfirmation(window.usPayment.message(result))) .catch((err) => showError(err.message)); } function showConfirmation(message) { confirm.textContent = message; list.style.display = 'none'; confirm.style.display = 'block'; } function loadClasses() { clearError(); list.style.display = 'block'; confirm.style.display = 'none'; apiFetch('offerings?kind=group_class') .then(renderClasses) .catch((err) => showError(err.message)); } loadClasses(); }());