# Shop · Customers directory + freeze — UI parity

Demo: `portals/shop/Customers.tsx`. Ours: `frontend/views/customers/index.php` (layout `tailwind`).

## Screens / components

### A. Page header
- **Demo** (`Customers.tsx:33-36`): `PageHeader` title "Customers", subtitle `"{commercialName} · directory, classifications & freeze list"`.
- **Ours** (`index.php:35-40`): h1 "Customers" + subtitle "Your customers from booking history, and the marketing-fee freeze list." No shop commercial name. **Partial** (no shop name interpolation).

### B. Booking-link / QR card — MISSING
- **Demo** (`Customers.tsx:38,57-95`): a prominent `BookingLinkCard` at the top: decorative deterministic **QR block** (`Qr`, `Customers.tsx:306-338`), the shop `deepLinkUrl` in a mono pill, a **Copy** button (clipboard + toast), an explainer about Navagoo-vs-shop customer attribution + marketing fee, and an **"Invite customer"** button (SMS+WhatsApp, simulated).
- **Ours**: entirely absent. No deep link, no QR, no copy, no invite button. **MISSING.** (Note: a separate `customer-invitations` feature exists in the repo, but it is not surfaced on this screen.)

### C. Segmented view switcher — MISSING
- **Demo** (`Customers.tsx:40-48`): `Segmented` with `Customers (N)` / `Classifications` / `Freeze list`.
- **Ours**: no switcher; directory + freeze render stacked on one page. **MISSING.**

### D. Customers table
- **Demo** (`Customers.tsx:97-143`): Avatar+name / Mobile (mono) / Gender / Bookings (right) / Classification badge (center). Empty: `<EmptyState title="No customers yet" />`.
- **Ours** (`index.php:43-90`): initials-avatar+name / Mobile (mono, `dir=ltr`) / Bookings / Last visit / Marketing-fee (Frozen pill or Freeze button). Empty row "No customers yet.". **Partial**: missing Gender + Classification columns; adds Last-visit + inline freeze.

### E. Classifications table — MISSING
- **Demo** (`Customers.tsx:145-188`): Customer / Mobile / Classification badge / Reason (`titleCase`) / Set-on date / "Overridden" amber badge. CardHeader subtitle: "Read-only · set on the customer's first booking, immutable (admin can override)".
- **Ours**: no such view. **MISSING.**

### F. Freeze list + grace banner
- **Demo** (`Customers.tsx:227-263`): a **grace-window banner** (accent inside grace / amber after) with `ShieldAlert` icon, then a Card with header "Freeze list" / subtitle "Existing customers exempt from the marketing fee", an "Add number" action, and the table (Name / Mobile / Added / trash remove). Empty: `EmptyState` with Snowflake icon "Freeze list is empty".
- **Ours** (`index.php:92-132`): freeze Card with header + subtitle, "Add to freeze list" button (brand pill), table (Name / Mobile / Added / Remove pill). Empty row "The freeze list is empty.". **No grace banner.** **Partial.**

### G. Add-to-freeze modal
- **Demo** (`Customers.tsx:264-301`): Modal, Mobile field (placeholder "+966 5…"), optional Name, Cancel + Add (Add disabled while mobile empty).
- **Ours** (`index.php:134-160`): full Tailwind modal, Mobile (`dir=ltr`, placeholder "9665XXXXXXXX", `inputmode=tel`), optional Name, Cancel + "Add to freeze list" (snowflake icon). Client-side requires non-empty mobile (`index.php:190`). **Match.**

### H. Remove confirmation
- **Demo** (`Customers.tsx:213-217`): async `confirm()` modal before removing.
- **Ours** (`index.php:183-184`): immediate POST, **no confirm dialog**. **Partial.**

### I. Interaction model
- Demo: store actions + toasts; no page reload.
- Ours (`index.php:162-194`): CSRF-safe `fetch` POST → `location.reload()`. Errors → `alert()`. Functional but coarser UX (full reload, native alert vs toast).

## RTL / i18n
- Ours wraps all copy in `Yii::t('frontend', …)` and forces `dir="ltr"` on mobile cells (`index.php:70,119,148`) — correct for Arabic. EN strings present (`common/messages/en/frontend.php:114-139`); AR present for the freeze strings (`common/messages/ar/frontend.php:127-129`). Demo is English-only. **Ours better here** — but any NEW strings for the missing tabs/banner/QR would need bilingual entries.

## Concrete UI gaps (ours, vs canonical demo)
1. Booking-link card (deep link + QR + Copy + Invite customer) — missing.
2. Segmented Customers / Classifications / Freeze switcher — missing.
3. Classifications tab/table — missing.
4. Grace-window banner (in-grace vs ended) — missing.
5. Directory **Gender** column — missing (value computed but unrendered).
6. Directory **Classification** badge column — missing.
7. Remove-from-freeze confirmation dialog — missing.
8. Toasts → replaced by full page reload + `alert()`.
