Front end: the student "upcoming lessons" panel now shows each booked
offering's name and length next to the time, and renders only the soonest
five lessons with a "Show all" reveal. GET /bookings returns offering_title
and duration_minutes so the list needs no extra request.
Admin: the Scheduler and My Lessons week/list views now show the booked
offering, and each lesson links to a detail view showing the policy versions
the student accepted (with acceptance time and IP) and their intake answers.
On My Lessons an instructor may only open their own lessons; the studio
Scheduler may open any.
composer test / composer lint / composer cs all pass.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
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]>
The admin dashboard and instructor My Lessons pages showed the raw
availability-slot database ID, which is meaningless to admins and
instructors. LessonController now takes AvailabilityRepository, looks up
each lesson's slot, and renders its window as e.g.
"Jul 6, 2026 9:00 AM-10:00 AM" via mysql2date. The date is repeated on
the end time only when a slot crosses midnight, and lessons whose slot
row no longer exists show an em dash.
Closes#47
Co-Authored-By: Claude Fable 5 <[email protected]>
- Bump phpstan/phpstan ^2.0 and szepeviktor/phpstan-wordpress ^2.0
- Move the analysis level into phpstan.neon (single source) and raise it to 10
- Add Val, a runtime coercion helper that narrows untyped WordPress boundary
values (wpdb rows, REST params, superglobals, options) with explicit checks
instead of blind casts, plus unit tests
- Type value-object fromRow() params as stdClass (what wpdb returns) and map
columns through Val so unexpected shapes degrade safely
- Use %i identifier placeholders for table names in all wpdb::prepare() calls
so every query string is a literal and identifiers are escaped by WordPress;
raises the minimum WordPress version to 6.2 where %i was introduced
- Guard wpdb::prepare() null result before wpdb::query() in updateTax()
- Fix nullable get_permalink()/strtotime() handling, list types at REST and
capability call sites, dead null-coalescing on checked superglobals, and
narrow get_users() results before mapping
- Register Val method names with the ValidatedSanitizedInput sniff so it
validates the real sanitizer around each superglobal read
- Update repository unit tests for the %i placeholder arguments
Co-Authored-By: Claude Fable 5 <[email protected]>
Studio Settings gains a default HST rate; the rate is frozen onto each
payment at booking and computed against the pre-tax subtotal, with the
total billed as subtotal + tax. The rate is overridable per booking on
My Lessons while unpaid (recomputing the tax amount), comped
registrations are never taxed, and receipts break out subtotal/HST/total.
Builds the payments report (roadmap #8) from us_payments: a monthly
per-instructor view with subtotal, HST collected, and grand-total
aggregation, plus a nonce-protected CSV export via admin-post. Studio
admins see all instructors and can filter; instructors are scoped to
their own rows. The Payment Report menu is gated on export_payments so
instructors (who lack manage_billing) can reach it.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The e-transfer destination is resolved at booking time (offering override ->
studio default) and frozen onto the payment, so each record keeps where the
student was directed. It can then be corrected per booking.
- StudioSettings: us_etransfer_email option + a Default e-transfer email field
on the Studio Settings page.
- Offering: etransfer_email column/field (instructor override) across VO, repo,
REST endpoint, admin controller, and form.
- Payment: etransfer_email column on the payment (frozen record) +
PaymentRepository::updateEtransferEmail; PaymentService freezes it from the
offering override or studio default at creation; booking/enrolment pass the
offering override.
- My Lessons: instructors edit the e-transfer email per pending lesson payment
(ownership-checked).
- Payments queue: studio admin can correct the email at confirmation (for when
a student sends it to the wrong place).
- Docs updated.
Tests: Payment/Offering rows + PaymentService freezing. composer test (148),
cs, and PHPStan level 6 all pass.
Refs #7
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]>
All classes are now organised by domain (Availability, Booking, Auth).
Each domain package contains its value object, repository, admin controller,
REST endpoint, and any shortcode pages under a matching sub-namespace.
Cross-cutting wiring (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar,
Schema) lives at src/ root. Tests mirror the domain structure.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>