v1.4.1
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5d98aedfa5 |
Merge pull request 'Editable policy name, one-page signup with adult birth year, group classes in upcoming lessons, and booking cleanup on user delete' (#161) from feature/demo-followups-2026-07-30 into main
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 3m2s
Release / Build and Publish Release (push) Successful in 2m59s
Release / Open next-version bump PR (push) Successful in 4s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m46s
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #161 |
||
|
|
8fd7bf983d
|
Name people by their name, not their email address
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / PHPStan (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Skipped
Anywhere the plugin named a person it could show their email instead — "Managed by [email protected]" in the students table, the same under Booked by, instructor names on the class pages. WordPress defaults a new account's `nickname` to its `user_login`, and signup uses the email address as the login. So every self-registered account carried its own address as its nickname, and UserName::format() fell straight through to it. The name they typed was in `display_name` all along. Accounts created by a guardian were never affected — GuardianService::createChild() sets `nickname` outright, which is exactly why children read correctly and their parents did not. UserName::format() now walks nickname then display name, skipping either when it is really the login or the email, so existing accounts read correctly with nothing to migrate. An identifier still never reaches the screen: an account with nothing but its address on file falls back to the id, as before. Signup also sets `nickname` at insert, so new accounts are right at the source rather than relying on the fallback. Tests: composer test (866), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
122f7a0f53
|
Show "Booked by" in the Account section of a student's detail page
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / Build Plugin Zip (pull_request) Skipped
The parent/guardian was only named further down under Profile, where it reads as background rather than as an account fact, and only when there was one — so a page with no such line was ambiguous between "books for themselves" and "the lookup found nothing". It now sits in the Account table beside display name and email, as the guardian's name linked to their own detail page, and always renders: a student who books for themselves says so outright. No email address — theirs is one click away on their own page, and repeating it here only makes the row harder to scan. The Profile section keeps only the note explaining the placeholder email, which is a different point. Tests: composer test (863), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
c9a1205fc0
|
Never drop an enrolled class from upcoming lessons; delete a guardian's children with them; pin the panel's line spacing
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Coding Standards (pull_request) Successful in 2m58s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 1m1s
CI / No Debug Code (pull_request) Successful in 3s
Three fixes from testing the branch. A group class was only listed when its schedule resolved to exact datetimes, which needs a class time *and* a duration — both optional on the offering form, and the schedule note exists precisely so a studio can write "Tuesdays 4:00pm" instead. A class configured that way vanished from the list, which is the one thing this feature must never do. So Offering::sessionStarts() splits "when does it meet" from "how long does it run" (sessionWindows() is that plus the duration, unchanged), and SessionSchedule degrades instead of disappearing: dated rows with an open end when there is no duration, and a single row carrying Offering::scheduleLabel() when there is no time to derive dates from. Only a class whose last day has passed drops out. Deleting a guardian now deletes the children linked to them, releasing each one's lessons and enrolments first. A child account is login-less and exists only so the guardian has somebody to book for; without the guardian nobody can reach it, book for it, or be billed for it, so it was left stranded on the roster still holding slots. A `handled` set makes the re-entrant delete_user each child deletion fires a no-op, and stops a circular link recursing. The upcoming panel never stated its own line-height, so a theme setting line-height: 0 above it — the usual icon-font reset — was inherited straight through. Below 1 that produces both reported symptoms at once: stacked lines overlap, and the status pill's background is shorter than the text in it. Pinned at the same id-level specificity as the rest. Tests: composer test (863), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
cb347ffca0
|
Demo follow-ups: editable policy name, one-page signup, group classes in upcoming lessons, deletion cleanup
CI / Tests (PHP 8.1) (pull_request) Successful in 1m0s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m0s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 3m8s
CI / Build Plugin Zip (pull_request) Skipped
CI / PHPStan (pull_request) Successful in 2m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
Five items from the latest demo pass: - A policy's title can be edited from the Policies screen. Only the title moves; the slug is what the gates resolve policies by, so a rename can never detach a policy from acceptances already recorded against it. - Signup is one page again. The studio's registration questions move from a second step behind "Next" onto the main form, in an "About you" panel above the students being added, and that panel also asks an adult student for their birth year (the same us_birth_year meta a child's uses). register.js disables and hides the whole panel for a pure guardian, since the questions describe a student. - The password is re-scored on submit, not only as it is typed. zxcvbn's dictionary arrives after page load, so a password typed straight away was never scored at all and the first the student heard of it was the server rejecting the whole form. - Group-class sessions appear alongside lessons wherever upcoming lessons are listed: the [us_scheduler] panel (students and instructors) and the admin student detail page. GroupClass\SessionSchedule derives them from Offering::sessionWindows(), the same derivation the billing scan uses. They carry kind = 'group_class' and no Cancel action - a session is one date in a term, not a booked slot. - Deleting a user releases what the account was holding: each upcoming lesson is cancelled, its slot freed for rebooking, its pending payment voided, and active class enrolments cancelled. Past lessons and paid history are left alone. Tests: composer test (851), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
258468093b |
Merge pull request 'Bump version to 1.4.1' (#160) from release/bump-1.4.1 into main
CI / PHPStan (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m48s
Reviewed-on: #160 |
||
|
|
969d864106 |
Merge pull request 'Ask who the signup is for, and ask each student the studio's questions' (#159) from feature/145-registering-for-choice into main
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m54s
CI / PHPStan (push) Successful in 2m55s
CI / Tests (PHP 8.3) (push) Successful in 2m44s
CI / Tests (PHP 8.1) (push) Successful in 53s
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 2m53s
Reviewed-on: #159 |
||
|
|
69179b75c9
|
Ask the account holder the studio's questions when they are a student too
CI / Tests (PHP 8.2) (pull_request) Successful in 43s
CI / Tests (PHP 8.1) (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 3m19s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Skipped
Under "both" the questions were collected per student only, so someone registering themselves alongside their children was never asked their own instrument, level or anything else — despite being able to book lessons. The account-scope questions describe a student, and under "both" the account holder is one. Their answers are recorded against their own user id, not shared with a child's, and recorded after the children so a rollback that deletes the account cannot leave answers pointing at a user that no longer exists. A pure guardian is unchanged: they are not a student, so anything posted for them is still ignored. Validation became two passes rather than one so the message can say whose answers are missing — with one pass, "both" had to blame "each student" for the account holder's own blank field. In the form, the two questions turn out to be independent: whether student blocks are in play, and whether the account holder answers for themselves. "Both" is the case that needs its own answer to each, so sync() now tracks them separately, and step two comes back into play under "both". Verified in a headless browser: 21 checks across all three choices, now including that "both" enables the account holder's own question panel and offers Next rather than the early submit. Closes #146 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
4e5382e259
|
Ask who the signup is for as a three-way choice
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Coding Standards (pull_request) Successful in 2m57s
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Replaces the single "I'm registering as a parent or guardian" tick with "Just myself" / "On behalf of one or more students" / "Both". Radios, not checkboxes as the feedback put it: the three answers are mutually exclusive, and "both" only means anything as a third choice alongside the other two. The tick could only ever say whether there were children to add. It could not say whether the account holder was a student, so bookableStudents() always offered them their own name and any guardian could book themselves a lesson nobody meant to sell. "On behalf of" now records us_guardian_only and leaves them out of the picker. That flag is stored as the negative on purpose. Every account predating this choice is a bookable student, and absence has to keep meaning exactly that, or the picker would quietly stop offering people themselves on upgrade. setGuardianOnly() clears the key rather than writing 0, so "not set" stays the single spelling of "yes, a student". A guardian-only account with nobody linked to it is still offered itself — an empty picker is no way to book at all, and they can put the account right from the profile page. An unrecognised or absent value reads as "just myself": the choice that collects the least and grants the least. A missing radio must never be taken as "register these children". Bumps to 1.4.0. The account holder's own questions stay out of play whenever students are being added, "both" included — asking them there is #146. Verified the form in a headless browser across all three choices: which blocks show, which fields carry `required`, whether the account holder's question panel is disabled, which submit is offered, and that switching back to "just myself" leaves no hidden required field blocking submit. Closes #145 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
28046e0fd1 |
Merge pull request 'Return to a bookable calendar after a booking is confirmed' (#158) from fix/143-return-to-bookable into main
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / No Debug Code (push) Successful in 1s
CI / Build Plugin Zip (push) Successful in 2m49s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
Reviewed-on: #158 |
||
|
|
699e479805
|
Return to a bookable calendar after a booking is confirmed
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
CI / Build Plugin Zip (pull_request) Skipped
CI / Coding Standards (pull_request) Successful in 3m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m46s
showConfirmation() hid the slot list and put the confirmation in its place,
which is a dead end: a student wanting a second lesson had nothing to click
and no way back short of reloading the page. Enrolling in a group class did
the same thing.
The confirmation is now a dismissible notice above the calendar. The
calendar is reloaded first — so the slot just taken is already gone and the
upcoming-lessons panel is current — and the notice is shown over it, which
is why loadSlots() had to start returning its promise. "It worked" and "book
another" are the same screen.
The notice clears when dismissed, when another slot's form is opened, and on
any reload of the calendar. group-classes.js gets the identical treatment.
It is built from DOM nodes rather than innerHTML because the message can
carry a studio's e-transfer address, and it is toggled with the `hidden`
attribute rather than an inline display — an inline style would outrank the
stylesheet's display:flex and stack the notice's parts. `hidden` needs the
!important guard for the same reason the upcoming-lessons panel does: the
div{display:block} theme reset outranks the UA sheet.
The slotList/list `display = 'block'` lines went with it. Nothing hides
those any more, so restoring them each load only implied otherwise.
Verified in a headless browser against a stubbed REST API: booking twice in
a row without a reload, the booked slot leaving the calendar, the upcoming
panel updating, dismissal, the notice clearing when the next form opens, and
the notice staying hidden under div{display:block}.
Closes #143
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
1b42d20541 |
Merge pull request 'Add an account block showing who is signed in' (#156) from feature/142-account-block into main
CI / Build Plugin Zip (push) Successful in 2m48s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.3) (push) Successful in 2m44s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #156 |
||
|
|
ab5212282d
|
Show only the name and email, not who the account books for
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m48s
CI / Coding Standards (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
CI / Build Plugin Zip (pull_request) Skipped
The block reports who is signed in and nothing more. Dropping the "Booking for …" line takes GuardianService with it — it was the only reason the page had a dependency at all, so AccountPage now constructs with no arguments. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
6e3affb1cb
|
Add an account block showing who is signed in
[us_account], or the Account block: the signed-in visitor's name, their email, a Sign out link, and — only when the account books for someone besides itself — the students it books for. A parent's first question on seeing "signed in as Grace" is whether this is the account their children's lessons are on. Two decisions worth naming. Signed out with no login page chosen, the block renders nothing. Its whole subject is the person signed in, which a stranger is not, and a bare "you are not signed in" in a site header is noise with no way to act on it. With a login page chosen it offers a Sign in link instead. The editor preview is populated regardless, so the block is never an invisible box to the person placing it. Signing out returns to the chosen login page, or to the current page when there is none. A block meant for a header should not also navigate someone somewhere when they use it; the login page wins when configured, because the page they were on may well be members-only. The name comes from UserName::format(), so the block never exposes a username the way display_name can. Also brings docs/features/editor-blocks.md back in step: it still described "four shortcodes" and had never listed the family block. Closes #142 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
7875cb1bf7 |
Merge pull request 'Fix main: two signup fixtures use a now-rejected password' (#157) from fix/green-main into main
CI / Coding Standards (push) Successful in 2m57s
CI / Tests (PHP 8.1) (push) Successful in 57s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m51s
CI / Tests (PHP 8.3) (push) Successful in 2m46s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #157 |
||
|
|
04cba9702c
|
Fix main: two signup fixtures use a now-rejected password
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 2m46s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 3m6s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m49s
CI / Build Plugin Zip (pull_request) Skipped
#154 and #155 each passed on their own branch and broke on landing together. #154 added two guardian-signup tests using 'password123' as their fixture; #155 then added PasswordPolicy, which rejects exactly that. Neither branch ever saw the other's change, because #155 was cut from main before #154 merged. Both tests now use the same policy-clearing fixture as the rest of the file. The deliberate 'password123' in the rejected-passwords provider stays — that one is the point. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d554e35d80 |
Merge pull request 'Validate signup email and password strength' (#155) from feature/150-signup-credential-validation into main
CI / Tests (PHP 8.2) (push) Failing after 43s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Failing after 2m44s
CI / Build Plugin Zip (push) Skipped
CI / Tests (PHP 8.1) (push) Failing after 50s
Reviewed-on: #155 |
||
|
|
b5b9a7ac54
|
Validate signup email and password strength
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Coding Standards (pull_request) Successful in 2m57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m44s
CI / Build Plugin Zip (pull_request) Skipped
The password was only ever checked for length. It is now checked on both sides, with each side doing the job it can actually do. The browser scores it with zxcvbn, through WordPress's own password-strength-meter script rather than a second opinion of our own, and refuses to submit below "medium". That is the nuanced test — it knows Tr0ub4dor&3 is weaker than it looks — but it is advice a client can decline to take. Auth\PasswordPolicy runs on the server and is the rule that holds. It does not try to reproduce a strength score in PHP; it rejects the categorically bad, which is what a server can check without shipping a dictionary: too short, a well-known leaked password, fewer than four distinct characters, or the user's own name or email inside it. No composition rules — NIST advises against them, and they mostly produce predictable substitutions. Both thresholds come from the same two constants, handed to JavaScript by wp_localize_script, so the sides cannot drift into disagreeing about what was accepted. The verdict is attached to the field with setCustomValidity() rather than by disabling a button. The form has up to three submits plus a "Next" that already gates on checkValidity(), and an invalid field stops all of them without any of them needing to know why. Email validation moved ahead of the password check, since the password is now checked against the email. A blank form therefore reports the email first, which also matches the order the fields appear in. Verified the browser half against a controllable scorer: each score band blocks or allows as intended, the identity list reaches the meter, and the gate stays open while zxcvbn's dictionary is still loading — the server covers that window. Closes #150 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f0149042cc |
Merge pull request 'Require a name and birth year for every student' (#154) from feature/148-required-name-and-birth-year into main
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m53s
CI / Coding Standards (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m45s
CI / Build Plugin Zip (push) Successful in 2m45s
Reviewed-on: #154 |
||
|
|
1d2f95d388
|
Require a name and birth year for every student
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Coding Standards (pull_request) Successful in 2m56s
CI / PHPStan (pull_request) Successful in 2m56s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m45s
CI / Build Plugin Zip (pull_request) Skipped
Both fields are marked in their labels the same way a required registration question is, and enforced on the server whichever form they arrive from: GuardianService::createChild() and updateChild() now refuse a blank name or an unusable birth year, and the signup form checks the same rule up front, before it creates a single user, so a bad block never leaves a half-registered family behind. normaliseBirthYear() became public and static so both paths share one definition of what a usable year is. The signup form cannot lean on the browser here. Its child blocks are hidden until the parent/guardian box is ticked, and a `required` field inside a hidden container makes the whole form unsubmittable with no control the user can reach to fix — the same trap the guardian's own question panel already sidesteps by disabling rather than hiding. So register.js puts `required` on and takes it off along with the block itself, and the server is what makes the rule hold with JavaScript off. The profile screen has no such problem: its forms are always visible, so the attribute is static there. One behaviour change beyond the requirement: a child block with anything typed into it is now reported back instead of dropped. Previously any block without a name was silently discarded, which would now mean losing a birth year the guardian had filled in. A wholly untouched spare block — the one the form always renders for "add another" — is still ignored. Verified the required-toggling in a headless browser: unticked submits, ticked blocks an empty block, a cloned block inherits the requirement, and re-unticking leaves nothing behind to block a non-guardian signup. Closes #148 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
2878beb221 |
Merge pull request 'Collect a birth year instead of a full date of birth' (#153) from feature/147-birth-year into main
CI / Tests (PHP 8.2) (push) Successful in 50s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m47s
CI / Build Plugin Zip (push) Successful in 2m35s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m44s
Reviewed-on: #153 |
||
|
|
7e2bba79fe
|
Collect a birth year instead of a full date of birth
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Coding Standards (pull_request) Successful in 3m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Signup and the profile page now ask for a four-digit year between 1900 and the current year. Anything else — a short year, a full date, a year in the future — is discarded rather than stored, so a typo cannot leave a nonsense age on the record. The year lives in a new us_birth_year user meta rather than reusing us_date_of_birth, which would have left one key holding two formats. The old key is not migrated in bulk. Instead GuardianService handles it in two halves: birthYear() falls back to the year of the old date when the new key is absent, so a student added before this change still shows one, and setBirthYear() deletes the old date on every save. That deletion is what makes the fallback safe rather than merely tidy. Without it, clearing the birth year on a student who predates the change would leave the old date behind for the fallback to read straight back, and the year could never be cleared at all. Stored in user meta, so no Schema.php change and no USC_VERSION bump. Closes #147 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
3a83decc82 |
Merge pull request 'Say "student" and "profile" in the UI, not "child" and "family"' (#152) from refactor/144-student-profile-copy into main
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / PHPStan (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m54s
CI / Tests (PHP 8.3) (push) Successful in 2m39s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #152 |
||
|
|
76caf178f0
|
Say "student" and "profile" in the UI, not "child" and "family"
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 40s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / PHPStan (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
Sweep the translatable strings across the frontend templates, the admin screens, the editor previews and the block inserter entry. Nothing else moves: the database columns, request parameters, form field names, CSS classes, the us_family shortcode and the us-scheduler/family block name are contracts with existing installs and with post content people have already saved, so renaming them would break sites for no user-visible gain. Two strings are reworded rather than swapped, because the direct substitution reads wrong: - The students list said "Child of Jane" and now says "Managed by Jane". "Student of Jane" would read as a teacher's pupil, which is exactly the wrong idea in a music studio. - A managed account is now "a managed student account" rather than "a student account", which would not distinguish it from the account holder. The guardian feature doc gains a short section on the split, so the next person to work on it does not read the mismatch as drift and "fix" it. Closes #144 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
8013d05d68 |
Merge pull request 'Stop upcoming lesson rows rendering on top of each other' (#151) from fix/149-upcoming-lesson-overlap into main
CI / Coding Standards (push) Successful in 2m56s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / PHPStan (push) Successful in 2m50s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m51s
Reviewed-on: #151 |
||
|
|
6b29c0e78e
|
Stop upcoming lesson rows rendering on top of each other
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 3m6s
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / Tests (PHP 8.2) (pull_request) Successful in 51s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m48s
CI / Build Plugin Zip (pull_request) Skipped
The panel's row and its two columns are divs with explicit flex rules, but
the text itself still sits in inline elements. A theme is free to take those
out of normal flow, and when it does the date and time land on the lesson
title and the status pill lands on the Cancel button. Pin position, float
and margin on the leaf elements at the same id-level specificity the rest of
the panel already uses, so a theme rule cannot lift them out of the column.
The rows behind "Show all" had the same shape of problem from the other
direction: `[hidden]` is only a UA-stylesheet rule, so the `div {
display: block }` reset that many themes still ship outranks it and the
collapsed rows render anyway. An author `!important` is the only way to win
that particular cascade.
Verified with a headless-browser harness rendering the exact markup
booking.js emits against twelve theme CSS patterns at two widths: before,
five patterns overlapped text or revealed the hidden rows; after, all pass.
Closes #149
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
7ea6616ba0 |
Merge pull request 'Trim CLAUDE.md to what the codebase can't tell you' (#141) from docs/trim-claude-md into main
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Coding Standards (push) Successful in 2m53s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 42s
CI / PHPStan (push) Successful in 2m46s
CI / Tests (PHP 8.3) (push) Successful in 2m49s
CI / Build Plugin Zip (push) Successful in 2m45s
Reviewed-on: #141 |
||
|
|
7fdf97b073
|
Trim CLAUDE.md to what the codebase can't tell you
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m54s
CI / PHPStan (pull_request) Successful in 3m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m47s
CI / Build Plugin Zip (pull_request) Skipped
Most of this file described the repo as it was around v1.0: three domain packages, two database tables, twenty-one classes. There are now eleven packages, fifteen tables, and well over a hundred classes, so those sections were not just redundant with `ls` and Schema.php — they were teaching the wrong shape of the codebase. Same for the CI section, which had drifted past the build job. Cut the command list (composer.json has the scripts), the bootstrap description, the directory tree, the table list, the Key Classes table, and the CI job summary. Kept every rule the code can't explain on its own: package-by-domain, no $wpdb outside repositories, capability checks rather than role names, and the Schema.php version-bump gotcha. Moved the Brain\Monkey and Mockery gotchas to tests/CLAUDE.md, which loads only when working under tests/ instead of in every session. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d3843186c0 |
Merge pull request 'Bump version to 1.3.1' (#140) from release/bump-1.3.1 into main
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
CI / Build Plugin Zip (push) Successful in 2m45s
CI / Tests (PHP 8.1) (push) Successful in 43s
Reviewed-on: #140 |
||
|
|
3c41d1119d |
Merge pull request 'Let parents register once and book for their children' (#139) from feature/parent-guardian-accounts into main
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m56s
CI / Tests (PHP 8.3) (push) Successful in 2m45s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / Coding Standards (push) Successful in 3m1s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #139 |
||
|
|
8122c158cf
|
Keep the guardian service within the PHP the plugin supports
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 2m54s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
`true` as a return type is PHP 8.2, but the plugin advertises 8.1, so the family screen's two service calls fataled on the 8.1 test job while every other job passed. They now return `?\WP_Error` — null on success — which matches RegistrationGate::validate() and works on 8.1. PHPStan was analysing against whatever PHP happened to be running (8.3 in CI, newer locally), so `composer lint` was green on syntax the plugin promises not to use. It is now pinned to the supported 8.1-8.3 range, which reproduces this failure at lint time instead of three jobs later. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
b772e1811e
|
Let parents register once and book for their children
A parent registers once and manages lessons for one or more children, who need no login of their own. A child is a real wp_users row with the student role but no usable login — so student_id keeps meaning "a WordPress user" on every table, and booking, credits, policies and enrolments work unchanged. A us_guardians link table maps guardian to child. The signup form gains a parent/guardian tick that reveals a block per child, with the account-signup questions asked per child rather than per guardian — they describe the student, not the account holder. Signup policies are recorded once per child with the guardian as the acceptor, which is the record that actually means something. A family that half-creates is rolled back entirely rather than leaving a guardian who cannot re-register. The booking and enrolment forms gain a "Who is this for?" picker listing children first, so the default selection is never the parent — booking for the wrong child is correctable, quietly billing a parent for their kid's lesson is not. POST /bookings and POST /enrollments take an optional student_id honoured only for that child's guardian; anything else is a 403. That check is the authorisation boundary of the feature. Payments and credits gain a payer: the charge names the child it was for and the guardian who owes it, so per-child reporting is unchanged while notices, receipts and the payment step reach the parent. Credit is held by the payer, so one child's cancellation can settle a sibling's charge, and the daily billing scan sends a guardian one notice covering every child. Closes #132 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
c25260a367 |
Merge pull request 'Bump version to 1.2.5' (#138) from release/bump-1.2.5 into main
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m51s
CI / PHPStan (push) Successful in 3m0s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #138 |
||
|
|
bbc85d88f1 |
Merge pull request 'Stop the availability form failing in silence' (#137) from fix/availability-silent-failures into main
CI / Build Plugin Zip (push) Successful in 2m50s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m50s
CI / Coding Standards (push) Successful in 2m53s
Release / Open next-version bump PR (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
Release / Build and Publish Release (push) Successful in 2m55s
Reviewed-on: #137 |
||
|
|
171b655bb8
|
Stop the availability form failing in silence
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 3m3s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m50s
CI / Build Plugin Zip (pull_request) Skipped
Adding availability for 5:30-6:00 PM with the lesson length left on its 60-minute default saved nothing and said nothing. A window is stored as consecutive lesson-length slots, so one that fits no lesson splits into none: splitByDuration() returned [], createFromWindow() inserted nothing, and addSlot() discarded the result and re-rendered the page unchanged. The REST endpoint already rejected that window with a 400. The admin form checked the same rules separately, and its copy was both laxer and mute — an unreadable date, an end before the start, and a two-day window were bare `return`s, and it never checked offering ownership at all, so a crafted POST could tie a slot to another instructor's offering and inherit their price and payment routing. Both callers now go through WindowValidator, which returns the window or a WP_Error explaining the refusal. The endpoint returns that error as is; the page renders its message as a notice. handleFormAction returns a [notice, error] pair so deletes report themselves too, and a successful add says how many slots it created. Two failures could also go unnoticed underneath: wpdb::insert's result was ignored, and insert_id still holds the previous statement's id after a failed write, so a failure looked like a success — and could become the recurrence group of a weekly series, orphaning every later occurrence. weeks was unbounded server-side despite the form's max=52. availability-admin.js narrows the lesson-length choices to those that fit the window and blocks submission when none do, which is what makes the original mistake hard to repeat. It is a convenience: the server validates regardless. Closes #130 |
||
|
|
d9dd576630 |
Merge pull request 'Link a signed-in visitor to the configured continue page' (#134) from feature/registration-logged-in-continue-link into main
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m51s
CI / PHPStan (push) Successful in 3m3s
CI / Tests (PHP 8.3) (push) Successful in 2m41s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #134 |
||
|
|
61b00c2ed3
|
Add the changelog entry for the continue link
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Coding Standards (pull_request) Successful in 3m0s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 3m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m53s
CI / Build Plugin Zip (pull_request) Skipped
|
||
|
|
7eb2afc6a3
|
Name the destination page in the continue link
"Continue to your account" says nothing about where the link goes. Use the chosen page's own title instead — "Continue to Book a Lesson" — so the visitor knows before clicking. An untitled page keeps the generic wording rather than rendering "Continue to ". |
||
|
|
8a985f04d6
|
Link a signed-in visitor to the configured continue page
The registration page's already-logged-in branch returned a bare sentence with nowhere to go, leaving the visitor to find their own way to their account. The invited-student branch a few lines above already built exactly the link that was missing. Extract that into continueLink() and use it for both logged-in outcomes. There is deliberately still no wp_login_url() fallback: sending someone already signed in to the login screen is the same dead end with extra steps, so with no page configured there is no link. Both messages now carry the us-register-form wrapper and enqueue the plugin stylesheet, which the invite branch emitted markup for but never loaded. Closes #131 |
||
|
|
da985c7f71 |
Merge pull request 'Add the changelog entry missed by #135' (#136) from docs/changelog-upcoming-lessons-layout into main
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / Tests (PHP 8.3) (push) Successful in 2m46s
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m54s
CI / PHPStan (push) Successful in 2m46s
CI / Build Plugin Zip (push) Successful in 2m50s
Reviewed-on: #136 |
||
|
|
7c91e1eef7
|
Add the changelog entry missed by #135
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m45s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m52s
The upcoming-lessons layout fix shipped without one. |
||
|
|
f3917d0784 |
Merge pull request 'Stop the upcoming-lessons row collapsing onto itself' (#135) from fix/upcoming-lessons-row-layout into main
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m56s
CI / Coding Standards (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m46s
CI / Build Plugin Zip (push) Successful in 2m53s
Reviewed-on: #135 |
||
|
|
b508ab92f8
|
Stop the upcoming-lessons row collapsing onto itself
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / PHPStan (pull_request) Successful in 2m54s
CI / Coding Standards (pull_request) Successful in 2m57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 2s
The lesson details and the actions rendered on top of each other. Three things left the panel fragile, all fixed here. The rules were bare class selectors while the visually identical .us-slot row next to them was written as `#us-booking-app .us-slot`. That inconsistency looks accidental, and it means a theme rule on div/span/strong outranks the panel's layout and flattens it. Every booking-page rule is now scoped under #us-booking-app. The row's two columns were spans carrying display:flex, so the layout only held while that declaration won. They are divs now — the layout no longer depends on overriding the inline default. The row had no flex-wrap and its title column no min-width:0, so a long offering title could not shrink and shoved the status pill and Cancel button out of the row. The 640px media query covered only the week grid, leaving the busier lesson rows with no narrow-viewport handling at all; they now stack details above actions. BlockPreview mirrors the markup change so the editor preview matches. Closes #133 |
||
|
|
2a661a10ff |
Merge pull request 'Bump version to 1.2.4' (#129) from release/bump-1.2.4 into main
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m58s
CI / Build Plugin Zip (push) Successful in 2m49s
CI / Tests (PHP 8.1) (push) Successful in 57s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.3) (push) Successful in 2m38s
Reviewed-on: #129 |
||
|
|
fabbd35fa7 |
Merge pull request 'View a policy version's content, and make policy text readable' (#128) from feature/policy-version-viewer-and-readable-text into main
CI / Tests (PHP 8.2) (push) Successful in 46s
CI / Tests (PHP 8.1) (push) Successful in 57s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m55s
CI / Coding Standards (push) Successful in 2m58s
CI / Tests (PHP 8.3) (push) Successful in 2m42s
CI / Build Plugin Zip (push) Successful in 2m49s
Release / Build and Publish Release (push) Successful in 2m50s
Release / Open next-version bump PR (push) Successful in 4s
Reviewed-on: #128 |
||
|
|
3a954bac57
|
View a policy version's content, and make policy text readable
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m12s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m50s
CI / PHPStan (pull_request) Successful in 3m4s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
The Policies admin page listed versions but never showed what any of them said, so revising a policy meant retyping it blind into an empty draft box. Each version row now has a View action that renders that version's text on the page, editable in place. A draft is saved back to itself; editing a published or archived version branches a new draft and leaves the original alone, because acceptances are recorded against policy_version_id and text a student agreed to must stay exactly as they saw it. That viewer also exposed why a studio reported the acceptance box as unreadable — one squashed line, overlapping words, a horizontal scrollbar. Bodies are typed into a bare textarea, so most carry no markup, and the raw text was emitted with its blank lines intact but nothing to turn them into paragraphs. PolicyVersion::bodyHtml() now renders every body the way WordPress renders post content (kses, then wpautop) and feeds all three consumers: the booking/enrolment JSON, the signup form, and the new viewer. Bodies written with markup are unaffected. The other half was that .us-policy-body had no CSS whatsoever and inherited whatever the theme did with an unstyled block in a form. It is now a bounded reading box that scrolls vertically and breaks long tokens, so a pasted URL cannot force the page sideways and a long policy cannot push the accept checkbox out of view. RegistrationPage was also never enqueueing the plugin stylesheet, which is why the signup gate looked worst of all. Closes #126 Closes #127 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
907f665876 |
Merge pull request 'Show price cadence on the front end and require a pay agreement at booking' (#125) from feature/price-cadence-pay-agreement into main
CI / Tests (PHP 8.2) (push) Successful in 53s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Failing after 2m42s
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / Coding Standards (push) Failing after 2m44s
CI / Tests (PHP 8.3) (push) Successful in 2m37s
CI / Build Plugin Zip (push) Skipped
Reviewed-on: #125 |
||
|
|
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]> |
||
|
|
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]> |
||
|
|
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]>
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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]>
|
||
|
|
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 |
||
|
|
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]>
|
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
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]> |
||
|
|
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 |
||
|
|
a281935811
|
Add invite-only group classes
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
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 2m49s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Group classes can now be marked invite-only (us_offerings.access_mode). Invite-only classes are hidden from the public catalog and reachable only when the instructor lets someone in via one of three paths, managed from My Lessons -> My Group Classes: - Add students directly: enrols them now with a pending payment. - Make available: grants registered students access to self-enrol through the normal paid flow (multi-select, emailed a notice). - Invite by email: tokenised registration invite tied to the class for a non-account address; after they register the class becomes enrollable. Reuses an existing pending invite instead of sending a second link. New us_group_access table records grants; GET /offerings merges granted invite-only classes for the caller; enrolment requires a grant (403 invite_required) and flips it to enrolled on success. composer test (487), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
25aeba9dc1
|
Merge pull request 'Add instructor group-class roster view under My Lessons' (#95) from feature/instructor-group-classes into main
CI / Tests (PHP 8.1) (push) Successful in 44s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / No Debug Code (push) Successful in 2s
CI / Coding Standards (push) Successful in 2m53s
CI / PHPStan (push) Successful in 2m49s
CI / Tests (PHP 8.3) (push) Successful in 2m39s
CI / Build Plugin Zip (push) Successful in 2m49s
Reviewed-on: #95 |
||
|
|
5f9d5ffc4f
|
Add instructor group-class roster view under My Lessons
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / Tests (PHP 8.1) (pull_request) Successful in 38s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m50s
CI / PHPStan (pull_request) Successful in 2m54s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
Instructors can now see their own group classes under My Lessons → My Group Classes (view_own_lessons): each class shows its active enrolment count against capacity plus a roster of enrolled students with enrolment and payment status. GroupClassController gains renderInstructorPage(), backed by the existing per-instructor enrolment query and a newly injected PaymentRepository for payment status. Wired as a submenu under the existing My Lessons menu, inside the same !view_all_lessons guard so owner-operators don't get a duplicate item. Closes #71 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
90fdde8c06
|
Merge pull request 'Cancellation cutoff: limit how close to a lesson a student can cancel' (#94) from feature/cancellation-cutoff into main
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / No Debug Code (push) Successful in 2s
CI / PHPStan (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m59s
CI / Tests (PHP 8.3) (push) Successful in 2m39s
CI / Build Plugin Zip (push) Successful in 2m44s
Reviewed-on: #94 |
||
|
|
169f7b6a13
|
Accept whole days only for the studio cancellation cutoff
CI / Tests (PHP 8.2) (pull_request) Successful in 38s
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 2m54s
CI / Coding Standards (pull_request) Successful in 3m2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
The Studio Settings cutoff field now takes an integer number of days (step 1, coerced with Val::int) instead of allowing half-day fractions, and displays the stored hours rounded to whole days. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
8b90b8d78d
|
Add cancellation cutoff limiting how close to a lesson a student can cancel
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 2m54s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / Build Plugin Zip (pull_request) Skipped
Students can no longer cancel their own lesson online once it starts within a configured window; instructors and studio admins can always cancel. - Studio default `us_cancellation_cutoff_hours` (stored/computed in hours, entered and displayed in days under Studio Settings → Cancellations). - Optional per-offering override `cancellation_cutoff_hours` (entered in hours); blank inherits the studio default, 0 allows anytime cancellation. - `Booking\CancellationPolicy` resolves the effective window and decides; `BookingEndpoint::cancel()` returns a 403 `cancellation_closed` when too late. The instructor status endpoint and studio-admin student actions bypass it. Closes #93 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
a777f5d05a
|
Merge pull request 'Point plugin metadata links at Unsupervised and Gitea' (#92) from feature/view-details-gitea-releases into main
CI / Tests (PHP 8.1) (push) Successful in 42s
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 2m39s
CI / PHPStan (push) Successful in 2m46s
CI / Coding Standards (push) Successful in 3m2s
CI / Build Plugin Zip (push) Successful in 2m46s
Reviewed-on: #92 |
||
|
|
83be388186
|
Point plugin metadata links at Unsupervised and Gitea
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 39s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m21s
CI / Coding Standards (pull_request) Successful in 2m52s
CI / PHPStan (pull_request) Successful in 2m53s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m36s
CI / Build Plugin Zip (pull_request) Skipped
Make the "By Unsupervised" author link go to https://unsupervised.ca (via a new Author URI header) and the plugin site / "View details" link point at the Gitea project instead of WordPress.org. Core's "View details" link opened a thickbox iframe against the WordPress.org plugin-information API, which 404s ("Plugin not found") for this off-directory plugin. A plugin_row_meta filter now replaces it with a new-tab link to the matching Gitea release tag page; embedding Gitea in the iframe is blocked by its X-Frame-Options anyway. Co-Authored-By: Claude Opus 4.8 <[email protected]> |