toga-ai 1.0.251 → 1.0.253
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.
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
| [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
9
|
| [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 |
|
|
10
10
|
| [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/, toga25-supply/src/hooks/useTableCellInteractions.ts |
|
|
11
|
-
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts |
|
|
11
|
+
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.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/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts |
|
|
12
12
|
| [Cypress Testing Harness (component + e2e)](workflows/cypress-testing.md) | The Cypress test harness for the `toga25-supply` frontend, bootstrapped from scratch (`cypress` was already a dependency but there was no config, no `cypress/` | toga25-supply/cypress.config.ts, toga25-supply/cypress/tsconfig.json, toga25-supply/cypress/support/component.tsx, toga25-supply/cypress/support/component-index.html, toga25-supply/cypress/support/e2e.ts, toga25-supply/cypress/support/commands.ts, toga25-supply/cypress/support/fixtures.ts, toga25-supply/cypress/support/mocks/useApprovalModalViewModel.ts, toga25-supply/cypress/component/SalesOrderApprovalModalsLayout.cy.tsx, toga25-supply/cypress/component/RecordApprovalModalLayout.cy.tsx, toga25-supply/cypress/component/EnterPoNumberModal.cy.tsx, toga25-supply/cypress/e2e/salesOrderApproval.cy.ts |
|
|
@@ -11,7 +11,11 @@ owners: [jcardinal, apeterson]
|
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/surface/useFetchSurfaceMeta.ts
|
|
13
13
|
- toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts
|
|
14
|
+
- toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts
|
|
14
15
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx
|
|
16
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx
|
|
17
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx
|
|
18
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx
|
|
15
19
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts
|
|
16
20
|
- toga25-supply/src/surface/evaluateSurfaceRule.ts
|
|
17
21
|
- toga25-supply/src/surface/actionRegistry.ts
|
|
@@ -56,15 +60,18 @@ feature flag — see below). Backend: [surface-resolver](../../_underscore/featu
|
|
|
56
60
|
**structural fingerprint**: `looksLikeBundle` now requires **both** an `elements` array **and** a
|
|
57
61
|
`surface` descriptor object (not the `elements` array alone — see the decoy gotcha below).
|
|
58
62
|
`extractBundle(raw, slug)` then **prefers** the bundle whose `surface.slug === slug`, falling back to
|
|
59
|
-
first-found, so a stray sibling bundle can't win.
|
|
60
|
-
discriminator key so a single-bundle envelope is not mistaken for a group. On a malformed payload it
|
|
63
|
+
first-found, so a stray sibling bundle can't win. On a malformed payload it
|
|
61
64
|
falls back to a safe empty bundle (empty `elements` + empty `messages`/`theme`/`vocabularies` maps)
|
|
62
65
|
so the page never crashes.
|
|
63
66
|
- **`useFetchSurfaceMetaGroup(slugs[])`** — the grouped sibling of `useFetchSurfaceMeta`, for a screen
|
|
64
67
|
needing many section surfaces at once. **Order-insensitive** (the slug list is normalized so cache
|
|
65
68
|
hits don't depend on argument order), `staleTime: Infinity`, hits the grouped endpoint
|
|
66
|
-
`GET /v2/surfaces/meta-group?slugs=...`, and extracts the `{surfaces}` map via
|
|
67
|
-
|
|
69
|
+
`GET /v2/surfaces/meta-group?slugs=...`, and extracts the SLUG-keyed `{surfaces}` map via the
|
|
70
|
+
recursive **`findSurfaceMap(raw, depth=5)`** — which walks the envelope tree and returns the
|
|
71
|
+
`surfaces` map whose **values look like bundles** (or an empty/null map), uniquely identifying the
|
|
72
|
+
inner slug-keyed map regardless of nesting or `data` wrapping. (`extractGroup`'s old
|
|
73
|
+
`data`/`data.<key>` probe and the `looksLikeGroup` `group`-discriminator check were both removed —
|
|
74
|
+
see the group-nesting gotcha below for why.) Returns `{}` for
|
|
68
75
|
unmigrated screens (`enabled:false`), so a non-roster client does no fetch.
|
|
69
76
|
- **`componentRegistry`** (`src/surface/componentRegistry.tsx`) — the **presentation sibling** of
|
|
70
77
|
`actionRegistry`, both keyed by the surface meta's `action.key`. `actionRegistry` maps key→behavior
|
|
@@ -154,6 +161,17 @@ three bespoke renderers `detailSection`/`locationCard`/`totalsCard` are untouche
|
|
|
154
161
|
`getDetailSections` are untouched — **only the SOURCE of section config moves JSON → Surface.**
|
|
155
162
|
Business logic stays in code: the recurring-lease `_totalLease` gate in the grid and approval
|
|
156
163
|
gating are **not** moved. (Registered-renderer seam: *config describes, code decides.*)
|
|
164
|
+
- **Full adapter-seam data path (verified this session, no direct `<SurfaceSection>`):**
|
|
165
|
+
`useFetchSurfaceMetaGroup(SALES_ORDER_SURFACE_SLUGS)` → `surfaceBundleToTenantFields(surfaces)` →
|
|
166
|
+
merged into `tenantFields` (roster clients only; `clientSlug` is the **uppercased** `getHostname()`
|
|
167
|
+
result matched against the roster) → **`buildPatchedTenantFields`** (which only overrides
|
|
168
|
+
`recordActionFields`, so it **preserves all surface section keys**) → `SalesOrderView` →
|
|
169
|
+
**`SalesOrderSummaryGrid`** (renders by `section.type` / `cardType`:
|
|
170
|
+
`detailSection`/`locationCard`/`totalsCard`, where `cardType` comes from `surface.config.cardType`)
|
|
171
|
+
+ **`getDetailSections`** (reads `section.isVisible` for the `displayToggle` sections, which carry a
|
|
172
|
+
`sectionVisibility` marker element). **Decision (confirmed by the developer this session): KEEP the
|
|
173
|
+
adapter approach — do NOT replace it with a direct `<SurfaceSection>` swap.** The bespoke grid
|
|
174
|
+
renderers and the `TenantFields` seam are the deliberate integration point.
|
|
157
175
|
- **Roster gate — `SALES_ORDER_SURFACE_MIGRATED_CLIENTS = [COMPASS, COMPASSCANADA, QUAD]`** (an
|
|
158
176
|
in-code roster in `surfaceBundleToTenantFields.ts`; the view-model
|
|
159
177
|
`useSalesOrderRecordModalLayoutModel.tsx` consults it). The view-model resolves sections from
|
|
@@ -182,6 +200,27 @@ seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster
|
|
|
182
200
|
bundle → the action bar renders nothing. Fix: `looksLikeBundle` must require **both** `elements` AND
|
|
183
201
|
a `surface` descriptor; use recursive `findBundle` + slug-preferring `extractBundle` (see How it
|
|
184
202
|
works). The durable backend follow-up is to confirm/stabilize the single-meta envelope contract.
|
|
203
|
+
- **The GROUPED meta envelope double-nests the slug-keyed `surfaces` map under an OUTER route-keyed
|
|
204
|
+
`surfaces` slot — match by value shape, never grab the first `surfaces`.** The meta-group V2 envelope
|
|
205
|
+
mirrors single-meta's bundle nesting: the real SLUG-keyed map sits one level deeper under a ROUTE-keyed
|
|
206
|
+
outer slot — `{ surfaces: { "meta-group": { group, surfaces: { "order-details": bundle, … } } },
|
|
207
|
+
elements: [] }`. The old `extractGroup` grabbed the **first** `surfaces` it saw (the outer route-keyed
|
|
208
|
+
one, whose values are *group* objects, not bundles), so `surfaces["order-details"]` was `undefined`
|
|
209
|
+
and `surfaceBundleToTenantFields` produced `{}` — every migrated-client SalesOrders section vanished.
|
|
210
|
+
Fix: recursive **`findSurfaceMap(raw, depth=5)`** returns the `surfaces` map whose **values look like
|
|
211
|
+
bundles** (or null/empty), uniquely identifying the inner slug-keyed map regardless of nesting or
|
|
212
|
+
`data` wrapping. Verified by trace across 6 envelope shapes (double-nested, resolver-return-at-top,
|
|
213
|
+
simple, data-wrapped, null-child, empty). **Note:** an earlier attempt that required a top-level
|
|
214
|
+
`group` discriminator on the probed level (`looksLikeGroup`) broke the fetch (returned `{}`) because
|
|
215
|
+
the V2 envelope carries no top-level `group` at that level — `looksLikeGroup` was removed entirely.
|
|
216
|
+
- **For migrated clients, a dropped (`surface: null`) display section silently vanishes — there is no
|
|
217
|
+
JSON fallback left.** Once a client's JSON section blocks are deleted (roster migration), a section
|
|
218
|
+
whose surface resolves to `surface: null` (a seed/ACL gap, e.g. `additional-order-details` /
|
|
219
|
+
`admin-notes` / `notes`) is dropped by `surfaceBundleToTenantFields` and the section disappears with
|
|
220
|
+
no fallback. That is a backend seed/ACL gap, **not** an FE bug. Separately, two sections are
|
|
221
|
+
intentionally gated in `getDetailSections`: `shipments` is **permanently hidden** (the view-model
|
|
222
|
+
hardcodes `shipmentsData: []` and `useShipments` is commented out; the section gates on
|
|
223
|
+
`shipmentsData.length > 0`), and `invoices` is data-gated on `invoiceData.length > 0`.
|
|
185
224
|
- **The surface `record` shape must match the rule field namespace — wrap as `{ order }`.** Seeded
|
|
186
225
|
Tier-1 rules namespace their field as `order._status`, so `getByPath(record, "order._status")` only
|
|
187
226
|
resolves if the host passes `record={{ order }}`. Passing the bare order object resolves to
|
|
@@ -210,6 +249,19 @@ seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster
|
|
|
210
249
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
211
250
|
|
|
212
251
|
## Change history
|
|
252
|
+
- 2026-06-30 — Follow-up: fixed the GROUPED meta fetch returning `{}` (every migrated-client
|
|
253
|
+
SalesOrders section had vanished). The meta-group envelope double-nests the slug-keyed `surfaces`
|
|
254
|
+
map under an outer ROUTE-keyed `surfaces` slot; the old `extractGroup` grabbed the first (outer,
|
|
255
|
+
group-valued) `surfaces`. Replaced it with recursive `findSurfaceMap(depth=5)` that returns the
|
|
256
|
+
`surfaces` map whose values are bundle-shaped (verified across 6 envelope shapes). **Reverted the
|
|
257
|
+
earlier same-session `group`-discriminator `looksLikeGroup` change** — requiring a top-level `group`
|
|
258
|
+
broke the fetch; `looksLikeGroup` removed entirely. Verified the SalesOrders section migration is
|
|
259
|
+
fully wired via the adapter seam (`useFetchSurfaceMetaGroup` → `surfaceBundleToTenantFields` →
|
|
260
|
+
`buildPatchedTenantFields` (overrides only `recordActionFields`) → `SalesOrderSummaryGrid` /
|
|
261
|
+
`getDetailSections`) and confirmed the decision to KEEP the adapter over a direct `<SurfaceSection>`.
|
|
262
|
+
Documented why some sections stay hidden (`shipments` hardcoded empty; `invoices` data-gated; and a
|
|
263
|
+
dropped `surface: null` display section vanishes with no JSON fallback for migrated clients — a
|
|
264
|
+
seed/ACL gap). (apeterson)
|
|
213
265
|
- 2026-06-30 — Migrated the SalesOrders record-modal sections onto Surface for the roster clients
|
|
214
266
|
[COMPASS, COMPASSCANADA, QUAD] via the registered-renderer seam: added `useFetchSurfaceMetaGroup`
|
|
215
267
|
(order-insensitive grouped `/surfaces/meta-group` fetch) + `surfaceBundleToTenantFields()` (adapts
|
|
@@ -221,7 +273,7 @@ seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster
|
|
|
221
273
|
- 2026-06-30 — Debugged + extended the client-side action-bar renderer (no client-specific work).
|
|
222
274
|
Fixed three rendering bugs: (1) the single-meta envelope's **decoy `elements: []`** masked the real
|
|
223
275
|
nested bundle — `looksLikeBundle` now requires `elements` AND a `surface` descriptor, with recursive
|
|
224
|
-
`findBundle(depth=4)` + slug-preferring `extractBundle` +
|
|
276
|
+
`findBundle(depth=4)` + slug-preferring `extractBundle` +
|
|
225
277
|
safe empty `messages`/`theme`/`vocabularies` fallback (`useFetchSurfaceMeta.ts`); (2) Tier-1 rules
|
|
226
278
|
always false because the host passed the bare order object instead of the `{ order }` envelope the
|
|
227
279
|
`order.*` field namespace requires (`SalesOrderTopBar.tsx`); (3) every action icon rendered as
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: surface-section-migration-fe
|
|
4
|
+
title: Surface action-bar + SalesOrders section migration — FE fixes
|
|
5
|
+
author: apeterson
|
|
6
|
+
repos: [toga25-supply]
|
|
7
|
+
framework: "2.0"
|
|
8
|
+
client: shared
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-06-30
|
|
11
|
+
updated: 2026-06-30
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: surface-section-migration-fe
|
|
15
|
+
**Date:** 2026-06-30
|
|
16
|
+
**Project/Repo:** toga25-supply (2.0)
|
|
17
|
+
**Task:** Make the Surface presentation layer actually render in the SalesOrders record modal — fix the action-bar (BUTTON_BAR) rendering, the grouped section fetch/extraction, Tier-1 rule evaluation, icons, and add a meta-driven custom-component path — all in the toga25-supply React frontend (no backend/DB this session).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
<!-- Include specific file paths and evidence -->
|
|
23
|
+
- **`extractBundle` single-meta decoy fix** — `src/surface/useFetchSurfaceMeta.ts`. The single-meta envelope `{ surfaces: { meta: <bundle> }, elements: [] }` has a decoy empty top-level `elements: []`. Tightened `looksLikeBundle` to require BOTH an `elements` array AND a `surface` object; replaced hard-coded raw/data probing with recursive `findBundle(raw, predicate, depth=4)`; `extractBundle(raw, slug)` now PREFERS the bundle whose `surface.slug === slug`, falling back to first-found. Trace verified: action-bar payload → 5 elements (was 0); direct/`data`/`data.meta` shapes still resolve. Type-check clean.
|
|
24
|
+
- **Grouped fetch `extractGroup` double-nesting fix** — `src/surface/useFetchSurfaceMeta.ts`. The meta-group envelope double-nests the slug-keyed `surfaces` map under an OUTER route-keyed `surfaces` slot: `{ surfaces: { "meta-group": { group, surfaces: { "order-details": bundle, ... } } } }`. Old `extractGroup` grabbed the outer (route-keyed) map → `surfaces["order-details"]` undefined → `surfaceBundleToTenantFields` returned `{}`. Replaced `looksLikeGroup` + the data-probe with recursive `findSurfaceMap(raw, depth=5)` that returns the `surfaces` map whose VALUES look like bundles (or null/empty). Trace verified across 6 shapes (double-nested, resolver-return-at-top, simple, data-wrapped, null-child, empty). Type-check clean.
|
|
25
|
+
- **Tier-1 rule record envelope** — `src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx`. Rules use field `order._status`, but the component passed the bare order; `getByPath(order,"order._status")` → undefined → all gates false. Fixed by passing `record={order ? { order } : null}`. Trace: `{order:{_status:"pendingApproval"}}` → true; `fulfilled` → false. `evaluateSurfaceRule.ts` was NOT the bug.
|
|
26
|
+
- **Icon resolution** — `src/surface/SurfaceActionBar.tsx`. `tokens.icon` is a literal FontAwesome name, not a theme-token slug; running it through `resolveThemeToken` (theme map has only color/bg keys) made every icon fall back to "check". Fixed to `element.tokens?.icon ?? "check"`; removed the now-unused `resolveThemeToken` import.
|
|
27
|
+
- **Meta-driven component registry** — NEW `src/surface/componentRegistry.tsx` (presentation sibling of `actionRegistry`, keyed by `action.key`). `"salesOrder.viewLog"` → `<ViewLogButton label onOpenLog={onActivate} />`. `SurfaceActionBar.renderElement` calls `getSurfaceComponent(element.action?.key)` and renders it (keyed Fragment) before the default icon button. Type-check clean.
|
|
28
|
+
- **Section migration verified end-to-end (adapter seam)** — data path: `useFetchSurfaceMetaGroup(SALES_ORDER_SURFACE_SLUGS)` → `surfaceBundleToTenantFields(surfaces)` → merged into `tenantFields` (migrated clients only) → `buildPatchedTenantFields` (only overrides `recordActionFields`, so it PRESERVES all surface section keys) → `SalesOrderView` → `SalesOrderSummaryGrid` (renders by `section.type` cardType) + `getDetailSections` (reads `section.isVisible`). Full project type-check: zero errors.
|
|
29
|
+
- **Debug cleanup** — removed all debug `console.log`s from the viewModel (×3), `SalesOrderRecordModalLayout.tsx`, and `getDetailSections.tsx`. `grep` confirms none remain; type-check clean.
|
|
30
|
+
- **Two `/capture` runs PUSHED** to team `_main`, updating `2.0/apps/toga25-supply/features/surface-frontend.md` (owners `[jcardinal, apeterson]`), including corrections of two stale notes from the first capture.
|
|
31
|
+
|
|
32
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
33
|
+
<!-- Exact failure reasons — do not vague-ify -->
|
|
34
|
+
- **Requiring a `group` discriminator key in `looksLikeGroup`** — added during the review pass to disambiguate single-vs-group. It RETURNED `{}` for the grouped fetch because the V2 envelope does not carry a top-level `group` at the probed level (the single-meta envelope has none either). Reverted, then `looksLikeGroup` removed entirely in favor of `findSurfaceMap`. DO NOT re-add a `group`-key requirement.
|
|
35
|
+
- **Matching the bundle/group on `elements` array ALONE** — the original `looksLikeBundle`/`looksLikeGroup` fingerprint. An empty `elements: []` decoy (single) and the outer route-keyed `surfaces` (group) both satisfied it, returning the wrong node. Must additionally require a `surface` descriptor (bundle) / match by bundle-shaped values (group).
|
|
36
|
+
- **Branching the custom component on `renderType === "BUTTON"`** — rejected by the user as "not driven by surface meta" — it hardcodes a component to a whole render category. Use the `action.key`-keyed `componentRegistry` instead (the chosen approach).
|
|
37
|
+
|
|
38
|
+
## Not tried yet (candidates for next session)
|
|
39
|
+
- **Seed/ACL fixes for the display-section surfaces** (`additional-order-details`, `admin-notes`, `notes`) — if still missing at runtime, their surface resolves `surface: null` and the adapter drops the key (no JSON fallback for migrated clients). This is the paused Track B (prod cross-cluster seed work), NOT an FE fix. See session `surface-layer` (jcardinal) for the prod cross-cluster blocker.
|
|
40
|
+
- **Re-enable `shipments`** — currently permanently hidden: `useShipments` is commented out and the viewModel hardcodes `shipmentsData: []`; `getDetailSections` gates on `shipmentsData.length > 0`. Re-wire `useShipments` if shipments need to render.
|
|
41
|
+
- **Runtime verification on a migrated client** — confirm on a `COMPASS`/`COMPASSCANADA`/`QUAD` hostname that the cards + detail sections render from Surface and the action bar shows all five buttons with correct icons.
|
|
42
|
+
- **Backend: stabilize the single-meta / meta-group envelope contract** so the FE shape-probing (`findBundle`/`findSurfaceMap`) can eventually relax.
|
|
43
|
+
|
|
44
|
+
## Current file state
|
|
45
|
+
| File | Status | Notes |
|
|
46
|
+
|------|--------|-------|
|
|
47
|
+
| src/surface/useFetchSurfaceMeta.ts | modified | `findBundle` + slug-preferred `extractBundle`; `findSurfaceMap` + `extractGroup`; `looksLikeGroup` removed. Type-check clean. |
|
|
48
|
+
| src/surface/SurfaceActionBar.tsx | modified | renames (element/surfaceMeta/recordSurfaceState); literal `tokens.icon`; `componentRegistry` branch; debug logs gone. |
|
|
49
|
+
| src/surface/componentRegistry.tsx | created | NEW presentation registry keyed by `action.key`; `salesOrder.viewLog` → ViewLogButton. |
|
|
50
|
+
| src/pages/.../view/sections/SalesOrderTopBar.tsx | modified | `record={order ? { order } : null}` envelope for Tier-1 rules. |
|
|
51
|
+
| src/pages/.../viewModel/useSalesOrderRecordModalLayoutModel.tsx | modified | debug logs removed; surface→tenantFields merge intact (unchanged logic). |
|
|
52
|
+
| src/pages/.../SalesOrderRecordModalLayout.tsx | modified | removed stray `console.log(error)`. |
|
|
53
|
+
| src/pages/.../helpers/getDetailSections.tsx | modified | removed `console.log(tenantFields)`. |
|
|
54
|
+
| src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx | modified | PRE-EXISTING change from session start — NOT touched this session; review separately. |
|
|
55
|
+
| .claude/ | untracked | local harness mirror; not part of this work. |
|
|
56
|
+
|
|
57
|
+
## Decisions made
|
|
58
|
+
- **Keep the adapter seam for section migration; do NOT replace with direct `<SurfaceSection>`** (user-confirmed). Rationale: the adapter (`surfaceBundleToTenantFields` → `tenantFields` → existing renderers) is complete and keeps business logic (the `_totalLease` recurring gate, card styling) in code; a direct-`<SurfaceSection>` rewrite would re-home all that for no functional gain. Rejected: per-section `<SurfaceSection surfaceSlug record/>` like SurfaceActionBar.
|
|
59
|
+
- **Custom components via an `action.key`-keyed `componentRegistry`** (sibling of `actionRegistry`), not a `renderType` branch. Rationale: fully meta-driven, same extensibility model as the existing registries; adding a custom button = one surface element + one registry entry.
|
|
60
|
+
- **Shape-probe by value, not by key/path** — both `findBundle` and `findSurfaceMap` identify the target by structural shape (bundle = `surface` + `elements`; slug-keyed map = values are bundles) and walk the tree, rather than hard-coding the V2 envelope nesting. Rationale: robust across route/version envelope changes.
|
|
61
|
+
|
|
62
|
+
## Blockers
|
|
63
|
+
- **Display-section surfaces may not be seeded/granted** for the migrated clients — if `additional-order-details`/`admin-notes`/`notes` are still missing at runtime, that is a seed/ACL (Track B) gap, not FE. Track B (prod cross-cluster Client seed rewrite) remains user-paused pending prod Core ids (see session `surface-layer`).
|
|
64
|
+
|
|
65
|
+
## Exact next step
|
|
66
|
+
> On a migrated-client hostname (`clientSlug` === `COMPASS`/`COMPASSCANADA`/`QUAD`), open a sales order and inspect the network response for `GET /v2/surfaces/meta-group` plus the resolved `surfaces` object: confirm the slug-keyed bundles for `order-details`, `ship-to`, `bill-to`, `order-summary`, `order-recurring`, `invoices`, `notes`, `admin-notes`, `additional-order-details`. For any whose bundle is `surface: null`, that slug is a seed/ACL gap (Track B) — list them and hand off to the backend seed pass; everything that resolves should now render via the verified adapter path.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
_Saved by /session-save on 2026-06-30_
|
package/package.json
CHANGED