EnrollmentEndpoint: GET /enrollments (student / instructor / studio-admin scoped) and POST /enrollments — validates the offering is a group_class, enforces capacity and no-duplicate, runs the reused RegistrationGate (intake questions + booking-scoped policies), creates the enrolment (status = active), records answers/acceptances (type enrollment).
Admin & front end
Group Classes admin page (view_all_lessons) listing active enrolments.
[us_group_classes] shortcode (GroupClassPage) + group-classes.js: lists open group classes → questions + policies enrol form → POST /enrollments.
Payment seam
Deferred to #7: an enrolment lands status = active, payment_id = null. The pay→confirm + receipt step plugs in later.
Notes
JS left untested, keeping parity with the repo's no-build vanilla-JS posture (business rules are enforced and tested server-side in RegistrationGate/endpoints). Adding Jest is a good fit alongside #7 (Stripe JS).
Instructor-specific enrolment view (the spec's "under My Lessons") is a follow-up; per-instructor data is already exposed via GET /enrollments.
Implements #4: students enrol in a group_class offering via the same
registration gate as private lessons (intake questions + booking-scoped
policy acceptance). Enrolment is capacity-enforced and prevents duplicates.
- Schema: us_group_enrollments table.
- Enrollment value object + EnrollmentRepository (countActiveForOffering,
hasActiveEnrollment, per-student/instructor/all-active queries, status).
- EnrollmentEndpoint: GET /enrollments (scoped) and POST /enrollments
(validates group_class, capacity, no-duplicate; reuses RegistrationGate;
records answers/acceptances type enrollment).
- GroupClassController + admin page (view_all_lessons): all active enrolments.
- Front-end: [us_group_classes] shortcode (GroupClassPage) + group-classes.js
enrol flow (list classes -> questions + policies -> POST /enrollments).
- Wiring in Plugin, RestRegistrar, AdminMenu, ShortcodeRegistrar.
Payment is the deferred seam (#7): enrolment lands active, payment_id null.
JS left untested for parity with the repo's no-build vanilla-JS posture.
Tests: tests/Unit/GroupClass/ (Enrollment, EnrollmentRepository).
composer test (121), cs, and PHPStan level 6 all pass.
Refs #4
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #4 (Group classes) — year-commitment enrolment reusing the registration gate built in #3.
Backend
us_group_enrollmentstable.Enrollmentvalue object (active/cancelled/completed) +EnrollmentRepository—countActiveForOffering+hasActiveEnrollment(capacity + duplicate guards), per-student/per-instructor/all-active queries,updateStatus.EnrollmentEndpoint:GET /enrollments(student / instructor / studio-admin scoped) andPOST /enrollments— validates the offering is agroup_class, enforces capacity and no-duplicate, runs the reusedRegistrationGate(intake questions + booking-scoped policies), creates the enrolment (status = active), records answers/acceptances (typeenrollment).Admin & front end
view_all_lessons) listing active enrolments.[us_group_classes]shortcode (GroupClassPage) +group-classes.js: lists open group classes → questions + policies enrol form →POST /enrollments.Payment seam
Deferred to #7: an enrolment lands
status = active,payment_id = null. The pay→confirm + receipt step plugs in later.Notes
RegistrationGate/endpoints). Adding Jest is a good fit alongside #7 (Stripe JS).GET /enrollments.Tests
tests/Unit/GroupClass/—Enrollment,EnrollmentRepository.composer test(121 tests),composer cs, PHPStan level 6, and no-debug all pass.Refs #4
🤖 Generated with Claude Code