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
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]>
This commit is contained in:
@@ -13,6 +13,9 @@ each change under the current top section as you work.
|
||||
|
||||
## [1.2.2]
|
||||
|
||||
### Added
|
||||
- The **Group Classes** block can now be pinned to a single class, under **Classes shown → Class** in the block sidebar (shortcode: `[us_group_classes offering="…"]`). Pick a class and the block shows only that one, so it can be embedded on a page that describes the class. In this mode the class's own description is left out to avoid repeating the page copy — the card shows the schedule, instructor, price, enrolment deadline and the enrol/withdraw controls. Leaving it on **All classes** keeps the full browsable catalog with descriptions.
|
||||
|
||||
## [1.2.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
+1
-1
@@ -192,7 +192,7 @@
|
||||
{ title: __('Classes shown', 'unsupervised-schedular') },
|
||||
el(GroupClassSelect, {
|
||||
label: __('Class', 'unsupervised-schedular'),
|
||||
help: __('Show only one group class, for embedding on a page dedicated to it.', 'unsupervised-schedular'),
|
||||
help: __('Show only one group class, for embedding on a page dedicated to it. That class’s description is left out — the card shows just the schedule, price and enrolment controls.', 'unsupervised-schedular'),
|
||||
value: attributes.offeringId,
|
||||
onChange: (offeringId) => setAttributes({ offeringId }),
|
||||
})
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
|
||||
// When the shortcode/block pins a single offering, only that class is
|
||||
// shown, so the page can be embedded alongside a full class description.
|
||||
// The class's own description is then omitted from the card — the page it
|
||||
// sits on already describes the class — leaving the schedule, price and
|
||||
// enrolment controls.
|
||||
const singleOfferingId = Number(app.dataset.offering || 0);
|
||||
|
||||
function apiFetch(path, options = {}) {
|
||||
@@ -148,7 +151,7 @@
|
||||
${whenLabel(o) ? `<p class="us-class-when">${escHtml(whenLabel(o))}</p>` : ''}
|
||||
${o.instructor_name ? `<p class="us-class-instructor">With ${escHtml(o.instructor_name)}</p>` : ''}
|
||||
${o.schedule_note ? `<p>${escHtml(o.schedule_note)}</p>` : ''}
|
||||
${o.description ? `<p>${escHtml(o.description)}</p>` : ''}
|
||||
${!singleOfferingId && o.description ? `<p>${escHtml(o.description)}</p>` : ''}
|
||||
<p>${escHtml(Number(o.price).toFixed(2))} ${escHtml(o.currency)}</p>
|
||||
${!enrolledMap.has(Number(o.id)) && isEnrollmentOpen(o) && enrolmentDeadline(o)
|
||||
? `<p class="us-enrol-deadline">Enrol by ${escHtml(formatDate(enrolmentDeadline(o)))}</p>`
|
||||
|
||||
@@ -30,7 +30,7 @@ Four blocks have sidebar (inspector) options:
|
||||
| `us-scheduler/student-login` | `bookingPageId` (number) | `0` | Page the "View available lessons" link points to for logged-in visitors, and the post-login redirect target. `0` = the current page. |
|
||||
| `us-scheduler/student-login` | `autoRedirect` (boolean) | `false` | Send logged-in visitors straight to the booking page instead of showing the link. Does nothing until a booking page is chosen. |
|
||||
| `us-scheduler/student-register` | `loginPageId` (number) | `0` | Page the "Sign in to your account" link points to after a student confirms their email. `0` = the WordPress login screen. Shortcode equivalent: `[us_student_register login_page_id="…"]`. |
|
||||
| `us-scheduler/group-classes` | `offeringId` (number) | `0` | Restrict the page to a single group class, for embedding on a page dedicated to that class. `0` = browse all classes. Shortcode equivalent: `[us_group_classes offering="…"]`. |
|
||||
| `us-scheduler/group-classes` | `offeringId` (number) | `0` | Restrict the page to a single group class, for embedding on a page dedicated to that class. The class description is then omitted — only the schedule, instructor, price and enrolment controls are shown, so the surrounding page's own copy is not repeated. `0` = browse all classes, descriptions included. Shortcode equivalent: `[us_group_classes offering="…"]`. |
|
||||
|
||||
The page selects list all published pages; if a chosen page is later deleted,
|
||||
the blocks fall back to their defaults. The group-classes block's class
|
||||
@@ -78,7 +78,9 @@ placeholder content:
|
||||
- **Booking** — `#us-booking-app` with sample `.us-day` / `.us-slot` rows and
|
||||
disabled Book buttons.
|
||||
- **Group classes** — `#us-group-app` with a sample `.us-class` card and a
|
||||
disabled Enrol button.
|
||||
disabled Enrol button. When `offeringId` pins a single class the preview
|
||||
drops the sample description, matching what the live page renders in that
|
||||
mode.
|
||||
- **Login** — the real `templates/frontend/login-page.php` template (it has
|
||||
no request-state dependencies).
|
||||
- **Registration** — a disabled sample of the `.us-register-form` fields.
|
||||
|
||||
@@ -176,7 +176,7 @@ class becomes enrollable for them — they choose whether to enrol.
|
||||
- Model: `Unsupervised\Schedular\GroupClass\Enrollment`
|
||||
- Admin controller: `Unsupervised\Schedular\GroupClass\GroupClassController` — `renderPage` (studio admin per-class summary, `view_all_lessons`) and `renderInstructorPage` (instructor summary + `?class_id` roster detail, `view_own_lessons`)
|
||||
- REST endpoint: `Unsupervised\Schedular\GroupClass\EnrollmentEndpoint`
|
||||
- Frontend: `Unsupervised\Schedular\GroupClass\GroupClassPage` (`[us_group_classes]` shortcode; `offering="…"` restricts it to a single class for embedding on a dedicated page — the block equivalent is the `offeringId` attribute)
|
||||
- Frontend: `Unsupervised\Schedular\GroupClass\GroupClassPage` (`[us_group_classes]` shortcode; `offering="…"` restricts it to a single class for embedding on a dedicated page — the block equivalent is the `offeringId` attribute). In single-class mode `assets/js/group-classes.js` leaves the class description out of the card, since the page it is embedded on already describes the class; the schedule, instructor, schedule note, price and enrolment controls are still shown.
|
||||
- Reuses `Registration\RegistrationGate` (intake answers + booking-scoped policy acceptance, type `enrollment`)
|
||||
|
||||
> **Payment:** a priced enrolment creates a payment via `Payment\PaymentService`
|
||||
|
||||
+20
-4
@@ -58,13 +58,29 @@ class BlockPreview {
|
||||
);
|
||||
}
|
||||
|
||||
public static function groupClasses(): string {
|
||||
/**
|
||||
* Sample group-class card.
|
||||
*
|
||||
* @param bool $singleClass Whether the block is pinned to one class, in
|
||||
* which case the live page omits the class
|
||||
* description and the preview does too.
|
||||
*/
|
||||
public static function groupClasses( bool $singleClass = false ): string {
|
||||
$note = $singleClass
|
||||
? __( 'Editor preview — the published page shows the chosen class with its live schedule and enrolment status.', 'unsupervised-schedular' )
|
||||
: __( 'Editor preview — students see live group classes on the published page.', 'unsupervised-schedular' );
|
||||
|
||||
$description = $singleClass
|
||||
? ''
|
||||
: '<p>' . esc_html__( 'A sample class shown so the page can be styled.', 'unsupervised-schedular' ) . '</p>';
|
||||
|
||||
return sprintf(
|
||||
'<div id="us-group-app">%s<div id="us-group-list"><div class="us-class"><h3>%s</h3><p>%s</p><p>%s</p><p>25.00 CAD</p><button type="button" class="us-enrol-btn" disabled>%s</button></div></div></div>',
|
||||
self::note( __( 'Editor preview — students see live group classes on the published page.', 'unsupervised-schedular' ) ),
|
||||
'<div id="us-group-app">%s<div id="us-group-list"><div class="us-class"><h3>%s</h3><p class="us-class-when">%s</p>%s<p>25.00 CAD</p><p class="us-enrol-deadline">%s</p><button type="button" class="us-enrol-btn" disabled>%s</button></div></div></div>',
|
||||
self::note( $note ),
|
||||
esc_html__( 'Beginner Group Class', 'unsupervised-schedular' ),
|
||||
esc_html__( 'Saturdays 10:00 AM–11:00 AM', 'unsupervised-schedular' ),
|
||||
esc_html__( 'A sample class shown so the page can be styled.', 'unsupervised-schedular' ),
|
||||
$description,
|
||||
esc_html__( 'Enrol by Sep 6, 2026', 'unsupervised-schedular' ),
|
||||
esc_html__( 'Enrol', 'unsupervised-schedular' )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,11 @@ class BlockRegistrar {
|
||||
* @param array<string, mixed> $attributes Block attributes.
|
||||
*/
|
||||
public function renderGroupClasses( array $attributes = [] ): string {
|
||||
return $this->isEditorPreview() ? BlockPreview::groupClasses() : $this->groupClassPage->render( $attributes );
|
||||
if ( ! $this->isEditorPreview() ) {
|
||||
return $this->groupClassPage->render( $attributes );
|
||||
}
|
||||
|
||||
return BlockPreview::groupClasses( Val::int( $attributes['offeringId'] ?? 0 ) > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,17 @@ class BlockPreviewTest extends TestCase
|
||||
self::assertStringContainsString('class="us-class"', $html);
|
||||
self::assertStringContainsString('class="us-enrol-btn" disabled', $html);
|
||||
self::assertStringContainsString('us-editor-note', $html);
|
||||
self::assertStringContainsString('A sample class shown so the page can be styled.', $html);
|
||||
}
|
||||
|
||||
public function testSingleClassGroupPreviewDropsTheDescriptionButKeepsScheduleAndEnrolment(): void
|
||||
{
|
||||
$html = BlockPreview::groupClasses(true);
|
||||
|
||||
self::assertStringNotContainsString('A sample class shown so the page can be styled.', $html);
|
||||
self::assertStringContainsString('class="us-class-when"', $html);
|
||||
self::assertStringContainsString('class="us-enrol-deadline"', $html);
|
||||
self::assertStringContainsString('class="us-enrol-btn" disabled', $html);
|
||||
}
|
||||
|
||||
public function testLoginPreviewIncludesTheRealLoginTemplate(): void
|
||||
|
||||
@@ -179,6 +179,19 @@ class BlockRegistrarTest extends TestCase
|
||||
self::assertStringContainsString('us-group-app', $this->registrar->renderGroupClasses());
|
||||
}
|
||||
|
||||
public function testEditorPreviewOfAPinnedGroupClassOmitsTheDescription(): void
|
||||
{
|
||||
$this->registrar->preview = true;
|
||||
$this->groupClassPage->shouldNotReceive('render');
|
||||
|
||||
$all = $this->registrar->renderGroupClasses();
|
||||
$single = $this->registrar->renderGroupClasses(['offeringId' => 12]);
|
||||
|
||||
self::assertStringContainsString('A sample class shown so the page can be styled.', $all);
|
||||
self::assertStringNotContainsString('A sample class shown so the page can be styled.', $single);
|
||||
self::assertStringContainsString('us-enrol-deadline', $single);
|
||||
}
|
||||
|
||||
public function testIsEditorPreviewIsFalseOutsideRestRequests(): void
|
||||
{
|
||||
// REST_REQUEST is undefined in the test process, so the real
|
||||
|
||||
Reference in New Issue
Block a user