# Shop · Marketing — UI parity

Demo: `portals/shop/Marketing.tsx`, `portals/marketing/Deals.tsx`.
Ours: `frontend/views/promo-code/{index,_form,create,update,view}.php`.

## Screen A — Marketing landing / deals list

| Element | Demo | Ours | Status |
|---|---|---|---|
| Page header | `PageHeader title="Marketing" subtitle="{shop} · promo codes & deals"` (`Marketing.tsx:9`) | `<h1>Promo codes List</h1>` + shop title subtitle (`index.php:76-82`). Title says "Promo codes" not "Marketing". | partial — label drift |
| New button | `Button icon=Plus "New deal"` opens **modal** (`Deals.tsx:74-78`) | `Create Promo Code` link → **full page** (`index.php:83-88`) | done (different pattern) |
| Table | `DataTable` columns: Deal / Discount / Usage / Expires / delete (`Deals.tsx:24-72`) | Tailwind table: Code / Type / Amount / Usage / Expiry / Status / Actions (`index.php:124-196`) | done+ (extra Code & Status cols) |
| Discount cell | Badge `tone=purple` `{v}%` or `SAR {v}` (`Deals.tsx:30-37`) | brand chip `N %` / `N SAR` (`index.php:33-44`) | done |
| Usage cell | **progress `<Bar>`** with count/cap labels (`Deals.tsx:38-46`) | **text** `uses / max_uses` (+ remaining) or "Unlimited" (`index.php:159-166`) | partial — no progress bar |
| Expiry | `date(d.expiry)` (`Deals.tsx:51`) | `formatter->asDate` (`index.php:151-153`) | done |
| Filter row | none | code search + status select + reset (`index.php:91-119`) | ours richer |
| Pagination | none (in-memory) | `LinkPager` + summary (`index.php:200-211`) | ours richer |
| Empty state | `EmptyState` Ticket "No deals yet / Create a promo code…" (`Deals.tsx:80-86`) | plain `<td colspan=7>` "No results found." (`index.php:138`) | partial — no illustrated empty state, no CTA copy |

## Screen B — Create / edit form
- Demo: **Modal** (`Deals.tsx:101-200`): Description, Type, Value, Usage cap, Expiry. No
  code, no status, no VAT hint.
- Ours: **full-page** aurora form (`_form.php`): two cards — "Discount" (Code, Discount
  Value w/ "VAT-inclusive" hint `_form.php:91`, Type) and "Validity" (Status, Expiry kartik
  DatePicker, Max Uses). Server `errorSummary` styled (`_form.php:66-72`). Cancel + Save.
- Gaps vs demo: none missing; ours is a superset. The demo's modal-vs-page is a deliberate
  divergence (our reskin standardised on full-page forms).

## RTL / states
- Form has explicit RTL handling for the DatePicker corners (`_form.php:231-234`). ✅
- Index uses logical props (`ps-/pe-/start-/text-start`) → RTL-safe. ✅
- Demo RTL is global via app dir; no per-component RTL in this slice.

## UI gaps to close (concrete)
1. Add a usage **progress bar** to the index usage cell to match `Bar` (data already present).
2. Replace the bare "No results found." `<td>` with an illustrated empty state + "Create
   promo code" CTA to match the demo's `EmptyState` (Ticket icon, marketing copy).
3. (Optional) Rename the page/title to "Marketing" to match the demo's framing, or keep
   "Promo codes" intentionally — note the divergence.

## Customer Invitations view (NO demo counterpart)
`frontend/views/customer-invitations/index.php` (488 lines) — tag-input number entry,
WhatsApp/SMS method toggle, AR/EN template preview, recipients table with delivery +
onboarding status chips, empty state, pager. This is a Navagoo-only surface; the demo only
shows a simulated "Invitation sent" toast (`Customers.tsx:89`). No parity assessment applies;
documented for completeness.
