toga-ai 1.0.375 → 1.0.376

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@
4
4
  |-----|---------|-------|
5
5
  | [TOGa 2.5 Supply — Architecture](architecture.md) | `toga25-supply` ("TOGa 2.5 Supply") is the **React/TypeScript frontend** for the 2.0 Supply application — an iteration and improvement of `toga2-supply`. | toga25-supply/src/main.tsx, toga25-supply/src/App.tsx, toga25-supply/src/routes.tsx, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/layout/, toga25-supply/src/pages/, toga25-supply/src/hooks/useTableCellInteractions.ts |
6
6
  | [Action-Button Rule Engine (Flag / Rule grammar)](features/action-button-rule-engine.md) | A declarative, fully config-driven rule engine that resolves the boolean-ish flags (`isEnabled`, `isVisible`, `isComplete`) on SalesOrder action-button options. | toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
7
+ | [Client API-Fetch Fields (useClientApiFields / apiFields.json)](features/client-api-fetch-fields.md) | The mechanism that resolves a client's **API-fetch projection** — which `fields` / `ojoin` / `join` / `where` to request from the TOGa API for a given page — fr | toga25-supply/src/fieldsConfig/resolveApiConfig.ts, toga25-supply/src/fieldsConfig/useClientApiFields.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx |
7
8
  | [Client-Configurable Fields (useClientFields / fieldsConfig)](features/client-configurable-fields.md) | The mechanism for config that **varies by client** (or client × role) — field overrides, filter buttons, group-by options, column pickers, layout toggles — with | toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/README.md, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/layout/VendorItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/ |
8
9
  | [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
9
10
  | [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: Client API-Fetch Fields (useClientApiFields / apiFields.json)
3
+ framework: "2.0"
4
+ repo: toga25-supply
5
+ project: TOGa 2.5 Supply
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-07-20
10
+ owners: [apeterson]
11
+ files:
12
+ - toga25-supply/src/fieldsConfig/resolveApiConfig.ts
13
+ - toga25-supply/src/fieldsConfig/useClientApiFields.ts
14
+ - toga25-supply/src/fieldsConfig/index.ts
15
+ - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json
16
+ - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx
17
+ - toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/apiFields.json
18
+ - toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx
19
+ related:
20
+ - client-configurable-fields.md
21
+ - surface-frontend.md
22
+ ---
23
+
24
+ ## What it is
25
+
26
+ The mechanism that resolves a client's **API-fetch projection** — which `fields` / `ojoin` /
27
+ `join` / `where` to request from the TOGa API for a given page — from **one colocated
28
+ `apiFields.json` file per page**, keyed by client slug. It is the fetch-projection sibling of
29
+ [client-configurable-fields](client-configurable-fields.md) (`useClientFields`): that hook
30
+ resolves **presentation** config (now largely moving to the DB-backed
31
+ [Surface layer](surface-frontend.md)); this hook resolves **what data to fetch**.
32
+
33
+ The split exists because presentation config migrated to Surface, leaving the API-fetch wiring
34
+ coupled to a now-legacy `orderViewFields`/`*RecordViewFields` blob (formerly the
35
+ `salesOrderDetailsConfig` / `itemRecordDetailsConfig` keys). This mechanism extracts the fetch
36
+ projection into its own scalable per-page file + a shared resolver, keeping the central
37
+ `fieldsConfig/index.ts` registry lean — **each page owns its own `apiFields.json`** rather than
38
+ folding every page's fetch config into `fieldsConfig/index.ts`.
39
+
40
+ ## How it works
41
+
42
+ Two new files in `src/fieldsConfig/`, re-exported from `fieldsConfig/index.ts`:
43
+
44
+ 1. **`apiFields.json` (per page, colocated in the page's `viewModel/FIELDS/`).** A map keyed by
45
+ client slug (uppercase — `DEFAULT`, `COMPASS`, `COMPASSCANADA`, `NYCHH`, …). The `DEFAULT`
46
+ block is the **full base**. Each per-client block is one of two shapes:
47
+ - **Delta** — `{ "extends": "DEFAULT", ...only the differences }`, merged over its parent.
48
+ Use for the common 1–3 field tweak; adding a field to `DEFAULT` then reaches every client.
49
+ - **Full override** — a complete config with **no `extends`**, returned verbatim. Use when a
50
+ client diverges substantially (many fields / different joins / different `where` clauses).
51
+
52
+ 2. **`resolveApiConfig(map, clientSlug)`** — a pure, recursive deep-merge resolver:
53
+ - **Array fields** (`fields`, `ojoin`, `join`) support `add<Field>` / `remove<Field>` sugar
54
+ (`addFields`, `removeFields`, `addOjoin`, …) to tweak the parent's array, **or** you restate
55
+ the array literally to replace it wholesale (a literal wins over add/remove).
56
+ - **Nested objects** merge recursively; **scalars / new keys** replace the parent value.
57
+ - **Array element identity** for dedupe/removal: strings by value, join objects by their
58
+ **first object key** (e.g. `{ "Items": {...} }` → `Items`), else by JSON form. Results are
59
+ deduped.
60
+ - Supports **chained `extends`** (a client can extend another client) with a **cycle guard**
61
+ (a re-visited slug returns its own delta un-merged). An **unknown client falls back to
62
+ `DEFAULT`**; a missing `DEFAULT` yields `{}`.
63
+
64
+ 3. **`useClientApiFields(map)`** — reads the client slug via
65
+ `useHostnameStore.getState().getHostname()` (the **same** slug source as `useClientFields`),
66
+ then `useMemo`s `resolveApiConfig(map, slug)` so consumers get a **stable object reference**
67
+ and the react-query fetch cache key does not churn every render. API-fetch fields are
68
+ **client-only** — there is no role dimension (they describe *what to fetch*, not a per-role
69
+ view).
70
+
71
+ The view model consumes it directly:
72
+
73
+ ```ts
74
+ const salesOrderDetailsConfig = useClientApiFields(apiFields) as Parameters<
75
+ typeof useFetchSalesOrderRecord
76
+ >[1];
77
+ ```
78
+
79
+ ## Hybrid dedup model (decision)
80
+
81
+ The per-client shape is deliberately a **hybrid**: `DEFAULT` base + per-client delta, with a
82
+ full-override escape hatch. Chosen over full-copy-per-client because differences are usually
83
+ 1–3 fields (the delta case, kept DRY so a field added to `DEFAULT` reaches every client) but can
84
+ be 5+ fields plus different joins/where clauses (the full-override case). Full-copy-per-client
85
+ drifts too fast across 6+ clients. The resolver location is likewise a decision: a **generic
86
+ colocated hook** (per-page `apiFields.json` + one shared hook), **not** folding every page's
87
+ fetch config into the central `fieldsConfig/index.ts`.
88
+
89
+ ## Adopting the pattern on a page
90
+
91
+ 1. Add `viewModel/FIELDS/apiFields.json`: a `DEFAULT` full base, plus a block per client that
92
+ diverges (`extends: "DEFAULT"` + `add<Field>`/`remove<Field>` deltas, or a no-`extends` full
93
+ override). A client whose config is byte-identical to `DEFAULT` can be **omitted** — the
94
+ resolver's DEFAULT fallback covers it.
95
+ 2. Wire the view model to `useClientApiFields(apiFields)`, casting to the fetch hook's param type
96
+ (`Parameters<typeof useFetch...>[1]`).
97
+ 3. Delete the now-dead fetch block from the legacy presentation JSON (single source of truth).
98
+
99
+ **Adopted so far:**
100
+ - **SalesOrder record modal** — `SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json`
101
+ (DEFAULT full base; COMPASS / COMPASSCANADA as `extends: DEFAULT` + `addFields` deltas, e.g.
102
+ `_totalLease`, `c_erpEntityId`). The view model's `salesOrderDetailsConfig` now comes from
103
+ `useClientApiFields(apiFields)` instead of `tenantFields.salesOrderDetailsConfig`; the dead
104
+ `salesOrderDetailsConfig` block was removed from `FIELDS/DEFAULT/orderViewFields.json`.
105
+ - **Item record modal** — `ItemRecordModalLayout/viewModel/FIELDS/apiFields.json` (DEFAULT full
106
+ base of 31 fields; COMPASS + COMPASSCANADA as `extends: DEFAULT` + a 6-field `addFields` delta:
107
+ `_leasePrice`, `_leasePrice12Months`, 4× `personaItems.*`; **NYCHH omitted** — byte-identical to
108
+ DEFAULT). The view model consumes `useClientApiFields(apiFields)` (dropping its own
109
+ `useClientFields`; view sections still read `useClientFields` in the view components). The dead
110
+ `itemRecordDetailsConfig` block was removed from all four client
111
+ `itemRecordViewFields.json` files, which now hold only presentation keys
112
+ (`header`/`sections`/`editItem`).
113
+
114
+ ## Gotchas
115
+
116
+ - **Field order in a `fields` projection array is irrelevant to the TOGa API fetch.** The API
117
+ returns requested fields regardless of order, and the reshape/view logic reads by key — so
118
+ append-style delta merges (added fields land at the end) are **safe** and do not change
119
+ behavior. This is why `add<Field>` sugar can append without needing positional control.
120
+ - **Omit a client that matches `DEFAULT`.** Don't author a byte-identical client block — the
121
+ resolver falls back to `DEFAULT` for any unknown/absent client, so an identical block is pure
122
+ drift risk (NYCHH on the Item modal is deliberately absent).
123
+ - **Same slug source as presentation.** The slug comes from `useHostnameStore.getHostname()` (the
124
+ same source as `useClientFields`); don't introduce a second slug resolution path.
125
+
126
+ ## Change history
127
+ - 2026-07-20 — Built the client-based API-fetch fields mechanism: `resolveApiConfig` (pure
128
+ recursive deep-merge with `add`/`remove` array sugar, literal-replace, chained `extends` +
129
+ cycle guard, DEFAULT fallback) + `useClientApiFields` (client-only, slug via `useHostnameStore`,
130
+ memoized for stable fetch cache keys), each page owning a colocated `apiFields.json`. Adopted on
131
+ the SalesOrder and Item record modals; removed the dead `salesOrderDetailsConfig` /
132
+ `itemRecordDetailsConfig` fetch blocks from the legacy presentation JSON. Decision: hybrid
133
+ DEFAULT-base + per-client-delta with a full-override escape hatch (over full-copy-per-client).
134
+ Discovered: `fields`-array order is irrelevant to the API fetch. (apeterson)
135
+ </content>
136
+ </invoke>
@@ -22,6 +22,7 @@ related:
22
22
  - ../architecture.md
23
23
  - column-visibility.md
24
24
  - action-button-rule-engine.md
25
+ - client-api-fetch-fields.md
25
26
  ---
26
27
 
27
28
  ## What it is
@@ -31,6 +32,14 @@ filter buttons, group-by options, column pickers, layout toggles — without har
31
32
  the view model or rebuilding per client. All such config resolves through `src/fieldsConfig`
32
33
  and the `useClientFields()` hook.
33
34
 
35
+ > **Scope — presentation, not fetch.** This doc covers **presentation/UI** config. A client's
36
+ > **API-fetch projection** (which `fields`/`ojoin`/`join`/`where` to request) now lives in a
37
+ > separate per-page `apiFields.json` resolved by `useClientApiFields` — see
38
+ > [client-api-fetch-fields](client-api-fetch-fields.md). The old fetch keys
39
+ > (`salesOrderDetailsConfig` in `orderViewFields`, `itemRecordDetailsConfig` in
40
+ > `itemRecordViewFields`) were **extracted out** of these presentation JSON files; those files
41
+ > now hold presentation keys only.
42
+
34
43
  ## How it works
35
44
 
36
45
  1. **Per-client JSON lives next to the consuming page _or layout_**, under `FIELDS/<CLIENT>/`
@@ -159,6 +168,11 @@ modal's chrome from the same per-client JSON, not from the component:
159
168
  `vendorItemRecordViewFields` are record-modal-layout examples living under `src/layout/.../viewModel/FIELDS/`.
160
169
 
161
170
  ## Change history
171
+ - 2026-07-20 — Extracted the **API-fetch projection** out of these presentation JSON blobs into a
172
+ new per-page `apiFields.json` + `useClientApiFields` mechanism (see
173
+ [client-api-fetch-fields](client-api-fetch-fields.md)). The `salesOrderDetailsConfig` block was
174
+ removed from `FIELDS/DEFAULT/orderViewFields.json` and the `itemRecordDetailsConfig` block from
175
+ all four `itemRecordViewFields.json` files; those files now hold presentation keys only. (apeterson)
162
176
  - 2026-07-01 — De-roled `orderViewFields` from client×role to **client-only**: COMPASS/COMPASSCANADA
163
177
  now read a flat client JSON via `mergeOrderView(...)` in `fieldsConfig/index.ts` (COMPASSCANADA's
164
178
  JSON flattened from byte-identical `{ADMIN,MANAGER}`). Fixed a latent bug where COMPASS's
@@ -29,7 +29,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
29
29
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
30
30
  - **ai-bdr** (AI-BDR) — 7 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
31
31
  - **toga2-commerce** (TOGa Commerce) — 9 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
32
- - **toga25-supply** (TOGa 2.5 Supply) — 9 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
32
+ - **toga25-supply** (TOGa 2.5 Supply) — 10 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
33
33
  - **toga-blox** (TOGa Blox) — 7 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
34
34
  - **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
35
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.375",
3
+ "version": "1.0.376",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",