# Shop · Dashboard — Parity Matrix

Demo (canonical): `portals/shop/Dashboard.tsx` · Ours: `frontend/controllers/SiteController.php`, `frontend/views/site/index.php`

> The demo Shop Dashboard is a **finance/rail dashboard**; ours is a **bookings-analytics
> dashboard**. They overlap on upcoming-bookings + two count KPIs only. Most demo features
> depend on Subscription/Plan/Charge/Transfer/Settlement/Invoice models that **do not exist**
> in our codebase, so they are unimplementable as-is without those models.

| Demo behavior | Demo ref | Our ref | Status | Note |
|---|---|---|---|---|
| Header title + shop·date subtitle | Dashboard.tsx:110-112 | index.php:38-44 | done | Ours "Hi, {name}" vs "Welcome back"; both show shop + date |
| Header action: View calendar | Dashboard.tsx:115-121 | — | missing | No header CTA |
| Header action: New walk-in booking | Dashboard.tsx:122-128 | — | missing | No header CTA |
| Subscription banner (plan/free-period/billing) | Dashboard.tsx:134-163 | — | missing | No Subscription/Plan model |
| KPI: Upcoming/Scheduled count | Dashboard.tsx:167-172 | SiteController.php:349-356; index.php:18,54 | partial | Ours range-scoped (not all-time) |
| KPI: Completed count | Dashboard.tsx:173-179 | SiteController.php:358-365; index.php:19 | partial | Range-scoped + adds trend |
| KPI: Net Available (withdrawable) | Dashboard.tsx:180-190; selectors.ts:365 | — | missing | No settlement/charge model |
| KPI: Costs to Date | Dashboard.tsx:191-197; selectors.ts:399-409 | — | missing | No Charge model |
| Withdrawal-min eligibility caption | Dashboard.tsx:186-189; selectors.ts:367 | — | missing | Field exists (base/Shop.php:216) but unused on dashboard |
| Recent bookings table (6, desc) | Dashboard.tsx:51-53,215 | SiteController.php:438-486; index.php:66-91 | partial | Ours = future-only asc; no value/badges |
| — booking Value column | Dashboard.tsx:89-94 | — | missing | |
| — BookingBadge (status) | Dashboard.tsx:96-104 | — | missing | Status not shown in table |
| — SettlementBadge | Dashboard.tsx:100; selectors.ts:461-478 | — | missing | No settlement concept |
| — customer avatar | Dashboard.tsx:72-73 | — | missing | |
| — "View all" + row click → bookings | Dashboard.tsx:206-219 | — | missing | Static table |
| — Duration column | — | SiteController.php:479; index.php:73,85 | done | Ours-only extra |
| Costs-to-date donut + 4 cost lines | Dashboard.tsx:224-258 | — | missing | No Charge model |
| Action card: copy booking link | Dashboard.tsx:263-274 | — | missing | No deepLinkUrl field |
| Action card: invite customer | Dashboard.tsx:275-282 | — | missing | |
| Action card: detailed charges | Dashboard.tsx:283-290 | — | missing | |
| Empty-bookings state | (implicit) | index.php:78-79 | done | Ours explicit + better |
| Store-rating card | — | SiteController.php:590-625; index.php:94-105 | n/a | Ours-only (not in demo dashboard) |
| Date-range filter (today/7d/30d/custom) | — | SiteController.php:270-329 | n/a | Ours-only |
| KPI trend deltas vs prev period | — | SiteController.php:426-436 | n/a | Computed; not rendered |
| In-progress / Cancelled KPIs | — | SiteController.php:367-383; index.php:20-21 | n/a | Ours-only |
| Status/occupancy/customer charts | — | SiteController.php:505-693 (un-rendered) | n/a | Computed but charts un-ported (index.php:108-111) |
| Access/verification gating | — | SiteController.php:84-148 | n/a | Ours-only (security) |
| RTL / bilingual | (LTR mock) | Yii i18n + text-start | done | Ours-only strength |

## Scoring (overlapping/demo-driven rows only)
- done: header title (1), duration extra is ours-only, empty-state (1) → header+empty = 2 done on demo-relevant axis
- partial: 2 count KPIs + recent-bookings table = 3
- missing: header actions ×2, subscription banner, Net Available, Costs KPI, withdrawal caption, Value col, BookingBadge, SettlementBadge, avatar, View-all/click, costs donut, 3 action cards = 16

Roughly 2 done + 3 partial (≈0.5 each) ≈ 3.5 credit out of ~21 demo-driven behaviors.

**Area score: 17%**

Most of the gap is structural: implementing the demo's dashboard requires first building the
finance domain (Subscription, Plan, Charge+VAT, Transfer/Settlement, Invoice, booking
`deepLinkUrl`). Cheap near-term wins that need no new domain: header action buttons, adding a
Value + status badge to the bookings table, the "invite customer" action card, and a
"View all → bookings" link.

## Verified verdict (adversarial)

Adversarial re-check of the analyst's "done"/"partial" rows against demo
`portals/shop/Dashboard.tsx` and `components/ui/Table.tsx`, vs our
`frontend/views/site/index.php` + `frontend/controllers/SiteController.php`.

| Row | Analyst | Verified | Evidence |
|---|---|---|---|
| Header title + shop·date subtitle | done | **done (caveat)** | Both render title + shop+date subtitle. Text/order diverge: ours `Hi, {name}` + `date · shop` (index.php:39-44); demo `Welcome back` + `shop · dateLong` (Dashboard.tsx:110-112). Structural match, not literal. |
| Empty-bookings state | done | **done** | Demo Table.tsx:68-71 renders default "No records." empty cell; ours renders explicit "No upcoming bookings" (index.php:78-79). Both have empty states; ours clearer. Confirmed. |
| KPI: Upcoming/Scheduled count | partial | **partial** | Ours range-scoped (SiteController.php:337,349); demo `k.counts.scheduled` all-time. Confirmed partial. |
| KPI: Completed count | partial | **partial** | Ours range-scoped (SiteController.php:338,358); demo all-time. Confirmed partial. |
| Recent bookings table (6, most-recent first) | partial | **partial (defect)** | Demo: descending by date, NO status filter, slice 6 (Dashboard.tsx:51-53). Ours: future-only (`>= today`), STATUS_SCHEDULED/INPROGRESS only, **ascending**, **`limit(5)`** (SiteController.php:443-448). The view's `array_slice(...,0,6)` (index.php:31) is dead — controller never returns >5, so the "6" claim is incorrect; actual cap is 5. Direction + scope also differ. |

All "missing" rows were spot-checked and stand (no Subscription/Plan/Charge/Settlement
models; no header CTAs; no Value/BookingBadge/SettlementBadge/avatar columns; no
View-all/row-click; no action cards). Those downgrades are correct.

**Overclaims found:**
- Recent-bookings "6" rows-shown is wrong — controller `limit(5)` caps it at 5; view slice-6 is dead code.
- "Header title — done" is a structural match only; title text and subtitle ordering are not identical to the demo.

**Adjusted area score: 15%** (was 17). The two "done" rows are soft/structural rather
than exact behavioral matches, and the bookings-table row carries a count defect; the
remaining missing rows confirm the structural finance-domain gap.
