The front-end booking calendar now opens in the Week view (anchored to the
week of the earliest open slot) with List still available. The Scheduler and
My Lessons admin pages gain a week calendar (usc_view/usc_week, bucketed via
a new generic WeekCalendar::bucket()) and open in it by default; the original
table remains as the List view since it carries the HST / e-transfer forms.
Closes#76
Co-Authored-By: Claude Fable 5 <[email protected]>
Generic slots (no tied offering) were bookable with no offering at all:
free, instantly confirmed, and with no intake questions. POST /bookings
now rejects offering-less bookings (400 offering_required), and a
student-chosen offering must be an active private-lesson type owned by
the slot's instructor whose duration matches the slot.
The registration form gains a Lesson type field: locked to the slot's
tied offering (title, duration, price) so the student sees what they
are booking, or a required picker of fitting offerings for generic
slots, with intake questions following the selection.
Fixes#55
Co-Authored-By: Claude Fable 5 <[email protected]>
Adds POST /bookings/{id}/cancel (owner-only, idempotent): marks the lesson
cancelled, releases the availability slot for rebooking, and voids a
still-pending payment so it leaves the admin confirmation queue. Paid
payments are untouched — refunds stay a manual admin decision.
The instructor PATCH /bookings/{id}/status path now does the same slot
release and payment voiding on cancellation (previously cancelled lessons
left their slot permanently booked), and reinstating a cancelled lesson
re-claims the slot, rejecting with 409 if the freed time was rebooked.
The "Your upcoming lessons" panel gets a Cancel button with a confirm
prompt; on success both the lesson list and the slot calendar refresh.
Co-Authored-By: Claude Fable 5 <[email protected]>
Booking a slot with no priced offering created the lesson but no payment,
yet the front end still called POST /payments/intent, which 400ed with
"Could not start payment for this registration" — the student saw an error
while the backend held a claimed slot and a lesson stuck at pending.
- POST /bookings and POST /enrollments now return a `payment` summary
({id, method, status}) or null when nothing is owed; the JS only runs
the payment step when a payment exists.
- Bookings with nothing owed are confirmed at creation — there is no
payment step that would ever confirm them later.
- The booking page now shows the student's upcoming lessons (GET /bookings,
now scoped to upcoming non-cancelled lessons with slot start/end times)
with a pending-payment/confirmed status badge.
Fixes#53
Co-Authored-By: Claude Fable 5 <[email protected]>
Availability windows were stored and served as a single bookable row, so a
9:00 AM-4:00 PM window showed to students as one giant slot and booking it
consumed the whole day; past and multi-day windows also leaked into the
booking page as nonsense entries.
- Split windows into consecutive lesson-length slots on save (REST and admin
form); each chunk is independently bookable and weekly recurrence creates a
series per chunk so "reserve this time weekly" holds the same hour each week
- Reject windows spanning multiple days or shorter than the lesson length
(400 invalid_window)
- Never return slots whose start has passed from GET /availability
- Migrate pre-split rows: Plugin::boot re-runs the Installer on version change
and AvailabilityRepository::splitOversizedWindows() rewrites unbooked
same-day oversized windows in place
- Display all times in 12-hour AM/PM form (booking page, wp-admin lists,
editor previews)
- Add a List | Week view toggle to the student booking page and the
instructor availability page, with previous/next-week navigation honouring
the site's start_of_week option (new WeekCalendar helper)
Co-Authored-By: Claude Fable 5 <[email protected]>
Implements #3: students register for a private lesson by picking a slot,
answering the offering's intake questions, and accepting booking-scoped
policies. Payment is a clean seam for #7 (lessons land pending; payment_id
null; instructor confirms via PATCH /bookings/{id}/status).
- Schema: us_lessons += offering_id, recurrence, series_id, payment_id.
- Lesson: new fields + recurrence constants.
- BookingRepository::insertSeries() builds a weekly series sharing a
series_id; AvailabilityRepository::findUnbookedInGroup() reserves a group.
- RegistrationGate (src/Registration/): validate + record intake answers and
booking-scoped policy acceptances. Reused by group enrolment (#4).
- BookingEndpoint::book(): offering_id, recurrence, answers,
accepted_policy_version_ids; single or weekly; records answers/acceptances
(type lesson).
- GET /policies?scope=booking filter.
- Front-end booking.js: slot -> questions + policies -> submit.
- Wiring: RegistrationGate built in Plugin, passed via RestRegistrar.
- Test-only WP_Error stub in tests/bootstrap.php for gate testing.
Refs #3
Co-Authored-By: Claude Opus 4.8 <[email protected]>
WordPress administrators (manage_options) now implicitly hold every
studio-admin capability via a user_has_cap filter, so the site owner runs
the studio without being assigned the separate us_studio_admin role. The
grant persists nothing and is removed on deactivation. The us_studio_admin
role still exists for non-administrator staff and does NOT confer any core
WordPress admin powers.
Also re-gate the studio-wide "Scheduler" dashboard off manage_options onto
a new view_all_lessons capability (added to the studio-admin cap set), so a
us_studio_admin user can see it too — previously it was administrator-only.
- RoleManager: STUDIO_ADMIN_CAPS constant, CAP_VIEW_ALL_LESSONS,
grantStudioCapsToAdministrators() user_has_cap filter
- AdminMenu + LessonController: Scheduler gated on view_all_lessons
- Docs: user-roles.md cap matrix + administrator note; lesson-booking.md
- Tests: administrators receive studio caps; non-admins do not
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Update availability, lesson-booking, and user-roles docs and add specs
for offerings, group classes, registration questions, versioned policies,
Stripe payments (with e-transfer/comp overrides and receipts), and
monthly per-instructor payment reporting. Tracked in issues #1-#9.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Custom DB tables for availability slots and lesson bookings
- Instructor (wp-admin) and student (front-end) roles with custom capabilities
- REST API under us-scheduler/v1 for availability CRUD and booking
- [us_booking] and [us_student_login] shortcodes for student front end
- PHPUnit + Brain\Monkey unit test suite (29 tests)
- Gitea Actions CI: lint, PHPStan, tests on PHP 8.1/8.2/8.3, no-debug check
- Feature docs under docs/features/
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>