Commit Graph
161 Commits
Author SHA1 Message Date
thatguygriffandClaude Opus 5 bfdc3b3380 Bill a monthly group class its fee once per month
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Coding Standards (pull_request) Successful in 2m58s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / Build Plugin Zip (pull_request) Skipped
A monthly group class multiplied its price by the sessions falling in the month,
the same rule private lessons use — so a class priced at 40.00 CAD meeting
weekly was billed 160.00 CAD on the 1st, and no studio could quote the price on
a class card without lying about it.

A group class is now billed its fee once for the month however many times it
meets, which is what the card quotes and what the student ticks to agree to.
Private lessons keep the per-lesson rule: their price is a per-lesson fee, and
that is why the card quotes it per lesson.

The session count still labels the month on the student's payment notice; it no
longer prices it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 15:22:46 -03:00
thatguygriffandClaude Opus 5 a276d53c1b Quote a monthly private lesson per lesson, a group class monthly
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 2m52s
CI / PHPStan (pull_request) Successful in 3m0s
A monthly charge covers every lesson that falls in the month, so a private
lesson's fee reads "50.00 CAD per lesson monthly" — the figure on its own would
suggest the whole month costs 50.00. A group class is enrolled in once, as a
single schedule, so its price is quoted as the monthly figure it is.

The pay agreement follows the same split: per-lesson for a monthly private
lesson, the monthly figure for a monthly group class. Weekly, full-term and
at-booking wording is unchanged.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 15:07:46 -03:00
thatguygriffandClaude Opus 5 9344ab7193 Show price cadence and require a pay agreement at booking
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m56s
CI / Coding Standards (pull_request) Successful in 2m59s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
Every price a student meets on the front end now carries the cadence it is
billed on — at booking, up front, weekly, monthly — so a bare amount can no
longer read as a one-off when it is a recurring charge.

Both registration forms then restate the price and require a second, separate
tick agreeing to pay it, distinct from the policy acceptances above it. The
agreed figure includes the studio HST so it matches Payment::total(), the amount
actually billed; the rate reaches the browser as a new localized `taxRate`.

A weekly reservation is charged per lesson for every week it claims, and a week
another student takes first is simply not claimed, so its total is quoted as a
ceiling ("up to 12 lessons") rather than a promise. Free offerings have nothing
to agree to and show no price block at all.

The formatting and the agreement live in one shared helper (`window.usPricing`,
registered as `us-scheduler-pricing`) so a price reads the same in the booking
form, the class catalogue and the editor preview.

Closes #124

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 14:43:38 -03:00
thatguygriff d6a515cc93 Merge pull request 'Bump version to 1.2.3' (#123) from release/bump-1.2.3 into main
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 2m51s
CI / PHPStan (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #123
2026-07-28 16:37:16 +00:00
Release Bot ae07930d6d Bump version to 1.2.3 and open changelog section 2026-07-28 16:36:48 +00:00
thatguygriff 3a25c397c5 Merge pull request 'Collapse the lesson-type filter behind a Show Only button' (#120) from feature/lesson-type-filter-collapsed into main
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / Tests (PHP 8.1) (push) Successful in 53s
CI / PHPStan (push) Successful in 2m56s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m48s
Release / Build and Publish Release (push) Successful in 2m49s
Release / Open next-version bump PR (push) Successful in 4s
Reviewed-on: #120
v1.2.2
2026-07-28 16:20:25 +00:00
thatguygriffandClaude Opus 5 264d9cba01 Add per-embed lesson-type and section options to the booking block
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 2m56s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
Three sidebar options on the Lesson Booking block, all mirrored as shortcode
attributes and carried to the front end as data attributes on
#us-booking-app (or as omitted containers):

- Lesson type (lessonTypeId / lesson_type) pins the calendar to a single
  private-lesson type: only the times bookable as it are listed, and it is
  the only type bookable there, auto-selected on the registration form. A
  pinned type that is no longer offered says so instead of showing an empty
  calendar.
- Show the lesson-type filter (showTypeFilter / show_filter) drops the
  "Show Only" control for studios that do not want it.
- Sections (displayMode / show) embeds one half of the page — the booking
  calendar or the student's upcoming lessons — so the two can live on
  different pages. The script skips the work belonging to a missing half:
  no availability or catalog request for an upcoming-only embed, no
  bookings request for a booking-only one. An unrecognised value renders
  the whole page. The editor preview follows the same setting.

Also fixes the expanded filter's first lesson type sharing a line with the
"Lesson type" heading — the choices now sit in their own row beneath it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 13:16:18 -03:00
thatguygriff 689ec833f3 Merge pull request 'Let offering managers read the offerings catalogue' (#122) from fix/offerings-read-permission into main
CI / Tests (PHP 8.1) (push) Successful in 46s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m58s
CI / PHPStan (push) Successful in 2m57s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #122
2026-07-28 16:07:09 +00:00
thatguygriffandClaude Opus 5 0f30f28e92 Let offering managers read the offerings catalogue
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 56s
CI / PHPStan (pull_request) Successful in 2m56s
CI / Coding Standards (pull_request) Successful in 2m58s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
The block editor's group-class picker fetches GET /offerings, whose
permission callback only accepted book_lesson — a capability held by
students alone. Administrators and instructors editing a page were
rejected with a 403 and the picker silently rendered an empty list.

Read access now accepts book_lesson or manage_offerings. The listing is
unchanged: active offerings only, public ones plus the invite-only
classes the caller has been granted, without the e-transfer email.

Closes #121

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 12:54:58 -03:00
thatguygriffandClaude Opus 5 9d11cc3b01 Collapse the lesson-type filter behind a Show Only button
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m52s
CI / PHPStan (pull_request) Successful in 3m3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
The filter took a row of the booking calendar before a student had asked for
it. The view toggle and a new "Show Only" button now share one control row,
and the lesson-type list is revealed between that row and the calendar.

The list stays open across re-renders once revealed, and collapsing it leaves
the filter applied — the button keeps its active styling and carries the
number of ticked types, so a collapsed filter is never invisible.

Closes #119

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 12:47:24 -03:00
thatguygriff add6605141 Merge pull request 'Filter booking calendar slots by available lesson type' (#118) from feature/lesson-type-filter into main
CI / Tests (PHP 8.1) (push) Successful in 53s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m49s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #118
2026-07-28 15:32:14 +00:00
thatguygriffandClaude Opus 5 edcacae816 Filter booking calendar slots by available lesson type
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 51s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / PHPStan (pull_request) Successful in 2m59s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m37s
CI / Build Plugin Zip (pull_request) Skipped
Not every open time can be booked as every private-lesson type: a slot tied
to an offering takes that offering only, and a generic slot only takes types
whose length fits. Students had no way to see that before clicking a time.

The booking calendar now carries a lesson-type filter — a checkbox per active
private-lesson type, fetched once from GET /offerings?kind=private_lesson.
Ticking types narrows the calendar to the times bookable as one of them and
re-anchors the week view on the earliest match. The registration form's
Lesson type picker is narrowed the same way, and a lone remaining type is
pre-selected with its intake questions loaded.

Bookability is decided by offeringFitsSlot(), the client-side mirror of the
rule POST /bookings enforces; the filter is a browsing aid and the server
still validates every booking. No ticks means no filter, and the whole
control is hidden when the studio offers fewer than two private-lesson types.

Closes #117

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 12:27:37 -03:00
thatguygriff 17487cde46 Merge pull request 'Send students to a chosen page when registration succeeds' (#116) from feature/registration-success-redirect into main
CI / Tests (PHP 8.2) (push) Successful in 54s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m47s
CI / PHPStan (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #116
2026-07-28 15:04:44 +00:00
thatguygriffandClaude Opus 5 13d6b3e14e Send students to a chosen page when registration succeeds
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m0s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / PHPStan (pull_request) Successful in 2m57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 2s
The Student Registration block's "After email confirmation" panel becomes
"After registration": the page it selects is now where a newly registered
student continues to, and a new autoRedirect toggle sends them there
instead of showing the link.

Only the two finished states qualify (RegistrationPage::isRegistrationComplete):
an invited student who is now logged in, and a self-signup back from the
emailed confirmation link. A validation error, an expired confirmation
link, and the intermediate "check your email" step all stay on the page so
their message is read.

The invited-student success previously had no link at all; it gains a
"Continue to your account" one. That path deliberately has no
WordPress-login-screen fallback — pointing someone already signed in at the
login screen helps nobody — so continueUrl() distinguishes "no page chosen"
from "page chosen", and the redirect does nothing until one is picked.

Closes #115

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 10:58:17 -03:00
thatguygriff d866aa7295 Merge pull request 'Omit the class description when the group block shows a single class' (#113) from feature/group-class-single-embed-description into main
CI / Tests (PHP 8.1) (push) Successful in 1m9s
CI / PHPStan (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m48s
CI / Tests (PHP 8.2) (push) Successful in 1m2s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 3m4s
Reviewed-on: #113
2026-07-28 13:48:07 +00:00
thatguygriffandClaude Opus 5 c4acdb7ca4 Omit the class description when the group block shows one class
CI / Tests (PHP 8.1) (pull_request) Successful in 1m13s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m2s
CI / PHPStan (pull_request) Successful in 3m3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
The Group Classes block can be pinned to a single class via its Class
option so it can be embedded on a page dedicated to that class. On such a
page the surrounding copy already describes the class, so the card
repeated it. In single-class mode the description is now left out and the
card shows only the schedule, instructor, schedule note, price, enrolment
deadline and the enrol/withdraw controls.

The editor preview follows the same rule: BlockPreview::groupClasses()
takes the mode from the block's offeringId attribute, drops the sample
description when a class is pinned, and notes what the published page
shows. Its sample card also gained the .us-class-when and
.us-enrol-deadline elements the live markup has always rendered.

Closes #114

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 10:39:09 -03:00
thatguygriff 30928addf8 Merge pull request 'Bump version to 1.2.2' (#112) from release/bump-1.2.2 into main
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 2m47s
CI / PHPStan (push) Successful in 3m12s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m48s
Reviewed-on: #112
2026-07-24 23:56:56 +00:00
Release Bot 27793fa0aa Bump version to 1.2.2 and open changelog section 2026-07-24 23:31:45 +00:00
thatguygriff c611268bdb Merge pull request 'Fix field-length saves, student wp-admin access, and empty instructor picker' (#111) from fix/field-length-student-admin-instructor-picker into main
CI / Tests (PHP 8.1) (push) Successful in 39s
CI / Tests (PHP 8.2) (push) Successful in 1m2s
CI / No Debug Code (push) Successful in 3s
CI / PHPStan (push) Successful in 2m54s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
Release / Build and Publish Release (push) Successful in 2m59s
Release / Open next-version bump PR (push) Successful in 5s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #111
v1.2.1
2026-07-24 23:27:30 +00:00
thatguygriffandClaude Opus 4.8 721c4be1d6 Fix field-length saves, student wp-admin access, and empty instructor picker
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m47s
CI / PHPStan (pull_request) Successful in 3m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
Three bug fixes for the 1.2.1 section:

- Fixed-size fields (question labels, offering titles/notes/e-transfer
  email, policy titles/slugs) no longer silently fail to save when the
  value exceeds its column length. The REST endpoints reject over-long
  values with a 400, the admin controllers refuse to insert them, and the
  form inputs carry a maxlength so the browser blocks over-long entry.
  Limits are MAX_* constants on the value objects, kept in lockstep with
  the schema columns.

- Students are kept out of wp-admin entirely. New StudentAdminGuard
  redirects front-end-only users (no back-office capability) away from the
  dashboard and hides the admin bar for them, while administrators, studio
  admins, and instructors keep full access.

- The Add/Edit Offering instructor picker now includes WordPress
  administrators when they act as instructors (the default single-account
  setup), so a solo studio owner is selectable instead of the dropdown
  being empty.

composer test (618), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 20:22:04 -03:00
thatguygriff 3aa65bad06 Merge pull request 'Bump version to 1.2.1' (#110) from release/bump-1.2.1 into main
CI / Tests (PHP 8.1) (push) Successful in 41s
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m52s
CI / Coding Standards (push) Successful in 3m4s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #110
2026-07-24 19:34:30 +00:00
Release Bot f3ba09b195 Bump version to 1.2.1 and open changelog section 2026-07-24 19:34:11 +00:00
thatguygriff 771942be8b Merge pull request 'Fix invite sign-in, add customizable invite-only text, repair account questions' (#109) from fix/registration-signin-and-account-questions into main
CI / Tests (PHP 8.1) (push) Successful in 40s
CI / Tests (PHP 8.2) (push) Successful in 1m5s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 2m53s
CI / PHPStan (push) Successful in 2m52s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 4s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #109
v1.2.0
2026-07-24 19:29:52 +00:00
thatguygriffandClaude Opus 4.8 242150569b Fix invite sign-in persistence, add invite-only text option, repair account questions
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m37s
CI / PHPStan (pull_request) Successful in 2m52s
CI / Coding Standards (pull_request) Successful in 3m6s
CI / Build Plugin Zip (pull_request) Skipped
Three registration fixes reported from live use:

- Accepting an invite now keeps the student signed in. The form was
  processed inside render() during the_content, so wp_set_auth_cookie()
  ran after headers were sent and the cookie never persisted — the new
  student was bounced back to the logged-out registration page. The
  submission is now handled on template_redirect (before output) with a
  post/redirect/get, so the cookie sticks and the student lands logged in.

- The "registration is by invitation only" message is now customisable via
  a new block attribute (inviteOnlyMessage / shortcode invite_only_message),
  falling back to the default wording when blank.

- Account-registration questions save again. dbDelta does not reliably
  relax a column from NOT NULL to NULL, so sites created before account-
  scope questions kept us_questions.offering_id NOT NULL and rejected
  account inserts ("Column 'offering_id' cannot be null"). A one-time,
  self-healing migration (guarded by its own option, not the version gate)
  re-applies the nullable definition on next load.

composer test, composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 16:24:15 -03:00
thatguygriff fae1fd08ba Merge pull request 'Add group-class withdrawal deadline and kind-aware offering form' (#108) from feature/group-class-withdrawal-deadline 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 / Coding Standards (push) Successful in 2m58s
CI / PHPStan (push) Successful in 3m18s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #108
2026-07-24 19:01:48 +00:00
thatguygriffandClaude Opus 4.8 2c4b481077 Add group-class withdrawal deadline and kind-aware offering form
CI / Tests (PHP 8.1) (pull_request) Successful in 44s
CI / Tests (PHP 8.2) (pull_request) Successful in 59s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m53s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
Group classes now carry an optional per-class withdrawal deadline. Up to
that day a student may withdraw themselves from the class; the withdrawal
frees the seat and voids any pending payment but never issues an account
credit. After the deadline self-withdrawal closes and a studio admin must
withdraw the student by hand (the admin path is never subject to the
deadline). A blank deadline keeps self-withdrawal open indefinitely.

Also make the Add/Edit Offering form show only the fields relevant to the
selected kind: group settings for group classes, weekly reservation for
private lessons. Progressive enhancement — without JS every field renders.

- New nullable us_offerings.withdrawal_deadline column; Offering model gains
  $withdrawalDeadline + isWithdrawalOpen().
- New student endpoint POST /enrollments/{id}/withdraw, gated by the deadline
  (403 withdrawal_closed), ownership-checked, idempotent.
- Front-end group-class page shows a Withdraw button while open.
- No USC_VERSION bump: 1.2.0 is unreleased and accumulates schema changes
  under its section, matching the scheduled-billing and credit features.

Tests: composer test (596), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 15:56:40 -03:00
thatguygriff f552c3952a Merge pull request 'Credit students for cancelled paid lessons' (#107) from feature/credit-cancelled-paid-lessons into main
CI / Tests (PHP 8.1) (push) Successful in 40s
CI / Tests (PHP 8.2) (push) Successful in 1m2s
CI / No Debug Code (push) Successful in 3s
CI / PHPStan (push) Successful in 2m52s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.3) (push) Successful in 2m38s
CI / Build Plugin Zip (push) Successful in 2m46s
Reviewed-on: #107
2026-07-24 18:40:10 +00:00
thatguygriffandClaude Opus 4.8 e8e66eef3c Credit students for cancelled paid lessons
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / PHPStan (pull_request) Successful in 3m12s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m52s
Cancelling a lesson that was already paid for now credits the student
that money instead of leaving it as a manual refund, and the daily
scheduled-billing scan applies any available credit against their due
charges before emailing the notice.

- New us_credits ledger + us_payments.credit_applied column (Payment::netDue).
- PaymentService::creditForCancelledLesson issues a per-lesson share of the
  covering payment's total; wired into all three cancel paths (student
  self-cancel, instructor status update, admin student-detail cancel).
- PaymentService::applyCredits draws credit down FIFO across a run's charges,
  marking a fully-covered charge paid-by-credit; the notice shows the credit
  applied and reduced total, and the admin queue shows net due.
- Student detail page shows a student's credit balance and history.

Ships as part of the unreleased 1.2.0 (same release as scheduled billing).

Tests: composer test (585), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 15:32:20 -03:00
thatguygriff cf296329a0 Merge pull request 'Consolidate changelog into 1.2.0 section ahead of release' (#106) from docs/changelog-1.2.0 into main
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / PHPStan (push) Successful in 2m52s
CI / Coding Standards (push) Successful in 3m8s
CI / Build Plugin Zip (push) Successful in 2m50s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m38s
Reviewed-on: #106
2026-07-24 18:14:15 +00:00
thatguygriffandClaude Opus 4.8 3f9aef7746 Consolidate changelog into 1.2.0 section
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Failing after 2m33s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Skipped
The plugin header carries 1.2.0 but CHANGELOG.md still topped out at the
untagged 1.1.3 section, and two shipped features (#104 lesson booking
detail, #105 weekly/monthly scheduled billing) were unrecorded. Neither
1.1.2 nor 1.1.3 was ever tagged, so their changes belong to the 1.2.0
release. Merge the untagged sections into a single 1.2.0 section and add
the two missing features so the release workflow publishes real notes.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 12:55:08 -03:00
thatguygriff d1dd30dc60 Merge pull request 'Add weekly and monthly scheduled billing for offerings' (#105) from feature/weekly-monthly-billing into main
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m46s
CI / PHPStan (push) Successful in 3m16s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m46s
Reviewed-on: #105
2026-07-24 15:38:59 +00:00
thatguygriffandClaude Opus 4.8 4328e8fb5f Add weekly and monthly scheduled billing for offerings
CI / Tests (PHP 8.2) (pull_request) Successful in 39s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m12s
CI / No Debug Code (pull_request) Successful in 3s
CI / PHPStan (pull_request) Successful in 2m52s
CI / Coding Standards (pull_request) Successful in 2m54s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m39s
CI / Build Plugin Zip (pull_request) Skipped
Offerings can now bill weekly (a pending payment 24h before each lesson)
or monthly (one payment on the 1st for that month's lessons), alongside
one-time and full-term. Applies to both private lessons and group classes.

- Offering: new `weekly`/`monthly` billing modes + `isScheduledBilling()`
- Booking/enrolment defer payment for scheduled modes; a single lesson
  booked after its due date has passed (e.g. an add-on in an already-billed
  month) is charged at booking instead
- ScheduledBillingRunner: daily WP-Cron scan generates due payments across
  four cases (private/group × weekly/monthly), deduped via lesson.payment_id
  and payments.period_key
- PaymentDueMailer: one consolidated itemised email per student per scan
- Notice batch: payments emailed together share a reference; the admin
  Payments queue groups them with a lump-sum total for e-transfer reconciliation
- Cancellation never voids a scheduled payment (Payment::isScheduled())
- Schema: us_payments gains due_date, period_key, notice_batch; USC_VERSION 1.2.0

composer test, composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 12:06:37 -03:00
thatguygriff 36e7178158 Merge pull request 'Show booked lesson info on upcoming lists and add admin booking detail' (#104) from feature/lesson-booking-detail into main
CI / Tests (PHP 8.2) (push) Successful in 39s
CI / Coding Standards (push) Successful in 2m52s
CI / PHPStan (push) Successful in 2m55s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / No Debug Code (push) Successful in 2s
CI / Build Plugin Zip (push) Successful in 2m46s
Reviewed-on: #104
2026-07-24 14:12:33 +00:00
thatguygriffandClaude Opus 4.8 32619a1b75 Show booked lesson info on upcoming lists and add admin booking detail
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Coding Standards (pull_request) Successful in 2m53s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / No Debug Code (pull_request) Successful in 3s
CI / PHPStan (pull_request) Successful in 3m12s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Skipped
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]>
2026-07-24 11:07:06 -03:00
thatguygriff 1a447743b3 Merge pull request 'Group-class enrolment deadline with instructor late-enrolment override' (#103) from feature/group-class-enrollment-deadline into main
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / PHPStan (push) Successful in 2m52s
CI / Tests (PHP 8.3) (push) Successful in 2m39s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 3m4s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #103
2026-07-24 13:45:54 +00:00
thatguygriffandClaude Opus 4.8 fc7c0fa966 Show "Enrol by <date>" on the group-class card while enrolment is open
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m47s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
The front end used the deadline only to gate the Enrol button; students had
no way to see when enrolment closes. Add an "Enrol by <date>" line to each
class card, shown while enrolment is still open, for the effective deadline
(the instructor's date, or the first class day by default).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 10:36:51 -03:00
thatguygriffandClaude Opus 4.8 bf29162587 Add group-class enrolment deadline with instructor late-enrolment override
CI / Tests (PHP 8.1) (pull_request) Successful in 48s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 3m2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m37s
CI / Build Plugin Zip (pull_request) Skipped
Group classes gain an instructor-set enrolment deadline (new
us_offerings.enrollment_deadline column) that defaults to the first day of
the class (term_start). Past the deadline students can no longer self-enrol:
the enrolment endpoint rejects it (403 enrollment_closed) and the front-end
class list shows "Enrolment has closed." in place of the Enrol button.

Instructors keep a manual path: the "Add students directly" control on each
class's details page now renders for public classes too (not just
invite-only) and deliberately bypasses the deadline and capacity, so a
student can be added as a late enrolment after the class has closed. Past
the deadline the details page labels these as late enrolments.

Bumps USC_VERSION to 1.1.3 for the schema change.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 10:19:38 -03:00
thatguygriff 991ed2f5ad Merge pull request 'Bump version to 1.1.2' (#102) from release/bump-1.1.2 into main
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
CI / Coding Standards (push) Successful in 2m45s
CI / PHPStan (push) Successful in 3m15s
CI / Build Plugin Zip (push) Successful in 2m53s
Reviewed-on: #102
2026-07-24 11:56:08 +00:00
Release Bot ad2ddefebf Bump version to 1.1.2 and open changelog section 2026-07-24 11:54:08 +00:00
thatguygriff 1fe28d5575 Merge pull request 'Show the Enable auto-updates toggle for the self-updater' (#101) from feature/auto-update-toggle into main
CI / Coding Standards (push) Successful in 2m52s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 40s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / PHPStan (push) Successful in 2m49s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 5s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #101
v1.1.1
2026-07-24 11:47:24 +00:00
thatguygriffandClaude Opus 4.8 51dd032668 Show the Enable auto-updates toggle for the self-updater
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 3m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Build Plugin Zip (pull_request) Skipped
WordPress only renders the "Enable auto-updates" toggle for a plugin that
appears in the update_plugins transient's response or no_update list, which
is what sets core's update-supported flag. UpdateChecker only populated the
response side (when a newer release existed), so between releases the plugin
was absent from the transient and the toggle never showed.

provideUpdate() now returns a no_update payload (installed version, empty
package) whenever no newer release is offered — including when the release
lookup fails — so the plugin stays in the transient and the toggle appears.
The response path (one-click and unattended updates) is unchanged.

Bumps to 1.1.1 so the fix ships to installed sites via the self-updater.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-24 08:46:31 -03:00
thatguygriff 37ec8a3315 Merge pull request 'Bump version to 1.1.1' (#100) from release/bump-1.1.1 into main
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / Tests (PHP 8.1) (push) Successful in 46s
CI / PHPStan (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m50s
CI / Build Plugin Zip (push) Successful in 2m49s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
Reviewed-on: #100
2026-07-23 21:29:37 +00:00
Release Bot f93c5aba05 Bump version to 1.1.1 and open changelog section 2026-07-23 21:09:01 +00:00
thatguygriff 013558019e Merge pull request 'Group-class scheduling, instructor assignment, and details/invite management' (#99) from feature/group-class-scheduling into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / Coding Standards (push) Successful in 2m54s
CI / PHPStan (push) Successful in 2m54s
CI / Tests (PHP 8.3) (push) Successful in 2m39s
CI / Build Plugin Zip (push) Successful in 2m46s
Release / Build and Publish Release (push) Successful in 2m47s
Release / Open next-version bump PR (push) Successful in 4s
Reviewed-on: #99
v1.1.0
2026-07-23 20:56:59 +00:00
thatguygriffandClaude Opus 4.8 87cfe921a9 Show instructor real name or nickname in group-class views, not the login
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m47s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
Add Auth\UserName::format(), which prefers a user's first + last name, then
their nickname, avoiding display_name (which can be the login/username).
Route the instructor name through it in both the front-end offerings response
(instructor_name) and the back-end group-class summary and details views.

Tests: composer test (513), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-23 17:50:34 -03:00
thatguygriff 00376799b9 Merge pull request 'Group-class scheduling, instructor assignment, and details/invite management' (#98) from feature/group-class-scheduling into main
CI / Coding Standards (push) Successful in 2m51s
CI / PHPStan (push) Successful in 2m55s
CI / Tests (PHP 8.3) (push) Successful in 2m37s
CI / Tests (PHP 8.2) (push) Successful in 37s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #98
2026-07-23 20:34:16 +00:00
thatguygriffandClaude Opus 4.8 b066bef353 Add group-class scheduling, instructor assignment, and details/invite management
CI / Tests (PHP 8.2) (pull_request) Successful in 39s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m52s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Skipped
Group classes now carry a specific class time (alongside date and duration)
and an assigned instructor:

- Schema: add `class_time` (TIME) to `us_offerings`; `Offering` gains
  `normalizeTime`/`sessionWindows`. (Rides the pending 1.0.0->1.1.0 dbDelta
  upgrade, so no version bump.)
- Offering form: class-time field, plus a studio-admin instructor picker
  (plain instructors always own their own classes).
- `ClassSlotReconciler`: assigning an instructor clears their open booking
  slots overlapping each session and flags already-booked lessons that clash
  (a booked lesson is never deleted). Uses new
  `AvailabilityRepository::findOverlapping`.
- Front end: `GET /offerings` exposes `instructor_name`; the enrolment page
  shows who teaches each class and when it meets.

Back-office group-class views redesigned:

- Instructor **My Group Classes** and studio-admin **Group Classes** are now
  per-class summaries with enrolment counts, not flat student lists.
- Each links through (`?class_id=<id>`) to a per-class **details page**
  (schedule panel, roster with payment status, and — for invite-only classes
  — the add/make-available/invite-by-email controls). Invite-only membership
  is managed entirely from this page.
- Invite actions are allowed for the class's owning instructor or any
  `view_all_lessons` studio admin, so an owner-operator (studio admin who also
  teaches) can reach every class's roster and invites from the Group Classes
  page.

Tests: composer test (508), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-23 17:29:48 -03:00
thatguygriff bba24566a6 Merge pull request 'Add CHANGELOG and publish it as release notes; auto-open next-version bump PR' (#97) from feature/changelog-release-notes into main
CI / Tests (PHP 8.1) (push) Successful in 39s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
CI / Build Plugin Zip (push) Successful in 2m48s
Reviewed-on: #97
2026-07-23 19:54:40 +00:00
thatguygriffandClaude Opus 4.8 4bca2fbc96 Add CHANGELOG and publish it as release notes; auto-open next-version bump PR
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 7m54s
CI / PHPStan (pull_request) Successful in 9m2s
CI / Coding Standards (pull_request) Successful in 9m12s
CI / Build Plugin Zip (pull_request) Skipped
Add CHANGELOG.md (one section per version, newest first; the top section
always reflects the current plugin header version — release status is
purely a matter of tagging).

The Release workflow now extracts the tagged version's changelog section
and publishes it as the Gitea release body (POST on create, PATCH when the
release was pre-created via the UI). After a stable release, a new
bump-version job bumps the plugin to the next patch version, opens a fresh
changelog section, and opens a PR. Pre-release tags are skipped.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-23 16:34:52 -03:00
thatguygriff 06c8d42cc0 Merge pull request 'Add invite-only group classes' (#96) from feature/invite-only-group-classes 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 / PHPStan (push) Successful in 2m49s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m36s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #96
2026-07-23 16:55:37 +00:00