# 20 · FRONTEND — Phase 1 (Inventory & Supply Chain) > **Authoritative for:** frontend user-flows, the rules to follow the existing architecture, and the validation posture. > **Navigation:** you arrived from `00-CORE.md`. The API contract this UI consumes is in `11-BACKEND-PHASE1.md`; business rules are in `10-BACKEND-PHASE1.md`. Record work in `Frontend/PROGRESS.md`. > **Note:** the Frontend project is **already initialized** (Next.js, App Router, TypeScript). This document does **not** scaffold structure — it governs how to build within what exists. --- ## 1. Stack & architecture rules | Concern | Rule | |---|---| | Framework | Next.js **App Router** + **TypeScript**. Work within the existing structure; do not restructure or introduce a competing router. | | Styling / UI | **Tailwind CSS** for all styling — no CSS modules, styled-components, or inline style objects. UI primitives come from **shadcn/ui** (`components/ui/`, built on `@base-ui/react` + `class-variance-authority`); use/extend existing components there before adding a new one, and generate new primitives via the shadcn CLI to keep the pattern consistent. | | State / forms | **Plain React hooks** (`useState`, `useReducer`, custom hooks). No form/state library. | | Validation | **Dependency-free** (hand-rolled helpers). See §3. | | API access | A single **typed fetch client** (`lib/api-client.ts`) against a **same-origin `/api/v1`**; all calls go through it. No scattered `fetch()` in components. | | Transport | The API is reached through a **Next `rewrites()` proxy** (`next.config.ts`: `/api/*` → `BACKEND_ORIGIN`, default `http://localhost:5224`). Same-origin by construction, so there is no CORS on the backend and none is needed. `BACKEND_ORIGIN` is **server-side only** — deliberately not `NEXT_PUBLIC_*`, since the browser only ever talks to the Next server. | | Types | TS types in `types/` **mirror the API DTOs** in `11-BACKEND-PHASE1.md`. When the contract changes, update these first. | | Auth | **httpOnly cookie session — there is no bearer token to store.** `POST /auth/login` sets `erp_at`/`erp_rt`/`XSRF-TOKEN`; the client just sends `credentials: "include"`. JS cannot read `erp_at` by design. `proxy.ts` guards `/dashboard/*` on the cookie's *presence* only (it cannot validate an RS256 JWT at the edge) — **the API remains the authority**. `lib/auth-session.ts` caches the user *profile* in localStorage for display only, because there is no `GET /auth/me`; it is not a credential. | Principles: - **The API contract is the source of truth.** The UI adapts to `11-BACKEND-PHASE1.md`, never the reverse. - **Keep components thin;** put data-fetching and derived state in hooks, request/response shaping in the API client. - **One typed client method per endpoint**, returning the DTO type from `types/`. --- ## 2. User flows The flow below is the end-to-end Phase-1 journey: replenishment need → procurement → receiving → QC hold → stock available → stock operations, with the reorder loop closing back. Colour = functional role. Roles now drive real sidebar visibility (`GET /auth/me`'s `navCodes`, see docs/10 C.8/docs/11 §2.0.1, admin screens at `/dashboard/settings/roles` and `/dashboard/settings/users`) but **per-endpoint RBAC is still not enforced** — this remains a UI-level filter only. > **Roles screen (2026-07-18):** `code` is never typed by an operator — it's derived client-side from `name` (uppercased, non-alphanumeric → `_`) and shown read-only, on both create and edit. The Create Role dialog also includes the permission checkbox tree (`components/auth/RolePermissionTree.tsx`, fed by `GET /nav`), so creating a role and assigning its sidebar permissions is one Save action; the detail page (`/dashboard/settings/roles/[id]`) remains for later edits. The Create User dialog's "User type" is a `