# Shop · Specialist time-off — UI parity

## Screens / components

| Demo component | Our view |
|---|---|
| `NewTimeOffModal.tsx` ("Block time" modal) | `frontend/views/booking/_timeoff_modal.php` |
| `TimeOffBlock.tsx` (calendar block) | inline in `frontend/views/booking/_calendar_day.php:133-161` |
| Open trigger (DayCalendar toolbar `setTimeOffOpen`) | `frontend/views/booking/calendar.php:85-87` ("Block time" button, `data-modal-open="timeoff"`) |
| Render in column (`SpecialistColumn.tsx:194-211`) | `_calendar_day.php` per-column loop |

## "Block time" modal — field-by-field

| Field | Demo | Ours | Status |
|---|---|---|---|
| Title / subtitle | "Block time" + shop commercial name (`NewTimeOffModal.tsx:84-85`) | "Block time" only — **no shop-name subtitle** (`_timeoff_modal.php:33`) | partial |
| Applies-to segmented | Segmented A specialist / Whole shop (`:99-107`) | Segmented buttons + hidden `scope` input (`_timeoff_modal.php:43-50`) | done |
| Specialist select | shown only when scope=specialist, active specialists of shop (`:109-119`) | shown/hidden by JS `applyScope` (`_timeoff_modal.php:53-60,141-142`); options come from `$agents` controller list | done |
| Date | `type=date`, default `defaultDate` (`:122-124`) | `type=date`, default `$date` (`:64-67`) | done |
| Reason | Select, 6 reasons (`:125-133`) | Select, same 6 (`_timeoff_modal.php:69-78`) | done |
| All-day toggle | Toggle "All day (open → close)" (`:136-139`) | checkbox in same pill row (`:81-85`) — checkbox vs styled toggle, cosmetic | done |
| From / To | shown when !allDay; **"Ends next day (+1d)" hint** when end<=start (`:141-150`) | shown/hidden by JS; **no overnight hint** (`_timeoff_modal.php:88-97`) | partial |
| Note | optional, placeholder "e.g. Maintenance" (`:152-154`) | same (`:99-103`) | done |
| Footer | Cancel + "Block time" (disabled when invalid) (`:86-95`) | Cancel + "Block time" — **button never disabled**, no live validity (`:107-113`) | partial |

## Calendar block

| Aspect | Demo (`TimeOffBlock.tsx`) | Ours (`_calendar_day.php:133-161`) | Status |
|---|---|---|---|
| Hatch fill | 45° repeating gradient, slate (`:31-33`) | 45° repeating gradient, slate (`:145`) | done |
| Label | `off.note \|\| titleCase(off.reason)` (`:43`) | reason label only (`getReasonLabel`), note goes to `title` tooltip (`:149,146`) | partial |
| Time label | shown when height ≥ ~34px (`compact` at `:34,45-47`) | shown when `$oHeight >= 34` (`:142,150-152`) | done |
| Hover remove X | opacity-0 → group-hover (`:49-59`) | identical group-hover X (`:154-158`) | done |
| Min height | `MIN_BLOCK_PX` clamp (`:26`) | `max(...,22.0)` (`:138`) | done |

## States

- **Empty:** no time-off rows → no blocks rendered. Both sides simply render nothing. done.
- **Error:** demo toasts success only; failure is store-local (never fails). Ours: controller returns `{success:false,message}`; client `alert()`s `json.message || "Could not block that time."` (`_timeoff_modal.php:201-206`). Ours actually surfaces server errors — acceptable.
- **RTL:** demo uses logical props (`inset-x-0`, `start`/`end`); ours uses `start-0 end-0`, `border-inline-start`, `[direction:rtl]` patterns consistent with the aurora reskin. Arabic strings present (`common/messages/ar/frontend.php`). done.
- **Success feedback divergence:** demo shows a toast and keeps SPA state; ours does `location.reload()` after a successful POST (`_timeoff_modal.php:202`) — full page reload instead of optimistic insert. Functional, heavier UX. partial.

## Concrete UI gaps
1. No shop-name subtitle in the modal header.
2. "Block time" submit button is always enabled — no client-side validity (empty date / equal times can be submitted).
3. No "Ends next day (+1d)" overnight hint on the From field.
4. Calendar block label ignores `note` (demo prefers note over reason); ours always shows reason and buries note in the tooltip.
5. Success path hard-reloads the page rather than inserting the block in place.
