Files
unsupervised-scheduler/src/BlockPreview.php
T
thatguygriffandClaude Opus 5 9344ab7193
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
Show price cadence and require a pay agreement at booking
Every price a student meets on the front end now carries the cadence it is
billed on — at booking, up front, weekly, monthly — so a bare amount can no
longer read as a one-off when it is a recurring charge.

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

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

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

Closes #124

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-28 14:43:38 -03:00

176 lines
6.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace Unsupervised\Schedular;
/**
* Static, script-free markup for the editor previews of the front-end blocks.
*
* The booking and group-class pages are populated by JavaScript on the live
* site, and the registration page requires a valid invite token — none of
* which exist inside the block editor. These previews reproduce the same
* wrapper elements and CSS classes the live pages use, filled with
* representative placeholder content, so themes can be styled against
* realistic markup without firing REST calls, redirects, or Stripe.js.
*/
class BlockPreview {
/**
* Sample booking page.
*
* @param string $mode Which halves the block embeds — one of
* {@see Booking\BookingPage::MODE_BOTH},
* `MODE_BOOKING` or `MODE_UPCOMING`. The preview shows
* the same sections the published page would.
*/
public static function booking( string $mode = Booking\BookingPage::MODE_BOTH ): string {
if ( Booking\BookingPage::MODE_UPCOMING === $mode ) {
return sprintf(
'<div id="us-booking-app">%s<div id="us-my-lessons">%s</div></div>',
self::note( __( 'Editor preview — students see their own lessons on the published page.', 'unsupervised-schedular' ) ),
self::upcomingLessons()
);
}
$days = [
[
'label' => __( 'Monday', 'unsupervised-schedular' ),
'slots' => [
[ '4:00 PM4:30 PM', 30 ],
[ '4:30 PM5:00 PM', 30 ],
],
],
[
'label' => __( 'Wednesday', 'unsupervised-schedular' ),
'slots' => [
[ '5:00 PM5:45 PM', 45 ],
],
],
];
$dayHtml = '';
foreach ( $days as $day ) {
$slotHtml = '';
foreach ( $day['slots'] as $slot ) {
$slotHtml .= sprintf(
'<div class="us-slot"><span>%s (%d min)</span><button type="button" class="us-book-btn" disabled>%s</button></div>',
esc_html( $slot[0] ),
(int) $slot[1],
esc_html__( 'Book', 'unsupervised-schedular' )
);
}
$dayHtml .= sprintf(
'<div class="us-day"><h3 class="us-day-heading">%s</h3>%s</div>',
esc_html( $day['label'] ),
$slotHtml
);
}
$lessons = Booking\BookingPage::MODE_BOOKING === $mode
? ''
: sprintf( '<div id="us-my-lessons">%s</div>', self::upcomingLessons() );
return sprintf(
'<div id="us-booking-app">%s%s<div id="us-slot-list">%s</div></div>',
self::note( __( 'Editor preview — students see live availability on the published page.', 'unsupervised-schedular' ) ),
$lessons,
$dayHtml
);
}
/**
* Sample "your upcoming lessons" panel, shared by the booking preview's
* full and upcoming-only modes.
*/
private static function upcomingLessons(): string {
return sprintf(
'<div class="us-my-lessons"><h3>%s</h3>'
. '<div class="us-my-lesson"><span class="us-my-lesson-info">'
. '<strong class="us-my-lesson-title">%s <span class="us-my-lesson-duration">(30 min)</span></strong>'
. '<span class="us-my-lesson-when">%s</span></span>'
. '<span class="us-my-lesson-actions">'
. '<span class="us-lesson-status us-lesson-status-confirmed">%s</span>'
. '<button type="button" class="us-cancel-lesson" disabled>%s</button>'
. '</span></div></div>',
esc_html__( 'Your upcoming lessons', 'unsupervised-schedular' ),
esc_html__( 'Piano Lesson', 'unsupervised-schedular' ),
esc_html__( 'Monday · 4:00 PM4:30 PM', 'unsupervised-schedular' ),
esc_html__( 'Confirmed', 'unsupervised-schedular' ),
esc_html__( 'Cancel', 'unsupervised-schedular' )
);
}
/**
* 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 class="us-class-when">%s</p>%s<p class="us-class-price">%s</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 AM11:00 AM', 'unsupervised-schedular' ),
$description,
// Prices on the live page always carry their cadence, so the sample does too.
esc_html__( '25.00 CAD up front', 'unsupervised-schedular' ),
esc_html__( 'Enrol by Sep 6, 2026', 'unsupervised-schedular' ),
esc_html__( 'Enrol', 'unsupervised-schedular' )
);
}
/**
* The live login form renders fine without any request state, so the
* preview includes the real template (the editing user is logged in, which
* would otherwise short-circuit to an "already logged in" message).
*/
public static function login(): string {
$error = '';
ob_start();
include USC_PLUGIN_DIR . 'templates/frontend/login-page.php';
return self::note( __( 'Editor preview — logged-in visitors are offered a link to the booking page instead.', 'unsupervised-schedular' ) ) . (string) ob_get_clean();
}
public static function registration(): string {
$fields = sprintf(
'<p><label for="us-reg-email">%s</label><input type="email" id="us-reg-email" value="[email protected]" readonly></p>',
esc_html__( 'Email', 'unsupervised-schedular' )
);
$fields .= sprintf(
'<p><label for="us-reg-name">%s</label><input type="text" id="us-reg-name"></p>',
esc_html__( 'Your name', 'unsupervised-schedular' )
);
$fields .= sprintf(
'<p><label for="us-reg-pass">%s</label><input type="password" id="us-reg-pass"></p>',
esc_html__( 'Password', 'unsupervised-schedular' )
);
$fields .= sprintf(
'<p><input type="submit" value="%s" disabled></p>',
esc_attr__( 'Create Account', 'unsupervised-schedular' )
);
return sprintf(
'<div class="us-register-form">%s<form>%s</form></div>',
self::note( __( 'Editor preview — the live form requires a valid invite link and lists signup policies.', 'unsupervised-schedular' ) ),
$fields
);
}
private static function note( string $text ): string {
return '<p class="us-editor-note">' . esc_html( $text ) . '</p>';
}
}