Commit Graph
77 Commits
Author SHA1 Message Date
thatguygriffandClaude Fable 5 ab055c7a0c Serve plugin updates from tagged Gitea releases
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m43s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m34s
CI / Build Plugin Zip (pull_request) Skipped
Closes #65

Declare an Update URI header and answer core's update_plugins_{hostname}
filter from a new Update\UpdateChecker that offers the latest published
Gitea release's zip asset when it is newer than the installed version,
with transient caching and silent degradation on API failures.

Add a release workflow that fires on v* tag pushes: verifies the tag
matches the plugin Version header, runs the tests, builds the plugin zip,
and attaches it to the release (reusing a UI-created release, flagging
hyphenated versions as pre-release so /releases/latest skips them).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 11:09:50 -03:00
thatguygriff e4af0c327c Merge pull request 'Open student registration with email confirmation and admin approval' (#64) from feature/student-self-signup into main
CI / Tests (PHP 8.1) (push) Successful in 36s
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / No Debug Code (push) Successful in 4s
CI / Coding Standards (push) Successful in 2m46s
CI / PHPStan (push) Successful in 2m54s
CI / Tests (PHP 8.3) (push) Successful in 2m37s
CI / Build Plugin Zip (push) Successful in 2m48s
Reviewed-on: #64
2026-07-18 13:58:00 +00:00
thatguygriffandClaude Opus 4.8 7370755951 Add open student registration with email confirmation and approval
CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
Students could previously join by invite only. Add an optional
self-approval mode, toggled from Studio Settings → Registration: anyone
may sign up on the existing [us_student_register] page, confirm their
email via a tokenised link, and then be approved by a studio admin
before the account is usable.

- Enabling the toggle mirrors WordPress's own membership settings
  (users_can_register + default_role = us_student) and snapshots their
  previous values so disabling restores them.
- WordPress's native registration form is blocked while open
  registration is on (login_init redirect + registration_errors
  fail-safe + register_url) so it cannot bypass signup policy acceptance.
- Pending accounts: unconfirmed email cannot log in; confirmed but
  unapproved can log in but the booking capability is withheld and the
  booking page shows an "awaiting approval" screen.
- Approve/reject from Students → Pending Students; reject hard-deletes
  the account so the email is freed to re-apply.
- Invite registration is unchanged; both modes coexist.

Account lifecycle lives in user meta (RegistrationStatus); no new tables.

Closes #63

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-18 10:50:21 -03:00
thatguygriff e7d8257973 Merge pull request 'Show enrolment status on the student group classes page' (#62) from feature/enrolled-status into main
CI / Tests (PHP 8.1) (push) Successful in 46s
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
CI / Build Plugin Zip (push) Successful in 2m45s
CI / PHPStan (push) Successful in 1m41s
CI / Coding Standards (push) Successful in 2m48s
Reviewed-on: #62
2026-07-06 02:44:20 +00:00
thatguygriffandClaude Fable 5 30b0112431 Show enrolment status on the student group classes page
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 1m17s
CI / Tests (PHP 8.3) (pull_request) Successful in 37s
CI / Coding Standards (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Has been skipped
The class list now loads the student's own enrolments alongside the
catalog; a class they already have an active enrolment in shows "You
are enrolled in this class." instead of the Enrol button, in both the
browse-all catalog and the single-class embed mode. Previously the
button always rendered and a duplicate attempt walked the student
through the whole questions/policies flow before failing with 409
already_enrolled. A cancelled enrolment does not block re-enrolling,
matching the server-side duplicate rule.

Closes #61

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 23:41:01 -03:00
thatguygriff a9f0b8c066 Merge pull request 'Group class term dates, single-class embed mode, and offering editing' (#60) from feature/group-class-terms into main
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 32s
CI / Build Plugin Zip (push) Successful in 1m42s
CI / Tests (PHP 8.1) (push) Successful in 43s
CI / PHPStan (push) Successful in 49s
CI / Coding Standards (push) Successful in 2m43s
Reviewed-on: #60
2026-07-06 02:22:06 +00:00
thatguygriffandClaude Fable 5 cde8704267 Group class term dates, single-class embed mode, and offering editing
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / Tests (PHP 8.1) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 1m14s
CI / PHPStan (pull_request) Successful in 1m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 37s
CI / Build Plugin Zip (pull_request) Has been skipped
Group class offerings now carry real dates: the add/edit form takes a
start date plus a sessions control (one-off, or weekly for N sessions;
the end date is computed as start + (N-1) weeks via
Offering::weeklyTermEnd). Dates are validated strictly (Y-m-d) and shown
in the offerings list and on the student-facing class card, including
the weekly session count.

[us_group_classes offering="<id>"] (block attribute offeringId, chosen
from a dropdown of active classes fetched from the public offerings
endpoint) restricts the page to a single class so the enrolment flow can
be embedded on a page dedicated to that class; a pinned class that is no
longer offered reports itself closed instead of falling back to the
catalog.

Offerings are now editable from the admin screen: an Edit button
prefills the shared add/edit form and saving posts usc_action=update.
Updates always preserve the original owner and currency, and non-admin
instructors can only load and update their own offerings. The form also
gains the previously missing description field and an Active toggle (the
admin-UI counterpart of the REST is_active flag) so an edit cannot wipe
data the form never collected.

Closes #59

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 23:18:59 -03:00
thatguygriff 9d8924132c Merge pull request 'Bulk delete availability slots from the admin list view' (#58) from feature/availability-bulk-delete into main
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 1m11s
CI / Tests (PHP 8.3) (push) Successful in 35s
CI / Coding Standards (push) Successful in 2m14s
CI / Build Plugin Zip (push) Successful in 1m42s
Reviewed-on: #58
2026-07-06 01:32:31 +00:00
thatguygriffandClaude Fable 5 c743ed5459 Bulk delete availability slots from the admin list view
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 1m40s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m34s
CI / PHPStan (pull_request) Successful in 2m52s
CI / Build Plugin Zip (pull_request) Has been skipped
The list view of Current Slots gets a checkbox per unbooked slot, a
select-all header checkbox, and a Delete selected button submitting a
new bulk_delete form action. Each id is ownership-checked through the
same path as single delete; the repository's is_booked guard refuses
booked slots as a second layer. Row checkboxes attach to the bulk form
via the HTML form attribute because the table already contains the
per-row delete forms and forms cannot nest.

Closes #57

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 22:25:35 -03:00
thatguygriff aaa24e524f Merge pull request 'Require an offering on every lesson booking, with a student-facing picker' (#56) from fix/require-booking-offering into main
CI / Tests (PHP 8.1) (push) Successful in 42s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m17s
CI / Tests (PHP 8.3) (push) Successful in 2m2s
CI / Coding Standards (push) Successful in 2m49s
CI / Build Plugin Zip (push) Successful in 2m15s
Reviewed-on: #56
2026-07-06 01:14:18 +00:00
thatguygriffandClaude Fable 5 c7d5d72c9c Require an offering on every lesson booking, with a student-facing picker
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m40s
CI / PHPStan (pull_request) Successful in 2m24s
CI / Coding Standards (pull_request) Successful in 2m49s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-07-05 22:09:27 -03:00
thatguygriffandClaude Fable 5 ab90dae638 Let students cancel their own lessons from the booking page
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 46s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / PHPStan (push) Successful in 1m11s
CI / Tests (PHP 8.3) (push) Successful in 1m0s
CI / Build Plugin Zip (push) Successful in 1m10s
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]>
2026-07-05 17:17:30 -03:00
thatguygriff 9dd3c39ddd Merge pull request 'Skip payment step for unpriced bookings, confirm them immediately, show students their lessons' (#54) from fix/unpriced-booking-flow into main
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 1m45s
CI / Tests (PHP 8.3) (push) Successful in 1m4s
CI / Coding Standards (push) Successful in 2m23s
CI / Build Plugin Zip (push) Successful in 2m16s
Reviewed-on: #54
2026-07-05 20:06:46 +00:00
thatguygriffandClaude Fable 5 5888032ed7 Skip payment step for unpriced bookings, confirm them immediately, show students their lessons
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / PHPStan (pull_request) Successful in 2m46s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Coding Standards (pull_request) Successful in 47s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m35s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-07-05 17:02:38 -03:00
thatguygriff 93dccd6352 Merge pull request 'Block options for login/booking link targets with optional auto-redirect' (#52) from feature/block-link-targets into main
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / PHPStan (push) Successful in 57s
CI / Tests (PHP 8.2) (push) Successful in 53s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 1m53s
CI / Tests (PHP 8.3) (push) Successful in 2m38s
CI / Build Plugin Zip (push) Successful in 53s
Reviewed-on: #52
2026-07-05 19:19:30 +00:00
thatguygriffandClaude Fable 5 9d89bc6d0e Add link-target and auto-redirect options to booking/login blocks
CI / Coding Standards (pull_request) Successful in 51s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m2s
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / PHPStan (pull_request) Successful in 1m24s
The booking block gains a loginPageId attribute choosing which page its
logged-out "log in to book a lesson" link points to (default remains the
WordPress login screen), and the student-login block gains a
bookingPageId attribute controlling the logged-in "View available
lessons" link and the post-login redirect target (default remains the
current page). Both blocks also gain an autoRedirect toggle, off by
default, that sends the visitor straight to the target page; block
rendering starts after output, so the redirect runs on
template_redirect by parsing the queried page's content for the block,
with a self-target guard against redirect loops. The link targets are
also available to the shortcodes as login_page_id/booking_page_id.

Also fixes a pre-existing fatal: WordPress passes an empty string (not
an array) to shortcode callbacks when a shortcode is used without
attributes, so bare [us_booking] etc. threw a TypeError against the
strictly-typed render(array $atts) methods. ShortcodeRegistrar now
wraps each callback to normalize non-array attribute values.

Closes #51

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 16:16:52 -03:00
thatguygriff 43497503b9 Merge pull request 'Split availability windows into bookable lesson-length slots; weekly calendar views; 12-hour times' (#50) from feature/slot-splitting-and-week-view into main
CI / PHPStan (push) Successful in 1m42s
CI / Tests (PHP 8.2) (push) Successful in 40s
CI / Tests (PHP 8.1) (push) Successful in 56s
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.3) (push) Successful in 34s
CI / Coding Standards (push) Successful in 2m45s
CI / Build Plugin Zip (push) Successful in 1m14s
Reviewed-on: #50
2026-07-05 19:14:50 +00:00
thatguygriffandClaude Fable 5 dbf61e8593 Word-bound the no-debug CI grep so method calls like ->add() don't match dd(
CI / Tests (PHP 8.2) (pull_request) Successful in 38s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 1m20s
CI / PHPStan (pull_request) Successful in 1m43s
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m5s
The unanchored dd\( pattern matched the substring in DateTimeImmutable::add(),
failing the check on non-debug code.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 16:12:37 -03:00
thatguygriffandClaude Fable 5 b7d5e3039e Split availability windows into bookable lesson-length slots with weekly calendar views
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / PHPStan (pull_request) Successful in 2m48s
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / No Debug Code (pull_request) Failing after 2s
CI / Coding Standards (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
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]>
2026-07-05 16:00:47 -03:00
thatguygriff 66f308e1da Merge pull request 'Replace Slot ID column in lessons list with the lesson's date/time' (#48) from feature/lessons-datetime-column into main
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / PHPStan (push) Successful in 1m38s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m44s
CI / Build Plugin Zip (push) Successful in 1m43s
CI / Tests (PHP 8.3) (push) Successful in 2m35s
Reviewed-on: #48
2026-07-05 18:57:16 +00:00
thatguygriffandClaude Fable 5 6ff733a71f Replace Slot ID column in lessons list with the lesson's date/time
CI / Tests (PHP 8.2) (pull_request) Successful in 1m20s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m21s
CI / Coding Standards (pull_request) Successful in 1m45s
CI / PHPStan (pull_request) Successful in 3m22s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-07-05 15:51:54 -03:00
thatguygriff aea731c2f8 Merge pull request 'Upgrade PHPStan to 2.x and raise analysis level from 6 to 10' (#46) from chore/phpstan-2-upgrade into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 56s
CI / PHPStan (push) Successful in 1m2s
CI / Coding Standards (push) Successful in 1m5s
CI / Build Plugin Zip (push) Successful in 56s
Reviewed-on: #46
2026-06-12 16:54:57 +00:00
thatguygriffandClaude Fable 5 1d6ac46ba3 Upgrade PHPStan to 2.x and raise analysis level from 6 to 10
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Coding Standards (pull_request) Successful in 57s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m1s
CI / PHPStan (pull_request) Successful in 1m11s
CI / Build Plugin Zip (pull_request) Has been skipped
- 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]>
2026-06-12 13:42:50 -03:00
thatguygriff b23508f726 Merge pull request 'Gutenberg dynamic-block wrappers for shortcodes with editor previews' (#45) from feature/editor-blocks into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 54s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / Tests (PHP 8.3) (push) Successful in 1m6s
CI / Coding Standards (push) Successful in 1m12s
CI / PHPStan (push) Successful in 1m12s
CI / Build Plugin Zip (push) Successful in 1m27s
Reviewed-on: #45
2026-06-12 15:14:02 +00:00
thatguygriffandClaude Fable 5 fc70cde9d5 Add Gutenberg dynamic-block wrappers for the front-end shortcodes
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m29s
CI / Coding Standards (pull_request) Successful in 1m57s
CI / PHPStan (pull_request) Successful in 2m14s
CI / Build Plugin Zip (pull_request) Has been skipped
Wrap the four shortcodes (us_booking, us_student_login,
us_student_register, us_group_classes) in dynamic blocks so pages can be
previewed and styled in the block editor. Front-end rendering delegates
to the same page objects the shortcodes use; in the editor's
block-renderer REST preview a static, script-free BlockPreview is
rendered instead (no live REST calls, redirects, or Stripe.js). The
editor script (vanilla JS, no build step) registers each block with
wp.serverSideRender previews and shortcode transforms; frontend.css is
attached as the block style so previews pick up theme styling.

Resolves #44

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-12 12:03:27 -03:00
thatguygriff 63e2fbcc5b Merge pull request 'Security fixes: CSV injection, policy body output, invite token hashing, slot datetime validation' (#43) from feature/security-review-fixes into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.3) (push) Successful in 54s
CI / Coding Standards (push) Successful in 1m1s
CI / PHPStan (push) Successful in 1m9s
CI / Build Plugin Zip (push) Successful in 1m6s
Reviewed-on: #43
2026-06-10 19:51:20 +00:00
thatguygriffandClaude Fable 5 f3f5c7801f Security fixes: CSV injection, policy body output, invite hashing, slot datetimes
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Tests (PHP 8.3) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 1m11s
CI / PHPStan (pull_request) Successful in 1m20s
CI / Build Plugin Zip (pull_request) Has been skipped
Four fixes from a security review pass:

- Neutralise CSV formula injection in the payments export: fields with a
  leading =, +, -, @, tab, or CR (e.g. a hostile student display name) are
  apostrophe-prefixed in PaymentReport::csvLine() so they open as text in
  Excel/Google Sheets. Fixes #39.
- Sanitise policy bodies with wp_kses_post at output in
  PolicyEndpoint::index() (the booking JS renders that HTML raw), so a
  future write path that forgets kses can never become stored XSS.
  Fixes #40.
- Store invite tokens hashed (SHA-256) at rest: a database leak can no
  longer redeem pending invites. The registration link is shown once, at
  creation; the pending list shows email/invited date; lookups hash the
  submitted token. Existing plaintext pending invites must be re-issued.
  Fixes #41.
- Validate availability slot datetimes on both creation paths (REST and
  admin form) via AvailabilitySlot::normalizeDateTime(): canonical and
  datetime-local forms normalise to Y-m-d H:i:s, garbage and end <= start
  are rejected (REST 400) instead of reaching the DATETIME column or
  throwing inside the weekly-series date arithmetic. Fixes #42.

composer test (204 tests, 594 assertions), PHPStan L6, and PHPCS all green.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 16:36:26 -03:00
thatguygriff 693246c1c1 Merge pull request 'Security hardening: booking auth, offering exposure, payments, invites (#31–#37)' (#38) from feature/security-fixes into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / Coding Standards (push) Successful in 58s
CI / PHPStan (push) Successful in 1m2s
CI / Tests (PHP 8.3) (push) Successful in 1m41s
CI / Build Plugin Zip (push) Successful in 55s
Reviewed-on: #38
2026-06-09 20:11:34 +00:00
thatguygriffandClaude Opus 4.8 061d09e034 Harden booking, offering exposure, payments, and invites
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m41s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Build Plugin Zip (pull_request) Has been skipped
Security fixes from a pen-test review (issues #31–#37):

- #31 Booking no longer trusts a client-supplied offering_id: a slot-tied
  offering is authoritative and any offering used must belong to the slot's
  instructor, closing a free/misrouted-payment bypass.
- #34 Availability slot creation rejects an offering the instructor does not
  own (AvailabilityEndpoint now takes OfferingRepository).
- #32 Offering/question/policy listing endpoints now require book_lesson
  instead of being public (no anonymous consumer exists); Offering::toArray
  also omits etransfer_email from listings as defense-in-depth.
- #33 Slots are claimed atomically (UPDATE ... WHERE is_booked = 0) before a
  lesson is inserted, preventing a double-booking race.
- #35 A single weekly booking is capped (MAX_WEEKLY_OCCURRENCES) and only
  creates lessons for slots it actually claimed.
- #36 Stripe secret/webhook keys are write-only in the settings UI and a blank
  submit keeps the stored value; secrets are never echoed back into HTML.
- #37 Pending invites expire after 14 days (Invite::isAcceptable), enforced at
  registration and surfaced on the admin invites list.

Adds BookingEndpointTest plus Invite/Offering/AvailabilityRepository coverage
and minimal WP_REST_Request/WP_REST_Response stubs. composer test (200),
lint, and cs all green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-09 17:08:22 -03:00
thatguygriff fe43f91fb1 Merge pull request 'Add Instructors admin page (create + per-capability access)' (#30) from feature/instructor-management into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.3) (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 50s
CI / Coding Standards (push) Successful in 54s
CI / PHPStan (push) Successful in 1m5s
CI / Build Plugin Zip (push) Successful in 50s
Reviewed-on: #30
2026-06-08 20:05:33 +00:00
thatguygriffandClaude Opus 4.8 b5c076c3d6 Add Instructors admin page (create + per-capability access)
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 1m5s
CI / PHPStan (pull_request) Successful in 1m11s
CI / Build Plugin Zip (pull_request) Has been skipped
Completes the instructor-management half of #9: the studio admin can now
create instructor accounts and toggle each instructor's capabilities.

- InstructorController (manage_instructors): list instructors, create a
  us_instructor WP user (emailing a set-password link), and a per-instructor
  capability detail view.
- InstructorCapabilities: pure, unit-tested rules for which managed caps an
  admin may assign and how a submitted form maps to assignments. Managed caps
  are manage_offerings, manage_questions, view_own_payments, export_payments;
  manage_availability and view_own_lessons are core to every instructor.
- A studio admin can never grant a capability it does not itself hold: only
  held caps (checked via current_user_can, so an administrator's dynamic grant
  counts) are offered, and on creation any managed cap the admin lacks is
  denied on the new instructor so they never exceed their creator. The role
  grants the managed caps by default; the page layers per-user overrides.
- AdminMenu: register the Instructors page in the people section.
- Tests for the capability logic; docs/features/user-roles.md updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 17:02:46 -03:00
thatguygriff fdadd2fb92 Merge pull request 'Make WP admins instructors too, with an Access toggle page' (#29) from feature/admin-access-control into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.3) (push) Successful in 47s
CI / Coding Standards (push) Successful in 54s
CI / Tests (PHP 8.1) (push) Successful in 59s
CI / Tests (PHP 8.2) (push) Successful in 1m0s
CI / PHPStan (push) Successful in 1m14s
CI / Build Plugin Zip (push) Successful in 57s
Reviewed-on: #29
2026-06-08 19:42:44 +00:00
thatguygriffandClaude Opus 4.8 67f8144a4a Make WP admins instructors too, and add an Access toggle page
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 58s
CI / PHPStan (pull_request) Successful in 1m9s
CI / Build Plugin Zip (pull_request) Has been skipped
A WordPress administrator previously inherited the studio-admin
capabilities but not `manage_availability`, so the studio owner running
as an admin had no way to reach "My Availability" or act as the
instructor — breaking single-instructor businesses.

Grant the instructor capabilities to administrators as well (via the
existing `user_has_cap` filter), and make both grants — studio-admin and
instructor — independently toggleable from a new Access admin page.

- RoleManager: extract `INSTRUCTOR_CAPS`; apply studio and instructor
  cap sets to administrators, each gated on a stored toggle (default on).
- AccessSettings + templates/admin/access.php: two options
  (`us_admin_grant_studio` / `us_admin_grant_instructor`), gated on the
  core `manage_options` capability so disabling a grant can never lock an
  administrator out of re-enabling it.
- AdminMenu: register the Access page after Studio Settings; keep the
  studio sidebar separator visible for any administrator.
- Tests for the toggles and the new settings reader; docs updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 16:39:41 -03:00
thatguygriff 0a78f4b1ac Merge pull request 'Add live Stripe card charges (PaymentIntent + Elements + webhook)' (#28) from feature/payments-stripe into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 52s
CI / Build Plugin Zip (push) Successful in 57s
CI / Coding Standards (push) Successful in 55s
CI / PHPStan (push) Successful in 1m10s
CI / Tests (PHP 8.2) (push) Successful in 1m29s
Reviewed-on: #28
2026-06-08 18:59:42 +00:00
thatguygriffandClaude Opus 4.8 925a4b79ba Add live Stripe card charges (PaymentIntent + Elements + webhook)
CI / No Debug Code (pull_request) Successful in 40s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m13s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m9s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m8s
CI / Build Plugin Zip (pull_request) Has been skipped
Completes the deferred half of payments: real credit-card processing on
top of the existing ledger/e-transfer/comp foundation.

- StripeGateway wraps stripe/stripe-php: creates idempotent PaymentIntents
  (amount in cents, registration ids in metadata) and verifies webhook
  signatures. Stripe calls sit behind protected seams for unit testing.
- PaymentService::createIntent resolves the client-side step for a new
  registration (card → client secret; e-transfer → display data; comp →
  none) with caller-ownership enforcement.
- PaymentService::handleWebhook finalises a payment exactly once on
  payment_intent.succeeded (mark paid → confirm → receipt) and marks it
  failed on payment_intent.payment_failed.
- PaymentEndpoint: POST /payments/intent (book_lesson) and public,
  signature-verified POST /payments/webhook.
- PaymentRepository: setStripeIntentId / findByStripeIntentId.
- StudioSettings: us_stripe_webhook_secret option, with the webhook URL
  and required events surfaced on the settings page.
- Front end: shared payment.js mounts Stripe Payment Elements and confirms
  the card (or shows e-transfer instructions); Stripe.js enqueued only when
  configured. Wired into booking and group-class flows.

Tests: new StripeGatewayTest; PaymentService card-intent + webhook cases;
repository coverage. composer test/lint/cs all green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 15:51:37 -03:00
thatguygriff 4b34d2464d Merge pull request 'Add HST/tax support and payment reporting with HST aggregation' (#27) from feature/hst-and-reporting into main
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Coding Standards (push) Successful in 59s
CI / Tests (PHP 8.1) (push) Successful in 56s
CI / Build Plugin Zip (push) Successful in 40s
CI / No Debug Code (push) Successful in 3s
CI / PHPStan (push) Successful in 1m13s
CI / Tests (PHP 8.3) (push) Successful in 52s
Reviewed-on: #27
2026-06-08 14:38:52 +00:00
thatguygriff 2aa0d5ad5d Merge remote-tracking branch 'origin/main' into feature/hst-and-reporting
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 4s
CI / Coding Standards (pull_request) Successful in 1m3s
CI / PHPStan (pull_request) Successful in 1m18s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
# Conflicts:
#	src/AdminMenu.php
2026-06-08 11:36:18 -03:00
thatguygriffandClaude Opus 4.8 553cfafa49 Add HST/tax support and payment reporting with HST aggregation
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Coding Standards (pull_request) Successful in 1m1s
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / No Debug Code (pull_request) Successful in 4s
CI / PHPStan (pull_request) Successful in 1m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-06-08 11:29:48 -03:00
thatguygriff d229b83b23 Merge pull request 'Group the studio admin sidebar into three sections' (#26) from feature/admin-menu-separator into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / Coding Standards (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 49s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / PHPStan (push) Successful in 1m10s
CI / Build Plugin Zip (push) Successful in 39s
Reviewed-on: #26
2026-06-08 14:13:21 +00:00
thatguygriffandClaude Opus 4.8 ca458bb8ae Group the studio admin menu into three sections with separators
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Coding Standards (pull_request) Successful in 54s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 59s
CI / PHPStan (pull_request) Successful in 1m14s
CI / Build Plugin Zip (pull_request) Has been skipped
Add sidebar separators (wp-menu-separator) that set the studio menus apart
from the core WordPress items and split them into three sections:

  [sep] Studio Settings · Policies · Invites · Offerings
  [sep] Students · Group Classes · Payments
  [sep] Scheduler  (then the instructor menus: My Availability, My Lessons)

Separators live at positions 29 / 34 / 38; each is only added when the user
can see a menu in the following section, to avoid orphaned dividers.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 11:06:47 -03:00
thatguygriff b73d81421f Merge pull request 'Add payments foundation (e-transfer/comp, Stripe config, receipts, e-transfer email)' (#25) from feature/payments into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Coding Standards (push) Successful in 1m2s
CI / Tests (PHP 8.2) (push) Successful in 1m2s
CI / PHPStan (push) Successful in 1m8s
CI / Tests (PHP 8.3) (push) Successful in 1m18s
CI / Build Plugin Zip (push) Successful in 51s
Reviewed-on: #25
2026-06-08 13:49:39 +00:00
thatguygriffandClaude Opus 4.8 9873cb5e30 Add e-transfer destination email (studio default + offering/booking overrides)
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 46s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / PHPStan (pull_request) Successful in 1m12s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-06-08 10:47:06 -03:00
thatguygriffandClaude Opus 4.8 6c4097b385 Add payments foundation (e-transfer/comp, Stripe config, receipts)
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 1m2s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements the payments foundation for #7. Without Stripe credentials
everything works on e-transfer (pending payment confirmed by a studio
admin); when Stripe keys are configured the default flips to credit card.
Per-student override (card/etransfer/comp) is set on the student detail.

- Schema: us_payments (amount DECIMAL dollars, method, status, receipt,
  stripe intent id).
- src/Payment/: Payment VO, PaymentRepository, StudioSettings (Stripe
  options + isStripeConfigured + settings page), BillingMethodResolver
  (per-student override; default card if configured else etransfer),
  ReceiptMailer, PaymentService (create at registration, link payment_id,
  comp->paid+confirm, markPaid->confirm+receipt), PaymentController
  (e-transfer confirmation queue), PaymentEndpoint (PATCH /payments/{id}).
- Booking + enrolment create the payment from the offering price; comp
  auto-confirms the lesson; setPaymentId on both repositories.
- Admin: Studio Settings + Payments menus (manage_billing); per-student
  billing method on the student detail page.
- Docs: payments.md + README updated.

Deferred to a follow-up: the live Stripe card charge (PaymentIntent +
Stripe.js Elements + webhook + stripe/stripe-php). Until then a card
payment is created pending and confirmed like an e-transfer.

Tests: tests/Unit/Payment/ (VO, repository, resolver, service, mailer).
composer test (147), cs, and PHPStan level 6 all pass.

Refs #7

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 10:24:01 -03:00
thatguygriff 071ef7fc2a Merge pull request 'Add student administration view (studio-admin)' (#24) from feature/student-administration into main
CI / No Debug Code (push) Successful in 34s
CI / Coding Standards (push) Successful in 55s
CI / PHPStan (push) Successful in 2m9s
CI / Tests (PHP 8.3) (push) Successful in 1m4s
CI / Tests (PHP 8.1) (push) Successful in 3m1s
CI / Tests (PHP 8.2) (push) Successful in 3m6s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #24
2026-06-08 12:40:23 +00:00
thatguygriffandClaude Opus 4.8 8fb5ff8270 Add student administration view (studio-admin)
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Coding Standards (pull_request) Successful in 56s
CI / PHPStan (pull_request) Successful in 57s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #22: a read-only Students area for studio admins.

- StudentController (manage_students): a list of us_student users with
  upcoming-lesson and active-enrolment counts, each linking to a detail page
  showing account info, upcoming/past lessons (offering, instructor, status),
  and group-class enrolments.
- StudentSchedule::partition() — pure, unit-tested upcoming/past split.
- Repo counts: BookingRepository::countUpcomingForStudent and
  EnrollmentRepository::countActiveForStudent (single-query, tested).
- Templates: templates/admin/students.php, student-detail.php.
- Students admin menu wired in AdminMenu (no Plugin change — the repos were
  already available there).
- Docs: README status flipped to implemented; feature spec updated.

Payment history slots into the detail when Payments (#7) lands.

Tests: StudentScheduleTest + the two repo count tests. composer test (127),
cs, and PHPStan level 6 all pass.

Refs #22

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-08 09:28:28 -03:00
thatguygriff d86e852edc Merge pull request 'Add README and student-administration feature spec' (#23) from docs/readme-and-student-admin into main
CI / Coding Standards (push) Successful in 51s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / PHPStan (push) Successful in 54s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / Build Plugin Zip (push) Successful in 52s
Reviewed-on: #23
2026-06-07 15:12:21 +00:00
thatguygriffandClaude Opus 4.8 8a3e9b65df Add README and student-administration feature spec
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m0s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 43s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
CI / Build Plugin Zip (pull_request) Has been skipped
- README.md: plugin overview, package-by-domain architecture, feature/status
  table linking each docs/features spec (implemented vs planned), shortcodes,
  REST namespace, roles, install via composer build, dev commands, and CI.
- docs/features/student-administration.md: spec for the planned studio-admin
  Students list + per-student detail (upcoming/past lessons, group enrolments);
  read-only; manage_students; no new tables. Tracked as #22.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 12:08:30 -03:00
thatguygriff 9799f7c1e2 Merge pull request 'Add group-class enrolment (year commitment, capacity, registration gate)' (#21) from feature/group-classes into main
CI / Tests (PHP 8.1) (push) Successful in 42s
CI / Coding Standards (push) Successful in 51s
CI / PHPStan (push) Successful in 56s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 41s
CI / Tests (PHP 8.3) (push) Successful in 45s
CI / Build Plugin Zip (push) Successful in 40s
Reviewed-on: #21
2026-06-07 14:49:40 +00:00
thatguygriffandClaude Opus 4.8 9cb5207dcd Add group-class enrolment (year commitment, capacity, registration gate)
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Coding Standards (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 1m4s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 42s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #4: students enrol in a group_class offering via the same
registration gate as private lessons (intake questions + booking-scoped
policy acceptance). Enrolment is capacity-enforced and prevents duplicates.

- Schema: us_group_enrollments table.
- Enrollment value object + EnrollmentRepository (countActiveForOffering,
  hasActiveEnrollment, per-student/instructor/all-active queries, status).
- EnrollmentEndpoint: GET /enrollments (scoped) and POST /enrollments
  (validates group_class, capacity, no-duplicate; reuses RegistrationGate;
  records answers/acceptances type enrollment).
- GroupClassController + admin page (view_all_lessons): all active enrolments.
- Front-end: [us_group_classes] shortcode (GroupClassPage) + group-classes.js
  enrol flow (list classes -> questions + policies -> POST /enrollments).
- Wiring in Plugin, RestRegistrar, AdminMenu, ShortcodeRegistrar.

Payment is the deferred seam (#7): enrolment lands active, payment_id null.
JS left untested for parity with the repo's no-build vanilla-JS posture.

Tests: tests/Unit/GroupClass/ (Enrollment, EnrollmentRepository).
composer test (121), cs, and PHPStan level 6 all pass.

Refs #4

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 11:43:33 -03:00
thatguygriff 0b3832309d Merge pull request 'Add lesson booking registration flow (offering, questions, policies)' (#20) from feature/registration-flow into main
CI / Coding Standards (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / PHPStan (push) Successful in 58s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.3) (push) Successful in 49s
CI / Build Plugin Zip (push) Successful in 57s
Reviewed-on: #20
2026-06-07 14:30:00 +00:00
thatguygriffandClaude Opus 4.8 6d163e5d0e Add lesson booking registration flow (offering, questions, policies)
CI / Coding Standards (pull_request) Successful in 1m51s
CI / PHPStan (pull_request) Successful in 2m17s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m24s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / Build Plugin Zip (pull_request) Has been skipped
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]>
2026-06-07 11:25:30 -03:00
thatguygriff d0dddd9075 Merge pull request 'Invite links: page selector + auto-redirect for stray tokens' (#19) from fix/invite-registration-page-selector into main
CI / Coding Standards (push) Successful in 57s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 48s
CI / PHPStan (push) Successful in 1m7s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 54s
Reviewed-on: #19
2026-06-07 13:43:33 +00:00
thatguygriffandClaude Opus 4.8 330900a246 Auto-redirect invite tokens to the registration page; rename invite button
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 59s
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
- RegistrationPage::maybeRedirectToRegistrationPage() (hooked on
  template_redirect): any front-end request carrying a us_invite token is
  redirected to the configured registration page (token preserved), unless
  already there. Covers links shared before a page was selected; no-op when
  no page is set.
- Invites button text: "Send Invite" -> "Generate Invitation Link".
- Doc updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 10:31:11 -03:00
thatguygriffandClaude Opus 4.8 372f981a08 Add registration-page selector to the Invites page
CI / Coding Standards (pull_request) Successful in 1m4s
CI / PHPStan (pull_request) Successful in 1m3s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
Invitation links previously pointed at the site home page, which usually
does not host the [us_student_register] shortcode. Let the studio admin
choose the registration page (stored in the us_registration_page_id
option); invitation links now point there, falling back to the home page
when unset (with a warning notice).

- RegistrationController: OPTION_PAGE constant; set_page action; pass the
  page id/url to the template.
- templates/admin/invites.php: wp_dropdown_pages selector + save; build the
  invite link from the selected page.
- Doc updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 10:24:33 -03:00
thatguygriff 9b8fa5d698 Merge pull request 'Add account registration with signup policy acceptance' (#18) from feature/account-registration into main
CI / Coding Standards (push) Successful in 47s
CI / PHPStan (push) Successful in 1m3s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #18
2026-06-05 19:52:57 +00:00
thatguygriffandClaude Opus 4.8 9c900d6553 Add account registration with signup policy acceptance
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 1m1s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
Implements #16: invite-only student self-registration through a front-end
page, accepting signup-scoped policies at account creation.

Policy domain:
- us_policies.acceptance_scope (signup/booking/both); Policy::appliesTo();
  PolicyRepository::findForScope(); scope threaded through PolicyService,
  the REST create, the admin controller, and the Policies form.
- PolicyAcceptance::REG_ACCOUNT (registration_id = the new user's ID).

Auth:
- Invite value object + InviteRepository; us_invites table.
- RegistrationController + Invites admin page (manage_students): invite an
  email, share the registration link, revoke.
- RegistrationPage ([us_student_register] shortcode): validates the invite
  token, collects name/password, renders signup-scoped published policies
  with required acceptance, creates the us_student user, records account-type
  acceptances, marks the invite accepted, and logs the user in.
- RoleManager: manage_students cap added to STUDIO_ADMIN_CAPS.

Invite-only is implemented; the us_registration_mode self_approval path is a
documented future seam.

Docs: docs/features/account-registration.md; policies.md updated.
Tests: tests/Unit/Auth/ (Invite, InviteRepository) plus Policy scope
updates. composer test (104), cs, and PHPStan level 6 all pass.

Refs #16

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 16:39:39 -03:00
thatguygriff 5eb096c5cf Merge pull request 'Extend availability (durations, weekly recurrence, calendar); price offerings in dollars' (#17) from feature/availability-extensions into main
CI / Build Plugin Zip (push) Successful in 40s
CI / Coding Standards (push) Successful in 44s
CI / PHPStan (push) Successful in 1m2s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 47s
CI / No Debug Code (push) Successful in 3s
Reviewed-on: #17
2026-06-05 19:05:15 +00:00
thatguygriffandClaude Opus 4.8 19e663d6fa Extend availability (durations, weekly recurrence, calendar); price offerings in dollars
CI / Coding Standards (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 1m2s
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Availability (#2):
- us_availability gains offering_id, duration_minutes (default 60), and
  recurrence_group; AvailabilitySlot carries the new fields.
- AvailabilityRepository::createWeeklySeries() generates N weekly rows
  sharing a recurrence_group; findAvailable() filters by offering and
  duration. Date math uses DateTimeImmutable::modify() (the no-debug CI
  regex `dd\(` matches `->add(`).
- REST GET filters by offering_id/duration_minutes; POST accepts
  duration_minutes, offering_id, recurrence (single|weekly) + weeks.
- Admin form adds duration, an offering picker, and one-off/weekly options
  (OfferingRepository wired into AvailabilityController).
- booking.js renders an agenda calendar (slots grouped by day, with
  duration). The richer booking UX lands with the booking-flow work.

Offering price in dollars:
- Switch us_offerings.price_cents (INT) to price DECIMAL(10,2); Offering
  uses float $price. Admin form and REST take dollars.
- Fix a pre-existing misalignment in the Offering insert/update $wpdb
  format arrays (billing_mode/capacity/is_active were mapped to the wrong
  specifiers, which would corrupt values) via a single COLUMN_FORMATS list.

Also bump PHPStan to --memory-limit=1G in the lint script; 128M now
crashes analysis as the codebase has grown.

Refs #2

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 15:43:48 -03:00
thatguygriff 5352fb7d69 Merge pull request 'Add Policies domain (drafting, versioning, tracked acceptance)' (#15) from feature/policies into main
CI / Coding Standards (push) Successful in 58s
CI / PHPStan (push) Successful in 1m7s
CI / Tests (PHP 8.2) (push) Successful in 58s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
CI / Build Plugin Zip (push) Successful in 40s
Reviewed-on: #15
2026-06-05 18:22:16 +00:00
thatguygriffandClaude Opus 4.8 6225e772f8 Add Policies domain (drafting, versioning, tracked acceptance)
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Tests (PHP 8.2) (pull_request) Successful in 56s
CI / Tests (PHP 8.3) (pull_request) Successful in 57s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #6: studio admins draft, version, and publish policies; the
public registration gate reads the current published version of each, and
acceptance is recorded against the exact version so a new version must be
re-accepted at the next booking.

- src/Policy/: Policy, PolicyVersion, PolicyAcceptance value objects;
  PolicyRepository, PolicyVersionRepository, AcceptanceRepository;
  PolicyService (orchestrates create/add-draft/publish across the policies
  and versions tables); PolicyEndpoint (REST); PolicyController +
  templates/admin/policies.php (Policies admin menu, manage_policies)
- us_policies, us_policy_versions, us_policy_acceptances tables in Schema
- REST: public GET /policies (current published versions); manage_policies
  for create, add version, edit draft, and publish
- Wiring in Plugin, RestRegistrar, AdminMenu

AcceptanceRepository is built now and consumed by the booking/enrolment
gate in #3/#4.

Also bump PHPStan to --memory-limit=1G in the composer lint script; the
default 128M now crashes the analysis as the codebase has grown.

Tests: tests/Unit/Policy/ (value objects, repositories, service).
composer test (90 total), cs, and PHPStan level 6 all pass.

Refs #6

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 15:00:54 -03:00
thatguygriff 74fb27ea05 Merge pull request 'Fix CI artifact double-zip so the plugin installs directly' (#14) from fix/ci-artifact-double-zip into main
CI / Coding Standards (push) Successful in 51s
CI / PHPStan (push) Successful in 1m6s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 42s
Reviewed-on: #14
2026-06-05 15:36:41 +00:00
thatguygriffandClaude Opus 4.8 2011319750 Fix CI artifact so the downloaded plugin installs directly
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 58s
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.3) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Gitea/Actions re-zips artifacts on download, so uploading the built plugin
zip produced a double-wrapped archive (a zip containing a zip). WordPress
then reported "No valid plugins were found" because the upload had no
plugin folder/header at its top level.

Unpack the built zip and upload the resulting plugin folder instead, so the
downloaded artifact's top level is unsupervised-schedular/ and installs
directly via Plugins -> Add New -> Upload.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 12:31:29 -03:00
thatguygriff 0988655aba Allow additional composer and tea commands
CI / PHPStan (push) Successful in 57s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 50s
CI / Build Plugin Zip (push) Successful in 42s
2026-06-05 12:17:31 -03:00
thatguygriff 3afa8d80f2 Merge pull request 'Grant studio-admin caps to administrators; re-gate Scheduler; mark 1.0.0-rc.1' (#13) from feature/admin-studio-caps into main
CI / Coding Standards (push) Successful in 48s
CI / PHPStan (push) Successful in 1m4s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #13
2026-06-05 15:13:29 +00:00
thatguygriffandClaude Opus 4.8 cbf05fe363 Mark version as 1.0.0-rc.1
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Reflect the in-progress, pre-release state. Bump to 1.0.0 before tagging a
release. Updates both the plugin header and USC_VERSION; the build/CI zip
artifact name tracks this automatically.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 12:08:04 -03:00
thatguygriffandClaude Opus 4.8 b4acae34a3 Grant studio-admin capabilities to WordPress administrators
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]>
2026-06-05 12:02:54 -03:00
thatguygriff 0b0dfe8f1c Merge pull request 'Add plugin zip build task and CI release artifact' (#12) from feature/build-and-release into main
CI / Coding Standards (push) Successful in 52s
CI / PHPStan (push) Successful in 1m3s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 51s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 58s
Reviewed-on: #12
2026-06-05 14:54:12 +00:00
thatguygriffandClaude Opus 4.8 e9d6c189bc Add plugin zip build task and CI release artifact
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 1m1s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
- bin/build-zip.sh + `composer build`: stage runtime files only, generate a
  production (no-dev) optimized autoloader, and emit
  dist/<slug>-<version>.zip with a single top-level plugin folder, ready to
  upload via wp-admin. Tests, tooling configs, docs, and dev dependencies
  are excluded; version is read from the plugin header.
- CI `build` job: on push to main (post-merge), after lint/static-analysis/
  test/no-debug pass, runs the build and uploads the zip via
  actions/upload-artifact.
- Ignore build/ and dist/.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 11:44:53 -03:00
thatguygriff 6397f08398 Merge pull request 'Add Registration Questions domain (per-offering intake forms)' (#11) from feature/registration-questions into main
CI / Coding Standards (push) Successful in 50s
CI / PHPStan (push) Successful in 56s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #11
2026-06-05 14:17:23 +00:00
thatguygriffandClaude Opus 4.8 e61d99daed Add Registration Questions domain (per-offering intake forms)
CI / Coding Standards (pull_request) Successful in 51s
CI / PHPStan (pull_request) Successful in 1m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
Implements #5: studio admin / instructors author intake questions scoped
per offering; answers are stored against a lesson or group enrolment via a
polymorphic registration reference.

- src/Registration/: Question + Answer value objects, QuestionRepository
  and AnswerRepository, QuestionEndpoint (REST), QuestionController +
  templates/admin/questions.php (Offerings -> Questions submenu)
- us_questions and us_question_answers tables in Schema.php
- REST: public GET /offerings/{id}/questions; POST/PATCH/DELETE /questions
  gated by manage_questions + offering ownership (owner or studio admin)
- Field types text/textarea/select/checkbox; select options stored as JSON
- Wiring in Plugin, RestRegistrar, AdminMenu

AnswerRepository is built now and consumed by the booking/enrolment flow
in #3/#4.

Tests: tests/Unit/Registration/ (19 tests). composer test (63 total), cs,
and PHPStan level 6 all pass.

Refs #5

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 11:11:06 -03:00
thatguygriff 5b6cc4e89b Merge pull request 'Add Offerings domain and studio-admin capabilities' (#10) from feature/offerings into main
CI / Coding Standards (push) Successful in 53s
CI / PHPStan (push) Successful in 59s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #10
2026-06-05 13:49:38 +00:00
thatguygriffandClaude Opus 4.8 36331388d1 Add Offerings domain and studio-admin capabilities
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 2s
Implements the offerings catalog (#1): private-lesson types and group
classes carrying pricing, billing mode (one_time/full_term), duration,
capacity, and term details. Adds the src/Offering/ domain (value object,
repository, REST endpoint, admin controller + template), the us_offerings
table, and an Offerings admin page.

Also lands the capability slice of #9: registers the us_studio_admin role
and the new capability strings (manage_instructors, manage_offerings,
manage_questions, manage_policies, manage_billing, view_all_payments,
view_own_payments, export_payments) so offering management gates correctly.

Tests: tests/Unit/Offering/ (value object + repository) and a studio-admin
case in RoleManagerTest. composer test, cs, and PHPStan level 6 all pass.

Refs #1 #9

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-05 10:33:02 -03:00
thatguygriffandClaude Opus 4.8 d3a2767976 Add feature specs for booking platform requirements
CI / Coding Standards (push) Successful in 2m23s
CI / PHPStan (push) Successful in 59s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
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]>
2026-06-05 09:58:50 -03:00
thatguygriffandClaude Sonnet 4.6 2fb2ca392d Restructure src/ and tests/ from package-by-type to package-by-domain
CI / Coding Standards (push) Successful in 43s
CI / PHPStan (push) Successful in 52s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 37s
CI / No Debug Code (push) Successful in 2s
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]>
2026-03-30 16:37:30 -03:00
thatguygriffandClaude Sonnet 4.6 ed49924f95 Fix all PHPCS coding standards violations
CI / Coding Standards (push) Successful in 44s
CI / PHPStan (push) Successful in 49s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / Tests (PHP 8.3) (push) Successful in 39s
CI / No Debug Code (push) Successful in 3s
- Add phpcs.xml.dist: excludes PSR-4 file naming, camelCase naming,
  short array syntax, and redundant per-method/property docblocks
- Fix wp_unslash() on all $_POST reads (LoginPage, AvailabilityController)
- Add phpcs:ignore for password field (must not be sanitized)
- Fix Yoda conditions throughout (AvailabilityRepository, AvailabilityEndpoint,
  BookingEndpoint, AvailabilityController)
- Fix inline comments to end with full stops (AdminMenu)
- Replace short ternary ?: with explicit full ternary (BookingEndpoint)
- Rename $namespace param to $route_namespace (reserved keyword warning)
- Add short descriptions to doc blocks that had tag-only blocks
- Add nonce suppression comment in handleFormAction (nonce verified by caller)
- Update composer.json and CI to use phpcs.xml.dist

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-30 16:20:49 -03:00
thatguygriffandClaude Sonnet 4.6 e24c3ce850 Fix PHPStan level 6 errors from CI
- Remove unused AvailabilityRepository/BookingRepository from BookingPage
  and ShortcodeRegistrar (template is a JS shell; no PHP data needed yet)
- Add @param array<string, string> to shortcode render() signatures
- Add @return array<string, mixed> to model toArray() methods
- Fix get_permalink() ?? '' — returns string|false not nullable, use cast
- Remove unused ignoreErrors pattern from phpstan.neon

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-30 12:59:16 -03:00
thatguygriffandClaude Sonnet 4.6 0fbafc9d18 Initial plugin scaffold: lesson scheduling WordPress plugin
CI / Coding Standards (push) Failing after 2m31s
CI / PHPStan (push) Failing after 50s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 40s
CI / No Debug Code (push) Successful in 2s
- 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]>
2026-03-30 12:44:46 -03:00