Files
libra/docs/tasks/24-5.md
T

9.2 KiB
Raw Blame History

id, title, status, date, parent
id title status date parent
24-5 24-5 Home Booking Card (Compact Booking and Day Strip) Ready for Dev 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 default month must render exactly as today, because resources/views/livewire/client/consultations/book.blade.php also 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 which AvailabilityService::getDateStatus($date) returns 'available' or 'partial' (the same two values selectDate() accepts today), each with its getAvailableSlots($date).
    • On mount, select the first strip day ($selectedDate) and load its slots with the existing loadAvailableSlots().
    • 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 current selectedTime on 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.
  • CSS (in resources/css/public.css, scoped body.site; mockup docs/ux-mockup.html lines 256–265): .days flex, gap 8px, overflow-x:auto, scrollbar-width:none; .day 66×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-lt text, cream number, shadow 0 10px 22px -10px rgba(45,54,36,.8). .slots flex-wrap gap 8px; .slot min-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 in mount(bool $compact = false) (keep the existing mount logic). The #[Layout] attribute stays; it is ignored when the component is embedded.
  • When $compact is 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:
    1. .f2 two-column row: full name (guestName, autocomplete="name") and phone (guestPhone, type="tel" dir="ltr" autocomplete="tel"), labels from the existing booking.* keys with *;
    2. email (guestEmail, type="email" dir="ltr" autocomplete="email");
    3. <livewire:availability-calendar variant="strip" /> — the strip variant itself renders a visible label site.booking_day (.85rem, weight 600, margin-bottom 6px) above .days and site.booking_time above .slots;
    4. case summary textarea (problemSummary, rows 3, label booking.problem_summary + *, help text booking.problem_summary_help);
    5. the captcha question and input (captchaAnswer, inputmode="numeric" dir="ltr", max-width 140px) exactly as the page version renders it;
    6. <button type="submit" class="btn btn-gold"> site.booking_submit, full width;
    7. <p class="note"> booking.limit_message. Validation errors render under each field as in the page version. Submitting without a selected slot shows the existing selectedTime error.
  • When $compact and $showConfirmation are true, the same .book-r panel shows the existing confirmation summary (date, time, name, email, phone, summary) with the existing Confirm (submit) and Back ($set('showConfirmation', false)) actions, styled btn-gold and btn-ghost.
  • When $compact and the visitor is logged in, the panel shows the existing booking.logged_in_title, booking.logged_in_message and the booking.go_to_dashboard link (route('client.consultations.book')) styled as btn-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 --gold and box-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 same wire:model names.
  • 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): .eyebrow site.art_booking (colour --gold-lt), <h2> site.booking_title (cream), <p> site.booking_lede (--gold-lt), .contact list — phone link tel:+970599353502 with <span dir="ltr">{{ __('footer.phone') }}</span>, email link mailto: footer.email, and the address footer.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 id book: 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/book and no form.
  • /booking and /client/consultations/book render the month calendar as before this task (variant defaults to month).
  • 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.