Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
9.2 KiB
id, title, status, date, parent
| id | title | status | date | parent |
|---|---|---|---|---|
| 24-5 | 24-5 Home Booking Card (Compact Booking and Day Strip) | QA Review | 2026-10-31 | 24 |
Description
Add section 8 of the home page: a forest booking card that embeds the existing guest-booking
component in a one-screen "compact" mode, with a 14-day strip of bookable days. The booking rules
must not change: name, email and phone required; case summary required, minimum 20 characters;
the math captcha (App\Services\CaptchaService); one booking per email per day; slots from
App\Services\AvailabilityService; the existing confirm step (showConfirmation); and the
existing submit() that redirects to route('booking.success'). Logged-in visitors see the
existing logged-in message instead of the form.
1. Availability calendar — resources/views/livewire/availability-calendar.blade.php
- Add
public string $variant = 'month';(settable from the tag:variant="strip"). The defaultmonthmust render exactly as today, becauseresources/views/livewire/client/consultations/book.blade.phpalso uses this component. - When
$variant === 'strip':- Compute
$stripDays: starting today, step one day at a time up to 60 days ahead and collect the first 14 dates for whichAvailabilityService::getDateStatus($date)returns'available'or'partial'(the same two valuesselectDate()accepts today), each with itsgetAvailableSlots($date). - On mount, select the first strip day (
$selectedDate) and load its slots with the existingloadAvailableSlots(). - Render a day strip:
<div class="days" role="group" aria-label="{{ __('site.booking_day') }}">of<button type="button" class="day" aria-pressed="true|false" wire:click="selectDate('Y-m-d')">with the weekday name (translatedFormat('l')) and<b>the day number. Then a slot row:<div class="slots" role="group" aria-label="{{ __('site.booking_time') }}">of<button type="button" class="slot" dir="ltr" aria-pressed=…>per slot, clicking calls the existing$parent.selectSlot('<date>', '<slot>'), and the button for the parent's currentselectedTimeon the selected date is pressed. Slot labels use the same time format as the month view. - No strip day available within 60 days → show
site.booking_none(ar «لا توجد مواعيد متاحة حالياً»; en "No times are available at the moment") in place of the strip and slots.
- Compute
- CSS (in
resources/css/public.css, scopedbody.site; mockupdocs/ux-mockup.htmllines 256–265):.daysflex, gap 8px,overflow-x:auto,scrollbar-width:none;.day66×72px min, radius 14px,1.5px solid rgba(45,54,36,.15), white, .78rem muted,<b>display font 1.35rem ink; hover gold border and lift 2px;[aria-pressed=true]forest background,--gold-lttext, cream number, shadow0 10px 22px -10px rgba(45,54,36,.8)..slotsflex-wrap gap 8px;.slotmin-height 44px, padding 8px 16px, pill, same border, white; hover gold border;[aria-pressed=true]gold fill, gold border, ink text.
2. Booking component — resources/views/livewire/pages/booking.blade.php
- Add
public bool $compact = false;and accept it inmount(bool $compact = false)(keep the existing mount logic). The#[Layout]attribute stays; it is ignored when the component is embedded. - When
$compactis true, render (instead of the page markup) a<form class="book-r" wire:submit="showConfirm">(mockup CSS lines 247–267: cream, padding 44px 44px 40px, margin 12px, radius 20px) containing, in order:.f2two-column row: full name (guestName,autocomplete="name") and phone (guestPhone,type="tel" dir="ltr" autocomplete="tel"), labels from the existingbooking.*keys with*;- email (
guestEmail,type="email" dir="ltr" autocomplete="email"); <livewire:availability-calendar variant="strip" />— the strip variant itself renders a visible labelsite.booking_day(.85rem, weight 600, margin-bottom 6px) above.daysandsite.booking_timeabove.slots;- case summary textarea (
problemSummary, rows 3, labelbooking.problem_summary+*, help textbooking.problem_summary_help); - the captcha question and input (
captchaAnswer,inputmode="numeric" dir="ltr", max-width 140px) exactly as the page version renders it; <button type="submit" class="btn btn-gold">site.booking_submit, full width;<p class="note">booking.limit_message. Validation errors render under each field as in the page version. Submitting without a selected slot shows the existingselectedTimeerror.
- When
$compactand$showConfirmationare true, the same.book-rpanel shows the existing confirmation summary (date, time, name, email, phone, summary) with the existing Confirm (submit) and Back ($set('showConfirmation', false)) actions, styledbtn-goldandbtn-ghost. - When
$compactand the visitor is logged in, the panel shows the existingbooking.logged_in_title,booking.logged_in_messageand thebooking.go_to_dashboardlink (route('client.consultations.book')) styled asbtn-gold. - Inputs and textarea (scoped
body.site .book-r): min-height 48px, padding 12px 16px, radius 12px,1.5px solid rgba(45,54,36,.18), white,font:400 1rem var(--body); focus border--goldandbox-shadow:0 0 0 4px rgba(166,137,102,.2); labels .85rem weight 600. Flux inputs may be replaced by plain inputs in compact mode, keeping the samewire:modelnames. - The non-compact (page) rendering is unchanged by this task.
3. Home page — resources/views/livewire/pages/home.blade.php
Replace {{-- booking card: task 24-5 --}} with
<section id="book"><div class="wrap"><div class="book" data-reveal> (mockup CSS lines 236–246:
forest, radius 28px, two columns 1fr 1.1fr, one column ≤1100px, shadow
0 50px 90px -40px rgba(30,34,25,.8)), containing:
<div class="book-l on-dark">(padding 56px 52px; 40px 24px ≤760px):.eyebrowsite.art_booking(colour--gold-lt),<h2>site.booking_title(cream),<p>site.booking_lede(--gold-lt),.contactlist — phone linktel:+970599353502with<span dir="ltr">{{ __('footer.phone') }}</span>, email linkmailto:footer.email, and the addressfooter.address, each with the 20px gold line icon from mockup lines 517–519 — and<x-site.balance mode="finale" style="width:190px;height:190px;margin-top:22px" />.<livewire:pages.booking :compact="true" />.
Language keys — add to lang/ar/site.php and lang/en/site.php
| Key | ar | en |
|---|---|---|
art_booking |
المادة ٠٦ — الاستشارة | Article 06 — Consultation |
booking_title |
احجز استشارة | Book a consultation |
booking_lede |
اختر يوماً ووقتاً متاحَين، وأخبرنا باختصار عن قضيتك. نراجع كل طلب ونؤكّد الموعد بالبريد الإلكتروني — دون الحاجة إلى حساب. | Choose an available day and time and tell us briefly about your case. We review every request and confirm by email — no account needed. |
booking_day |
اليوم | Day |
booking_time |
الوقت | Time |
booking_submit |
اطلب هذا الموعد | Request this appointment |
booking_none |
لا توجد مواعيد متاحة حالياً | No times are available at the moment |
Files
- Modify:
resources/views/livewire/availability-calendar.blade.php,resources/views/livewire/pages/booking.blade.php,resources/views/livewire/pages/home.blade.php,resources/css/public.css,lang/ar/site.php,lang/en/site.php. - Do not modify
app/Services/AvailabilityService.php,app/Services/CaptchaService.php,resources/views/livewire/client/consultations/book.blade.php, or any migration.
Acceptance Criteria
/shows the booking card with idbook: the forest panel (title, lede, phone, email, address, finale balance) and the cream form with fields in the order name, phone, email, day strip, slots, summary, captcha, submit, one-per-day note.- The day strip lists up to 14 dates, all of which have at least one slot in
AvailabilityService::getAvailableSlots(); the first is pressed on load and its slots show; clicking another day loads that day's slots. - A guest who fills valid data, picks a slot, answers the captcha and confirms creates exactly
one guest consultation (same attributes as a booking made on
/booking) and lands on/booking/success. - A summary of fewer than 20 characters, a wrong captcha answer, a missing slot, or a second
booking with the same email on the same day each produce the same error as on
/booking, and no consultation is created. - Logged in as a client,
/shows the logged-in message with a link to/client/consultations/bookand no form. /bookingand/client/consultations/bookrender the month calendar as before this task (variantdefaults tomonth).- The finale balance is tilted at the top of
/and level when scrolled to the booking card at the end of the page. - At 390px the card is one column, the day strip scrolls horizontally inside the card, and the page has no horizontal scroll.
Sub-tasks
None.