Complete all for Items

This commit is contained in:
2026-07-17 14:27:51 +05:30
parent f72b24fcaa
commit 62a5d857de
103 changed files with 2540 additions and 3259 deletions
+19 -15
View File
@@ -14,9 +14,10 @@
| 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/`) against `NEXT_PUBLIC_API_BASE_URL`; all calls go through it. No scattered `fetch()` in components. |
| 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 | Store the bearer token from `/auth/login`; attach `Authorization: Bearer <token>` in the API client. |
| 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.
@@ -115,7 +116,7 @@ Each screen calls the endpoints in `11-BACKEND-PHASE1.md`. System steps (blue) a
| Flow | Screens | Key endpoints |
|---|---|---|
| Login | Login | `POST /auth/login` |
| Login | Login | `POST /auth/login` (sets the session cookies), `POST /auth/logout` |
| Replenishment | Reorder alerts | `GET /stock/reorder-alerts`, `POST /stock/reorder-alerts/{itemId}/requisition` |
| Procurement | Requisition, RFQ, PO | `POST /requisitions`, `/rfqs`, `/rfqs/{id}/quotations`, `GET /rfqs/{id}/comparison`, `POST /purchase-orders`, `PUT /purchase-orders/{id}` |
| Receiving | GRN | `POST /grns`, `POST /grns/{id}/confirm`, `POST /grns/{id}/lines/{lineId}/release` |
@@ -128,20 +129,23 @@ Each screen calls the endpoints in `11-BACKEND-PHASE1.md`. System steps (blue) a
### 2.2 Master data screens (supporting, outside the core flow)
Vendors, Items, Categories, Subcategories, UOM, Warehouses, Brands, and Item Types are supporting master-data CRUD screens the flow above depends on but doesn't itself route through, so they're intentionally absent from the diagram/table. List screens follow one pagination convention: `page`/`pageSize`/`q`/`sortOrder` params, page size 5, debounced search, Previous/Next controls.
> **2026-07-16 — these are real backend entities now; the UI has NOT caught up.** Brand, Subcategory, Item Type (the frontend's "Variant Categories") and a Product Configuration gate were built on the backend (`docs/11 §2.3/2.6/2.7/2.8`). The screens below still run on `lib/api/mock-data.ts` and do not call any of it. Reconciling them is outstanding frontend work — the contract drift is listed in §2.2.1.
> **2026-07-17 — the frontend is connected to the real API.** `lib/api/mock-data.ts` is **deleted**; every `lib/api/*.ts` module calls the backend. The drift listed here previously has been reconciled — what follows records the decisions so they are not re-litigated.
**Brand** (`app/dashboard/products/brands`) and **Variant Category** (`app/dashboard/products/variants`) began as UI-only additions with no backend. The Item variant builder on `/dashboard/products/new` reads the Variant Category list live: checking a category (Color, Size, or any custom one added inline from that same page) reveals a value-entry section for it, and one Item is auto-created per combination across however many categories are checked, with an auto-generated SKU. See `Frontend/PROGRESS.md` (2026-07-15 entries) for the full rationale and discarded design iterations.
**Brand** (`app/dashboard/products/brands`), **Item Type** (`app/dashboard/products/item-types`, formerly "Variant Category") and **Subcategory** (`app/dashboard/products/categories/[id]`) are real backend entities (`docs/11 §2.3/2.6/2.7`). The item builder on `/dashboard/products/new` reads the Item Type list live from `GET /item-types`: checking a type (Color, Size, or any custom one added inline from that same page) reveals a value-entry section for it, and one Item is created per combination across however many types are checked, with a client-generated SKU. See `Frontend/PROGRESS.md` for the history.
#### 2.2.1 Contract drift to reconcile (backend is authoritative — §1)
- **`variantCategoriesApi``GET /item-types`.** Same shape (name-only list), new name. `variantCategoryId``itemTypeId`.
- **`Item.itemType``stockNature`.** The `Stocked|NonStocked|Service` field was renamed. `itemType` now means something else entirely (Color/Size), so this rename is not cosmetic — read `docs/11 §2.7` before touching it.
- **Send both category FKs.** `effectiveCategoryId = subCategoryId ?? categoryId` must become `categoryId` **and** `subCategoryId`; the server rejects a subcategory that doesn't belong to the category (422). Subcategories are their own resource now, not `Category.parentId`, and **`GET /categories?tree=true` no longer exists**.
- **Colour hex-packing stays frontend-only.** There is no value table, so `"Red|#EF4444"`, `encodeColorValue`/`decodeColorValue`/`isColorCategory` have nothing to reconcile against — keep them.
- **SKU generation stays client-side** (`buildVariantSku`) and is now the *only* record of which colour/size an item is; the server only uniqueness-checks it. Nothing can query items by colour.
- **`remove()` must become `PATCH /{id}/status`.** There are no `DELETE` endpoints on any master (FR-MD-08) the mock's unconditional delete has no backend equivalent.
- **`initialQty` remains unbacked** — no Stock Core wiring; still informational-only.
- **New: Product Configuration** (`GET`/`PUT /product-config`) gates subcategories/brands/item-types. This is the backend for the toggle screen; note **only 3 of that design's ~13 toggles exist**, and `itemTypesEnabled` is advisory — the frontend is what honours it (`docs/11 §2.8`). No `Switch` primitive exists in `components/ui/` yet.
- **Non-transactional create loop:** the builder's per-row `itemsApi.create()` has no transaction — a `SKU_DUPLICATE` on row 7 of 12 leaves 6 items created. Real HTTP calls will make this failure mode visible in a way mock data never did.
#### 2.2.1 Resolved contract decisions (backend is authoritative — §1)
- **`variantCategoriesApi``itemTypesApi`** (`GET /item-types`); `variantCategoryId``itemTypeId`.
- **`Item.itemType``stockNature`.** `itemType` now means a Color/Size dimension master — a different concept (`docs/11 §2.7`). The item-detail label reads "Stock nature".
- **Both category FKs travel.** The builder sends `categoryId` **and** `subCategoryId`; the old `subCategoryId ?? categoryId` collapse lost the parent. The server rejects a mismatched pair with 422. Subcategories are their own resource `Category.parentId` and `?tree=true` are gone.
- **Colour hex-packing stays frontend-only.** There is no value table server-side, so `"Red|#EF4444"` + `encodeColorValue`/`decodeColorValue`/`isColorCategory` have nothing to reconcile against. Kept as-is.
- **SKU generation stays client-side** (`buildVariantSku`) and is the *only* record of which colour/size an item is; the server only uniqueness-checks it. **Nothing can query items by colour** — accepted (`docs/10 Part C.9`).
- **`remove()` `updateStatus(id, "Inactive")`** everywhere. There are no `DELETE` endpoints on any master (FR-MD-08); the lists show a Status column and Deactivate/Activate.
- **`initialQty` is gone** from the builder — the Item contract has no such field and there is no initial-receipt flow. Stock arrives via a GRN.
- **Product Configuration** (`app/dashboard/products/settings`, `GET`/`PUT /product-config`) — only **3** of the original design's ~13 toggles exist. `subcategoriesEnabled`/`brandsEnabled` are server-enforced (`CONFIG_DISABLED`); **`itemTypesEnabled` is advisory** and this app is what honours it (it hides the builder's type section). The UI states that distinction on the screen rather than implying a guarantee.
- **Non-transactional create loop:** the builder's per-row `itemsApi.create()` has no transaction — a `SKU_DUPLICATE` on row 7 of 12 leaves 6 items created. The error message now says how many landed rather than implying nothing happened. A transactional bulk-create endpoint would be the real fix.
- **GRN edit/delete removed** — the API has no `PUT`/`DELETE` for a GRN; receipts are corrected by reversing documents (FR-X-05).
- **RFQ invited-vendors is not persisted** — `POST /rfqs` validates `vendorIds` then discards them, so the list/detail screens show quotations received instead of vendors invited.
- **Known gap — serial numbers:** FR-GRN-04 requires capturing serials on receipt, but `CreateGrnLineInput` has no such field (only `batch`). The UI does not collect them rather than silently discarding them. Needs a backend change to honour the requirement.
---