From b508ab92f845e0f8168f4c90a4bb5108fa7a4ce5 Mon Sep 17 00:00:00 2001 From: James Griffin Date: Tue, 28 Jul 2026 22:55:11 -0300 Subject: [PATCH] Stop the upcoming-lessons row collapsing onto itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- assets/css/frontend.css | 73 +++++++++++++++++++++++++-------- assets/js/booking.js | 11 +++-- docs/features/lesson-booking.md | 9 ++++ src/BlockPreview.php | 8 ++-- 4 files changed, 77 insertions(+), 24 deletions(-) diff --git a/assets/css/frontend.css b/assets/css/frontend.css index 99a6b5a..e305d00 100644 --- a/assets/css/frontend.css +++ b/assets/css/frontend.css @@ -34,47 +34,72 @@ margin-top: 8px; } -.us-my-lessons { +/* + * The upcoming-lessons panel. Every rule here is scoped under #us-booking-app — + * the same id-level specificity .us-slot above uses — because these rows sit in + * whatever layout the theme provides and carry more content than a calendar + * cell. Bare class selectors lost to theme rules on div/span/strong, which + * collapsed the flex layout and piled the details on top of the actions. + */ +#us-booking-app .us-my-lessons { margin-bottom: 24px; } -.us-my-lesson { +#us-booking-app .us-my-lesson { + box-sizing: border-box; + max-width: 100%; border: 1px solid #ddd; border-radius: 4px; padding: 12px 16px; margin-bottom: 8px; display: flex; + flex-wrap: wrap; justify-content: space-between; align-items: center; - gap: 12px; + gap: 8px 12px; } -.us-my-lesson-info { +/* + * `min-width: 0` lets the title column shrink below its content width — without + * it a long offering title cannot compress and shoves the status pill and + * Cancel button out of the row. The flex-basis keeps the details and the + * actions on one line while there is room, and wraps them once there is not. + */ +#us-booking-app .us-my-lesson-info { display: flex; flex-direction: column; gap: 2px; + flex: 1 1 14em; + min-width: 0; } -.us-my-lesson-title { +#us-booking-app .us-my-lesson-title, +#us-booking-app .us-my-lesson-when { + overflow-wrap: break-word; + word-break: break-word; +} + +#us-booking-app .us-my-lesson-title { font-size: 1.05em; } -.us-my-lesson-duration { +#us-booking-app .us-my-lesson-duration { font-weight: normal; color: #666; } -.us-my-lesson-when { +#us-booking-app .us-my-lesson-when { color: #555; } -.us-my-lesson-actions { +#us-booking-app .us-my-lesson-actions { display: flex; - gap: 12px; + flex-wrap: wrap; + gap: 8px 12px; align-items: center; } -.us-show-all-lessons { +#us-booking-app .us-show-all-lessons { background: transparent; border: 1px solid #ccc; border-radius: 4px; @@ -82,11 +107,11 @@ cursor: pointer; } -.us-show-all-lessons:hover { +#us-booking-app .us-show-all-lessons:hover { border-color: #888; } -.us-cancel-lesson { +#us-booking-app .us-cancel-lesson { background: transparent; border: 1px solid #ccc; border-radius: 4px; @@ -95,24 +120,26 @@ color: #c00; } -.us-cancel-lesson:hover { +#us-booking-app .us-cancel-lesson:hover { border-color: #c00; } -.us-lesson-status { +#us-booking-app .us-lesson-status { + display: inline-block; font-size: 0.85em; font-weight: 600; padding: 2px 10px; border-radius: 10px; background: #eee; + white-space: nowrap; } -.us-lesson-status-confirmed { +#us-booking-app .us-lesson-status-confirmed { background: #e2f5e5; color: #1a7d2e; } -.us-lesson-status-pending { +#us-booking-app .us-lesson-status-pending { background: #fdf3d7; color: #8a6d1a; } @@ -345,6 +372,20 @@ .us-week-day { min-height: 0; } + + /* + * A lesson row carries a title, a date/time, a status pill and a button — + * more than fits one narrow line, so stack the details above the actions + * rather than letting them wrap into each other. + */ + #us-booking-app .us-my-lesson { + flex-direction: column; + align-items: stretch; + } + + #us-booking-app .us-my-lesson-info { + flex: 0 0 auto; + } } /* Shown only in block-editor previews (see BlockPreview). */ diff --git a/assets/js/booking.js b/assets/js/booking.js index 11261a9..aa51ff7 100644 --- a/assets/js/booking.js +++ b/assets/js/booking.js @@ -582,16 +582,19 @@ function lessonRowHtml(l) { const title = l.offering_title ? escHtml(String(l.offering_title)) : 'Lesson'; const duration = l.duration_minutes ? ` (${escHtml(String(l.duration_minutes))} min)` : ''; + // The two columns are divs, not spans: as spans the layout only held up + // while the stylesheet's display:flex won, and a theme rule on span + // collapsed the row onto itself. return `
- +
${title}${duration} ${escHtml(dayLabel(dayKey(l.start_dt)))} · ${escHtml(timeOf(l.start_dt))}–${escHtml(timeOf(l.end_dt))} - - +
+
${escHtml(lessonStatusLabel(String(l.status)))} - +
`; } diff --git a/docs/features/lesson-booking.md b/docs/features/lesson-booking.md index d81797b..af19038 100644 --- a/docs/features/lesson-booking.md +++ b/docs/features/lesson-booking.md @@ -154,6 +154,7 @@ instructor may only open their own lessons; the studio **Scheduler** may open an - Admin lesson detail presenter: `Unsupervised\Schedular\Booking\LessonDetail` (per-lesson intake answers + policy acceptances), template `templates/admin/lesson-detail.php` - REST endpoint: `Unsupervised\Schedular\Booking\BookingEndpoint` - Frontend: `Unsupervised\Schedular\Booking\BookingPage`, `Unsupervised\Schedular\Auth\LoginPage` +- Upcoming-lessons panel: rendered client-side into `#us-my-lessons` by `assets/js/booking.js` (`lessonRowHtml`/`renderMyLessons`), mirrored for the editor by `BlockPreview::upcomingLessons()` — keep the two markup shapes in step. > **Payment seam:** a priced booking is created with `status = pending` and its > payment linked via `payment_id`; the lesson is confirmed when the payment is @@ -161,6 +162,14 @@ instructor may only open their own lessons; the studio **Scheduler** may open an > Unpriced bookings skip the seam entirely and are confirmed at creation. > `GET /policies?scope=booking` returns just the booking-gate policies the form > must collect. +> +> **Frontend CSS scoping:** every rule for the booking page's own markup is +> written under `#us-booking-app` (`assets/css/frontend.css`). These panels sit +> inside whatever layout the active theme provides, and bare class selectors lose +> to theme rules on `div`/`span`/`strong` — which flattens the flex layout and +> renders the lesson details on top of the actions. The row's two columns are +> `div`s for the same reason: the layout must not depend on overriding the +> inline default. New booking-page rules should follow both conventions. ## Tests - `tests/Unit/Booking/BookingRepositoryTest.php` diff --git a/src/BlockPreview.php b/src/BlockPreview.php index fec2615..a6682ef 100644 --- a/src/BlockPreview.php +++ b/src/BlockPreview.php @@ -86,13 +86,13 @@ class BlockPreview { private static function upcomingLessons(): string { return sprintf( '

%s

' - . '
' + . '
' . '%s (30 min)' - . '%s' - . '' + . '%s
' + . '
' . '%s' . '' - . '
', + . '
', esc_html__( 'Your upcoming lessons', 'unsupervised-schedular' ), esc_html__( 'Piano Lesson', 'unsupervised-schedular' ), esc_html__( 'Monday · 4:00 PM–4:30 PM', 'unsupervised-schedular' ),