toga-ai 1.0.222 → 1.0.223
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.
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/architecture.md +4 -3
- package/knowledge/2.0/apps/_underscore/features/surface-resolver.md +29 -6
- package/knowledge/2.0/apps/api2/features/surface-meta-option.md +12 -1
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +21 -7
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +47 -14
- package/package.json +1 -1
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
| [NetSuite REST Client (_Component_Api_Netsuite) — record writes & SuiteQL](features/netsuite-rest-client.md) | `_Component_Api_Netsuite` is the **2.0 `_underscore` NetSuite REST client** — the shared primitive every worker2/api2 NetSuite caller uses for record GETs, Suit | _underscore/Component/Api/Netsuite/Netsuite.php |
|
|
13
13
|
| [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
|
|
14
14
|
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
|
|
15
|
-
| [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Core/Surface.php, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php |
|
|
15
|
+
| [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Core/Surface.php, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php |
|
|
16
16
|
| [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql |
|
|
17
17
|
| [Units for Items for Purchase Orders — Data Structure](features/units-for-items-for-purchase-orders.md) | Describes how unit (serialized inventory) data is linked to sales-order and purchase-order line items behind the `units-for-items-for-purchase-orders` TableView | |
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: ["jcardinal", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/_underscore.php
|
|
@@ -71,7 +71,7 @@ Tier-1 trivial declarative rules (frozen `all/any/none + {field,op,value}` gramm
|
|
|
71
71
|
client-side; Tier-2 business logic computed in PHP returning booleans (lives in
|
|
72
72
|
`_Model_<Client>_*` overrides + interceptors). Config/logic boundary: **config describes,
|
|
73
73
|
PHP decides.** M2M-safe delivery via opt-in `surface=<slug>` request option → response
|
|
74
|
-
`meta.surface`, never `data`. Generic FE machinery destined for `@agilant/toga-blox`.
|
|
74
|
+
`meta.surface`, never `data`. Generic FE machinery destined for `@agilant/toga-blox`. **Surface is the enforced presentation path — no feature flag.** The `SURFACE_ENABLED`/`VITE_SURFACE_ENABLED` flag and all legacy pre-Surface fallback branches were removed; legacy per-screen FIELDS config is deleted as each screen migrates (no dual-path fallback). The meta endpoint is query-string form: `GET /v2/surfaces/meta?slug=<slug>`.
|
|
75
75
|
|
|
76
76
|
**Rejected alternatives:** a generic EAV settings cascade (the old `*Settings` tables —
|
|
77
77
|
proven near-empty in prod); raw-CSS-in-config (the old "Dummy Fields" anti-pattern).
|
|
@@ -80,7 +80,7 @@ proven near-empty in prod); raw-CSS-in-config (the old "Dummy Fields" anti-patte
|
|
|
80
80
|
allowlist that throws on unknown ops, with a CI test asserting the op-set; defensive
|
|
81
81
|
resolver with logged fallbacks; debug-bundle endpoint ships WITH the resolver; `c_longValue`
|
|
82
82
|
on overrides with strict per-attribute casting; per-surface meta() cutover with a parity
|
|
83
|
-
diff (do NOT delete meta() until every page is migrated); `BLANK_CLIENT_DATABASE` as a CI gate.
|
|
83
|
+
diff (do NOT delete meta() until every page is migrated); `BLANK_CLIENT_DATABASE` as a CI gate. Reserved Core id blocks: `Records` 333–341, `RecordFields` 2246–2433 (renumbered 2026-06-29, seed files only — a known file-vs-DB id drift in already-provisioned envs, to reconcile by re-provisioning). Anything spanning both id schemes must be id-agnostic (match by `route=`, not id).
|
|
84
84
|
|
|
85
85
|
## Naming convention & autoloader
|
|
86
86
|
|
|
@@ -324,3 +324,4 @@ multi-file UI components (`.php`/`.html`/`.css`/`.js`) invoked as `<_ComponentNa
|
|
|
324
324
|
## Change history
|
|
325
325
|
- 2026-06-11 — Documented lazy transaction gotcha in `_Database::register()` (rgirish)
|
|
326
326
|
- 2026-06-25 — Added the Surface platform UI presentation/configuration layer (DB-driven UI config replacing `Page::meta()`, CTO-reviewed AGREE-WITH-ADJUSTMENTS) (jcardinal)
|
|
327
|
+
- 2026-06-29 — Surface made the enforced (un-flagged) presentation path; reserved id blocks renumbered to Records 333–341 / RecordFields 2246–2433 (known seed-vs-provisioned drift). (jcardinal)
|
|
@@ -6,10 +6,11 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Core/Surface.php
|
|
13
|
+
- dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql
|
|
13
14
|
- _underscore/Model/Core/SurfaceElement.php
|
|
14
15
|
- _underscore/Model/Core/Action.php
|
|
15
16
|
- _underscore/Model/Core/Vocabulary.php
|
|
@@ -30,8 +31,9 @@ related:
|
|
|
30
31
|
The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a
|
|
31
32
|
cached resolver, `_Model_Core_Surface::resolve(&$api, string $slug)`, that merges base config +
|
|
32
33
|
cascade overrides + ACL + messages + theme tokens into one flat, serializable bundle. It is the
|
|
33
|
-
**replacement for `_Model_Core_Page::meta()`** and is shipped as a scripted-API method
|
|
34
|
-
`GET /v2/surfaces/
|
|
34
|
+
**replacement for `_Model_Core_Page::meta()`** and is shipped as a scripted-API method called as
|
|
35
|
+
`GET /v2/surfaces/meta?slug=<slug>` (RecordScript args come from the **query string**, not the
|
|
36
|
+
path — see gotchas). Platform/shared — not client-specific. Schema:
|
|
35
37
|
[surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
|
|
36
38
|
|
|
37
39
|
## The 9 models
|
|
@@ -62,8 +64,8 @@ Inputs come from `$api` (the same accessor `_Model_Client_TableView::meta` uses)
|
|
|
62
64
|
table pipeline (referenced, never duplicated).
|
|
63
65
|
7. Assemble the flat bundle. **Zero INSERTs.**
|
|
64
66
|
|
|
65
|
-
A `
|
|
66
|
-
resolver, not "later").
|
|
67
|
+
A `metaDebug()` inspector returns the resolved bundle plus the raw cascade layers (shipped with the
|
|
68
|
+
resolver, not "later"). It is **not** named `debug()` — see gotchas.
|
|
67
69
|
|
|
68
70
|
## Caching & invalidation
|
|
69
71
|
|
|
@@ -97,8 +99,29 @@ resolver, not "later").
|
|
|
97
99
|
falls back to sane defaults and never crashes a page. Strict per-`attribute` casting with logged
|
|
98
100
|
fallback on bad `SurfaceOverrides` data.
|
|
99
101
|
- **Cross-DB reads are batched** (DB_CORE vs DB_CLIENT) and FKs are soft — tolerate dangling refs.
|
|
102
|
+
- **A scripted-API method MUST NOT collide with a non-static `_Model` base method.** Naming the
|
|
103
|
+
inspector `debug()` caused a fatal *"Cannot make non static method _Model::debug() static"* — the
|
|
104
|
+
framework base `_Model` defines a NON-static `debug()` (Model.php:167) and the scripted-API
|
|
105
|
+
dispatch calls the method statically. Renamed to `metaDebug` and remapped the RecordScript
|
|
106
|
+
(`route 'debug' → phpMethod 'metaDebug'`). Vet any new scripted-API method name against the
|
|
107
|
+
`_Model` base (e.g. `debug`, and check others) before mapping it.
|
|
108
|
+
- **The endpoint is `GET /v2/surfaces/meta?slug=<slug>`, not `/v2/surfaces/<slug>/meta`.** TOGA
|
|
109
|
+
RecordScripts read their args from the **query string**; the path form makes the engine parse the
|
|
110
|
+
slug as a record uuid and return 404 EV-6. Pattern for any RecordScript:
|
|
111
|
+
`/<route>/<scriptRoute>?<arg>=...`.
|
|
112
|
+
- **Core metadata records exposed to the app must grant CORE role Public (id 1) READ.** The meta
|
|
113
|
+
RecordScript is ACL-gated on the `surfaces` Core record, and authenticated app users carry CORE
|
|
114
|
+
role Public(1); a seed that grants only Super User(3)/Base(4) returns 403 EZ-1. Grant Public(1)
|
|
115
|
+
`allowRead` on the `surfaces` record (full AclRecordPermissions → AclLogicGroups →
|
|
116
|
+
AclLogicGroupExpressions → AclRecordExpressions 'all' chain), mirroring how `apps`/`records`/
|
|
117
|
+
`record-fields` already expose Public READ. Only the `surfaces` record gets Public read — the
|
|
118
|
+
admin-CRUD sibling records stay Super-User-only (the resolver reads their tables server-side).
|
|
100
119
|
|
|
101
120
|
## Change history
|
|
102
|
-
- 2026-06-
|
|
121
|
+
- 2026-06-29 — Deploy fixes: renamed the scripted inspector `debug()`→`metaDebug()` (name collided
|
|
122
|
+
with non-static `_Model::debug()`, fatal); corrected the endpoint to query-string form
|
|
123
|
+
`GET /v2/surfaces/meta?slug=` (path form 404'd EV-6); granted CORE Public(1) READ on the `surfaces`
|
|
124
|
+
record so app users stop getting 403 EZ-1 on meta (id-agnostic migration matches by `route='surfaces'`). (jcardinal)
|
|
125
|
+
- 2026-06-25 — Built the 9 models + `resolve()`/`metaDebug()` as the cached, parameterized,
|
|
103
126
|
zero-write-on-read replacement for `_Model_Core_Page::meta()`; cache-bust interceptors on all 9
|
|
104
127
|
models (outside the transaction). meta() retained until per-page cutover with parity diff. (jcardinal)
|
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -42,6 +42,17 @@ traffic), behavior is **byte-for-byte unchanged** — pure data, zero extra work
|
|
|
42
42
|
the logic.
|
|
43
43
|
- **One call, no extra round-trip** — the state pass rides the existing data fetch.
|
|
44
44
|
|
|
45
|
+
## Gotchas
|
|
46
|
+
|
|
47
|
+
- **Meta endpoint 403 EZ-1 for normal app users → grant CORE Public(1) READ on the `surfaces`
|
|
48
|
+
record.** Authenticated app users carry CORE role Public (id 1); the meta RecordScript is
|
|
49
|
+
ACL-gated on the `surfaces` Core record, and a seed granting only Super User(3)/Base(4) rejects
|
|
50
|
+
them. Grant Public(1) `allowRead` on `surfaces` only (the admin-CRUD sibling records stay
|
|
51
|
+
Super-User-only — the resolver reads their tables server-side). See
|
|
52
|
+
[surface-resolver](../../_underscore/features/surface-resolver.md) gotchas for the full chain.
|
|
53
|
+
|
|
45
54
|
## Change history
|
|
55
|
+
- 2026-06-29 — Documented the meta-endpoint ACL: normal app users (CORE Public role 1) need
|
|
56
|
+
`allowRead` on the `surfaces` record or meta returns 403 EZ-1. (jcardinal)
|
|
46
57
|
- 2026-06-25 — Added the `surface=<slug>` opt-in option to the V2 engine; attaches per-record action
|
|
47
58
|
state under `meta.surface` (M2M-safe, defensive, absent = unchanged). (jcardinal)
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Database Changes (dbchanges2) Repository Architecture](architecture.md) | `dbchanges2` is the **schema-migration / SQL change-set repository** for the entire 2.0 platform. | Core/, Client/, Client_<Tenant>/, Logs/, Logs_Client/, _modules/ |
|
|
6
|
-
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql |
|
|
6
|
+
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql |
|
|
7
7
|
| [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | How to manually stand up a new 2.0 client (tenant). | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
|
|
@@ -6,12 +6,14 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql
|
|
13
13
|
- dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql
|
|
14
14
|
- dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql
|
|
15
|
+
- dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql
|
|
16
|
+
- dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql
|
|
15
17
|
- dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql
|
|
16
18
|
- dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql
|
|
17
19
|
- dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql
|
|
@@ -60,11 +62,13 @@ SurfaceElements`). Core migrations run first; Client after. The session built/se
|
|
|
60
62
|
**SalesOrders + login** vertical (not Tickets) as the review proof:
|
|
61
63
|
|
|
62
64
|
- `Core/2026-06-25a - SurfaceCoreTables.sql` — the 6 Core tables.
|
|
63
|
-
- `Core/2026-06-25b - SurfaceRecordsAndFields.sql` — `Core.Records`+`RecordFields` for all 9 tables (
|
|
65
|
+
- `Core/2026-06-25b - SurfaceRecordsAndFields.sql` — `Core.Records`+`RecordFields` for all 9 tables (`Records` ids **333–341**, `RecordFields` ids **2246–2433**; `recordId` via `model=` subselect). Also maps the meta `RecordScript` `route 'debug' → phpMethod 'metaDebug'` (the inspector cannot be named `debug` — see [surface-resolver](../../_underscore/features/surface-resolver.md)).
|
|
64
66
|
- `Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql` — Core seed + the `RecordScripts` resolve row.
|
|
67
|
+
- `Core/2026-06-29a - ItemsSurfaceSeed.sql` — the Items LIST screen seed: surfaces `items-list` (TABLE → existing `items` TableView id 9, recordId 21) + `items-list-actions` (BUTTON_BAR: refresh/columns/newItem) + Actions + ~11 Messages. Single DEFAULT bundle, no Client overrides.
|
|
68
|
+
- `Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql` — grants CORE role Public(1) `allowRead` on the `surfaces` record (so app users stop getting 403 EZ-1 on meta). **Id-agnostic**: matches the record by `route='surfaces'`, so it works whether the record id is 333 or the legacy 2300.
|
|
65
69
|
- `Client/2026-06-25a - SurfaceClientTables.sql` — the 3 Client tables + `INSERT IGNORE Languages('en','English')`.
|
|
66
70
|
- `Client/2026-06-25b - SurfaceClientSeed.sql` — `ThemeTokens` + `MessageTranslations(en)`.
|
|
67
|
-
- `Client/2026-06-25c - SurfaceClientAcl.sql` — full ACL chain for the 3 CLIENT-aclDatabase records.
|
|
71
|
+
- `Client/2026-06-25c - SurfaceClientAcl.sql` — full ACL chain for the 3 CLIENT-aclDatabase records (recordId refs updated to the 333-block renumber).
|
|
68
72
|
|
|
69
73
|
## Gotchas
|
|
70
74
|
|
|
@@ -79,12 +83,22 @@ SurfaceElements`). Core migrations run first; Client after. The session built/se
|
|
|
79
83
|
- **`SurfaceOverrides.value` is a stringly-typed escape hatch** — a long `CONFIG`/label override
|
|
80
84
|
would silently truncate at varchar(255); that is why `c_longValue mediumtext` exists, and the
|
|
81
85
|
resolver MUST cast strictly per `attribute` (bool/int/string/json) with a logged fallback.
|
|
82
|
-
- **Reserved id
|
|
83
|
-
|
|
86
|
+
- **Reserved id blocks (renumbered 2026-06-29):** Surface `Core.Records` ids **333–341** (9 tables),
|
|
87
|
+
`Core.RecordFields` ids **2246–2433**. (Originally seeded at 2300–2308 / 2400+, renumbered at the
|
|
88
|
+
team's request.) `RecordScripts.recordId` and the Core+Client ACL `recordId` refs were updated to
|
|
89
|
+
match. Use `model=` subselects for `recordId`, never hardcoded ids.
|
|
90
|
+
- **Known file-vs-DB id drift.** The renumber changed the **seed files only** — already-provisioned
|
|
91
|
+
envs still hold the old 2300/2400 ids. This is intentional/known, to be reconciled by
|
|
92
|
+
re-provisioning later. Anything that must work across both (e.g. the Public-READ ACL grant) must
|
|
93
|
+
be **id-agnostic** — match the record by `route='surfaces'`, not by id.
|
|
84
94
|
- **CLIENT-aclDatabase records** (`SurfaceOverrides`, `MessageTranslations`, `ThemeTokens`) need the
|
|
85
95
|
full 4-step ACL chain + field permissions in **each** client DB; resolve `roleId` by subselect.
|
|
86
96
|
|
|
87
97
|
## Change history
|
|
88
|
-
- 2026-06-
|
|
89
|
-
|
|
98
|
+
- 2026-06-29 — Renumbered the reserved seed id blocks (Records 2300-2308→**333–341**, RecordFields
|
|
99
|
+
2400+→**2246–2433**) in the seed files only — known file-vs-DB drift in provisioned envs. Added
|
|
100
|
+
the Items LIST seed (`ItemsSurfaceSeed.sql`) and the id-agnostic CORE Public(1) READ ACL grant on
|
|
101
|
+
`surfaces` (`SurfaceMetaPublicReadAcl.sql`, matches by `route='surfaces'`). (jcardinal)
|
|
102
|
+
- 2026-06-25 — Initial schema for the Surface layer: 6 Core + 3 Client tables, `Records`/`RecordFields`,
|
|
103
|
+
SalesOrders+login seed, ACL chain, and `BLANK_CLIENT_DATABASE` append. Typed
|
|
90
104
|
columns chosen over EAV (the old `*Settings` cascade was empirically near-empty in prod). (jcardinal)
|
|
@@ -8,4 +8,4 @@
|
|
|
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/
|
|
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/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/actionRegistry.ts, 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/fieldsConfig/index.ts |
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-29
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/surface/useFetchSurfaceMeta.ts
|
|
@@ -16,11 +16,13 @@ files:
|
|
|
16
16
|
- toga25-supply/src/surface/SurfaceSection.tsx
|
|
17
17
|
- toga25-supply/src/surface/resolve.ts
|
|
18
18
|
- toga25-supply/src/surface/types.ts
|
|
19
|
-
- toga25-supply/src/surface/featureFlag.ts
|
|
20
19
|
- toga25-supply/src/surface/index.ts
|
|
21
20
|
- toga25-supply/src/pages/Login/LoginPage.tsx
|
|
22
21
|
- toga25-supply/src/pages/SalesOrders/SalesOrders.tsx
|
|
23
22
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx
|
|
23
|
+
- toga25-supply/src/pages/Items/ItemsPage.tsx
|
|
24
|
+
- toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx
|
|
25
|
+
- toga25-supply/src/fieldsConfig/index.ts
|
|
24
26
|
related:
|
|
25
27
|
- ../../_underscore/features/surface-resolver.md
|
|
26
28
|
- meta-driven-table-data.md
|
|
@@ -29,14 +31,17 @@ related:
|
|
|
29
31
|
## What it is
|
|
30
32
|
|
|
31
33
|
The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from
|
|
32
|
-
`GET /v2/surfaces/
|
|
34
|
+
`GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported JSON field bundles. Lives in
|
|
33
35
|
`src/surface/` for now with a `TODO(blox)` to extract the generic machinery into `@agilant/toga-blox`
|
|
34
|
-
so all 2.0 apps inherit it.
|
|
36
|
+
so all 2.0 apps inherit it. Surface is now the **enforced, unconditional presentation path** (no
|
|
37
|
+
feature flag — see below). Backend: [surface-resolver](../../_underscore/features/surface-resolver.md).
|
|
35
38
|
|
|
36
39
|
## How it works
|
|
37
40
|
|
|
38
41
|
- **`useFetchSurfaceMeta`** — the single fetch-once/React-Query-cached choke point for a surface's
|
|
39
|
-
meta. Loading a different *record* into the same surface reuses cached meta.
|
|
42
|
+
meta. Loading a different *record* into the same surface reuses cached meta. Builds the URL as
|
|
43
|
+
`/surfaces/meta?slug=...` (query string), **not** `/surfaces/<slug>/meta` (the path form makes the
|
|
44
|
+
engine parse the slug as a record uuid → 404 EV-6).
|
|
40
45
|
- **`evaluateSurfaceRule`** — the Tier-1 rule evaluator: a **frozen `all/any/none` + `{field,op,value}`
|
|
41
46
|
grammar** that **throws on an unknown op** (generalized from the SalesOrders
|
|
42
47
|
`buildPatchedTenantFields`/`evaluateEnableRule`/`resolveFlag` helpers). Evaluated client-side against
|
|
@@ -47,25 +52,53 @@ so all 2.0 apps inherit it. Backend: [surface-resolver](../../_underscore/featur
|
|
|
47
52
|
- **theme/message resolvers** (`resolve.ts`) — token → CSS and ICU message rendering.
|
|
48
53
|
- **`SurfaceActionBar`/`SurfaceActions` + `SurfaceSection`** — generic renderers.
|
|
49
54
|
|
|
50
|
-
##
|
|
55
|
+
## Enforced, not flagged
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
Surface is the **unconditional** presentation path. The `VITE_SURFACE_ENABLED` / `SURFACE_ENABLED`
|
|
58
|
+
feature flag was **removed entirely** (`featureFlag.ts` deleted, all gating conditionals and the
|
|
59
|
+
legacy pre-Surface fallback branches at the wired screens removed). Legacy per-screen `FIELDS` config
|
|
60
|
+
+ rendering are deleted as each screen migrates — there is no dual-path fallback.
|
|
61
|
+
|
|
62
|
+
## What's wired
|
|
63
|
+
|
|
64
|
+
Login + sales-orders list + the sales-order modal + the **Items LIST** screen. The SalesOrder action
|
|
65
|
+
bar reproduces `orderViewFields.json` approve/deny/approvalWorkflow/viewLog/editOrder by delegating
|
|
66
|
+
to the existing `onActionClick`/`onOpenLog` handlers. TABLE-type surfaces stay on the existing
|
|
67
|
+
TableView pipeline via `tableViewSlug` (see [meta-driven-table-data](meta-driven-table-data.md)) —
|
|
68
|
+
the Surface layer references the TableView, never replaces it.
|
|
69
|
+
|
|
70
|
+
## Per-screen migration recipe (proven on SalesOrders + Items)
|
|
71
|
+
|
|
72
|
+
The screen-by-screen full-app refactor follows one recipe:
|
|
73
|
+
1. Seed the screen's surface(s) in a `Core/*SurfaceSeed.sql` — a TABLE surface that references the
|
|
74
|
+
screen's existing `TableView` id via `tableViewSlug`, plus a BUTTON_BAR/actions surface + its
|
|
75
|
+
Actions + Messages (e.g. Items: `items-list` TABLE → TableView id 9 / recordId 21, and
|
|
76
|
+
`items-list-actions` refresh/columns/newItem).
|
|
77
|
+
2. Rewire the page + its view-model to consume the surface bundle (`ItemsPage.tsx` /
|
|
78
|
+
`useItemsPageViewModel.tsx`).
|
|
79
|
+
3. **Delete** the legacy `FIELDS` config: the page's `*Fields.json` and its `fieldsConfig/index.ts`
|
|
80
|
+
entries.
|
|
81
|
+
Keep TABLE rendering on the existing TableView pipeline; only the chrome (actions/labels/visibility)
|
|
82
|
+
moves to Surface. A screen with no client/role/lang variance ships a single DEFAULT bundle (no
|
|
83
|
+
Client overrides), as Items did.
|
|
58
84
|
|
|
59
85
|
## Gotchas
|
|
60
86
|
|
|
61
|
-
- **
|
|
62
|
-
|
|
87
|
+
- **No feature flag, no fallback.** Surface is the enforced path; a missing/broken surface bundle
|
|
88
|
+
does not silently fall back to legacy JSON — that path is deleted per screen as it migrates.
|
|
89
|
+
- **Endpoint is query-string form** — `GET /v2/surfaces/meta?slug=...`, not `/surfaces/<slug>/meta`
|
|
90
|
+
(the path form 404s EV-6).
|
|
63
91
|
- **Tier-1 only on the client.** Business-logic gates (Tier-2) arrive as resolved booleans from the
|
|
64
92
|
backend (`meta.surface`); never re-encode business rules in the FE evaluator.
|
|
65
93
|
- **`tsc` not yet run** this session (the private `@agilant/toga-blox` registry needs npm creds);
|
|
66
94
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
67
95
|
|
|
68
96
|
## Change history
|
|
97
|
+
- 2026-06-29 — DECISION: Surface is now enforced, not flagged — removed `VITE_SURFACE_ENABLED`/
|
|
98
|
+
`SURFACE_ENABLED` (deleted `featureFlag.ts` + all gating/legacy-fallback branches). Fixed
|
|
99
|
+
`useFetchSurfaceMeta` to the query-string endpoint `/surfaces/meta?slug=` (path form 404'd EV-6).
|
|
100
|
+
Migrated the Items LIST screen (second proof of the per-screen recipe; deleted `itemsPageFields.json`
|
|
101
|
+
+ its fieldsConfig entries). (jcardinal)
|
|
69
102
|
- 2026-06-25 — Built `src/surface/` (fetch hook, frozen Tier-1 rule evaluator, action registry, theme/
|
|
70
103
|
message resolvers, action-bar + section renderers); wired login + SalesOrders list + SO modal behind
|
|
71
104
|
the OFF-by-default `SURFACE_ENABLED` flag. Generic machinery destined for toga-blox. (jcardinal)
|
package/package.json
CHANGED