# Parity — Shop · Booking detail + status timeline + actions

Demo (canonical) refs are under `/private/tmp/Navagoo_MI_dev/navagoo-app/src`.
Ours refs are under the repo root (`frontend/`, `common/`).

| Demo behavior | Demo ref | Our ref (file:line) | Status | Note |
|---|---|---|---|---|
| Single shared detail body reused by drawer + calendar modal | `BookingDetailBody.tsx:28` | `frontend/views/booking/_detail_modal.php` + `view.php:32` + `BookingController.php:430` | done | Same one-body, two-hosts pattern (+ full page). |
| 3-column layout: details · services · timeline+actions | `BookingDetailBody.tsx:75` | `_detail_modal.php:112` | done | Demo container-query; ours `md:` breakpoint. Payment summary relocated to middle col. |
| Booking details rows (appt, booked-on, specialist, mobile, payment method) | `BookingDetailBody.tsx:83-118` | `_detail_modal.php:118-131` | partial | No specialist avatar in row; Mobile always shown; no **Group** row. |
| Payment rows: collected online / in-person / tip / outstanding / refund | `BookingDetailBody.tsx:120-165` | `_detail_modal.php:167-189` | missing | Ours shows Subtotal/Discount/VAT/Total only. |
| Services list (thumb, name, duration, price) + total | `BookingDetailBody.tsx:170-211` | `_detail_modal.php:135-165` | done | Minus per-service image (scissors only). Empty state present. |
| Status timeline rail (done/current/future, pulse) | `BookingTimeline.tsx` | `frontend/views/booking/_timeline.php` | done | Structure + dot/connector/pulse mirrored. |
| Timeline node timestamps + detail sub-lines | `BookingTimeline.tsx:53-124` | `_timeline.php:118-129` | missing | Ours shows label + LIVE pill only — no times, no "Booked/Completed/Cancelled by/refund" lines. |
| "Now" live pill on current node | `BookingTimeline.tsx:190-197` | `_timeline.php:124-127` | partial | Ours labels it **"LIVE"** (wording differs). |
| Allowed transitions from one source of truth | `lib/status.ts:64` | `_detail_modal.php:197` + `BookingController.php:453` | partial | Behaviour ~matches default; duplicated, not centralized, not admin-configurable. |
| Start Service (→ in_progress) action | `BookingDetailBody.tsx:229,250` | `_detail_modal.php:199-204` | done | Teal accent button → status 4. |
| Mark completed action | `BookingDetailBody.tsx:223` | `_detail_modal.php:218-223` | partial | Present, but no collect-balance gate (see below). |
| Complete hard-gate → collect outstanding first | `BookingDetailBody.tsx:60`, `modals.tsx:73,978` | — | missing | Ours posts straight to complete; server auto-assumes full payment (`BookingController.php:463`). |
| In-store collect (cash/card + card tip) | `CollectPaymentBody modals.tsx:898` | — | missing | No collect UI in this area. |
| `outstandingBalance` computation | `finance.ts:404` | — | missing | Not computed/surfaced in the body. |
| `collectionStatus` chip | `finance.ts:412`, `modals.tsx:1067` | — | missing | No Collection chip in modal header. |
| Settlement chip | `modals.tsx:1071` | — | missing | No Settlement chip. |
| No-show action | `status.ts:52`, `BookingDetailBody.tsx:224` | `_detail_modal.php:226-231` | partial | Present but allowed from in_progress too (demo: scheduled only). |
| No-show grace window (disable + tooltip / hide) | `status.ts:93`, `BookingDetailBody.tsx:242`, `modals.tsx:64` | — | missing | No time gate; always enabled; server accepts anytime. |
| Cancel action gated by workflow | `status.ts:52`, `BookingDetailBody.tsx:52` | `_detail_modal.php:232-236` | done | Shown for non-terminal; completed cannot cancel (`BookingController.php:694`). |
| Cancel modal: by-whom + refund-zone preview + refund amount | `modals.tsx:160-258` | — | missing | Ours is a bare `confirm()` + empty reason (`view.php:75`, `BookingController.php:686`). |
| Refund zone from policy + time | `finance.ts:318` | `common/models/Booking.php:45` (`getRefundTypeForNow`) | partial | Engine exists but NOT wired into the cancel flow/UI. |
| Customer refund computation | `finance.ts:326` | — | missing | Not computed in cancel flow. |
| Marketing-fee reversal disposition shown | `modals.tsx:240-247` | — | missing | Not surfaced. |
| Reschedule capability gated to scheduled | `status.ts:78` | `_detail_modal.php:206` | done | Shown for SCHEDULED|ACCEPTED. |
| Reschedule slot picker | `modals.tsx:263`, `SlotPicker.tsx` | `_reschedule_modal.php` + `BookingController.php:506,615` | done | Real free-slot day picker. |
| Reschedule placement/conflict guard (4 reasons) | `modals.tsx:285-303` | `BookingController.php:569` (`checkPlacement`) | done | Same overlap/time-off/availability/can-perform reasons, server-enforced. |
| Drawer header: classification + new/returning badges | `BookingDrawer.tsx:46-49` | — | missing | Ours header has status pill only. |
| Row actions menu (per-row) mirroring detail actions | `modals.tsx:43` | — | partial | Our list opens full detail; no compact per-row action menu equivalent here. |
| Shop-scoped authorization on every action | (single-tenant store) | `BookingController.php:433,450,538,698` | done | `checkOwnership` everywhere — ours ahead (real auth). |
| Bilingual + RTL detail UI | (en-only) | `_detail_modal.php`, `_timeline.php` (logical props, `Yii::t`) | done | Ours ahead; verify new strings land in ar+en. |

## Summary of biggest gaps (priority)

1. **Complete-with-balance hard gate + in-store collect (cash/card/tip)** — entirely missing (R6–R8).
2. **Cancel modal with by-whom + refund-zone preview + refund amount** — missing; engine exists but unwired (R10–R13).
3. **No-show grace window + scheduled-only restriction** — missing/divergent (R4, R5).
4. **Payment rows (collected/outstanding/tip/refund) + Collection/Settlement chips** — missing.
5. **Timeline timestamps & detail sub-lines** — missing (structure done, content thin).
6. Minor: specialist avatar / service thumbnail / Group row; "LIVE"→"Now" wording.

Area score: 52%

## Verified verdict (adversarial)

Re-read BOTH demo and our refs for every "done"/high claim. Corrections below;
unlisted "missing"/"partial" rows are confirmed as the analyst stated.

| Feature | Analyst | Verified | Evidence |
|---|---|---|---|
| Reschedule capability gated to scheduled | done | **partial** | Demo `DEFAULT_RESCHEDULE_STATUSES = ['scheduled']` — scheduled ONLY (`lib/status.ts:78`). Ours shows Reschedule for SCHEDULED **and** ACCEPTED (`_detail_modal.php:199,206`). Divergent allow-list. |
| Cancel gated by workflow / completed cannot cancel | done | **partial** | Server excludes COMPLETED (`BookingController.php:694`) ✓. But demo `DEFAULT_TRANSITIONS.in_progress = ['completed']` → NO `cancelled`, so demo does not allow cancel from in_progress. Ours offers Cancel for in_progress (`_detail_modal.php:226`). UI over-permits vs demo. |
| Allowed transitions / server enforcement | partial | **partial (confirmed, weaker)** | `actionTransition` whitelists only the *target* (`BookingController.php:453`), never the *from*. A SCHEDULED booking can POST status=3 (complete), skipping in_progress; no_show accepted from any state. No from-status workflow guard server-side. |
| Start Service (→in_progress) | done | **done (note)** | Behavior matches (`_detail_modal.php:200`). Token drift: ours `bg-accent-500`; demo `var(--color-accent-400)` (`BookingDetailBody.tsx:238`). Cosmetic. |
| Status timeline rail (done/current/future, pulse) | done | **done (note)** | Dots/connectors/pulse mirrored (`_timeline.php` vs `BookingTimeline.tsx`). Wrapper differs: demo `<ol mx-auto w-fit>` centered + `left-[13.5px]/bottom-1`; ours `<ol relative>` + `start-[13px]/bottom-0`. Cosmetic. |
| "Now" live pill wording | partial→ (n/a sep row) | **partial (confirmed)** | Ours `LIVE` (`_timeline.php:126`), demo `Now` (`BookingTimeline.tsx:196`). |
| Reschedule slot picker + conflict guard | done | **done** | `_reschedule_modal.php` present; `moveBooking()` runs `checkPlacement` + slot rebuild in a transaction (`BookingController.php:569,577`). Confirmed. |
| Shop-scoped authorization | done | **done** | `checkOwnership` on detail/transition/move/cancel (`BookingController.php:433,450,538,698`). Confirmed; ahead of single-tenant demo. |
| Bilingual + RTL | done | **done** | New strings land in BOTH `ar/frontend.php` (Start service, No-show, Reschedule, Cancel booking, LIVE, Specialist, Booking details, Services booked, Payment summary, In Progress, No services) and `en/frontend.php`. Logical props used. Confirmed. |
| Single shared body / 3-col layout / services list | done | **done** | One-body two-hosts confirmed (`_detail_modal.php`, `view.php:32`, `actionDetailPartial:430`). Confirmed. |

### Additional confirmed gaps (analyst correct)
- Payment rows (collected online/in-person/tip/outstanding/refund) — missing; ours shows Subtotal/Discount/VAT/Total (`_detail_modal.php:167-189`) vs `BookingDetailBody.tsx:120-165`.
- Complete hard-gate → collect outstanding first — missing; `changeStatus` collect gate (`BookingDetailBody.tsx:60`) absent. Ours auto-creates Payment at full `total_amount` (`BookingController.php:469-483`).
- In-store collect, `outstandingBalance`/`collectionStatus` (`lib/finance.ts:404,412`), Settlement chip, Cancel modal (by-whom + refund-zone preview + refund amount, `modals.tsx:160-258`), refund-zone wiring (engine `Booking.php:45` unwired), marketing-fee disposition, timeline timestamps/sub-lines (`BookingTimeline.tsx:53-124`), drawer classification + new/returning badges (`BookingDrawer.tsx:46-49`) — all missing/partial as stated.

### Adjusted area score: **46%**
Two "done" rows downgraded to partial (reschedule allow-list divergence; cancel-from-in_progress over-permission), plus confirmed weak server-side transition enforcement. Net effect lowers the area from 52% to ~46%.
