# Reschedule / Reassign + Slot Picker — UI Parity

## Demo screens/components
- **`SlotPicker.tsx`** — the reusable free-slot picker. Structure:
  - **Book now** pill (top-right, above card), glowing (`animate-glow`) until
    selected; greys out + tooltip "No opening to start right now" when null
    (`SlotPicker.tsx:163-187`).
  - **Day navigator** — centered: prev arrow (disabled at today), a **tappable
    teal pill** (`bg-accent-400`) that calls `input.showPicker()` to open the
    native date calendar, next arrow; a `relativeDays` sublabel below
    (e.g. "Today", "in 3 days") (`:191-247`).
  - **Slot grid** — `grid-cols-3 sm:grid-cols-4`, `max-h-52` scroll. Active slot
    = brand fill; first slot tagged **"Earliest"** with a tiny uppercase badge
    (`:289-318`).
  - **Empty states** (`:255-280`): off-day → "{name} is off this day"; working
    but full → "No openings for a {duration} service"; plus a **"Next available ·
    {date} →"** jump button, or "No openings in the next 8 weeks."
  - **Book-now-only** state: "That's the only opening — use Book now above." (`:282-287`).
  - Not-ready state: "Pick a specialist and services to see open times." (`:251-253`).
- **`RescheduleModal`** (`modals.tsx:263-348`) — Modal with title `Reschedule {id}`,
  body = a `Field` wrapping `SlotPicker` (with `ignoreBookingId` + `defaultDayKey`),
  a **red conflict banner** (AlertTriangle + reason text) when placement fails,
  footer Cancel / Reschedule (Reschedule disabled until a valid slot is picked).
- **Reassign UI** = drag-and-drop across columns in the Day calendar
  (`DayCalendar.tsx` / `SpecialistColumn.tsx`), no modal.
- The Reschedule action surfaces from the booking detail/action sheet, gated on
  `canReschedule` (`modals.tsx:128`, `BookingDetailBody.tsx:53`).

## Our view — `frontend/views/booking/_reschedule_modal.php`
- Tailwind "aurora" modal (`data-modal-root="reschedule"`), opened
  programmatically from `[data-reschedule-open]` triggers.
- Header (title + subtitle "Pick a new open time for this booking."), body with
  a bordered card containing:
  - **Day navigator** — prev arrow (disabled at/before today), a **display-only**
    teal pill (`bg-accent-400`) with date label, next arrow. No `relativeDays`
    sublabel.
  - **Slot grid** — `grid-cols-3 sm:grid-cols-4`, `max-h-60` scroll; each slot a
    bordered button (no active/selected styling because picking immediately POSTs).
  - **Empty state** — "No open times this day." + `calendar-off` icon. Single
    message; does not distinguish off-day vs full.
  - **Loading** ("Loading…") and **error** ("Could not load open times.") states.
- Footer: Cancel only (no confirm — a slot click is the confirm).
- Bilingual: all strings via `Yii::t('frontend', …)`.

## Concrete UI gaps (demo → ours)
| Demo element | Our status |
|---|---|
| **Book now** glowing pill | Missing |
| **Earliest** badge on first slot | Missing |
| **Next available · {date} →** jump from empty day | Missing |
| Off-day vs "no openings for a {duration} service" distinction | Missing (one generic message) |
| Tappable date pill → native calendar (`showPicker`) | Missing (pill is display-only) |
| `relativeDays` sublabel ("Today", "in N days") | Missing |
| Selected-slot highlight + explicit confirm button | Missing (pick = immediate POST) |
| **Live conflict banner** before submit | Missing (conflict only via POST `alert`) |
| Not-ready "pick a specialist & services" state | N/A — modal always opens for a known booking |
| Reassign = drag across columns (calendar) | Present via `reassignUrl` drag wiring |

## RTL / i18n
- Demo is LTR-only (no RTL handling observed in these components).
- Ours uses `Yii::t('frontend', …)` so it inherits the app's `ar`/`en` direction
  handling. New strings introduced here (none yet) must land in both
  `common/messages/{ar,en}/frontend.php`. Existing modal strings should be
  verified present in `ar`.
