From 9e1e6a57b7235e87041536dd2cd2bc31cdcf78b5 Mon Sep 17 00:00:00 2001 From: Philip Peterson <1326208+philip-peterson@users.noreply.github.com> Date: Wed, 3 Jun 2026 21:20:04 -0700 Subject: [PATCH] Hoist Tailwind classes to CX object; fix no-slots overlay flash - Move all Tailwind class strings to a module-level CX constant so the JIT scanner sees complete literals in one place rather than scattered across template expressions - Convert no-slots overlay and wrapper from inline styles to CX entries (adds z-10 to fix stacking above FullCalendar grid) - Fix no-availability message flashing on month navigation: reset fetchedRef in datesSet so eventsSet ignores stale pre-fetch firings Co-Authored-By: Claude Sonnet 4.6 --- web/modules/custom/riverside_pt/css/app.css | 12 ++ .../riverside_pt/js/components/rpt-booking.js | 132 +++++++++++------- .../src/Controller/ScheduleController.php | 1 - 3 files changed, 93 insertions(+), 52 deletions(-) diff --git a/web/modules/custom/riverside_pt/css/app.css b/web/modules/custom/riverside_pt/css/app.css index 3d978ec..12fdf13 100644 --- a/web/modules/custom/riverside_pt/css/app.css +++ b/web/modules/custom/riverside_pt/css/app.css @@ -635,6 +635,10 @@ html { transform: translateY(-10px) rotate(-45deg); } +.pointer-events-none{ + pointer-events: none; +} + .static{ position: static; } @@ -667,6 +671,10 @@ html { top: 50%; } +.z-10{ + z-index: 10; +} + .mx-auto{ margin-left: auto; margin-right: auto; @@ -1307,6 +1315,10 @@ html { padding-top: 2rem; } +.pt-20{ + padding-top: 5rem; +} + .text-left{ text-align: left; } diff --git a/web/modules/custom/riverside_pt/js/components/rpt-booking.js b/web/modules/custom/riverside_pt/js/components/rpt-booking.js index a2d31fa..8462eb0 100644 --- a/web/modules/custom/riverside_pt/js/components/rpt-booking.js +++ b/web/modules/custom/riverside_pt/js/components/rpt-booking.js @@ -29,6 +29,48 @@ function formatPhone(raw) { return "(" + d.slice(0, 3) + ") " + d.slice(3, 6) + "-" + d.slice(6); } +// All Tailwind class strings live here so the JIT scanner sees complete +// literals in one place rather than spread across template expressions. +const CX = { + // ── Type selector ────────────────────────────────────────────────────── + selectorLabel: "text-xs tracking-widest uppercase text-pt-blue-500 font-semibold mb-5", + selectorGrid: "grid grid-cols-1 sm:grid-cols-2 gap-3 mb-10", + typeBtn: "flex items-center gap-4 p-4 w-full rounded-xl border transition-colors", + typeBtnActive: "bg-pt-blue-500 border-pt-blue-500", + typeBtnInactive: "bg-white border-pt-blue-200 hover:border-pt-blue-500", + typeCircle: "w-8 h-8 rounded-full shrink-0 flex items-center justify-center border", + typeCircleActive: "border-white/60", + typeCircleInactive: "border-pt-blue-200", + typeLabel: "font-serif text-[1.0625rem] font-normal leading-snug", + typeLabelActive: "text-white", + typeLabelInactive: "text-gray-900", + typeDuration: "text-[0.6875rem] tracking-widest font-semibold mt-0.5", + typeDurationActive: "text-white/70", + typeDurationInactive: "text-pt-blue-500", + + // ── Form ─────────────────────────────────────────────────────────────── + formSection: "mt-8 pt-8 border-t border-pt-blue-200", + formHeading: "text-xs tracking-widest uppercase text-pt-blue-500 font-semibold mb-6", + formGrid: "grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-5 mb-5", + formLabel: "block text-sm font-medium text-gray-700 mb-1", + formRequired: "text-red-500", + formInput: "w-full border border-pt-blue-200 bg-white px-3 py-2 text-gray-900 text-sm focus:outline-none focus:border-pt-blue-500 transition-colors", + formTextarea: "resize-none w-full border border-pt-blue-200 bg-white px-3 py-2 text-gray-900 text-sm focus:outline-none focus:border-pt-blue-500 transition-colors", + formError: "text-red-500 text-sm mb-4", + submitBtn: "px-[4em] py-[1em] bg-pt-blue-500 text-white text-sm font-medium transition-colors border-2 border-pt-blue-500 hover:bg-pt-blue-600 hover:border-pt-blue-600 disabled:opacity-50", + + // ── Calendar overlay ────────────────────────────────────────────────── + calWrapper: "relative", + noSlotsOverlay: "absolute inset-0 z-10 flex items-center justify-center pt-20 pointer-events-none", + + // ── Success state ────────────────────────────────────────────────────── + successSection: "mt-8 pt-8 border-t border-pt-blue-200", + successBox: "p-6 bg-green-50 border border-green-200 text-green-800", + successTitle: "font-medium", + successBody: "text-sm mt-1", + successLink: "mt-3 text-sm text-green-700 underline hover:text-green-800", +}; + var selectedDate = null; var selectedDateSlots = []; @@ -114,6 +156,7 @@ function Booking({ settings }) { }); setSelectedSlotId(null); setNoSlotsInMonth(false); + fetchedRef.current = false; if (selectedDate) { var dayEl = calEl.current.querySelector(".fc-daygrid-day[data-date=\"" + selectedDate + "\"]"); if (dayEl) { @@ -263,13 +306,10 @@ function Booking({ settings }) { var selectedSlot = slots.find(function (s) { return s.id === selectedSlotId; }); - var inputClass = "w-full border border-pt-blue-200 bg-white px-3 py-2 text-gray-900 text-sm focus:outline-none focus:border-pt-blue-500 transition-colors"; - var labelClass = "block text-sm font-medium text-gray-700 mb-1"; - return html`
-

Select Appointment Type

-
+

Select Appointment Type

+
${TYPES.map(function (t) { var active = service === t.id; return html` @@ -277,22 +317,16 @@ function Booking({ settings }) { key=${t.id} onClick=${function () { setService(t.id); }} style="text-align:left; cursor:pointer;" - class=${ - "flex items-center gap-4 p-4 w-full rounded-xl border transition-colors " + - (active ? "bg-pt-blue-500 border-pt-blue-500" : "bg-white border-pt-blue-200 hover:border-pt-blue-500") - } + class=${CX.typeBtn + " " + (active ? CX.typeBtnActive : CX.typeBtnInactive)} > -
+
${active ? CHECK : null}
-

+

${t.label}

-

+

${t.duration}

@@ -302,10 +336,10 @@ function Booking({ settings }) {
-
+
${noSlotsInMonth ? html` -
+

No availability this month

@@ -331,10 +365,10 @@ function Booking({ settings }) {
${success ? html` -
-
-

Request received!

-

Thank you. We'll contact you shortly to confirm your appointment.

+
+
+

Request received!

+

Thank you. We'll contact you shortly to confirm your appointment.

` : null} ${selectedSlot ? html` -
-

- Your Details -

+ +

Your Details

-
+
-
-
-
- +
- ${submitError ? html`

${submitError}

` : null} + ${submitError ? html`

${submitError}

` : null} - diff --git a/web/modules/custom/riverside_pt/src/Controller/ScheduleController.php b/web/modules/custom/riverside_pt/src/Controller/ScheduleController.php index 068a032..ac18b88 100644 --- a/web/modules/custom/riverside_pt/src/Controller/ScheduleController.php +++ b/web/modules/custom/riverside_pt/src/Controller/ScheduleController.php @@ -14,7 +14,6 @@ use Symfony\Component\HttpFoundation\Request; class ScheduleController extends ControllerBase { private PrivateTempStore $tempStore; - private $configFactory; public function __construct( PrivateTempStoreFactory $tempStoreFactory,