From 763729459311b2b727b673fdc347aa85190f8c82 Mon Sep 17 00:00:00 2001 From: Naser Mansour Date: Sat, 31 Oct 2026 18:51:15 +0200 Subject: [PATCH] =?UTF-8?q?24-6:=20booking=20page=20and=20booking=20succes?= =?UTF-8?q?s=20=E2=80=94=20implemented,=20QA=20Review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- docs/tasks/24-6.md | 2 +- docs/tasks/manual-tests/24-6.md | 16 ++ docs/tasks/statuses.md | 2 +- lang/ar/site.php | 1 + lang/en/site.php | 1 + resources/css/public.css | 18 ++ .../livewire/availability-calendar.blade.php | 9 +- .../livewire/pages/booking-success.blade.php | 27 +- .../views/livewire/pages/booking.blade.php | 264 +++++++----------- .../Design/BookingPageRedesignTest.php | 88 ++++++ tests/Feature/Design/HomeBookingCardTest.php | 2 +- 11 files changed, 238 insertions(+), 192 deletions(-) create mode 100644 docs/tasks/manual-tests/24-6.md create mode 100644 tests/Feature/Design/BookingPageRedesignTest.php diff --git a/docs/tasks/24-6.md b/docs/tasks/24-6.md index 3eae8bc..ea9ec44 100644 --- a/docs/tasks/24-6.md +++ b/docs/tasks/24-6.md @@ -1,7 +1,7 @@ --- id: 24-6 title: 24-6 Booking Page and Booking Success -status: Ready for Dev +status: QA Review date: 2026-10-31 parent: 24 --- diff --git a/docs/tasks/manual-tests/24-6.md b/docs/tasks/manual-tests/24-6.md new file mode 100644 index 0000000..086e13f --- /dev/null +++ b/docs/tasks/manual-tests/24-6.md @@ -0,0 +1,16 @@ +# Manual tests — 24-6 Booking Page and Booking Success + +Automated (passed 2026-10-31, `tests/Feature/Design/BookingPageRedesignTest.php`, 7 tests): `/booking` shows section head, forest card +(contact, static balance), month calendar inside `.book-r`; slot selection reveals fields and the full flow books exactly once and +redirects; every validation error renders under its field; logged-in client sees the message and no form; calendar days carry the +hook classes with `wire:click` intact; CSS scoped; `/booking/success` shows balance, title, message and Home with no `data-flux-` +output in `
`. Whole Design test directory (70 tests), `tests/Feature/Client` and `GuestBookingTest` run (1 pre-existing +stale-copy failure there). UI loop: no horizontal scroll at 390/768/1440 on both pages. + +Flagged, not changed (spec): `/booking` has no `

` (the section-head component, per spec, renders the title as an `

`); UI loop reports h1Count 0 there. One added key `site.captcha_refresh` (aria-label of the captcha refresh button, which previously had none). + +- [ ] `/booking` as a guest: forest card + month calendar; today (or any open day) has a gold dot, past days are dim, picking a day then a time reveals the form. +- [ ] Complete a booking through this page (confirm step, Confirm): arrives at `/booking/success`; cancel the test booking afterwards. +- [ ] Wrong captcha / short summary / missing field: each error appears under its field. +- [ ] Logged in as a client: `/booking` shows the logged-in message and link, no form; `/client/consultations/book` calendar looks as before (not restyled). +- [ ] `/booking/success`: balance, title, message, Home button; 390px no sideways scroll. diff --git a/docs/tasks/statuses.md b/docs/tasks/statuses.md index d4b7f59..dfd18be 100644 --- a/docs/tasks/statuses.md +++ b/docs/tasks/statuses.md @@ -142,7 +142,7 @@ | 24-3 | 24-3 Signature Balance Component | QA Review | | 24-4 | 24-4 Home Page Sections (Hero to Latest Articles) | QA Review | | 24-5 | 24-5 Home Booking Card (Compact Booking and Day Strip) | QA Review | -| 24-6 | 24-6 Booking Page and Booking Success | Ready for Dev | +| 24-6 | 24-6 Booking Page and Booking Success | QA Review | | 24-7 | 24-7 About Page | Ready for Dev | | 24-8 | 24-8 Articles and Legal Pages | Ready for Dev | | 24-9 | 24-9 Auth Pages Type and Controls | Ready for Dev | diff --git a/lang/ar/site.php b/lang/ar/site.php index ce89a85..739df20 100644 --- a/lang/ar/site.php +++ b/lang/ar/site.php @@ -71,4 +71,5 @@ return [ 'booking_time' => 'الوقت', 'booking_submit' => 'اطلب هذا الموعد', 'booking_none' => 'لا توجد مواعيد متاحة حالياً', + 'captcha_refresh' => 'سؤال جديد', ]; diff --git a/lang/en/site.php b/lang/en/site.php index 92e616f..8760e65 100644 --- a/lang/en/site.php +++ b/lang/en/site.php @@ -71,4 +71,5 @@ return [ 'booking_time' => 'Time', 'booking_submit' => 'Request this appointment', 'booking_none' => 'No times are available at the moment', + 'captcha_refresh' => 'New question', ]; diff --git a/resources/css/public.css b/resources/css/public.css index 536e230..c7abef2 100644 --- a/resources/css/public.css +++ b/resources/css/public.css @@ -314,6 +314,24 @@ body.site .book-r .btn+.btn{margin-top:10px} body.site .f2{grid-template-columns:1fr} } +/* ---------- Booking page and success (24-6) ---------- */ +body.site .book-r .picked{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;padding:14px 18px;margin-bottom:18px;border-radius:14px;background:#fff;border:1px solid var(--line)} +body.site .book-r .picked b{display:block;font-family:var(--display);font-size:1.15rem} +body.site .book-r .picked span{display:block;font-size:.92rem;color:var(--muted)} +body.site .book-r .picked .btn{width:auto;margin:0;min-height:44px;padding:8px 18px} +body.site .book-r .cap-row{display:flex;gap:10px;align-items:center} +body.site .book-r .cap-row .refresh{width:auto;margin:0;min-height:48px;padding:0 16px} +body.site .book-r .site-cal-nav{min-width:44px;min-height:44px;border-radius:999px;border:1.5px solid var(--line);background:#fff;color:var(--ink)} +body.site .book-r .site-cal-day{min-width:44px;min-height:44px;border-radius:10px;position:relative;font-weight:500} +body.site .book-r .site-cal-day.is-available{background:#fff;color:var(--ink);box-shadow:inset 0 0 0 1px var(--line)} +body.site .book-r .site-cal-day.is-available::after{content:"";position:absolute;inset-inline:0;bottom:4px;margin-inline:auto;width:6px;height:6px;border-radius:50%;background:var(--gold)} +body.site .book-r .site-cal-day.is-available:hover{box-shadow:inset 0 0 0 1.5px var(--gold)} +body.site .book-r .site-cal-day.is-selected{background:var(--forest);color:var(--cream);box-shadow:none} +body.site .book-r .site-cal-day.is-unavailable{background:transparent;color:var(--muted);opacity:.5;cursor:default} +body.site .success-card{max-width:560px;margin-inline:auto;background:var(--cream);border:1px solid var(--line);border-radius:20px;box-shadow:var(--shadow-2);padding:48px 40px;text-align:center} +body.site .success-card h1{font-size:clamp(2rem,3.4vw,2.6rem);margin-bottom:12px} +body.site .success-card p{color:var(--muted);margin:0 0 26px} + /* ---------- Reveal (hidden only when JavaScript runs) ---------- */ body.site.js [data-reveal]{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform .9s var(--ease)} body.site.js [data-reveal].in{opacity:1;transform:none} diff --git a/resources/views/livewire/availability-calendar.blade.php b/resources/views/livewire/availability-calendar.blade.php index b7636c2..41255a8 100644 --- a/resources/views/livewire/availability-calendar.blade.php +++ b/resources/views/livewire/availability-calendar.blade.php @@ -239,13 +239,13 @@ new class extends Component @else
- + {{ $monthName }} - +
@@ -268,7 +268,10 @@ new class extends Component - - - - {{ __('booking.continue') }} - {{ __('common.loading') }} - - - @else - {{-- Confirmation Step --}} - - {{ __('booking.confirm_booking') }} -

{{ __('booking.confirm_message') }}

+ @if(!$showConfirmation) +
+ + + @error('guestName')

{{ $message }}

@enderror +
+
+ + + @error('guestEmail')

{{ $message }}

@enderror +
+
+ + + @error('guestPhone')

{{ $message }}

@enderror +
+
+ + +

{{ __('booking.problem_summary_help') }}

+ @error('problemSummary')

{{ $message }}

@enderror +
+
+ +
+ + +
+ @error('captchaAnswer')

{{ $message }}

@enderror +
+ + @else +

{{ __('booking.confirm_booking') }}

+

{{ __('booking.confirm_message') }}

+
+
{{ __('booking.guest_name') }}
{{ $guestName }}
+
{{ __('booking.guest_email') }}
{{ $guestEmail }}
+
{{ __('booking.guest_phone') }}
{{ $guestPhone }}
+
{{ __('booking.date') }}
{{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}
+
{{ __('booking.time') }}
{{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}
+
{{ __('booking.duration') }}
45 {{ __('common.minutes') }}
+
{{ __('booking.problem_summary') }}
{{ $problemSummary }}
+
+ @error('selectedTime') + + @enderror + + + @endif + @endif + @endif + + -
-

{{ __('booking.guest_name') }}: {{ $guestName }}

-

{{ __('booking.guest_email') }}: {{ $guestEmail }}

-

{{ __('booking.guest_phone') }}: {{ $guestPhone }}

-

{{ __('booking.date') }}: - {{ \Carbon\Carbon::parse($selectedDate)->translatedFormat('l, d M Y') }}

-

{{ __('booking.time') }}: - {{ \Carbon\Carbon::parse($selectedTime)->format('g:i A') }}

-

{{ __('booking.duration') }}: 45 {{ __('common.minutes') }}

-
- -
-

{{ __('booking.problem_summary') }}:

-

{{ $problemSummary }}

-
-
- -
- - {{ __('common.back') }} - - - {{ __('booking.submit_request') }} - {{ __('common.submitting') }} - -
- - @error('selectedTime') - - {{ $message }} - - @enderror - @endif - @endif - @endif - - + + + @endif diff --git a/tests/Feature/Design/BookingPageRedesignTest.php b/tests/Feature/Design/BookingPageRedesignTest.php new file mode 100644 index 0000000..1b966f8 --- /dev/null +++ b/tests/Feature/Design/BookingPageRedesignTest.php @@ -0,0 +1,88 @@ +create(['day_of_week' => $dow, 'is_active' => true, 'start_time' => '09:00', 'end_time' => '13:00']); + } +}); + +test('booking page shows the section head, the forest card and the month calendar', function () { + $html = $this->get('/booking')->assertOk()->getContent(); + + expect($html) + ->toContain('class="sec-head"') + ->toContain('class="book"') + ->toContain('book-l on-dark') + ->toContain('data-balance="static"') + ->toContain('tel:+970599353502') + ->toContain('book-r') + ->toContain('calendar-grid') + ->not->toContain('class="days"'); + + expect(strpos($html, 'book-r'))->toBeLessThan(strpos($html, 'calendar-grid')); +}); + +test('selecting a slot reveals the fields and the full flow books once', function () { + $date = now()->addDay()->format('Y-m-d'); + + $c = Volt::test('pages.booking') + ->call('selectSlot', $date, '09:00') + ->assertSee('wire:model="guestName"', false) + ->assertSee('wire:model="guestEmail"', false) + ->assertSee('wire:model="guestPhone"', false) + ->assertSee('wire:model="problemSummary"', false) + ->assertSee('wire:model="captchaAnswer"', false) + ->set('guestName', 'Test Guest')->set('guestEmail', 'g@example.com')->set('guestPhone', '+970599000000') + ->set('problemSummary', 'A sufficiently long description of my case.') + ->set('captchaAnswer', session('captcha_answer')) + ->call('showConfirm')->assertSet('showConfirmation', true) + ->call('submit')->assertRedirect(route('booking.success')); + + expect(Consultation::count())->toBe(1); +}); + +test('validation errors render under their fields', function () { + $date = now()->addDay()->format('Y-m-d'); + + Volt::test('pages.booking')->call('selectSlot', $date, '09:00') + ->call('showConfirm') + ->assertHasErrors(['guestName', 'guestEmail', 'guestPhone', 'problemSummary', 'captchaAnswer']) + ->assertSeeHtml('class="err"'); +}); + +test('logged in clients see the message and no form on the booking page', function () { + $html = $this->actingAs(User::factory()->client()->create())->get('/booking')->assertOk()->getContent(); + + expect($html)->toContain(__('booking.logged_in_title'))->toContain(route('client.consultations.book')) + ->not->toContain('wire:model="guestName"'); +}); + +test('month calendar days carry hook classes without changing behaviour', function () { + $html = Volt::test('availability-calendar')->html(); + + expect($html)->toContain('site-cal-day')->toContain('is-available')->toContain('site-cal-nav')->toContain('wire:click="selectDate('); +}); + +test('calendar css is scoped to the public site', function () { + $css = file_get_contents(resource_path('css/public.css')); + expect($css)->toContain('body.site .book-r .site-cal-day')->toContain('body.site .book-r .site-cal-day.is-selected'); +}); + +test('success page shows the balance, title, message and home button without flux output', function () { + $html = $this->get('/booking/success')->assertOk()->getContent(); + + expect($html)->toContain('data-balance="static"')->toContain(__('booking.success_title'))->toContain(__('booking.success_message')) + ->toContain(__('site.nav_home')); + + preg_match('~~s', $html, $main); + expect($main[0])->not->toContain('data-flux-'); +}); diff --git a/tests/Feature/Design/HomeBookingCardTest.php b/tests/Feature/Design/HomeBookingCardTest.php index 8093486..8d90cd8 100644 --- a/tests/Feature/Design/HomeBookingCardTest.php +++ b/tests/Feature/Design/HomeBookingCardTest.php @@ -145,7 +145,7 @@ test('home page has the booking card with the finale balance', function () { test('the booking page itself is not compact', function () { $html = $this->get('/booking')->assertOk()->getContent(); - expect($html)->not->toContain('book-r')->toContain('calendar-grid'); + expect($html)->not->toContain('class="days"')->not->toContain('wire:submit="showConfirm"')->toContain('calendar-grid'); }); test('booking card keys exist in both languages', function () {