Commit graph

12 commits

Author SHA1 Message Date
Philip Peterson
95a0a3e004 Use labels 2026-06-03 23:33:09 -07:00
Philip Peterson
96eafc4f5c Dont autoselect service type 2026-06-03 23:16:40 -07:00
Philip Peterson
2dd6c7da22 Add email field to booking form + dev mail mocking
- Add required email input (with autocomplete="email") to the booking
  details form in the homepage Preact widget (rpt-booking.js).
  Update EMPTY_FORM, submit payload, confirmedAppointment, and success
  summary to include it. The form now collects: first/last name, email,
  phone, comments.

- Update ScheduleController::storeSlot to extract/pass email in the
  booking_request mail params (and require it for the full-contact path).
  Log failures with details; return a structured error with a user-friendly
  message instead of bare "mail_failed".

- riverside_pt_mail hook now includes the user's email in the notification
  body (when provided).

- Dev improvements for mail:
  - In DEBUG mode (default on localhost), force php_mail interface in
    settings.php so the mailer uses the sendmail_path override.
  - Dockerfile + entrypoint.sh now provide/install a fake-sendmail.sh
    that prints the full email (To, Subject, headers, body from the
    hook) to stderr (visible in `docker compose logs`) and always
    succeeds (exit 0). This prevents "sh: 1: /usr/sbin/sendmail: not
    found" and guarantees booking submissions never return the
    "unable to send confirmation email" error in dev.
  - In non-DEBUG, still uses symfony_mailer + Postmark as before.
  - The fake is also baked into the image for consistency.

- JS error handling now prefers the server-provided 'message' from
  the JSON error response (better UX for real mail failures).

- Update CLAUDE.md with the new email field + dev mail mocking behavior.

- New file: docker/php/fake-sendmail.sh (the mock).

This addresses the recent "mail_failed" issues while keeping production
email via Postmark.
2026-06-03 23:05:06 -07:00
Philip Peterson
5293f3f347 Fixes to calendar lifecycle 2026-06-03 22:47:31 -07:00
Philip Peterson
59b7e57b5e Remount for service 2026-06-03 22:36:45 -07:00
Philip Peterson
f882149a37 Fix for race condition 2026-06-03 22:21:43 -07:00
Philip Peterson
8962fc5f0e Smooth scroll, booking refactor, success summary
- Add scroll.js: data-scroll-to attribute drives smooth scrollIntoView;
  scroll-margin-top at md+ accounts for fixed header offset
- Wire Services, FAQ, Book An Appointment, View Our Services nav/hero
  links to on-page anchors; don't close hamburger on scroll-link clicks
- Refactor booking calendar: own the fetch (useEffect + dateRange state)
  instead of handing URL to FullCalendar; removes fetchedRef complexity;
  noSlotsInMonth derived cleanly from fetchLoading + fetchedEvents
- Success state shows appointment summary (name, service, date/time);
  hides calendar/form on success; no "book another" button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:14:39 -07:00
Philip Peterson
9e1e6a57b7 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 <noreply@anthropic.com>
2026-06-03 21:20:04 -07:00
Philip Peterson
1b7577fa17 Calendar polish: selection persistence, no-slots overlay, various fixes
- Persist selected date across month navigation using module-level vars;
  datesSet re-applies is-selected and restores slots when returning
- Show "No availability this month" overlay after a fetch returns empty;
  gated on initializedRef+fetchedRef so auto-advance phase is silent
- Fix Dec 31 overflow: showNonCurrentDates:false hides adjacent-month days
- Fix fc-day-disabled background tint in calendar.css
- Gate auto-advance on loading() callback so removeAllEventSources()
  spurious eventsSet() fires don't trigger premature month jumping
- Inline overlay styles to avoid Tailwind cascade uncertainty; document
  the module-level CX constant pattern as the general fix
- firstName added to booking form; storeSlot sends email directly when
  full contact info present, skipping tempstore redirect
- Remove BookingForm.php and /schedule/book route (replaced by inline form)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 20:51:43 -07:00
Philip Peterson
e3c2e3e3a1 Add nice non-restrictive phone number input formatting for booking
- New pure-vanilla progressive formatter that turns typed digits into
  (123) 456-7890 (or 1 (800) 555-1212 for +1 numbers) on the fly.
- Works for free-form input: typing, pasting, editing anywhere, backspacing,
  extra chars, etc. — no hard mask or input restrictions.
- Applied to:
  - The Preact <rpt-booking> widget phone field (home page quick booking + details).
  - The final Drupal confirmation form at /schedule/book (via lightweight
    enhancer + .rpt-phone class).
- Prefills last_name / phone / comments on the confirmation form when the
  widget was used to pick the slot (so collected phone isn't lost).
- No new dependencies whatsoever:
  * Zero npm / package.json / composer changes.
  * Zero additional CDN / external scripts (uses native String.replace,
    regex, and input event + selection APIs only).
  * The new js/phone-format.js is simply attached via the pre-existing
    'app' library (already loaded on all riverside_pt.* routes).
  * Formatter logic duplicated in the ESM component (tiny pure function).
- The two other modified files (calendar.css, calendar.js) were left
  uncommitted as they are unrelated to this feature.
2026-06-03 20:35:40 -07:00
Philip Peterson
58988a4fe8 Fix calendar auto-advance and add fault injection for availability
- Remove client-side serviceEarliestDate; instead auto-advance month by
  month until the server returns events (capped at 12 months)
- Only mark initialized when a date is actually found, so empty months
  don't block auto-select on subsequent fetches
- Add per-service fault injection flags in ScheduleController::events
  to force zero availability for testing the no-slots UI path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 19:52:14 -07:00
Philip Peterson
b000b824ed Service-aware booking: selector drives calendar, inline request form
- Merge rpt-appt-type + calendar into unified rpt-booking Preact component;
  service state drives event source via useEffect, no DOM event bus
- Each service has distinct availability (different slot density, start hours);
  surgical rehab only available 46+ days out
- Slot click reveals inline Last name / Phone / Comments form; submits all
  fields together to storeSlot rather than redirecting immediately
- Fix nextBusinessDay() timezone bug (toISOString is UTC; use local date
  components instead); pre-select first available day >= next business day
- Today always has no availability (backend now starts from tomorrow)
- Replace all raw hex colour values with named palette tokens throughout
  templates and JS components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 19:39:35 -07:00