Files
ERP-core/Frontend/PROGRESS.md
T
Sasanka 0415794473 Refactor API modules to remove mock implementations and integrate real endpoints
- Updated stock-transfers.ts to replace mock data with API requests for stock transfers.
- Refactored stock.ts to utilize API calls for stock inquiries instead of mock data.
- Modified uoms.ts to implement real API requests for unit of measure operations.
- Transitioned vendors.ts to use actual API endpoints for vendor management, removing mock data handling.
- Updated warehouses.ts to replace mock implementations with real API calls for warehouse and bin management.
- Refactored wastage.ts to utilize stock adjustments and reason codes APIs, removing mock data dependencies.
- Adjusted procurement.ts to align with backend DTO shapes, ensuring consistency with planned backend structures.
2026-07-14 16:01:47 +05:30

152 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend — PROGRESS (Phase 1: Inventory & Supply Chain)
Legend: `[ ]` not started · `[~]` in progress · `[x]` done
Spec: `docs/20-FRONTEND.md` (flows + rules) · `docs/11-BACKEND-PHASE1.md` (API contract)
Convention: `docs/01-DOC-GUIDE.md §6`. Update this file in the **same commit** as the code. When ticking `[x]`, append a short note + any deviation.
## 0. Foundation
- [x] `NEXT_PUBLIC_API_BASE_URL` wired (`.env.local` / `.env.local.example`, alongside the existing `NEXT_PUBLIC_AUTH_API_BASE_URL`) — points at `Backend/ERPCore`'s https profile (`7112`)
- [x] Typed API client / fetch wrapper (`lib/api-client.ts`: `apiRequest`/`apiRequestWithETag`/`buildQuery`) + bearer token handling (`lib/auth-token.ts`, degrades gracefully — no login endpoint wired yet, see §1)
- [x] Shared TS types mirroring API DTOs (`types/common.ts`, `types/master-data.ts`, `types/procurement.ts`, `types/grn.ts`) — built only for the subset GRN needs (Item/Warehouse/Bin/Vendor/Uom/PurchaseOrder/Grn)
- [~] Client validation helpers (`lib/validations/grn.ts`) — **deviation**: uses `zod` (already a project dependency, used by `lib/validations.ts`/login), not hand-rolled, to stay consistent with the codebase's existing pattern rather than introduce a second validation approach. UX-only; server remains authoritative (docs/20-FRONTEND.md §3)
- [x] `ProblemDetails` normalizer (`ApiError` in `lib/api-client.ts`) + `code → message` map (`lib/error-map.ts`)
> **Scope note:** this foundation was built alongside the GRN feature and only covers the endpoints GRN consumes (items, warehouses/bins, vendors, uoms, purchase-orders, grns). Other master-data/procurement API methods still need their own `lib/api/*.ts` files when those screens are built.
> **2026-07-14 — mock data removed, `lib/api/mock-data.ts` deleted.** Every `lib/api/*.ts` file's commented-out real `fetch` block was restored and the in-memory mock block deleted, per user request. Master Data + Procurement modules (`categories`, `uoms`, `warehouses`, `items`, `vendors`, `requisitions`, `rfqs`, `purchase-orders`) now call the live backend (`Backend/ERPCore` §1/§2, both implemented + smoke-tested). GRN/Stock/Purchase-Return/Reason-Code modules (`grns`, `stock`, `stock-transfers`, `stock-adjustments`, `stock-counts`, `purchase-returns`, `reason-codes`, `wastage`) also now call real (documented-or-assumed) endpoint paths, but **no backend controller exists for any of them yet** (`Backend/PROGRESS.md` §3/§4/§5 are unstarted, and Purchase Return is explicitly deferred) — those calls will 404 against a running backend until that work happens. This was a deliberate tradeoff the user confirmed explicitly (see the two `AskUserQuestion` exchanges this session) rather than silently faking data.
## 1. Auth
- [~] Login screen — UI built (`app/login`); not yet wired to `POST /auth/login` / token storage
- [~] Forgot password — add email screen — UI built (`app/login/forgot`); not yet wired to API
- [~] Forgot password — verify OTP screen — UI built (`app/login/forgot/otp`); not yet wired to API
- [~] Forgot password — change password screen — UI built (`app/login/forgot/reset`); not yet wired to API
> **2026-07-14:** left untouched during the mock-data removal pass — there was never a mock `lib/api/auth.ts` to begin with (these screens simply don't call anything yet), and `Backend/PROGRESS.md` §6 confirms no `POST /auth/login` controller exists (`ICurrentUser`/JWT validation are wired, but there's no token issuer). Nothing to wire until that lands.
## 2. Master Data screens
- [x] Items (`app/dashboard/products` list + filters, `/new` create, `/[id]` full edit + ETag/If-Match with 412-conflict handling + activate/deactivate) — FR-MD-01/08. Reused the pre-existing "Products" sidebar entry/stub route rather than adding a new nav item. **2026-07-14: wired to the live backend** (`lib/api/items.ts`), no more mock data.
- [x] UOM + conversions (`app/dashboard/products/uoms` flat list + create dialog; conversions edited inline on the Item detail page via `PUT /items/{itemId}/uom-conversions`) — FR-MD-02/03. **2026-07-14: wired to the live backend.** Note: the real `ItemDetailDto` does not include a `conversions` field (only `Reorder`) — the Item detail page's conversion editor now round-trips purely through `PUT /items/{itemId}/uom-conversions`'s own request/response, not the GET response.
- [x] Categories (`app/dashboard/products/categories` indented tree view + create dialog with parent picker) — FR-MD-04. **2026-07-14: wired to the live backend.**
- [x] Vendors (`app/dashboard/vendors` list + search/status-filter + create dialog, `[id]` full edit + ETag/If-Match with 412-conflict handling + activate/deactivate) — FR-MD-06. First screen this session to exercise the ETag/`If-Match`/412 pattern end-to-end (`lib/api-client.ts`'s `ApiResult<T>` was built earlier but unused until now). **2026-07-14: wired to the live backend.**
- [x] Warehouses + Bins (`app/dashboard/warehouse` list + create-warehouse dialog, `[id]` bin list + create-bin dialog) — FR-WH-01/FR-MD-07. **2026-07-14: wired to the live backend**; added `warehousesApi.get()` (the page needed it but the original stub never had it) and wrapped the real `GET /warehouses/{id}/bins` (`IReadOnlyList<BinDto>`, not paged) into a synthetic single-page `PagedResponse<Bin>` so existing `.items`-based call sites didn't need touching.
- [x] Item reorder settings — edited inline on the Item detail page (per-warehouse point/qty rows) via `PUT /items/{itemId}/reorder` — FR-MD-05. **2026-07-14: wired to the live backend.**
## 3. Procurement screens
- [x] Requisition (`app/dashboard/procurement/requisitions` list, `/new` create, `/[id]` detail + Submit) — FR-PROC-01. **2026-07-14: wired to the live backend**; dropped `RequisitionSummary.lineCount` (the real `RequisitionSummaryDto` doesn't return it) from the list screen.
- [x] RFQ + quotations + comparison view (`.../rfqs` list, `/new` create with vendor multi-invite, `/[id]` detail: lines, comparison matrix, record-quotation form, "Create PO from vendor") — FR-PROC-02. **2026-07-14: wired to the live backend**, with real contract mismatches found and fixed (see the dedicated deviation note below — this one needed real rework, not just an API-client swap).
- [x] Purchase Order (`.../purchase-orders` list, `/new` create — auto-approved, prefillable from a Requisition or an RFQ+vendor quotation via query params — `/[id]` detail: edit-while-open with ETag/If-Match, Cancel with reason) — FR-PROC-03..07. **2026-07-14: wired to the live backend.**
- [~] Purchase Return (`.../purchase-returns` list, `/new` create against a Confirmed/Closed GRN's lines) — FR-PROC-08; also reachable from a `Rejected` GRN line via a "Create Return" button on the GRN detail page. **Still `[~]`:** the API client now calls real endpoint paths, but `Backend/PROGRESS.md` §2 explicitly defers Purchase Return until GRN + Stock Core exist — these calls 404 against a live backend today.
> **2026-07-14 — RFQ real-backend contract mismatches found while removing mock data (`Backend/ERPCore/Dtos/Procurement/RfqDtos.cs` / `RfqService.cs` vs. the frontend's speculative types):**
> - The backend does **not** persist which vendors were invited to an RFQ (`RfqService.MapRfq` never sets a vendor list) — `Rfq`/`RfqSummary` no longer carry `vendorIds`. `types/procurement.ts` and the `[id]`/list pages were updated to stop relying on it; the RFQ detail page now shows "Quoted: ..." (derived from `RfqComparison.vendorIds`, i.e. vendors who have actually submitted a quotation) instead of "Invited: ...".
> - `CreateRfqRequest.RequisitionId` is `[Required]` server-side, not optional as the frontend assumed — `app/dashboard/procurement/rfqs/new/page.tsx` now requires picking a Submitted requisition (a picker was added for the case where one wasn't passed in via `?requisitionId=`) before an RFQ can be created.
> - `RfqComparisonDto`'s real shape is `{ rfqId, vendorIds, rows: [{ itemId, qty, quotes: [{ vendorId, quotationId, unitPrice, leadDays }] }] }` — the frontend's assumed `{ lines: [{ cells }] }` naming was wrong; `types/procurement.ts` (`RfqComparisonRow`/`RfqComparisonCell`) and both consuming pages (RFQ detail, PO-from-RFQ prefill) were corrected to match.
> - The "Record a quotation" vendor picker on the RFQ detail page now offers any active vendor who hasn't already quoted (matching what `AddQuotationAsync` actually validates — vendor exists + hasn't already quoted, not "was invited") rather than a now-nonexistent "pending invited vendors" list.
> - `GET /rfqs` (list) still does not exist on `RfqsController` (only `GET /rfqs/{id}`) — `rfqsApi.list()` calls it anyway per the user's "remove all mock data" instruction, so the RFQ list screen 404s until that endpoint is added. Flagged here for whoever picks up `Backend/PROGRESS.md` §2.
> - Also fixed while cross-checking DTOs: `ReqLine.requiredBy` is nullable (`DateOnly?` server-side, not a mandatory string), and `RequisitionSummary` never had a `lineCount` field (removed from the requisitions list column and the RFQ picker label).
## 4. Receiving screens
- [~] GRN list (`app/dashboard/receiving/grn/page.tsx`) — loading/empty/error states, links to detail
- [~] GRN create (`app/dashboard/receiving/grn/new/page.tsx`) — "Against PO" (picks an Approved/PartiallyReceived PO, prefills open lines) and "Direct receipt" (vendor + manual lines) modes; per-line bin, qty, unit cost, hold status, and batch (batchNo+expiryDate) or serial-number-list capture driven by the item's `trackingMode`
- [~] GRN confirm (`app/dashboard/receiving/grn/[id]/page.tsx`) — renders returned `createdLayers`/`ledgerRefs`/`poStatus` as a confirmation panel (20-FRONTEND §4); sends a stable `Idempotency-Key` per detail-page session
- [~] Inspection hold release / reject — Release/Reject buttons per on-hold line, shown once the GRN is `Confirmed`
- Sidebar: added "Receiving" nav entry (`components/Layouts/AppSidebar.tsx`) → `/dashboard/receiving/grn`
> **`[~]` not `[x]`, by design:** these screens are built against the documented+planned contract in `docs/11-BACKEND-PHASE1.md` §4, but **no GRN backend exists yet** (this was frontend-only work; see the deviation below).
>
> **2026-07-14 — mock data removed:** `lib/api/grns.ts` now calls real endpoint paths (`apiRequest`/`apiRequestWithETag` against `/grns`) instead of an in-memory store. There is still no `GrnsController` on the backend (`Backend/PROGRESS.md` §3 unstarted), so every call here 404s against a running backend — this was a deliberate, user-confirmed tradeoff (see `AskUserQuestion` exchange this session), not an oversight. Also added `grnsApi.getWithETag()` (the assumed `GET /grns/{id}` didn't have an ETag-returning variant, but `app/dashboard/receiving/grn/[id]/edit/page.tsx`'s `update()` call needs an `If-Match` token to send — matching the same pattern `purchase-orders.ts` already uses for `get`/`getWithETag`).
>
> **Deviation — `GET /grns` and `GET /grns/{id}`:** the API doc only specifies `POST /grns`, `POST /grns/{id}/confirm`, `POST /grns/{id}/lines/{id}/release` (no list/detail read). A list screen and a confirm/release screen both need to reload a GRN, so `lib/api/grns.ts` (`grnsApi.list`/`grnsApi.get`) and `types/grn.ts` assume these two GET endpoints will exist once the backend is built — flag this to whoever implements `Backend/PROGRESS.md` §3 so `docs/11-BACKEND-PHASE1.md` gets the corresponding doc update.
## 5. Stock screens
- [~] Stock hub (`app/dashboard/stock/page.tsx`) — card grid linking to all 7 areas below
- [~] Stock enquiry (`.../stock/enquiry`) — onHand/available/onHold/inTransit/reserved, search by SKU/name + warehouse filter, links to Valuation per row
- [~] Ledger view (`.../stock/ledger`) — filterable by item/warehouse/date range, paginated
- [~] Valuation view (`.../stock/valuation`) — item+warehouse picker (also reachable via `?itemId=&warehouseId=` from Enquiry), FIFO layer breakdown + totals
- [~] Transfer (`.../stock/transfers` list, `/new` create, `/[id]` dispatch → receive) — cost-preserving per line (FR-STK-06)
- [~] Adjustment (`.../stock/adjustments` list, `/new` create) — reason code mandatory, auto-posts on submit (no separate confirm step, matching FR-STK-07)
- [~] Count (`.../stock/counts` list, `/new` create, `/[id]` enter counts → post) — posting creates a linked variance adjustment
- [~] Reorder alerts (`.../stock/reorder-alerts`) — items ≤ reorder point, one-click "Create requisition"
- [~] Wastage (`.../stock/wastage` report, `/new` record) — **not a documented endpoint or SRS document type**: damage/theft-loss/expiry write-offs are modeled as Stock Adjustments with a loss-type reason code (FR-STK-07); `lib/api/wastage.ts` is a frontend-only lens over `stockAdjustmentsApi` + the shared mock ledger (filters to reason codes `DMG`/`LOSS`/`EXPWO`, flattens adjustment lines to per-item wastage records, sums matching outbound ledger entries for cost). No new backend concept — confirmed with the user before building (asked whether "Wastage" meant this vs. a distinct document type).
- Sidebar: added "Stock" nav entry (`components/Layouts/AppSidebar.tsx`) → `/dashboard/stock`; top-bar titles mapped per route (`components/Layouts/Header.tsx`)
> **`[~]` not `[x]`, by design — same posture as §4 Receiving:** built frontend-only (user request), against the documented+assumed Stock Core contract (`docs/11-BACKEND-PHASE1.md` §5), with **no real backend**.
>
> **2026-07-14 — mock data removed:** the in-memory Stock Core (`mockStockLayers`/`mockStockLedger`/`receiveLayer`/`consumeFifo`/etc., previously in `lib/api/mock-data.ts`) is gone. `stock.ts`/`stock-transfers.ts`/`stock-adjustments.ts`/`stock-counts.ts`/`reason-codes.ts` now call real endpoint paths; `stockApi.onHandList()` was rewritten to compose real (`itemsApi.list` × `warehousesApi.list` × `stockApi.onHand` per pair) calls instead of iterating a mock-derived key set. `wastage.ts` was rewritten the same way — it now calls `reasonCodesApi.list()`/`stockAdjustmentsApi.list()+get()`/`stockApi.ledger()` instead of reading mock arrays directly, so `wastageReasonCodeIds()` and `wastageApi.list()` are now `async` (both call sites in `app/dashboard/stock/wastage/{page,new/page}.tsx` were updated accordingly). **None of §5's backend exists yet** (`Backend/PROGRESS.md` §4/§5 unstarted), so every one of these calls 404s against a running backend — a deliberate, user-confirmed tradeoff (see `AskUserQuestion` exchange this session), not an oversight. `tsc --noEmit` and `eslint` are clean (only the pre-existing `login/page.tsx` error and the established `set-state-in-effect` pattern remain, unchanged from before this pass).
>
> **Deviations (same pattern as GRN, see §4):** `GET`/detail list endpoints for transfers/adjustments/counts (`lib/api/stock-transfers.ts`, `stock-adjustments.ts`, `stock-counts.ts`) are assumed extensions beyond `docs/11-BACKEND-PHASE1.md` §5.4-5.6, which document only the transactional POSTs/PUT. `stockApi.onHandList()` (used by the Enquiry screen) is also not a documented endpoint — it's a frontend-only convenience; a real backend would want a proper list endpoint instead. Flag all of these to whoever implements `Backend/PROGRESS.md` §4/§5 (Stock Core + stock transactions).
>
> **Simplifications (mock-data limitations, not spec decisions):** `StockLayer` has no per-bin field (matches the real ER model, docs/10 Part C.5 — only `StockLedger` carries `bin_id`), so Count lines don't attempt bin-level snapshotting. Transfers don't expose batch selection in the create UI (FIFO picks layers regardless of batch). Adjustment increases always cost at "last known cost" for that item/warehouse (FR-STK-07); there's no landed-cost/manual-cost override. In-transit quantity is shown for visibility at the destination warehouse only and is not subtracted a second time from the source's `available` (dispatch already reduced the source layer's `qtyRemaining`) — the docs' `available = onHand onHold reserved inTransit(out)` formula is ambiguous on this point given dispatch semantics; this was a judgment call, noted here for whoever builds the real backend to confirm or correct.
## 6. Validation posture (20-FRONTEND §3)
- [~] Client format/required/range checks on all forms — done for GRN create (`lib/validations/grn.ts`); not yet done for other forms
- [x] Surface server `ProblemDetails` incl. domain codes; map to fields/messages — `lib/error-map.ts` (`errorMessage`/`fieldErrors`), used by GRN create/detail
- [ ] `412` conflict → prompt refetch before retry — `apiRequestWithETag` surfaces the ETag but no screen edits a GRN yet (GRN has no PUT), so untested in practice
- [x] No client-side gating on stock/availability/status (server-authoritative) — GRN create always submits to the server and surfaces `OVER_RECEIPT_TOLERANCE`/etc. via `error-map.ts` rather than pre-blocking
## 7. UX states
- [~] Loading / empty / error states on every list — done for the GRN list/create/detail screens; other screens still unbuilt
- [x] Transactional actions show server-returned side effects as confirmation — GRN confirm renders `createdLayers`/`ledgerRefs`/`poStatus` from the response
## Done
<!-- move [x] items here with date + note if the active list grows long -->
### 2026-07-13 — GRN screens + frontend foundation (frontend-only; no backend changes)
- Foundation: `lib/api-client.ts`, `lib/error-map.ts`, `lib/auth-token.ts`, `types/{common,master-data,procurement,grn}.ts`, `lib/api/{grns,purchase-orders,warehouses,items,vendors,uoms}.ts` — scoped to what the GRN flow needs, not the full API surface.
- Added the shadcn `select` primitive (`npx shadcn add select`) — wasn't in `components/ui/` yet; needed for PO/vendor/warehouse/bin/item/hold-status pickers.
- Screens: GRN list, GRN create (PO-based + direct receipt, batch/serial capture by `trackingMode`), GRN detail (confirm + release/reject). Sidebar nav entry added.
- **This was explicitly frontend-only** (user interrupted an initial backend+frontend plan and asked for frontend only). No GRN backend exists — `Backend/PROGRESS.md` §3/§4 are unchanged. The screens are built against the contract in `docs/11-BACKEND-PHASE1.md` §4 plus two assumed-but-undocumented endpoints (`GET /grns`, `GET /grns/{id}`, see §4 note above); none of it is runnable end-to-end yet.
- Verified: `tsc --noEmit` clean for all new/edited files (one pre-existing, unrelated error remains in `app/login/page.tsx`); `eslint` clean aside from two `react-hooks/set-state-in-effect` warnings matching an already-existing pattern in `hooks/use-mobile.ts`; all three routes confirmed rendering (200, correct content, no error boundary) via SSR against the dev server.
### 2026-07-13 — Stock Management screens (frontend-only; no backend changes)
- `types/stock.ts`: full DTO set for on-hand, ledger, valuation, transfers, adjustments, counts, reorder alerts (docs/11 §5).
- `lib/api/mock-data.ts` gained a real in-memory Stock Core: `mockStockLayers`/`mockStockLedger` + `receiveLayer`/`consumeFifo`/`postLedgerEntry`/`computeOnHand`/`lastKnownCost` helpers, plus `mockItemReorders` and `mockReasonCodes` seed data. `lib/api/grns.ts`'s `confirm()` was refactored to post through these helpers instead of fabricating a response, and now also accrues PO `qtyReceived`/recomputes PO status — so GRN and Stock screens are genuinely connected this session.
- New API modules: `lib/api/stock.ts` (on-hand/ledger/valuation/reorder-alerts), `stock-transfers.ts`, `stock-adjustments.ts`, `stock-counts.ts`, `reason-codes.ts` — same commented-real-block + active-mock-block pattern as the GRN modules.
- Screens: hub, Enquiry, Ledger, Valuation, Transfers (list/new/detail with dispatch+receive), Adjustments (list/new, auto-post), Counts (list/new/detail with enter-counts+post), Reorder Alerts. New shared badge set `components/stock/status-badges.tsx` (same fixed-size red/green/yellow convention as `components/receiving/status-badges.tsx`). Sidebar + header-title mappings added.
- Same posture as the GRN pass: `[~]` not `[x]`, frontend built ahead of a nonexistent Stock Core backend, deviations/simplifications recorded in the §5 note above. `tsc --noEmit` and `eslint` clean (only the same pre-existing/established issues as the GRN pass).
### 2026-07-13 — Wastage screens (frontend-only; no backend changes)
- `lib/api/wastage.ts`: no new backend concept — confirmed with the user that "Wastage" should be a focused UI lens over the just-built Stock Adjustments (damage/theft-loss/expiry write-off reason codes), not a distinct document type. Filters `mockStockAdjustments` to loss-type reason codes, flattens to per-item `WastageRecord`s, and computes cost per record from matching outbound `mockStockLedger` entries.
- Screens: `.../stock/wastage` (report — totals cards, warehouse/reason filters, per-item table) and `.../stock/wastage/new` (single-line record form, reason dropdown restricted to wastage-type codes, posts via the existing `stockAdjustmentsApi.create`). Added a "Wastage" card to the Stock hub and header-title mappings.
- Verified: `tsc --noEmit` clean (same pre-existing `login/page.tsx` error only); `eslint` clean aside from one more instance of the already-established `set-state-in-effect` pattern.
### 2026-07-13 — Warehouse Management screens (frontend-only; no backend changes)
- Scope, per user selection out of the four FR-WH sub-areas offered (Warehouses & Bins / Stock Locator / Batch & Serial / Putaway): **Warehouses & Bins master data only** (FR-WH-01, FR-MD-07). The other three (bin-level Stock Locator, Batch/Serial tracking, Putaway) were **not** built — flagged here so a future pass knows they're still open, not forgotten.
- `lib/api/warehouses.ts` gained `create`/`get`/`createBin` (previously list/listBins only, read-only) — duplicate-code validation mirrors the real `SKU_DUPLICATE`-style 400 pattern used elsewhere. `mock-data.ts` gained `allocateWarehouseId`/`allocateBinId`.
- Screens: `app/dashboard/warehouse` (list + "New Warehouse" `Dialog` form) and `app/dashboard/warehouse/[id]` (bin list + "New Bin" `Dialog` form) — used `components/ui/dialog.tsx` instead of a full page for these two-field creates, since a whole page felt heavy for that. Sidebar "Warehouses" entry + header-title mapping added.
- Housekeeping: removed two stray duplicate route folders (`app/dashboard/receiving/grn/create new GRN/`, `.../view GRN/`) that were byte-for-byte copies of the real `new/` and `[id]/` GRN pages under garbled folder names — almost certainly an IDE artifact from an earlier malformed file-open path, not intentional work (confirmed untracked in git before removing). Also noted, but deliberately left alone: `app/warehouse/*`, `components/warehouse/`, `lib/warehouse/` are pre-existing **empty** scaffold folders (no files at all) from initial project setup — Warehouse Management was built under `app/dashboard/warehouse/*` instead so it gets the dashboard chrome (sidebar/header) for free, consistent with every other screen this session.
- Verified: `tsc --noEmit` clean (same pre-existing `login/page.tsx` error only); `eslint` clean aside from one `exhaustive-deps` warning (not an error) on `[id]/page.tsx`'s `loadBins` helper.
### 2026-07-13 — Vendor (Supplier) management screens (frontend-only; no backend changes)
- Confirmed with the user first: "supplier/shop" has no distinct "Shop" entity in the SRS/docs — scoped this to the documented Vendor master (FR-MD-06, docs/11 §2.4), "supplier" being the standard ERP synonym.
- `lib/api/vendors.ts` extended from list-only to `get`/`create`/`update`/`updateStatus`. This is the **first screen to exercise the ETag/If-Match/412 pattern**: `mock-data.ts` gained a per-vendor concurrency-token map (`getVendorVersion`/`bumpVendorVersion`/`initVendorVersion`, standing in for the real backend's `xmin` — the public `Vendor` type has no version field of its own since it travels as an HTTP `ETag` header, not a body field) so `update()` genuinely rejects a stale `If-Match` with `CONCURRENCY_CONFLICT`, matching `docs/11 §1.6` and `20-FRONTEND.md §3.2`.
- Screens: `app/dashboard/vendors` (list, search + status filter, "New Vendor" dialog) and `app/dashboard/vendors/[id]` (full edit form using the real `apiRequestWithETag`-shaped `ApiResult<T>`, a dedicated conflict banner with "Reload before retrying" per the 412 UX rule rather than a generic toast, and an Activate/Deactivate toggle via `PATCH status`, FR-MD-08 — deactivate, not hard-delete). Sidebar "Vendors" entry + header-title mapping added.
- Verified: `tsc --noEmit` clean (same pre-existing `login/page.tsx` error only); `eslint` clean aside from the same established `set-state-in-effect` pattern used throughout this session.
- **Follow-up (same day):** `vendorsApi.list()` didn't actually paginate (always returned page 1 / all matches, same latent gap the GRN list had before its own pagination pass) — fixed to slice by `page`/`pageSize` properly, added the same Previous/Next pagination controls used on the GRN and Stock list screens, and seeded 8 more sample vendors so there's something real to page through.
### 2026-07-13 — Procurement screens: Requisition → RFQ → PO → Purchase Return (frontend-only; no backend changes)
- `types/procurement.ts` grew from a GRN-support subset (PO read types only) to the full §3 DTO set: Requisition/ReqLine, Rfq/RfqLine/Quotation/RfqComparison, PO create/update/cancel request types, PurchaseReturn/PurchaseReturnLine — mirrors `docs/11-BACKEND-PHASE1.md` §3 request/response JSON exactly (no `deliveryDate` field on PO lines, since the documented `POST /purchase-orders` example doesn't carry one despite FR-PROC-03's prose — contract-over-prose per `docs/20-FRONTEND.md` §1).
- `lib/api/mock-data.ts`: added `mockRequisitions`/`mockRfqs`/`mockQuotations`/`mockPurchaseReturns` + allocators, a PO concurrency-token map (`getPoVersion`/`bumpPoVersion`/`initPoVersion`, same out-of-band ETag pattern as vendors), and `consumeLayerByGrnLine` — a *new* consumption path deliberately separate from `consumeFifo`: a Purchase Return disposes of the exact layer its GRN line created (often `OnHold`/`Rejected`, which `consumeFifo`'s hold filter would otherwise skip), not "the oldest open layer for this item/warehouse". Seeded Requisition #210 to match the existing `mockPurchaseOrders[0].requisitionId` so the two screens cross-reference.
- New API modules: `lib/api/requisitions.ts`, `lib/api/rfqs.ts` (create/addQuotation/comparison — comparison is computed client-side from recorded quotations), `lib/api/purchase-returns.ts`. `lib/api/purchase-orders.ts` extended from list/get-only (its original GRN-support scope) to full create/update/cancel; added `getWithETag`/`isPoEditable` without touching the existing plain `get()` GRN's create-flow already depends on, so no existing call site broke.
- **Wiring, not just new screens:** `stockApi.createReorderRequisition` (Stock → Reorder Alerts, built in an earlier session) previously fabricated a response with no backing record; it now pushes a real row into `mockRequisitions`, so a reorder-triggered requisition genuinely shows up in the new Requisitions list — same "connect the mock modules together" posture as GRN confirm → Stock Core.
- Screens: Requisition (list/new/detail+Submit), RFQ (list/new with checkbox vendor multi-invite reusing the Stock Count's `Checkbox` pattern/new with quotation-recording form + vendor-by-line comparison table/"Create PO from vendor"), Purchase Order (list/new — accepts `?requisitionId=` or `?rfqId=&vendorId=` to prefill lines and pricing/detail with inline edit-while-open using the vendor `[id]` page's ETag+412-conflict-banner pattern, plus a Cancel-with-reason flow client-disabled when any line has receipts), Purchase Return (list/new — pick a Confirmed/Closed GRN, checkbox+qty its lines; also reachable via a new "Create Return" button next to `Rejected` lines on the GRN detail page, matching the SRS flow diagram's Reject→Purchase Return step). New `components/procurement/status-badges.tsx` (same fixed-width badge convention as Receiving/Stock) and `lib/validations/procurement.ts` (zod-free hand-rolled, matching the GRN validation file's style, not its `zod` deviation).
- Sidebar: added "Procurement" nav entry between Vendors and Receiving; header title mappings added for all new routes.
- Same `[~]` posture as every other module this session: built against the documented+assumed Procurement contract (`docs/11-BACKEND-PHASE1.md` §3), no Procurement backend exists (`Backend/PROGRESS.md` §2 unchanged).
- Verified: `tsc --noEmit` clean (same pre-existing `login/page.tsx` error only); `eslint` clean aside from the same established `set-state-in-effect` pattern used throughout this session (confirmed it also fires on the pre-existing `grn/page.tsx`/`vendors/page.tsx`/`hooks/use-mobile.ts` — not a regression); `npm run build` compiles successfully via Turbopack (same pre-existing login type-check failure blocks the full build, unrelated). All 12 new routes plus the GRN-detail "Create Return" link (including three query-param-prefilled variants) confirmed rendering 200 with no error boundary via SSR against the dev server.
### 2026-07-13 — Master Data screens: Items, UOM, Categories, Reorder settings (frontend-only; no backend changes)
- `types/master-data.ts`: `ItemListItem` (the GRN/PO/Requisition/RFQ item-picker subset built in earlier sessions) is now a derived view of a new full `Item` type — SKU/name/description/category/baseUom/defaultVendor/type/trackingMode/taxClass/status plus `reorder: ItemReorderSetting[]` (matches the documented `GET /items/{itemId}` example inline) and `conversions: UomConversion[]` (**deviation**: the doc's example response only shows `reorder`, and conversions are otherwise reachable only via `PUT /items/{itemId}/uom-conversions` with no matching GET — embedding them on the full resource, like the assumed `GET /grns`/`GET /grns/{id}` reads elsewhere in this app, lets the Item detail screen show current conversions before editing). Also added `Category`/`CategoryTreeNode`, `CreateUomRequest`, `CreateCategoryRequest`, and Item create/update/reorder/conversion request types (docs/11 §2.1-2.3).
- `lib/api/mock-data.ts`: `mockItems` changed storage shape from `ItemListItem[]` to full `Item[]` (only `mock-data.ts` and `lib/api/items.ts` touched it directly, confirmed by grep, so no other call site broke) — `lib/api/items.ts`'s `list()` now maps down to `ItemListItem`, same "full record → mapped summary" pattern as `mockPurchaseOrders``PurchaseOrderSummary`. Added a per-item concurrency-token map (`getItemVersion`/`bumpItemVersion`/`initItemVersion`, same out-of-band ETag pattern as vendors/POs), `mockCategories` seeded with a 2-root/1-child tree matching the category IDs the existing sample items already reference (12 "Fasteners" under 3 "Hardware"; 20 "Power Tools"), and a UOM id allocator.
- New API modules: `lib/api/categories.ts` (`list`/`tree`/`create``tree()` builds the nested structure client-side from the flat list, since the mock has no separate tree-storage concept). `lib/api/items.ts` grew from list-only (its original GRN-picker scope) to full `get`/`create`/`update`/`updateStatus`/`updateReorder`/`updateUomConversions`; `lib/api/uoms.ts` gained `create`.
- Screens: Items (`app/dashboard/products`**reused the pre-existing "Products" sidebar entry and stub route** rather than adding a new nav item, since it was already wired to an empty placeholder page; list has search + category/tracking-mode/status filters + pagination, `/new` create, `/[id]` detail combining three independently-saved sections in one page — basic info with ETag/If-Match + 412-conflict banner mirroring the Vendor `[id]` page, a Reorder Settings row-editor posting `PUT /items/{itemId}/reorder`, and a UOM Conversions row-editor posting `PUT /items/{itemId}/uom-conversions` — matching how the API groups these as sub-resources of Item rather than separate top-level screens). UOM (`app/dashboard/products/uoms` — flat list + create dialog, same shape as the Warehouses list). Categories (`app/dashboard/products/categories` — indented recursive tree view + create dialog with a parent picker). `lib/validations/master-data.ts` added (hand-rolled, matching the GRN validation file's style, not its `zod` deviation). Header title mappings added for all `/dashboard/products/*` routes.
- **Housekeeping:** removed `app/dashboard/vendors/view vendors/` — confirmed byte-for-byte identical to `vendors/[id]/page.tsx` and untracked in git, same IDE-artifact pattern (malformed file-open path) as the garbled GRN duplicate folders removed in the Warehouse Management pass; noted here rather than silently dropped. Left `app/dashboard/receiving/grn/[id]/edit/` alone — it's untracked too but is a distinct, non-duplicate GRN-edit screen, not an artifact.
- Same `[~]` posture as every other module this session: built against the documented+assumed Master Data contract (`docs/11-BACKEND-PHASE1.md` §2), no Master Data backend exists (`Backend/PROGRESS.md` §1 unchanged).
- Verified: `tsc --noEmit` clean after clearing a stale `.next` type cache that still referenced the just-deleted `view vendors` route (same pre-existing `login/page.tsx` error only remains); `eslint` clean aside from the same established `set-state-in-effect` pattern; `npm run build` compiles successfully via Turbopack (same pre-existing login type-check failure, unrelated). All 5 new/changed routes confirmed rendering 200 with no error boundary against the dev server (one false-alarm 500 during testing traced to an unrelated stale process already bound to port 3000, not this code — retested clean on the actual dev server port).
### 2026-07-14 — Mock data removed everywhere; Master Data + Procurement wired to the live backend
- User asked to integrate the (now-built) backend into the frontend, frontend-only, no mock data. Confirmed scope first: the backend only has controllers for Master Data (`Backend/PROGRESS.md` §1) and Procurement minus returns (§2) — GRN (§3), Stock Core (§4), Stock Transactions (§5), and Auth (§6) have no controllers at all yet. User's explicit instruction after that was still "remove mock data in all" — so every `lib/api/*.ts` module was switched to real `fetch` calls, accepting that GRN/Stock/Purchase-Return/Reason-Code calls will 404 against a live backend until that work exists (not silently left mocked).
- **Deleted `lib/api/mock-data.ts` entirely** and restored the real-`fetch` implementation in every one of: `categories`, `uoms`, `warehouses` (added missing `get()`; wrapped the real non-paged `GET /warehouses/{id}/bins` array response into a synthetic `PagedResponse<Bin>` so existing `.items` call sites kept working), `items`, `vendors`, `requisitions`, `purchase-orders`, `purchase-returns`, `reason-codes`, `grns` (added `getWithETag()` for the GRN edit page's `If-Match`), `stock`, `stock-transfers`, `stock-adjustments`, `stock-counts`. `wastage.ts` had no prior real-mode block (it's a frontend-only lens with no documented endpoint of its own) — rewrote it to compose the now-real `reasonCodesApi`/`stockAdjustmentsApi`/`stockApi` calls instead of reading mock arrays directly; its two exports became `async` as a result, and both call sites (`app/dashboard/stock/wastage/{page,new/page}.tsx`) were updated.
- **Did not blindly trust the frontend's pre-written "real implementation" comments** — cross-checked every Master Data/Procurement DTO against the actual `Backend/ERPCore/Dtos/**/*.cs` and controllers before wiring, since those blocks were written speculatively before/alongside the real backend and had drifted in the RFQ case (see the dedicated §3 deviation note above): the backend doesn't persist RFQ-invited vendors, `requisitionId` is required (not optional) to create an RFQ, `RfqComparisonDto` uses `rows`/`quotes`/`quotationId` (not `lines`/`cells`), and `RequisitionSummaryDto` has no `lineCount`. Fixed `types/procurement.ts` and the RFQ list/detail/new pages plus the PO-from-RFQ prefill accordingly, rather than shipping types that would silently be `undefined` at runtime.
- Left `app/login/*` untouched — no mock auth existed to remove, and there's still no `POST /auth/login` controller to wire to.
- Verified: `tsc --noEmit` clean (only the pre-existing, unrelated `login/page.tsx` resolver-typing error remains — confirmed pre-existing via `git` history, not introduced here). `eslint` shows the same established `react-hooks/set-state-in-effect`/`static-components` pattern as before, confirmed unchanged by spot-checking it also fires on files untouched this session (`app/login/forgot/reset/page.tsx`). Did not start the backend/Postgres or click through the UI live in this pass — verification was type-check + lint only; the Master Data/Procurement screens should be smoke-tested against a running `dotnet run` + Postgres before considering this "done" in practice.