# UI — Logic / data-model area

This area is **logic + data model + selectors**, not a screen. It has no dedicated
UI of its own; it is the substrate every other screen reads. This file records the
UI-visible consequences of the data-model differences so the screen-level parity
agents can cross-reference.

## Demo: where the model surfaces in UI

- **Booking status chips / calendar tints** read `lib/status.ts`
  (`STATUS_COLOR`, `STATUS_LABEL`, `STATUS_ORDER`, `status.ts:10-43`) — ONE source of
  truth for 5 statuses: scheduled (purple), in_progress (amber), completed (teal),
  no_show (slate), cancelled (red).
- **Status changer / row-action menu** only offers moves allowed by
  `allowedNextStatuses` (`status.ts:64-69`) over the admin-editable
  `GlobalConfig.bookingTransitions`. Terminal statuses show no further actions.
- **Reschedule action + calendar drag-lock** gated by `canRescheduleStatus`
  (`status.ts:85-90`); default only `scheduled` is movable.
- **No-show button** disabled until `canMarkNoShow` (`status.ts:93-95`) — grace
  minutes after appointment start.
- **Shop dashboard KPI cards** are a direct render of `shopKpis` (`selectors.ts:324`):
  earned, revenue (all channels), net take-home, withdrawable, costs, in-grace badge,
  and an **in-store cash vs card** split.
- **Group-booking views** ("party" summaries) render `groupsForShop` (`selectors.ts:295`)
  — partySize, aggregate status (`'mixed'` when children differ), Σ value/revenue/
  outstanding, Collect-all.
- **Settlement state badge** per booking from `settlementStateFor` (pending / in
  transfer / settled, `selectors.ts:461`).

## Ours: equivalent surfaces

- Status colors/labels live on the model as `getBookingStatusesWithColors()` and
  `statuses()`/`statusesFilter()` (`common/models/base/Booking.php:339-397`), but there
  is **no shared `allowedNextStatuses` machine** — each view/controller decides which
  actions to show. Risk of divergence across backend/frontend/api.
- Our status set is larger and numeric (NEW=0, SELECTED_NOT_PAID=1, SCHEDULED=2,
  COMPLETED=3, INPROGRESS=4, CANCELED=5, ACCEPTED=6, CANCELED_BY_SHOP=7, NO_SHOW=9 —
  `Booking.php:73-81`) vs the demo's 5 string statuses. UI must map 9→5.
- No-show grace exists in data (`Shop.no_show_threshold_minutes`, default 15, range
  5–30, `base/Shop.php:169-171`) — matches `canMarkNoShow` intent.
- Reschedule limit exists (`Shop.reschedule_limit`, `Booking.reschedule_count`,
  `base/Booking.php:54`) — but this is a *count cap*, NOT the demo's
  *status-gated* reschedule (`canRescheduleStatus`). Different semantics.
- No group-booking UI (no `groupBookingId`).
- No in-store cash/card split surface in the model.

## Concrete UI gaps (driven by the data model)

1. No single source of truth for allowed status transitions → action menus can drift
   between portals.
2. 9 vs 5 statuses: `ACCEPTED` and `SELECTED_NOT_PAID`/`NEW` have no demo analogue;
   `CANCELED` vs `CANCELED_BY_SHOP` collapse into the demo's single `cancelled` +
   `cancelledBy`.
3. No group/party rollup views.
4. No in-store cash-vs-card off-rail KPI.
5. No customer-classification badge (navagoo-sourced vs shop-owned) on bookings.
