# WP0 — Foundation / السقالة

**Repo:** FE only — `/home/moonui4/public_html/moon-erp` (branch `hazemdev4`)
**Depends on:** nothing
**Review:** Codex · **[FIN]:** no · **Migration:** no

## Goal

Stand up a second Angular application, `storefront`, inside the existing `moon-erp` workspace, with the theme's exact design tokens and a working data layer — so that a single smoke page can fetch `GET /api/store/settings` and `GET /api/store/products` and render real data. No theme pages are ported in this WP; this is plumbing only.

## Exact files

**Create (via CLI, then edit):**
- `ng generate application storefront --standalone --style=scss --routing --skip-tests` → creates `projects/storefront/` (workspace already has `newProjectRoot: "projects"`, confirmed).
- `projects/storefront/src/app/core/config/app-config.service.ts` — runtime config loader
- `projects/storefront/src/app/core/http/api-client.service.ts` — typed HTTP wrapper over the store API
- `projects/storefront/src/app/core/http/auth.interceptor.ts`
- `projects/storefront/src/app/core/http/error.interceptor.ts`
- `projects/storefront/src/app/core/state/auth.state.ts`
- `projects/storefront/src/app/core/state/cart.state.ts`
- `projects/storefront/src/app/core/models/` — `product.model.ts`, `category.model.ts`, `cart.model.ts`, `settings.model.ts`, `api.model.ts` (paginated envelope + error shapes)
- `projects/storefront/src/assets/config.json` + `config.json.example`
- `projects/storefront/src/assets/i18n/ar.json` + `en.json`
- `projects/storefront/tailwind.config.js` (or the v4 `@theme` equivalent — see below)

**Modify:**
- `/home/moonui4/public_html/moon-erp/angular.json` — the new project's build target
- `/home/moonui4/public_html/moon-erp/.gitignore` — add `projects/storefront/src/assets/config.json`

**Do NOT touch:** anything under `src/` (that is the admin app `moon-erp`), `angular.json`'s existing `moon-erp` project block, or the backend.

## Design tokens — port VERBATIM from the theme

The theme loads Tailwind from a runtime CDN with this inline config (identical in all 29 pages, `index.html` lines 17–58). Reproduce it exactly:

```js
colors: {
  primary: { 50:"#FFF8E1",100:"#FFECB3",200:"#FFE082",300:"#FFD54F",400:"#FFCA28",
             500:"#FF9800",600:"#FB8C00",700:"#F57C00",800:"#EF6C00",900:"#E65100" },
  secondary: { 50:"#E3F2FD",100:"#BBDEFB",500:"#2196F3",600:"#1E88E5" },
  success: "#4CAF50", warning: "#FFC107", danger: "#F44336",
},
fontFamily: { arabic: ["Tajawal","sans-serif"], english: ["Inter","sans-serif"] },
boxShadow: {
  soft: '0 2px 15px -3px rgba(0,0,0,0.07), 0 10px 20px -2px rgba(0,0,0,0.04)',
  card: '0 0 0 1px rgba(0,0,0,0.05), 0 1px 3px rgba(0,0,0,0.1)',
}
```

⚠️ The admin app uses **Tailwind 4** (`@tailwindcss/postcss`, tokens in an `@theme` block in `styles.scss`, **no `tailwind.config.js`**). Match that mechanism for the storefront — put these tokens in the storefront's own `@theme` block. Do **not** introduce a Tailwind 3 config file into a Tailwind 4 workspace.

Also port `theme/assets/css/style.css` (139 lines: `.card`, `.btn-primary` gradient, `.input-field`, `.badge`, `.fade-in`, `.spinner`, `.skeleton`, `.text-gradient`, `.glass-effect`, the orange webkit scrollbar, and the **global `* { transition-property: …; duration: 150ms }`** — that global transition is load-bearing for the theme's feel; reproduce it deliberately).

🔴 **Do NOT port `style-ar.css` / `style-en.css` in this WP.** Every rule in them is keyed on `body[dir=...]` while the theme sets `dir` on `<html>`, so none of them were ever active. Adopting them blindly would change the rendering. They are audited in WP6.

Fonts: Tajawal (300/400/500/700/800) + Inter (300–700). Icons: Material Icons in three families (`Material Icons`, `Material Icons Outlined`, `Material Icons Round`), used as **ligature text** — the exact families must be preserved or icon metrics shift. CDN is acceptable for WP0; self-hosting is decided in WP6.

## Runtime config (mirror the admin app's proven pattern)

`projects/storefront/src/assets/config.json` — **gitignored**, per-install:

```json
{ "apiUrl": "https://moonui4.elbaset.com/moon-erp-be/api/store", "defaultLang": "ar" }
```

- `environment.ts` / `environment.prod.ts` set `apiUrl: ''` **on purpose** so a missing/broken config fails loudly.
- `main.ts` must `await loadAppConfig()` **before** `bootstrapApplication(...)`, mutating `environment.apiUrl`, exactly as `src/main.ts` does for the admin app. On failure, paint a plain-HTML "Configuration error" panel.
- Commit `config.json.example` with a neutral `YOUR-ENV` placeholder. **Never commit `config.json`.**

## HTTP layer — the contract facts that must be encoded

1. **Base URL** = `/api/store`. Public and customer routes share it. Never call `/api/store/admin`.
2. **Auth header** = `Authorization: Bearer <token>` (guard `store-customer`). ⚠️ This differs from the admin app, which uses `X-Authorization` — do not copy that interceptor blindly.
3. **`Accept-Language: ar|en`** on every request, from the language service.
4. **Token location** = top-level `token` key, sibling of `data` — NOT inside `data`.
5. **Money normalization (do this once, here).** Decimal columns serialize as **strings** (`"25.500"`); computed fields as **numbers** (`51.0`). Normalize every money field to `number` at the HTTP boundary so no component ever does `parseFloat`.
6. **No nulls.** The backend's `CoercesNullDefaults` rewrites every null to `""` / `0` / `false`. So `category_id === 0` means "none" and `average_rating` is **omitted entirely** when absent. Model types must **not** use `| null` for these; use falsy checks, and make `average_rating?: number`.
7. **Error interceptor must branch on three different 422 shapes:**
   - validation → `{ message, errors: { field: string[] } }`
   - checkout → `{ valid: false, errors: string[] }` ← flat array
   - business rule → `{ message, ...extras }`
   Branch on `Array.isArray(body.errors)` before treating `errors` as a field map.
8. **401** → clear the token + redirect to login. There is **no logout endpoint**; logout is a client-side token drop.
9. Paginated envelope is standard Laravel: `{ data, links, meta:{ current_page, last_page, per_page, total, ... } }`. Some endpoints (`categories/tree`, `manufacturers`, `tags`, `governorates`, `cities`) are **unpaginated** `{ data: [...] }`.

## State layer

- `auth.state.ts` — signals for `token`, `customer`, `isAuthenticated`. Persist the token in `localStorage`. Expose `login()/logout()/restore()`.
- `cart.state.ts` — signals for the **client-side guest cart** (there is no guest cart on the backend; all cart routes require auth). Persist to `localStorage`. Expose `itemCount` for the header badge. The merge-on-login replay is implemented in WP3 — WP0 only needs the shape and persistence.

## i18n

- ngx-translate v17, same mechanism as the admin app: `provideTranslateHttpLoader()` **inside** `provideTranslateService({loader})`, and `fallbackLang` (not `defaultLanguage`).
- Seed `ar.json` and `en.json`. **Every string is a key from the very first component** — the theme has zero `data-i18n` and all-hardcoded Arabic, so discipline starts now or WP6 becomes a rewrite.
- Adopt the admin app's **`I18N_VERSION` cache-buster** pattern (`?v=${I18N_VERSION}` suffix): the i18n JSONs are not fingerprinted, so without it clients see raw keys after a deploy.
- Language service sets `<html lang>` and `<html dir>`. Default `ar`.

## Build target

`angular.json` for `storefront`: output `dist/storefront/`, and the deploy build uses `--base-href /store/`. Keep the admin app's `moon-erp` target untouched.

## Acceptance criteria

- [ ] `npx ng build storefront` is green; `npx ng build` (admin app) is **still** green — zero regression.
- [ ] `ng serve storefront` renders a smoke page that fetches `GET /api/store/settings` and `GET /api/store/products` from the real moonui4 backend and displays the store settings plus a plain list of real product names and prices.
- [ ] A product price renders as a **number** (proof that money normalization works), and a product with no image does not crash (proof of the no-null handling).
- [ ] Deliberately breaking `config.json` produces the loud "Configuration error" panel, not a silent blank page.
- [ ] The Tailwind `primary-500` swatch renders `#FF9800` and body copy renders in Tajawal.
- [ ] No hardcoded user-facing string — the smoke page's labels come from `ar.json`/`en.json`, and switching `Accept-Language` changes the backend's returned names.
- [ ] `git status` shows `projects/storefront/src/assets/config.json` as **ignored**, and `config.json.example` as tracked.

## Tests

No Pest work (FE-only WP). The gate is: **both** `ng build storefront` and `ng build` green, plus the manual smoke checks above. Record build output in the ledger.

## Out of scope

Do not port any of the 29 theme pages. Do not build the header/footer shell (WP1). Do not implement cart merge-on-login (WP3), auth screens (WP4), or checkout (WP5). Do not touch the backend. Do not run `/fullpush`, deploy, or merge to `main`.
