toga-ai 1.0.378 → 1.0.380
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/architecture.md +16 -0
- package/knowledge/2.0/apps/_underscore/features/surface-resolver.md +24 -1
- package/knowledge/2.0/apps/api2/INDEX.md +1 -1
- package/knowledge/2.0/apps/api2/workflows/environment-configuration-and-provisioning.md +31 -1
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +37 -0
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +52 -2
- package/package.json +1 -1
|
@@ -344,6 +344,22 @@ multi-file UI components (`.php`/`.html`/`.css`/`.js`) invoked as `<_ComponentNa
|
|
|
344
344
|
- **`_Database::register()` auto-starts a lazy transaction (since Apr 2 2026, commit `fa7835ed`).** Any code that calls `register()` and then writes to that DB must call `_Database::transactionCommit()` before the request ends — otherwise MySQL silently rolls back all writes when the connection closes. Lazy transactions only materialise on the first write, so read-only callers are unaffected. See `_underscore/Database.php:48`. First discovered when Rate SAML user provisioning silently discarded all new user INSERTs (Jun 2026).
|
|
345
345
|
- **PHP "Unclosed '{'" parse errors report a MISLEADING line number.** When a `.php` file loaded by the autoloader (`Loader.php`) has a dropped/unbalanced brace, PHP reports `Unclosed '{' on line N` where N is the **outermost `class X {` line** and fails at EOF — NOT at the true location of the missing `}`. Worse, because the file loads lazily via the SPL autoloader, the runtime trace points at the **caller** that triggered the autoload (e.g. a `new _Email()` call site), not the broken file. **Triage rule:** for an "Unclosed '{'" error, the real culprit is a missing `}` somewhere between the reported line and EOF of the file that failed to load — run `php -l <file>` (it reports the EOF line) and scan the whole file. **Merge-conflict resolutions are a common source of a single dropped brace** — review the entire merge, not just the one file the error appears to name. (First hit: production 500 EO-1, Jul 2026 — a `}` dropped from `_Email::send()` during merge `685e4a14` surfaced as a trace pointing at the `new _Email()` caller.)
|
|
346
346
|
|
|
347
|
+
- **A controller exception is silently swallowed by `Route.php`, then masked as a view error.** In
|
|
348
|
+
dispatch, `_underscore/Route.php` (~lines 462–468) wraps the controller call in
|
|
349
|
+
`catch (Exception $e) { _Database::transactionRollback(); }` — **no log, no rethrow, no debug
|
|
350
|
+
bypass.** The controller response is left null, so Route.php falls through to its generic throw at
|
|
351
|
+
line 525: `Failed to determine how to render view for route '<route>' ... './View/Index/<method>.html'`.
|
|
352
|
+
**Therefore this "Failed to determine how to render view" fatal is very often a MASK for a real
|
|
353
|
+
exception thrown inside the controller method** (commonly `_Controller_Index::api()`), not a genuine
|
|
354
|
+
routing/view misconfiguration. A frequent culprit is a DB connect / "Unknown database" failure in
|
|
355
|
+
`api()`'s **unguarded pre-execute block** (it registers `DB_LOGS` and queries `Logs.Api` for the
|
|
356
|
+
transactionId-uniqueness check *before/outside* the only try/catch, which wraps just
|
|
357
|
+
`$api->execute()` at ~line 205). When diagnosing the line-525 error, look for a thrown exception in
|
|
358
|
+
the controller — not the view layer. (Note: an *unmatched host* does NOT trigger this; `api()`
|
|
359
|
+
returns a response object that gets JSON-encoded — only a thrown exception hits the line-525 path.)
|
|
360
|
+
See `2.0/apps/api2/workflows/environment-configuration-and-provisioning.md`. Fix candidate: have
|
|
361
|
+
Route.php log/rethrow the swallowed exception (or expose it under debug) instead of discarding it.
|
|
362
|
+
|
|
347
363
|
## Change history
|
|
348
364
|
- 2026-06-11 — Documented lazy transaction gotcha in `_Database::register()` (rgirish)
|
|
349
365
|
- 2026-06-25 — Added the Surface platform UI presentation/configuration layer (DB-driven UI config replacing `Page::meta()`, CTO-reviewed AGREE-WITH-ADJUSTMENTS) (jcardinal)
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-20
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Core/Surface.php
|
|
@@ -106,6 +106,21 @@ which surfaced latent resolution gaps in `_buildBundle`/`_resolveElement`:
|
|
|
106
106
|
- `bundle.surface.titleMessageKey` is set from `titleMessageId`.
|
|
107
107
|
- The 2nd `_loadMessages` return is merged into `$messageKeyById`; dangling message ids are logged.
|
|
108
108
|
|
|
109
|
+
### Boolean labels — `messages.trueLabel` / `messages.falseLabel`
|
|
110
|
+
Each resolved element now also emits **`messages.trueLabel`** and **`messages.falseLabel`**, sourced
|
|
111
|
+
from the element's **`trueMessageId`** / **`falseMessageId`** (the unified boolean-label pair on
|
|
112
|
+
`SurfaceElements` — see the [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md)).
|
|
113
|
+
`_buildBundle`/`_serializeElement` collect those two ids into the same message batch and id→key remap
|
|
114
|
+
as the other slots. **Additive + backward-compatible:** an element that sets neither id emits `null`
|
|
115
|
+
for both, so existing surfaces are unchanged. This makes `Core.Messages` the single source of truth for
|
|
116
|
+
Yes/No + Active/Inactive labels (no Vocabulary needed) so **BOOLEAN-`valueType` toggles render their
|
|
117
|
+
caption and boolean status badges get a label**. Historically the serializer only emitted
|
|
118
|
+
`label`/`tooltip`/`placeholder`/`disabledTooltip` and **silently dropped** `trueMessageId`/
|
|
119
|
+
`falseMessageId` — SalesOrders never hit this because its status badge uses a Vocabulary and it has no
|
|
120
|
+
boolean rows; the item-record modal (the first boolean consumer) exposed the gap. Rides in
|
|
121
|
+
`_underscore`, so it takes effect only once the framework branch is deployed (framework is pulled at
|
|
122
|
+
deploy).
|
|
123
|
+
|
|
109
124
|
## Vocabulary / label per-client relabeling (slugs are internal, labels are messages)
|
|
110
125
|
|
|
111
126
|
Clients **never rename a Vocabulary**. `Vocabularies.slug` (`sales-order-status`) and
|
|
@@ -338,6 +353,14 @@ Core record grants + their logic-group expressions all evaluate `all`/`"1"`. The
|
|
|
338
353
|
match Compass, a follow-up migration aligning both `meta` and `meta-group` to roles 1,3,4 is needed.
|
|
339
354
|
|
|
340
355
|
## Change history
|
|
356
|
+
- 2026-07-20 — Extended the resolver to emit `messages.trueLabel`/`messages.falseLabel` per element,
|
|
357
|
+
sourced from `trueMessageId`/`falseMessageId` (collected into the same message batch + id→key remap);
|
|
358
|
+
additive + backward-compatible (unset → null). Closes a latent gap: the serializer historically emitted
|
|
359
|
+
only label/tooltip/placeholder/disabledTooltip and dropped the boolean-label ids, so BOOLEAN-valueType
|
|
360
|
+
toggles rendered blank and boolean status badges had no label — SalesOrders sidestepped it (Vocabulary
|
|
361
|
+
badge, no boolean rows); the item-record modal (first boolean consumer) surfaced it. Keeps Core.Messages
|
|
362
|
+
the single source of truth for Yes/No + Active/Inactive. Takes effect only once the `_underscore`
|
|
363
|
+
framework branch is deployed. (apeterson)
|
|
341
364
|
- 2026-07-17 — Materialized the per-client sales-order button diffs (re-adding the overrides wiped in
|
|
342
365
|
the earlier rebuild): Compass/CompassCanada ADMIN = all four record-action buttons visible + both
|
|
343
366
|
`VISIBILITY_RULE`/`ENABLED_RULE` widened to `[pendingApproval, pendingInitialApproval]`, MANAGER =
|
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
| [Tickets API (/v2/tickets)](features/tickets-api.md) | The generic ticket endpoint of the 2.0 REST API. | Component/Api/V2/V2.php |
|
|
14
14
|
| [V2 API error/message codes (EV/EZ troubleshooting map)](features/v2-api-error-codes.md) | The V2 JSON engine (`Component/Api/V2/V2.php`) returns short **message codes** in the response `error` field, grouped by family: `EN-*` authentication, `EZ-*` a | api2/Component/Api/V2/V2.php |
|
|
15
15
|
| [AWS CodePipeline Deployment via CodeConnections (GitHub → Elastic Beanstalk)](workflows/codepipeline-codeconnections-deploy.md) | 2.0 apps (`api2`, `_underscore`) are deployed through **AWS CodePipeline**. | |
|
|
16
|
-
| [New Environment Configuration & Provisioning (api2)](workflows/environment-configuration-and-provisioning.md) | What it takes for a 2.0 API environment (e.g. | api2/Config/<environment>.ini, api2/Controller/Index.php, dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql, _underscore/Route.php |
|
|
16
|
+
| [New Environment Configuration & Provisioning (api2)](workflows/environment-configuration-and-provisioning.md) | What it takes for a 2.0 API environment (e.g. | api2/Config/<environment>.ini, api2/Controller/Index.php, dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql, dbchanges2/Logs/, _underscore/Route.php |
|
|
@@ -6,12 +6,13 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-20
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Config/<environment>.ini
|
|
13
13
|
- api2/Controller/Index.php
|
|
14
14
|
- dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql
|
|
15
|
+
- dbchanges2/Logs/
|
|
15
16
|
- _underscore/Route.php
|
|
16
17
|
related: []
|
|
17
18
|
---
|
|
@@ -52,6 +53,16 @@ the real cause.
|
|
|
52
53
|
DBs must be provisioned and the `dbchanges2` migrations applied before the app can connect.
|
|
53
54
|
Until then, every request throws on the first Core query.
|
|
54
55
|
|
|
56
|
+
5. **Seed the base `Logs` schema — separate from the per-tenant `Logs_<Client>` DBs.** The
|
|
57
|
+
Core-level logs database `Logs` (`Core.Databases` id `11`, framework alias `_underscore::DB_LOGS`;
|
|
58
|
+
tables `Api`, `Error`, `Webhook`) is a **distinct** database from each client's `Logs_<Client>`
|
|
59
|
+
schema and is easy to miss on an all-in-one cluster where every `Logs_<Client>` exists but base
|
|
60
|
+
`Logs` does not. `_Controller_Index::api()` registers `DB_LOGS` and queries `Logs.Api` on **every**
|
|
61
|
+
request (transactionId-uniqueness check) — so if base `Logs` is absent, **every** API request 500s
|
|
62
|
+
with an "Unknown database 'Logs'" thrown before execute, which then surfaces as the misleading
|
|
63
|
+
Route.php:525 view fatal (see Edge cases). `dbchanges2` has a dedicated `Logs/` migration folder
|
|
64
|
+
for this database; apply it alongside `Core` and the tenant schemas.
|
|
65
|
+
|
|
55
66
|
## Systems involved
|
|
56
67
|
- **api2** — `Config/<env>.ini` (DB + API hosts); `Controller/Index.php::api()` registers `Core`
|
|
57
68
|
from `[database]` then dispatches by hostname.
|
|
@@ -79,8 +90,27 @@ Diagnostic order:
|
|
|
79
90
|
2. Connect to the resolved host and `SHOW DATABASES` — confirm `Core` actually exists (a fresh
|
|
80
91
|
cluster has only `information_schema/mysql/performance_schema/sys` → not seeded yet).
|
|
81
92
|
3. Confirm `Core.DatabaseHosts` has rows for this `environmentId` (Core Logs DB id is `11`).
|
|
93
|
+
4. **`SHOW DATABASES` and confirm the base `Logs` schema exists**, not just `Logs_<Client>`.
|
|
94
|
+
`SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='Logs'` should return one
|
|
95
|
+
row (compare against prod, where it lives on `prod-logs`). Zero rows = base `Logs` missing → every
|
|
96
|
+
request 500s with the Route.php:525 view fatal. The failure **cannot self-report**: `api()`'s
|
|
97
|
+
error handler writes into `Logs.Api` (the same missing schema) when the env's `[api]` block has no
|
|
98
|
+
`log_filepath`, so the diagnosis must come from `SHOW DATABASES`, not the logs. Fix by applying the
|
|
99
|
+
`dbchanges2/Logs/` migration (tables `Api`, `Error`, `Webhook`).
|
|
100
|
+
|
|
101
|
+
**What is NOT the cause here:** on `sandbox-client` (Environments id 24) the Core DB, the env's
|
|
102
|
+
`DatabaseHosts` rows, JWT-secret `Parameters`, and `Domains` were all present and correct — the base
|
|
103
|
+
`Logs` schema was the *only* thing missing. Don't chase seed/config/connectivity when only base `Logs`
|
|
104
|
+
is absent.
|
|
82
105
|
|
|
83
106
|
## Change history
|
|
107
|
+
- 2026-07-20 — `sandbox-client` API 500'd on every request (Route.php:525 view fatal). Root cause: the
|
|
108
|
+
base `Logs` schema (`DB_LOGS`, `Core.Databases` id 11; tables Api/Error/Webhook) was missing on the
|
|
109
|
+
all-in-one cluster although every `Logs_<Client>` existed. `_Controller_Index::api()` queries
|
|
110
|
+
`Logs.Api` before its try/catch, so "Unknown database 'Logs'" was swallowed by Route.php and masked
|
|
111
|
+
as a view error; it also couldn't self-log (no `log_filepath`, so errors write to the missing Logs).
|
|
112
|
+
Fixed by applying the `dbchanges2/Logs/` migration. Added base-`Logs` provisioning step 5 + the
|
|
113
|
+
`SHOW DATABASES` diagnostic. (jcardinal)
|
|
84
114
|
- 2026-06-16 — Documented after fixing `qc-security`: `[database] hostname` was reversed
|
|
85
115
|
(`qc.security...`), which threw on Core connect and surfaced as the Route.php:525 view fatal.
|
|
86
116
|
Added the `2026-06-16a` Core migration registering `DatabaseHosts` for the stage/demo/qa-*/qc-*
|
|
@@ -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/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, 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-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.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, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.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/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, 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-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.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, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md |
|
|
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/ |
|
|
@@ -45,6 +45,12 @@ files:
|
|
|
45
45
|
- dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql
|
|
46
46
|
- dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql
|
|
47
47
|
- dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql
|
|
48
|
+
- dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql
|
|
49
|
+
- dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql
|
|
50
|
+
- dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql
|
|
51
|
+
- dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql
|
|
52
|
+
- dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql
|
|
53
|
+
- dbchanges2/Core/2026-07-17 - README - RUN ORDER.md
|
|
48
54
|
related:
|
|
49
55
|
- ../../_underscore/features/surface-resolver.md
|
|
50
56
|
---
|
|
@@ -213,6 +219,29 @@ buttons above): Core seeds the section **off**, each client opts in via a `Surfa
|
|
|
213
219
|
`shipping`); the FE resolves `salesOrderNotes[].note` by that slug (client-agnostic). Per-client slug
|
|
214
220
|
variance is handled by a **`CONFIG` override** on the element (REPLACE semantics) — no FE change.
|
|
215
221
|
|
|
222
|
+
### Item-record modal gating (Edit button + vendor-items table, 2026-07-20)
|
|
223
|
+
The item-record modal's per-client gating uses the same Core-neutral-default → client-opt-in rule as
|
|
224
|
+
the SalesOrder buttons/sections above, applied via two mechanisms:
|
|
225
|
+
|
|
226
|
+
- **Header Edit button (existing element).** The `item-record-header` Edit button is seeded OFF in Core
|
|
227
|
+
(the neutral-default action-button rule — see the TRUE NEUTRAL DEFAULT section). Compass + Compass
|
|
228
|
+
Canada opt in with a client-wide `IS_VISIBLE='1'` override (`Client_*/2026-07-20c -
|
|
229
|
+
ItemRecordEditButtonEnable.sql`). The override resolves the element **id-agnostically** by
|
|
230
|
+
`renderType='BUTTON'` + `config.kind='itemEdit'` (never a hardcoded element id/uuid — those regenerate
|
|
231
|
+
per environment), `NOT EXISTS`-guarded, client-wide (`roleId`/`personaId` NULL).
|
|
232
|
+
- **Vendor-items table (new dedicated toggle surface).** `Core/2026-07-20c -
|
|
233
|
+
VendorItemsToggleSurfaceSeed.sql` seeds a dedicated **`item-record-vendor-items`** SECTION surface
|
|
234
|
+
carrying a single hidden marker element (`config.role='sectionVisibility'`, the **marker pattern**
|
|
235
|
+
above), OFF in Core. The FE reads that marker's resolved `isVisible` as `vendorItemsEnabled` and
|
|
236
|
+
conditionally renders the vendor table (see
|
|
237
|
+
[surface-frontend](../../toga25-supply/features/surface-frontend.md)). Compass + Compass Canada opt in
|
|
238
|
+
via `Client_*/2026-07-20d - ItemRecordVendorItemsEnable.sql` (client-wide `IS_VISIBLE='1'` on the
|
|
239
|
+
marker, resolved by `Surfaces.slug='item-record-vendor-items'` + `config.role='sectionVisibility'`).
|
|
240
|
+
|
|
241
|
+
Both clients' `c`/`d` overrides and the Core seed were added to `Core/2026-07-17 - README - RUN
|
|
242
|
+
ORDER.md`. Verified working in the UI for both clients. (Boolean toggle/badge labels in this modal
|
|
243
|
+
resolve through the resolver's new `trueLabel`/`falseLabel` emission — see the resolver doc.)
|
|
244
|
+
|
|
216
245
|
## ThemeTokens — per-tenant, physically isolated in each client DB
|
|
217
246
|
|
|
218
247
|
`ThemeTokens` lives **inside each tenant's own database** (`Client_Compass.ThemeTokens`,
|
|
@@ -366,6 +395,14 @@ rule resumes.
|
|
|
366
395
|
override is added). **Open follow-up.**
|
|
367
396
|
|
|
368
397
|
## Change history
|
|
398
|
+
- 2026-07-20 — Applied the Core-neutral-default → client-opt-in pattern to the **item-record modal**.
|
|
399
|
+
Header Edit button seeded OFF in Core; Compass + Compass Canada opt in via `Client_*/2026-07-20c`,
|
|
400
|
+
resolving the element id-agnostically by `renderType='BUTTON'`+`config.kind='itemEdit'` (NOT EXISTS,
|
|
401
|
+
client-wide). New dedicated `item-record-vendor-items` toggle surface (`Core/2026-07-20c`) carrying a
|
|
402
|
+
`config.role='sectionVisibility'` marker (marker pattern, OFF in Core) that the FE reads as
|
|
403
|
+
`vendorItemsEnabled`; Compass + Compass Canada opt in via `Client_*/2026-07-20d` (resolved by
|
|
404
|
+
`Surfaces.slug`+`config.role`). All added to the `Core/2026-07-17 - README - RUN ORDER.md`; verified
|
|
405
|
+
in the UI for both clients. (apeterson)
|
|
369
406
|
- 2026-07-20 — Applied the Core-neutral-default → client-opt-in pattern to two SalesOrder display
|
|
370
407
|
sections. **admin-notes** (marker pattern): `Core/2026-07-20a` hides it by default (marker
|
|
371
408
|
`isVisible=0`, resolved id-agnostically by `Surfaces.slug`+`config.role='sectionVisibility'`);
|
|
@@ -9,6 +9,6 @@
|
|
|
9
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 |
|
|
10
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 |
|
|
11
11
|
| [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 |
|
|
12
|
-
| [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/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, 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/SurfaceRowActions.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
|
+
| [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/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, 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/SurfaceRowActions.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, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx |
|
|
13
13
|
| [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How `toga25-supply` deploys to **all** of its environments on AWS Amplify from a **single shared `amplify.yml`**. | toga25-supply/amplify.yml, toga25-supply/src/api/api.ts, toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/vite.config.ts, toga25-supply/package.json |
|
|
14
14
|
| [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 |
|
|
@@ -39,6 +39,11 @@ files:
|
|
|
39
39
|
- toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx
|
|
40
40
|
- toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts
|
|
41
41
|
- toga25-supply/src/fieldsConfig/index.ts
|
|
42
|
+
- toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts
|
|
43
|
+
- toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts
|
|
44
|
+
- toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx
|
|
45
|
+
- toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx
|
|
46
|
+
- toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx
|
|
42
47
|
related:
|
|
43
48
|
- ../../_underscore/features/surface-resolver.md
|
|
44
49
|
- meta-driven-table-data.md
|
|
@@ -109,8 +114,8 @@ legacy pre-Surface fallback branches at the wired screens removed). Legacy per-s
|
|
|
109
114
|
|
|
110
115
|
## What's wired
|
|
111
116
|
|
|
112
|
-
Login + sales-orders list + the sales-order modal + the **
|
|
113
|
-
**Inventory** LIST screens. The SalesOrder action
|
|
117
|
+
Login + sales-orders list + the sales-order modal + the **item-record modal (VIEW, all clients)** +
|
|
118
|
+
the **Items**, **VendorItems**, and **Inventory** LIST screens. The SalesOrder action
|
|
114
119
|
bar reproduces `orderViewFields.json` approve/deny/approvalWorkflow/viewLog/editOrder by delegating
|
|
115
120
|
to the existing `onActionClick`/`onOpenLog` handlers. TABLE-type surfaces stay on the existing
|
|
116
121
|
TableView pipeline via `tableViewSlug` (see [meta-driven-table-data](meta-driven-table-data.md)) —
|
|
@@ -219,6 +224,39 @@ third section pattern alongside card sections (`cardType` → grid renderer) and
|
|
|
219
224
|
seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster seed fix is on hold
|
|
220
225
|
(see [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md)).
|
|
221
226
|
|
|
227
|
+
## Item-record modal (VIEW) — migrated via an adapter seam, ALL clients (no roster)
|
|
228
|
+
|
|
229
|
+
The item-record modal's **VIEW** sections are now resolved from Surface for **every client** — unlike
|
|
230
|
+
the SalesOrders modal, there is **no migration roster**. It mirrors the SalesOrders adapter-seam
|
|
231
|
+
approach (config source moves JSON → Surface; the existing view is untouched):
|
|
232
|
+
|
|
233
|
+
- **`surfaceBundleToItemViewFields()`** (`ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts`,
|
|
234
|
+
new; barrel `helpers/index.ts`) adapts the grouped Surface bundle into the
|
|
235
|
+
`{ header, editItem, sections, vendorItemsEnabled }` shape the view already consumed.
|
|
236
|
+
- **`useItemRecordModalViewModel`** fetches the `item-record-*` surface group via
|
|
237
|
+
`useFetchSurfaceMetaGroup`, adapts it, and threads it into **`ItemRecordView`** as a **`viewFields`**
|
|
238
|
+
prop. The per-client JSON (`itemRecordViewFields`) is now a **fallback only** — used when the group
|
|
239
|
+
fails to resolve. **Edit mode stays JSON-driven (deferred).**
|
|
240
|
+
- **Boolean labels** flow through `messages.trueLabel`/`messages.falseLabel` (added to
|
|
241
|
+
`SurfaceElementMessages` in `src/surface/types.ts`), which the resolver now emits from each element's
|
|
242
|
+
`trueMessageId`/`falseMessageId` (see
|
|
243
|
+
[surface-resolver](../../_underscore/features/surface-resolver.md)) — so BOOLEAN toggles/badges render
|
|
244
|
+
their caption instead of blank.
|
|
245
|
+
|
|
246
|
+
**Per-client gating rides the Core-default-OFF → SurfaceOverride opt-in pattern** (the same
|
|
247
|
+
"approvals-gate" shape used on SalesOrders — `elements.some(el => el.isVisible)`); the seed side lives
|
|
248
|
+
in [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md):
|
|
249
|
+
|
|
250
|
+
- **Header Edit button** — an element in `item-record-header`, matched **id-agnostically** by
|
|
251
|
+
`renderType='BUTTON'` + `config.kind='itemEdit'`. Core seeds it OFF; Compass / Compass Canada opt in.
|
|
252
|
+
- **Vendor-items table** — a dedicated `item-record-vendor-items` **toggle surface** carrying a
|
|
253
|
+
`config.role='sectionVisibility'` marker element; the FE reads it as **`vendorItemsEnabled`** and
|
|
254
|
+
conditionally renders the vendor table in `ItemRecordView`. Core-OFF; Compass / Compass Canada opt in.
|
|
255
|
+
|
|
256
|
+
> **Seed-intent to verify (flagged to the dev):** the Core item-record Pricing section seed now also
|
|
257
|
+
> shows **`leasePrice`** for all clients, whereas the old DEFAULT JSON showed only unit price. Confirm
|
|
258
|
+
> that is the intended seed shape.
|
|
259
|
+
|
|
222
260
|
## Gotchas
|
|
223
261
|
|
|
224
262
|
- **The single-meta envelope can hide the real bundle behind a DECOY `elements: []`.** The V2 envelope
|
|
@@ -293,6 +331,18 @@ seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster
|
|
|
293
331
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
294
332
|
|
|
295
333
|
## Change history
|
|
334
|
+
- 2026-07-20 — Migrated the **item-record modal VIEW** onto Surface for **ALL clients (no roster)** via
|
|
335
|
+
the adapter seam: new `surfaceBundleToItemViewFields()` adapts the `item-record-*` surface group
|
|
336
|
+
(`useFetchSurfaceMetaGroup`) into the existing `{header, editItem, sections, vendorItemsEnabled}`
|
|
337
|
+
shape, threaded into `ItemRecordView` as a `viewFields` prop with the per-client JSON
|
|
338
|
+
(`itemRecordViewFields`) as resolve-failure fallback only; edit mode stays JSON-driven (deferred).
|
|
339
|
+
Added `trueLabel`/`falseLabel` to `SurfaceElementMessages` (`src/surface/types.ts`) so BOOLEAN
|
|
340
|
+
toggles/badges render their caption (backend resolver now emits them). Per-client gating uses the
|
|
341
|
+
Core-default-OFF → SurfaceOverride opt-in pattern: header Edit button (`item-record-header`, matched
|
|
342
|
+
by `renderType='BUTTON'`+`config.kind='itemEdit'`) and a dedicated `item-record-vendor-items` toggle
|
|
343
|
+
surface (marker `config.role='sectionVisibility'` → FE `vendorItemsEnabled`), both opted into by
|
|
344
|
+
Compass / Compass Canada. Flagged: Core Pricing seed now also shows `leasePrice` for all clients
|
|
345
|
+
(verify seed intent). (apeterson)
|
|
296
346
|
- 2026-07-20 — Made the SalesOrders `notes` display section **field-driven** per client.
|
|
297
347
|
`buildSection`'s `displayToggle` branch now handles field-carrying sections: it maps each `FIELD`
|
|
298
348
|
element to a field config (`elementToFieldConfig` now carries `config.noteTypeSlug`) and **derives**
|
package/package.json
CHANGED