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
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]>
This commit is contained in:
@@ -20,6 +20,7 @@ An offering is anything a student can register for: a private-lesson type (30 or
|
||||
| `capacity` | SMALLINT | Group only — max enrolments; NULL for private |
|
||||
| `term_start` | DATE | Group / term offerings — first day; NULL otherwise |
|
||||
| `term_end` | DATE | Group / term offerings — last day; NULL otherwise |
|
||||
| `class_time` | TIME | Group only — time of day each session starts; NULL otherwise |
|
||||
| `schedule_note` | VARCHAR(191) | Group only — human-readable schedule, e.g. "Tuesdays 4:00pm"|
|
||||
| `cancellation_cutoff_hours` | SMALLINT UNSIGNED | Optional per-offering cancellation cutoff in hours; NULL inherits the studio default (see `cancellation-cutoff.md`) |
|
||||
| `access_mode` | VARCHAR(20) | `public` (listed in the catalog) or `invite_only` (group classes hidden from the catalog — see `group-classes.md`) |
|
||||
@@ -40,6 +41,28 @@ sessions** (`term_end = term_start + (N−1) weeks`, computed by
|
||||
student-facing class card shows the date (one-off) or the date range with the
|
||||
weekly session count.
|
||||
|
||||
## Class Time and Sessions
|
||||
A group class also carries `class_time` — the time of day each session starts —
|
||||
validated by `Offering::normalizeTime()` (strict `H:i`/`H:i:s`; garbage leaves it
|
||||
NULL). `class_time` + `term_start`/`term_end` + `duration_minutes` together define
|
||||
the concrete session windows: `Offering::sessionWindows()` returns one
|
||||
`{start, end}` per session (weekly across the term, or a single window for a
|
||||
one-off), and returns an empty list unless date, time, and a positive duration are
|
||||
all set. These windows drive availability reconciliation (see **Instructor
|
||||
assignment** below and `group-classes.md`).
|
||||
|
||||
## Instructor assignment
|
||||
Every offering has an owning `instructor_id`. A studio admin
|
||||
(`manage_instructors`) sees an **Instructor** picker on the offering form and may
|
||||
assign a group class to any instructor; a plain instructor never sees the picker
|
||||
and always owns the classes they create (the posted value is ignored for them, and
|
||||
updates never reassign the owner otherwise). When a group class is saved with an
|
||||
assigned instructor and a full schedule, `Offering\ClassSlotReconciler` clears that
|
||||
instructor's **open** availability slots overlapping each session so students can't
|
||||
book them, and reports any **already-booked** lesson that clashes as a conflict for
|
||||
the studio to resolve by hand (a booked lesson is never deleted). The result is
|
||||
surfaced as an admin notice after saving.
|
||||
|
||||
## Admin Interface
|
||||
Studio admin and instructors manage offerings under **Offerings** in wp-admin.
|
||||
- Studio admin (`manage_offerings`) manages offerings for any instructor.
|
||||
@@ -61,11 +84,14 @@ Studio admin and instructors manage offerings under **Offerings** in wp-admin.
|
||||
|
||||
## Implementation
|
||||
- Repository: `Unsupervised\Schedular\Offering\OfferingRepository`
|
||||
- Model: `Unsupervised\Schedular\Offering\Offering`
|
||||
- Model: `Unsupervised\Schedular\Offering\Offering` (`normalizeTime`, `sessionWindows`)
|
||||
- Admin controller: `Unsupervised\Schedular\Offering\OfferingController`
|
||||
- REST endpoint: `Unsupervised\Schedular\Offering\OfferingEndpoint`
|
||||
- REST endpoint: `Unsupervised\Schedular\Offering\OfferingEndpoint` (public listing includes `instructor_name`)
|
||||
- Availability reconciliation: `Unsupervised\Schedular\Offering\ClassSlotReconciler` (uses `Availability\AvailabilityRepository::findOverlapping`)
|
||||
|
||||
## Tests
|
||||
- `tests/Unit/Offering/OfferingControllerTest.php`
|
||||
- `tests/Unit/Offering/OfferingRepositoryTest.php`
|
||||
- `tests/Unit/Offering/OfferingTest.php`
|
||||
- `tests/Unit/Offering/OfferingEndpointTest.php`
|
||||
- `tests/Unit/Offering/ClassSlotReconcilerTest.php`
|
||||
|
||||
Reference in New Issue
Block a user