toga-ai 1.0.764 → 1.0.766
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/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +11 -1
- package/knowledge/2.0/apps/dbchanges2/workflows/targeting-the-right-database.md +71 -1
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +3 -3
- package/knowledge/2.0/apps/toga25-supply/features/record-modals-and-nested-tables.md +53 -0
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +70 -2
- package/knowledge/2.0/apps/toga25-supply/features/transfer-orders-page.md +115 -3
- package/knowledge/clients/nychh/profile.md +31 -7
- package/package.json +1 -1
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
| [Framework branch running AHEAD of schema — environment-wide 1054/EO-1 after a deploy](workflows/framework-branch-ahead-of-schema.md) | A **third** kind of 2.0 schema drift, distinct from the two already documented: nobody's database went backwards — **the code went forwards**. | _underscore/Model/Core/RecordField.php, _underscore/Model/Client/TableViewField.php, _underscore/Model/Client/TableView.php, _underscore/Model/Core/Surface.php, _underscore/Model/Client/Urgency.php, _underscore/Model/Client/RecordFieldSetting.php, _underscore/Model/Client/ClientRecordFieldSetting.php, api2/Component/Api/V2/V2.php, dbchanges2/Core/2026-08-14c - RecordFieldsValidationMetadata.sql, dbchanges2/Client/2026-08-14a - RecordFieldSettingsClientTable.sql, dbchanges2/Client/2026-08-13a - UserNavPreferencesClientTable.sql, dbchanges2/Client/2026-08-13c - PersonaNavVisibilityClientTable.sql |
|
|
11
11
|
| [Local vs prod MySQL config parity — why “it passed locally” is not evidence](workflows/local-vs-prod-mysql-config-parity.md) | Several migration failures that look like "prod-only bugs" are actually **per-machine MySQL server-configuration differences**. | |
|
|
12
12
|
| [Repairing non-prod metadata drift (works in prod, broken in beta/dev-sandbox)](workflows/nonprod-metadata-drift-repair.md) | Almost all 2.0 platform behavior is **metadata** — `Core.Records`/`RecordFields`, `Core.RecordScripts`, `Core.ApiPayloadInterceptors`, and per-client `Acl*` row | dbchanges2/Core/2026-06-30a - ItemFulfillmentStageDefaultInterceptor.sql, dbchanges2/Client_Compass/2026-08-06 - RemoveBrokenSalesOrderItemPostPostInterceptor.sql, dbchanges2/Core/2026-07-16a - TrackingNumberSignatureTypeRecordField.sql, dbchanges2/_modules/netsuite/2026-07-10a - UnitInventoryFields.sql, dbchanges2/Client_Aig/_modules.txt, dbchanges2/Client/2026-07-22c - TrackingNumberMeasureIdsFieldPermission.sql, api2/Config/beta.ini, api2/Config/sandbox-dev.ini, dbchanges2/Client/2026-07-22a - EntitlementServiceAddressId.sql, dbchanges2/Client/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql |
|
|
13
|
-
| [Targeting the right database (tenant restores, and running a change file against the wrong DB)](workflows/targeting-the-right-database.md) | Two ways to point at the wrong database — a dump taken from the wrong host, and a change file piped into the wrong schema. | api2/Config/production.ini, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql |
|
|
13
|
+
| [Targeting the right database (tenant restores, and running a change file against the wrong DB)](workflows/targeting-the-right-database.md) | Two ways to point at the wrong database — a dump taken from the wrong host, and a change file piped into the wrong schema. | api2/Config/production.ini, api2/Config/sandbox-client.ini, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql |
|
|
14
14
|
| [Verifying whether a dbchanges2 migration actually ran in an environment](workflows/verifying-a-migration-ran.md) | dbchanges2 has no execution ledger you can query — "did this file run here?" has to be answered from the **rows the file would have produced**. | Client/2026-08-11b - SalesOrderPurchaseOrdersField.sql, Client/2026-08-13 - UnitsForItemsPO_SalesOrderItemsJoin.sql |
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-03
|
|
10
10
|
owners: [jcardinal, apeterson, tcox]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
|
|
@@ -399,6 +399,11 @@ and `NOT EXISTS`-guarded.
|
|
|
399
399
|
`config.valueKey='_totalPurchase'`. Requires the small FE adapter extension to emit `isConcatenated`
|
|
400
400
|
(see [surface-frontend](../../toga25-supply/features/surface-frontend.md)). Authored this session;
|
|
401
401
|
only Quad confirmed applied locally.
|
|
402
|
+
**Do NOT add `"suffix":"+"` to this element's config to get the "$4,737.00 +" joiner (2026-09-03).**
|
|
403
|
+
blox renders a field-level `suffix` **unconditionally**, but the stacked lease line auto-hides when
|
|
404
|
+
`_totalLease` is null/0 — a seeded `+` would dangle on every non-lease order. The joiner is FE-side
|
|
405
|
+
and conditional (`withStackedCurrencyJoiner`, see
|
|
406
|
+
[surface-frontend](../../toga25-supply/features/surface-frontend.md)).
|
|
402
407
|
|
|
403
408
|
## ThemeTokens — per-tenant, physically isolated in each client DB
|
|
404
409
|
|
|
@@ -1288,6 +1293,11 @@ uncommitted at the time. See
|
|
|
1288
1293
|
[Transfer Orders page](../../toga25-supply/features/transfer-orders-page.md).
|
|
1289
1294
|
|
|
1290
1295
|
## Change history
|
|
1296
|
+
- 2026-09-03 — Flagged that the Compass / Compass Canada stacked-Total `CONFIG` override must **not**
|
|
1297
|
+
gain a `"suffix":"+"`: blox renders a field `suffix` unconditionally while the stacked lease line
|
|
1298
|
+
auto-hides on null/0, so a seeded `+` dangles on non-lease orders. The joiner is conditional FE code
|
|
1299
|
+
(`withStackedCurrencyJoiner`) — see
|
|
1300
|
+
[surface-frontend](../../toga25-supply/features/surface-frontend.md). (tcox)
|
|
1291
1301
|
- 2026-09-02 — Wrote down the **`Core.Records` / `Core.RecordFields` id protocol**, the counterpart to
|
|
1292
1302
|
this doc's reserved-block discipline: they are the only two hand-maintained-id tables platform-wide,
|
|
1293
1303
|
so **ask the developer for the next free id**, never `AUTO_INCREMENT` them, and **never infer the
|
|
@@ -6,10 +6,11 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-03
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Config/production.ini
|
|
13
|
+
- api2/Config/sandbox-client.ini
|
|
13
14
|
- dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql
|
|
14
15
|
related:
|
|
15
16
|
- ../architecture.md
|
|
@@ -39,6 +40,30 @@ Connection **locations** only — passwords live in the config file, never here.
|
|
|
39
40
|
|
|
40
41
|
Username is `admin`. **The password is in the matching section of
|
|
41
42
|
`api2/Config/production.ini` — read it from there; never paste it anywhere, including here.**
|
|
43
|
+
The sandbox-client host's password lives in `api2/Config/sandbox-client.ini` `[database]`; DNS
|
|
44
|
+
resolves `client.sandbox.database.togahub.com` to the **writer**, so `DROP`/`CREATE` work against
|
|
45
|
+
that same hostname.
|
|
46
|
+
|
|
47
|
+
🚨 **The password values in these ini files are WRAPPED IN DOUBLE QUOTES.** An extractor that only
|
|
48
|
+
strips `password[ \t]*=[ \t]*` keeps the quotes and you get
|
|
49
|
+
`ERROR 1045 (28000): Access denied for user 'admin'@'<ip>' (using password: YES)`. The password *is*
|
|
50
|
+
being sent — just with literal `"` around it — so it reads like a permissions or VPN problem and is
|
|
51
|
+
not one. **Strip the quotes.** Also compare the whole section header (`$0 == "[" S "]"`), or
|
|
52
|
+
`[database]` matches `[databaseClient]` / `[databaseLogs]` and you extract the wrong password:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# zsh/bash: export the password for one section of one ini, quotes stripped
|
|
56
|
+
setpw() {
|
|
57
|
+
local pw
|
|
58
|
+
pw=$(awk -v S="$2" '$0=="["S"]"{f=1;next} /^\[/{f=0} f && /^password/{
|
|
59
|
+
sub(/^password[ \t]*=[ \t]*/,""); gsub(/[ \t\r]+$/,""); print; exit}' "$1")
|
|
60
|
+
pw=${pw%\"}; pw=${pw#\"}
|
|
61
|
+
export MYSQL_PWD="$pw"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`MYSQL_PWD` keeps the password out of `ps` and history the same way a defaults file does — use it
|
|
66
|
+
when the client rejects `--defaults-extra-file` (some builds do).
|
|
42
67
|
|
|
43
68
|
**Keep the password out of your shell history and out of `ps`** by building a throwaway
|
|
44
69
|
credentials file from the ini instead of passing `-p` on the command line:
|
|
@@ -66,6 +91,40 @@ Delete the file when you are done.
|
|
|
66
91
|
4. **Verify the restored copy against the SOURCE you intended**, repeating step 1 against the
|
|
67
92
|
restored schema.
|
|
68
93
|
|
|
94
|
+
## Resetting a shared environment from production (dump → drop → reload)
|
|
95
|
+
|
|
96
|
+
Used 2026-09-03 to bring sandbox-client's stale `Client_Nychh` TableViews back in line with prod.
|
|
97
|
+
The developer runs every one of these; do not execute them for them.
|
|
98
|
+
|
|
99
|
+
1. **Dump each source database**, one file each — prod `Core` from
|
|
100
|
+
`reader1.core.database.togahub.com`, prod tenant from `reader1.client.database.togahub.com`:
|
|
101
|
+
```
|
|
102
|
+
mysqldump --single-transaction --quick --routines --triggers --events \
|
|
103
|
+
--set-gtid-purged=OFF --no-tablespaces <DB> > <DB>.sql
|
|
104
|
+
```
|
|
105
|
+
`--set-gtid-purged=OFF` because RDS has GTID on; `--no-tablespaces` because the `admin` user
|
|
106
|
+
lacks `PROCESS` (otherwise **error 1227**).
|
|
107
|
+
2. 🚨 **`tail -1` every dump file and confirm it ends with `-- Dump completed` BEFORE you drop
|
|
108
|
+
anything.** A truncated dump loads without complaining. DNS dropped mid-session on a host that
|
|
109
|
+
had resolved seconds earlier (`ERROR 2005 Unknown MySQL server host`) — the dump just stops.
|
|
110
|
+
3. **Prove which environment each connection actually reached.** Better than `@@hostname`: check the
|
|
111
|
+
resolved IP — production resolves into **`10.201.x`**, sandbox into **`10.200.x`**.
|
|
112
|
+
4. **Drop + create the target schema, then pipe the dump in.**
|
|
113
|
+
5. **Verify with a row count known to differ** (step 1 of the procedure above), against the schema
|
|
114
|
+
you intended to copy.
|
|
115
|
+
|
|
116
|
+
### ⚠ Which `mysql` / `mysqldump` binary you are running is load-bearing
|
|
117
|
+
|
|
118
|
+
- **A MariaDB-branded `mysqldump` rejects `--set-gtid-purged=OFF`.** Both TOGA clusters are **MySQL
|
|
119
|
+
8.0.42**, so use a **MySQL 8.0** client. On an XAMPP machine both ship and neither is on `PATH`:
|
|
120
|
+
`xamppfiles/bin/mysqldump` is **MariaDB 10.4** (rejects the flag);
|
|
121
|
+
`xamppfiles/mysql/bin/mysqldump` is **MySQL 8.0.44** (correct). Run `mysqldump --version` before
|
|
122
|
+
you trust it.
|
|
123
|
+
- **A shell alias can hijack a remote connection.** XAMPP's `mysql` alias hardcodes
|
|
124
|
+
`-uroot -pmysql --socket=…`, so a command you wrote for a remote host silently talks to localhost.
|
|
125
|
+
Call the client by **full path** for anything remote — and remember an exact match with your local
|
|
126
|
+
DB is evidence of a bad restore, not a good one (see the gotcha below).
|
|
127
|
+
|
|
69
128
|
## Gotchas
|
|
70
129
|
|
|
71
130
|
- 🚨 **A wrong-source restore is silent and total.** A dump taken from `127.0.0.1`, believed to be
|
|
@@ -98,6 +157,17 @@ Delete the file when you are done.
|
|
|
98
157
|
[Verifying whether a migration actually ran](./verifying-a-migration-ran.md).
|
|
99
158
|
|
|
100
159
|
## Change history
|
|
160
|
+
- 2026-09-03 — Added the **reset-a-shared-environment-from-production** procedure (the `mysqldump`
|
|
161
|
+
flag set, `--set-gtid-purged=OFF` for RDS GTID, `--no-tablespaces` for the missing `PROCESS`
|
|
162
|
+
grant, the mandatory `tail -1` "`-- Dump completed`" check because a truncated dump loads
|
|
163
|
+
silently, and the resolved-IP environment check: prod `10.201.x` vs sandbox `10.200.x`). Recorded
|
|
164
|
+
two traps that both masquerade as something else: **the ini password values are wrapped in double
|
|
165
|
+
quotes**, so a naive extractor produces `ERROR 1045 Access denied` that looks like a permissions
|
|
166
|
+
or VPN problem (fixed extractor + `MYSQL_PWD` for clients that reject `--defaults-extra-file`);
|
|
167
|
+
and **which client binary you run matters** — a MariaDB `mysqldump` rejects
|
|
168
|
+
`--set-gtid-purged=OFF`, and a shell alias hardcoding `-uroot --socket=…` hijacks a remote
|
|
169
|
+
connection to localhost. Captured while resetting sandbox-client (`Core` +
|
|
170
|
+
`Client_Nychh`) from prod after its `purchase-orders` TableView was found stale. (apeterson)
|
|
101
171
|
- 2026-09-01 — Added the **backup-database hazard for `Client/` fan-outs**: the production client
|
|
102
172
|
cluster hosts dated snapshots alongside live tenants (`Client_Compass_20260831_120000`, found
|
|
103
173
|
during a 36-database ACL survey), and a naive "every `Client_*` schema" loop will run against
|
|
@@ -9,12 +9,12 @@
|
|
|
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/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql |
|
|
10
10
|
| [Force Logout on Deployment (useDeploymentGuard)](features/force-logout-on-deployment.md) | On large deployments the backend bumps the Core parameter `META_LAST_REFRESH_DATETIME`. | toga25-supply/src/hooks/useDeploymentGuard.tsx, toga25-supply/src/App.tsx |
|
|
11
11
|
| [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/TransferOrders/viewModel/useTransferOrdersPageViewModel.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 |
|
|
12
|
-
| [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/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/layout/PrimaryTableServerLayout/PrimaryTableServerLayout.tsx, toga25-supply/src/layout/PrimaryTableServerLayout/types.ts, 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/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts, toga25-supply/src/layout/RecordApprovalModal/api/approvalDecisionsApi.ts, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css |
|
|
12
|
+
| [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/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/layout/PrimaryTableServerLayout/PrimaryTableServerLayout.tsx, toga25-supply/src/layout/PrimaryTableServerLayout/types.ts, 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/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/pages/TransferOrders/helpers/buildInventoryPurchaseOrderUrl.ts, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts, toga25-supply/src/layout/RecordApprovalModal/api/approvalDecisionsApi.ts, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css |
|
|
13
13
|
| [Side navigation & default route — an empty nav renders a BLANK PAGE and gets reported as "cannot log in"](features/side-navigation-and-default-route.md) | The 2.5 side nav is **100% backend-driven** by the `navigation` surface bundle, and the same list also decides **which routes exist** and **where `/` lands**. | toga25-supply/src/routes.tsx, toga25-supply/src/layout/AppLayout/viewModel/useAppLayoutViewModel.ts, api2/Component/Api/V2/V2.php, _underscore/Model/Core/Surface.php |
|
|
14
14
|
| [SSO redirect & public-vs-user session gating (useAuthenticationFlow)](features/sso-redirect-and-session-gating.md) | How 2.5 Supply decides, on every navigation, whether an anonymous visitor should be bounced to their client's SSO IdP instead of the local `/login` form. | toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/src/routes.tsx, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/api/api.ts |
|
|
15
|
-
| [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/App.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/SalesOrderApprovalModalsLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/RecordApprovalModalLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalTimelineView.tsx, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css, toga25-supply/src/utils/formatDateTime.ts, toga25-supply/src/utils/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/layout/RecordApprovalModal/, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/ServiceRequestRecordModalLayout.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/view/ServiceRequestsView.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/useServiceRequestRecordModalLayoutModel.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, 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/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.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/hooks/useSalesOrderVip.ts, 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, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
15
|
+
| [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/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.test.ts, toga25-supply/src/App.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/SalesOrderApprovalModalsLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/RecordApprovalModalLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalTimelineView.tsx, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css, toga25-supply/src/utils/formatDateTime.ts, toga25-supply/src/utils/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/layout/RecordApprovalModal/, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/ServiceRequestRecordModalLayout.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/view/ServiceRequestsView.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/useServiceRequestRecordModalLayoutModel.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, 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/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.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/hooks/useSalesOrderVip.ts, 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, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
16
16
|
| [Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)](features/talos-integration.md) | toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md). | toga25-supply/src/api/talos.ts, toga25-supply/src/hooks/useTalosSession.ts, toga25-supply/src/hooks/useTalosThreads.ts, toga25-supply/src/hooks/useTalosThreads.test.ts, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/hooks/useTalosSurface.test.ts, toga25-supply/src/App.tsx, toga25-supply/db-migrations/PLAYBOOK.md, dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/src/utils/talosMessages.ts, toga25-supply/src/utils/talosMessages.test.ts, toga25-supply/src/layout/AppLayout/AppLayout.tsx, toga25-supply/src/components/Header/Header.tsx, toga25-supply/src/components/Header/Header.module.css, toga25-supply/src/index.css, toga25-supply/src/assets/talos-owl.png |
|
|
17
|
-
| [Transfer Orders page (TableView → Core surfaces → React page + record modal)](features/transfer-orders-page.md) | The Transfer Orders screen — list + read-only record modal — built end to end on 2026-08-28 from a Claude Design prototype. | toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/PanelSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/ContactSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/SelectTargetLocationModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferItemPickerTable.tsx, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderNote.php, _underscore/Model/Client/TransferOrderNoteType.php, dbchanges2/Client/2026-09-01b - TransferOrderNotesTables.sql, dbchanges2/Client/2026-09-01c - TransferOrderContactCreatedByAndDescription.sql, dbchanges2/Core/2026-09-01c - TransferOrderNotesRecords.sql, dbchanges2/Core/2026-09-01d - TransferOrderContactCreatedByAndDescriptionRecordFields.sql, toga25-supply/src/pages/TransferOrders/TransferOrders.tsx, toga25-supply/src/pages/TransferOrders/hooks/useTransferOrdersTableState.ts, toga25-supply/src/pages/TransferOrders/viewModel/useTransferOrdersPageViewModel.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrdersTableLayout/TransferOrdersTableLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/hooks/useTransferOrderRecord.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/viewModel/useTransferOrderRecordModalLayoutModel.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/view/TransferOrderRecordView.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/index.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/usePurchaseOrderItemRows.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTargetLocationOptions.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTransferSourceLocation.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useLocationContacts.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateLocationContact.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/AddContactModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferNotesField.tsx, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, toga25-supply/src/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/routes.tsx, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql |
|
|
17
|
+
| [Transfer Orders page (TableView → Core surfaces → React page + record modal)](features/transfer-orders-page.md) | The Transfer Orders screen — list + read-only record modal — built end to end on 2026-08-28 from a Claude Design prototype. | toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/PanelSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/ContactSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/SelectTargetLocationModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferItemPickerTable.tsx, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderNote.php, _underscore/Model/Client/TransferOrderNoteType.php, dbchanges2/Client/2026-09-01b - TransferOrderNotesTables.sql, dbchanges2/Client/2026-09-01c - TransferOrderContactCreatedByAndDescription.sql, dbchanges2/Core/2026-09-01c - TransferOrderNotesRecords.sql, dbchanges2/Core/2026-09-01d - TransferOrderContactCreatedByAndDescriptionRecordFields.sql, toga25-supply/src/pages/TransferOrders/TransferOrders.tsx, toga25-supply/src/pages/TransferOrders/hooks/useTransferOrdersTableState.ts, toga25-supply/src/pages/TransferOrders/viewModel/useTransferOrdersPageViewModel.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrdersTableLayout/TransferOrdersTableLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/hooks/useTransferOrderRecord.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/viewModel/useTransferOrderRecordModalLayoutModel.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/view/TransferOrderRecordView.tsx, toga25-supply/src/pages/TransferOrders/helpers/buildInventoryPurchaseOrderUrl.ts, toga25-supply/src/pages/TransferOrders/TransferOrder.module.css, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/index.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/usePurchaseOrderItemRows.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTargetLocationOptions.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTransferSourceLocation.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useLocationContacts.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateLocationContact.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/AddContactModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferNotesField.tsx, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, toga25-supply/src/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/routes.tsx, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql |
|
|
18
18
|
| [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, toga25-supply/.env.development |
|
|
19
19
|
| [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 |
|
|
20
20
|
| [Porting a page (or query) from toga2-supply to toga25-supply](workflows/porting-a-page-from-toga2-supply.md) | `toga25-supply` re-implements pages that already exist in `toga2-supply`. | toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/PRUDENTIAL/exportApiFields.json, toga25-supply/src/pages/SalesOrders/api/prudentialExportApi.ts, toga2-supply/src/pages/Orders/api/OrdersApi.ts, toga2-supply/src/components/ui/Tables/hooks/useExportableData.tsx |
|
|
@@ -24,6 +24,7 @@ files:
|
|
|
24
24
|
- toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx
|
|
25
25
|
- toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json
|
|
26
26
|
- toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx
|
|
27
|
+
- toga25-supply/src/pages/TransferOrders/helpers/buildInventoryPurchaseOrderUrl.ts
|
|
27
28
|
- toga25-supply/src/hooks/useTableCellInteractions.ts
|
|
28
29
|
- toga25-supply/src/hooks/useServerTableUrlState.ts
|
|
29
30
|
- toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts
|
|
@@ -166,6 +167,37 @@ interactions so both are reachable on one nested row (Inventory `items-for-purch
|
|
|
166
167
|
projection: a **hidden (`isVisible=0`) `Items.uuid` column** is added to the table view — see
|
|
167
168
|
[tableview-field-metadata](../../api2/features/tableview-field-metadata.md).
|
|
168
169
|
|
|
170
|
+
### Deep-linking from a modal INTO a nested table (close, then navigate — never both)
|
|
171
|
+
|
|
172
|
+
A value inside one record modal can link to a filtered, pre-expanded row on another page's
|
|
173
|
+
`GenericNestedTables` grid. Reference: the Transfer Order modal's Purchase Order value →
|
|
174
|
+
Inventory (`src/pages/TransferOrders/helpers/buildInventoryPurchaseOrderUrl.ts`).
|
|
175
|
+
|
|
176
|
+
The URL is built from three `useServerTableUrlState` params, and each one has a rule:
|
|
177
|
+
|
|
178
|
+
| Param | Shape | Rule |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| grouping | `grouping=<elementKey>` | **Pin it.** The default is whichever grouping the client's surface returns first, so a reseed can silently aim the link at a view without the level you want. An unknown key falls back to the default anyway. |
|
|
181
|
+
| column filter | `<slug>_<columnId>_filter=<mode>:<value>` | Set it through `URLSearchParams.set` with the value `mode + ":" + encodeURIComponent(value)` — the same serializer `useServerTableUrlState` uses. Hand-writing the query string double-encodes, or breaks a value containing `:` / `,`. `<columnId>` is the **table-meta column id**, not the record field name. |
|
|
182
|
+
| active row | `<slug>=<uuid>` | Named **exactly** the table slug (`activeRowUuid = searchParams.get(slug)`). On an `"expand"` level this **opens the child sub-table inline on load** — the same effect as a row click. Omit it when the uuid is unknown; the filter alone still lands on the row, collapsed. |
|
|
183
|
+
|
|
184
|
+
**Sequencing: hold the target in a `useRef`, fire it from `onClosed`, and RETURN instead of calling
|
|
185
|
+
`onClose()`.** Never `setTimeout` to match the slide-down: a timer either cuts the animation short or
|
|
186
|
+
fires after the user opened something else.
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
const pendingNavigation = useRef<string | null>(null);
|
|
190
|
+
|
|
191
|
+
<TableRecordModal
|
|
192
|
+
onClosed={() => {
|
|
193
|
+
const target = pendingNavigation.current;
|
|
194
|
+
pendingNavigation.current = null;
|
|
195
|
+
if (target) { navigate(target); return; } // NOT onClose() first
|
|
196
|
+
onClose();
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
169
201
|
## Pattern 6 — Create/edit record modal (form-driven)
|
|
170
202
|
|
|
171
203
|
Scaffold per the `ItemRecordModalLayout` pattern (`src/layout/ItemRecordModalLayout/`). Form-driven
|
|
@@ -205,6 +237,19 @@ Scaffold per the `ItemRecordModalLayout` pattern (`src/layout/ItemRecordModalLay
|
|
|
205
237
|
**suffix** can't be used because `useServerTableUrlState`'s columnFilters parser treats any
|
|
206
238
|
`${slug}_*` key as a column filter — hence a prefix.
|
|
207
239
|
- **`additionalData` in the query key is mandatory** for WHERE-filtered modal tables (Pattern 4).
|
|
240
|
+
- 🚨 **Never call `onClose()` and then `navigate()` — it races two navigations and strips your
|
|
241
|
+
params.** `onClose` is `handleModalClose` in `src/hooks/useServerTableUrlState.ts`, which clears
|
|
242
|
+
the URL with `setSearchParams`. That queues a **second** navigation in the same tick as yours, both
|
|
243
|
+
racing to own the URL; the loser silently drops the grouping / filter / active-row params, so a
|
|
244
|
+
deep link lands on the page with its sub-table collapsed (or unfiltered). Navigating away replaces
|
|
245
|
+
the whole query string anyway, so clearing it first is pointless as well as risky. **Navigate and
|
|
246
|
+
return.**
|
|
247
|
+
- **A bordered scroll box inside a modal must be `flex: 0 1 auto`, not `flex: 1`.** The element that
|
|
248
|
+
carries the border is the table the user sees: `flex: 1` stretches it to the bottom of the modal,
|
|
249
|
+
so a single row renders inside a box of empty white. Put `flex: 1` on the wrapper that draws
|
|
250
|
+
nothing, and keep `min-height: 0` + `overflow: auto` on the bordered box so it still shrinks and
|
|
251
|
+
scrolls when the rows outgrow the space. Hit on the Transfer Order modal's items table; the design
|
|
252
|
+
agrees (its items table passes no `fillerHeight`, so it reserves no rows it does not have).
|
|
208
253
|
- **Per-consumer cache `scope`** — two consumers fetching the same record with different field
|
|
209
254
|
configs must use a `scope` discriminator in the query key or they overwrite each other's cache
|
|
210
255
|
(record modal vs. approval modal: the approval modal omitting item-image fields blanked the
|
|
@@ -286,6 +331,14 @@ Scaffold per the `ItemRecordModalLayout` pattern (`src/layout/ItemRecordModalLay
|
|
|
286
331
|
[Transfer Orders page](transfer-orders-page.md).
|
|
287
332
|
|
|
288
333
|
## Change history
|
|
334
|
+
- 2026-09-03 — Added the **deep-link-from-a-modal-into-a-nested-table** pattern (pin `grouping`;
|
|
335
|
+
build the column-filter chip with the `useServerTableUrlState` serializer, keyed by the table-meta
|
|
336
|
+
**column id**; set `<slug>=<uuid>` to pre-expand the row) and the rule that makes it work:
|
|
337
|
+
**hold the target in a `useRef`, fire it from `onClosed`, and `return` — never `onClose()` then
|
|
338
|
+
`navigate()`**, because `onClose` clears the URL with `setSearchParams` and the two navigations
|
|
339
|
+
race, stripping the link's params. Also recorded the modal scroll-box flex rule (`flex: 0 1 auto`
|
|
340
|
+
on the bordered box, `flex: 1` on the wrapper). Found wiring the Transfer Order modal's Purchase
|
|
341
|
+
Order value to Inventory. (apeterson)
|
|
289
342
|
- 2026-09-03 — Documented that the FE **appends `.uuid`** to a `GenericNestedTables` swap level's
|
|
290
343
|
`additionalDataSlug`, so the stored `Core.SurfaceElements.config` value must be the **bare table
|
|
291
344
|
name** (`PurchaseOrders`), not `<Table>.uuid` — a `.uuid` in the stored value produces a doubled
|
|
@@ -6,9 +6,11 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-03
|
|
10
10
|
owners: [jcardinal, apeterson, tcox]
|
|
11
11
|
files:
|
|
12
|
+
- toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts
|
|
13
|
+
- toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.test.ts
|
|
12
14
|
- toga25-supply/src/App.tsx
|
|
13
15
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx
|
|
14
16
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css
|
|
@@ -354,13 +356,23 @@ full replacement**, because the surfaces cover only part of `recordActionFields`
|
|
|
354
356
|
|
|
355
357
|
**Overlay, not replacement (decision).** The 3 surfaces cover only 3 of the 7 `recordActionFields`
|
|
356
358
|
keys; keys NOT yet migrated stay JSON-sourced (`guardrailCopy`, `actionOptions` [the approve
|
|
357
|
-
step-two enable rule], `approvalWorkflow
|
|
359
|
+
step-two enable rule], `approvalWorkflow`). The JSON approve/deny
|
|
358
360
|
blocks also carry **functional** attributes the surface does not emit (`characterLimit 255`,
|
|
359
361
|
`inputContainerWidth`, submit `valueKeys`). An `overlayDefined()` helper copies only *defined* surface
|
|
360
362
|
values, so JSON-only functional fields survive: **Surface owns all display attributes** (labels,
|
|
361
363
|
visibility, icons, `isRequired`, placeholder, `textAreaHeight`); JSON fills gaps and functional
|
|
362
364
|
wiring. The per-client `approvalActionFields.json` files are kept on disk as reference (not deleted).
|
|
363
365
|
|
|
366
|
+
**`orderContentDetailsWorkflow` IS surface-driven — it is the SAME array as approve/deny (verified
|
|
367
|
+
2026-09-03).** `overlayDecisionSurfaceFields` points **both** `orderContentDetails` and
|
|
368
|
+
`orderContentDetailsWorkflow` at `decision.orderContentDetails`
|
|
369
|
+
(`surfaceBundlesToDecisionFields.ts` ~L458), so the Approval Workflow modal's summary rows come from
|
|
370
|
+
`sales-order-decision-summary` exactly like the approve/deny modals. The header comment in
|
|
371
|
+
`useApprovalModalViewModel.tsx` (~L155) still lists `orderContentDetailsWorkflow` among the
|
|
372
|
+
"unmigrated keys" — that comment is **stale**. Practical effect: any seed or adapter change to the
|
|
373
|
+
decision summary lands in **all three** modal modes at once, and the two grids share one `colSpan`
|
|
374
|
+
value (see the colSpan caveat below).
|
|
375
|
+
|
|
364
376
|
**Concatenated summary values (`isConcatenated`).** `surfaceBundlesToDecisionFields.ts` now passes an
|
|
365
377
|
element's `config.isConcatenated` through **verbatim** onto the decision-summary row: it exports a
|
|
366
378
|
`DecisionSummaryConcat` type (string shorthand | descriptor `{valueKey, prefix?, suffix?, valueType?,
|
|
@@ -372,6 +384,46 @@ side in [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md
|
|
|
372
384
|
the FE ignored. blox `BaseDetailField` (`renderBaseDetailFieldValue.tsx`) already supports
|
|
373
385
|
`valueKey`+`suffix`+`layout:'stacked'`+`valueType:'currency'`. tsc + eslint clean.
|
|
374
386
|
|
|
387
|
+
### The stacked-Total `+` joiner is FE code, NOT a seeded `suffix` (2026-09-03)
|
|
388
|
+
|
|
389
|
+
Design wants the two stacked Total lines to read as one sum, so the primary line gets a trailing
|
|
390
|
+
`+` — but **only when the lease line is actually there**:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
$4,737.00 +
|
|
394
|
+
$107.00/mo
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
`withStackedCurrencyJoiner(field, data)` (`src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts`)
|
|
398
|
+
returns the field with `suffix: "+"` (appended after any existing suffix) **only** when all of this
|
|
399
|
+
holds: `field.valueType === 'currency'`, `field.isConcatenated` is a **descriptor** (not the string
|
|
400
|
+
shorthand) with `layout: 'stacked'` and `valueType: 'currency'`, **and** the add-on value resolves to
|
|
401
|
+
something non-null / non-empty / non-zero. Otherwise it returns the **same field reference**. The
|
|
402
|
+
visibility check deliberately mirrors blox `renderBaseDetailFieldValue.resolveSecondValue` — dotted
|
|
403
|
+
`valueKey` walk plus the optional `getValue` resolver — so the `+` and the second line appear and
|
|
404
|
+
disappear together.
|
|
405
|
+
|
|
406
|
+
Wired in `RecordApprovalModalLayout.tsx` `renderField` as
|
|
407
|
+
`field={isWorkflow ? withStackedCurrencyJoiner(field, data) : field}`, covering the workflow modal's
|
|
408
|
+
editable **and** read-only views. **Approve/deny modals are deliberately unchanged** — extending to
|
|
409
|
+
them is a one-line change (drop the `isWorkflow` check).
|
|
410
|
+
|
|
411
|
+
**Why this is app code and not blox or a seed (decision).** blox `BaseDetailField` renders a
|
|
412
|
+
field-level `suffix` **unconditionally** after a currency primary (`base = "$ X" + " " + suffix`),
|
|
413
|
+
while the stacked add-on line **auto-hides** on null/empty/0. There is no surface or blox option for
|
|
414
|
+
"suffix only when the second line shows", so a seed-side `suffix:"+"` on the Total element would
|
|
415
|
+
leave a **dangling `+`** on every order with no monthly lease. Doing it in blox would need a new
|
|
416
|
+
concat-descriptor option plus a publish, a pin bump in toga25-supply, and SQL for Compass and Compass
|
|
417
|
+
Canada — a lot of machinery for a presentation flourish. This is the same line as the grid-geometry
|
|
418
|
+
boundary below: **config describes, code decides.** (blox pin at the time: `1.1.1-production.139`.)
|
|
419
|
+
|
|
420
|
+
Covered by `stackedCurrencyJoiner.test.ts` (13 vitest cases) — the joiner is pure, so it is testable
|
|
421
|
+
without rendering the modal.
|
|
422
|
+
|
|
423
|
+
**Not implemented:** the mockup also showed `(36 months)` after the lease line. Nothing in the
|
|
424
|
+
current surface config, seeds, or FE code resolves a lease **term**, so it was left out — it needs a
|
|
425
|
+
data source before it can be rendered.
|
|
426
|
+
|
|
375
427
|
**Shared summary, separate per-action chrome (decision).** The 8 order-summary rows are identical for
|
|
376
428
|
approve and deny, so they come from **one** shared surface (`sales-order-decision-summary`); the action
|
|
377
429
|
chrome (title, reason-input required-ness, confirm button, icons, toasts) diverges, so it is **two**
|
|
@@ -914,6 +966,22 @@ button except **View Log** renders and does nothing on click. Whether the SR mod
|
|
|
914
966
|
actions surface is **still an open decision**, not an oversight.
|
|
915
967
|
|
|
916
968
|
## Change history
|
|
969
|
+
- 2026-09-03 — **Built the stacked-Total `+` joiner in the Approval Workflow modal**
|
|
970
|
+
(`withStackedCurrencyJoiner`, new `RecordApprovalModal/helpers/stackedCurrencyJoiner.ts` + 13
|
|
971
|
+
vitest cases; one-line wiring in `RecordApprovalModalLayout` `renderField`, workflow modal only —
|
|
972
|
+
approve/deny untouched). It adds `suffix:"+"` to a currency primary **only** when its
|
|
973
|
+
`isConcatenated` descriptor is `layout:'stacked'` + `valueType:'currency'` **and** the add-on value
|
|
974
|
+
will actually render (mirrors blox `resolveSecondValue`, incl. `getValue` + dotted `valueKey`), so
|
|
975
|
+
the `+` never dangles on an order with no monthly lease. **DECISION: this stays app code** — blox
|
|
976
|
+
renders a field `suffix` unconditionally and there is no "suffix only if the second line shows"
|
|
977
|
+
option, so a seeded `suffix:"+"` would be wrong for non-lease orders, and a blox option would cost
|
|
978
|
+
a publish + pin bump + SQL for Compass and Compass Canada. Same boundary as grid geometry: config
|
|
979
|
+
describes, code decides. Also **corrected** the doc's claim that `orderContentDetailsWorkflow` is
|
|
980
|
+
unmigrated/JSON-sourced — `overlayDecisionSurfaceFields` points it at the **same**
|
|
981
|
+
`sales-order-decision-summary` array as approve/deny (the stale claim also survives as a comment in
|
|
982
|
+
`useApprovalModalViewModel.tsx` ~L155). Mockup's `(36 months)` lease term is **not** implemented —
|
|
983
|
+
no data source for it. tsc + eslint clean, vitest 18/18 in `src/layout/RecordApprovalModal`; blox
|
|
984
|
+
pin `1.1.1-production.139`; uncommitted on `TRUE-80850`. (tcox)
|
|
917
985
|
- 2026-09-02 — **FIXED the surface bundle being cached in `localStorage` for up to 24h, so a
|
|
918
986
|
permission or feature change could not take effect.** `App.tsx`'s `PersistQueryClientProvider` had
|
|
919
987
|
**no `dehydrateOptions`**, so every query — including the `staleTime: Infinity` surface bundle —
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-03
|
|
10
10
|
owners: [apeterson, bala]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts
|
|
@@ -29,6 +29,8 @@ files:
|
|
|
29
29
|
- toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/hooks/useTransferOrderRecord.ts
|
|
30
30
|
- toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/viewModel/useTransferOrderRecordModalLayoutModel.ts
|
|
31
31
|
- toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/view/TransferOrderRecordView.tsx
|
|
32
|
+
- toga25-supply/src/pages/TransferOrders/helpers/buildInventoryPurchaseOrderUrl.ts
|
|
33
|
+
- toga25-supply/src/pages/TransferOrders/TransferOrder.module.css
|
|
32
34
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx
|
|
33
35
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/index.ts
|
|
34
36
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/usePurchaseOrderItemRows.ts
|
|
@@ -261,6 +263,79 @@ fields. One FK hop out from the order they need `calcDepth 2`; at the default of
|
|
|
261
263
|
the loaded state swaps element types at the same tree position and replays the slide-up animation —
|
|
262
264
|
see the gotcha in [record-modals-and-nested-tables](./record-modals-and-nested-tables.md).
|
|
263
265
|
|
|
266
|
+
### The items table HUGS its rows — `flex: 0 1 auto`, not `flex: 1`
|
|
267
|
+
|
|
268
|
+
`TransferOrder.module.css`. **`.tableScroll` is the box that carries the border, so it is the table
|
|
269
|
+
the user sees — it must size to its rows.** Under `flex: 1` it stretched to the bottom of the modal,
|
|
270
|
+
so a one-line transfer drew a bordered box with ~400px of empty white under its single row.
|
|
271
|
+
|
|
272
|
+
- `.tableScroll` → **`flex: 0 1 auto`**: sizes to content, and still **shrinks** (paired with the
|
|
273
|
+
existing `min-height: 0`) when the rows outgrow the space, at which point `overflow: auto` scrolls
|
|
274
|
+
under the sticky header.
|
|
275
|
+
- `.tableRegion` keeps **`flex: 1`** and absorbs the slack. It draws nothing, so stretching it is
|
|
276
|
+
invisible.
|
|
277
|
+
- Everything above the table (`.reviewCards`, `.sectionHead`, `.itemsHeadFlush`) already carries
|
|
278
|
+
`flex-shrink: 0`, so the table shrinking never squashes them.
|
|
279
|
+
|
|
280
|
+
**Ground truth:** the design's `TransferItemsTable` (`demo/transfer-modal.jsx:598`) passes **no
|
|
281
|
+
`fillerHeight`** to `SupplyTable` — it reserves no rows it does not have. Only `CmFulTable` reserves,
|
|
282
|
+
and it asks for it explicitly. The Create modal's picker (`TransferItemPickerTable.tsx`) hugs the
|
|
283
|
+
same way.
|
|
284
|
+
|
|
285
|
+
### The Purchase Order value deep-links into Inventory, scoped to that PO
|
|
286
|
+
|
|
287
|
+
`helpers/buildInventoryPurchaseOrderUrl.ts` (new) + `TransferOrderRecordModalLayout.tsx`.
|
|
288
|
+
`onOpenPurchaseOrder` had been **declared** on `TransferOrderRecordView` and threaded to
|
|
289
|
+
`TransferReviewBody`'s `purchaseOrder.onOpen` since the modal was built, but nothing ever passed it —
|
|
290
|
+
so the PO always rendered as plain text. It now navigates:
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
/inventory?grouping=posByItems&purchase-orders_customerPurchaseOrder_filter=includes%3A<number>&purchase-orders=<uuid>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Three params, all addressing Inventory grouping `posByItems` level 0 (`fetchSlug: purchase-orders`):
|
|
297
|
+
|
|
298
|
+
- **`grouping=posByItems` is PINNED, not left to the default.** It *is* the default today
|
|
299
|
+
(`groupingOptions[0]` in `useInventoryPageViewModel`), but that default is whichever grouping the
|
|
300
|
+
client's surface returns first — a reseed that reorders them would silently aim the link at a view
|
|
301
|
+
with no `purchase-orders` level. An unknown key falls back to the default, so pinning can only help.
|
|
302
|
+
- **The filter chip is set through `URLSearchParams.set`, with the value
|
|
303
|
+
`"includes:" + encodeURIComponent(number)`** — mirroring the serializer in `hooks/useServerTableUrlState.ts` exactly. Hand-writing the query string
|
|
304
|
+
double-encodes, or breaks a value containing `:` or `,`. The key's middle segment is the **column
|
|
305
|
+
id from the table meta** (`customerPurchaseOrder`), not the record field name — renaming that
|
|
306
|
+
`TableViewFields` column breaks the link with nothing to warn you.
|
|
307
|
+
- **`purchase-orders=<uuid>` is the active-row param**, named **exactly** the table slug
|
|
308
|
+
(`activeRowUuid = searchParams.get(slug)`). That level's `rowInteraction` is the default `"expand"`,
|
|
309
|
+
so setting it **opens the child sub-table inline on page load** — the same thing a row click does.
|
|
310
|
+
Omitted when the uuid is unknown; the filter alone still lands on the row, just collapsed.
|
|
311
|
+
|
|
312
|
+
The uuid is read off the record (`transferOrder.purchaseOrder.uuid`, already in the record fetch — no
|
|
313
|
+
API change), **not** off the surface: the surface says *which number to show*; the route it navigates
|
|
314
|
+
to is FE wiring (the config/logic boundary, [frontend standard](../../../standards/frontend.md) §20).
|
|
315
|
+
|
|
316
|
+
> ⚠ **Never `onClose()` before `navigate()`.** See the gotcha below — it races two navigations and
|
|
317
|
+
> silently strips the params the link is made of.
|
|
318
|
+
|
|
319
|
+
### Blank Item column in the RECORD modal too — `COLUMN_FALLBACK_KEYS`
|
|
320
|
+
|
|
321
|
+
Same NYCHH data gap as the picker, different table. Confirmed on client-sandbox: `transfer-order-items`
|
|
322
|
+
line 1 returns `"title": null` with a real `partNumber`. `item.description` was added to
|
|
323
|
+
`TRANSFER_ORDER_ITEM_FIELDS` (`hooks/useTransferOrderRecord.ts`), and `TransferOrderRecordView` gained:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
const COLUMN_FALLBACK_KEYS: Record<string, string[]> = { item: ["item.description"] };
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
tried in order when a column's own surface `valueKey` resolves empty.
|
|
330
|
+
|
|
331
|
+
- **Deliberately a FLAT MAP keyed by `config.columnKey`** — *not* string surgery on the valueKey.
|
|
332
|
+
Deriving `item.description` by swapping the tail of `item.title` breaks silently the day a tenant
|
|
333
|
+
repoints that column.
|
|
334
|
+
- Same chain the Create modal's picker already uses, except the record modal keeps its **em-dash**
|
|
335
|
+
for both-empty rather than `"(untitled item)"`.
|
|
336
|
+
- **No part-number fallback** — Part Number has its own column, so that would duplicate a cell
|
|
337
|
+
instead of naming the item. The real fix is still populating `Items.title`.
|
|
338
|
+
|
|
264
339
|
## The prod-only state this vertical sits on top of — four UNTRACKED layers
|
|
265
340
|
|
|
266
341
|
Standing the page up locally failed **one layer at a time**, each with a different-looking error,
|
|
@@ -570,11 +645,24 @@ column binds the parent order's `purchaseOrder.number` and repeats on every row.
|
|
|
570
645
|
resolves NULL because their PO links are **upstream** — see
|
|
571
646
|
[NYCHH PO direction](../../../../clients/nychh/features/po-number-upstream-direction.md). It is left
|
|
572
647
|
visible in Core (neutral default; other tenants populate `TransferOrders.purchaseOrderId` normally).
|
|
648
|
+
**The header's PO value is now a link into Inventory** (above); for NYCHH it is the same null, so
|
|
649
|
+
nothing renders there for this tenant yet.
|
|
573
650
|
|
|
574
651
|
## Rollout status & open items
|
|
575
652
|
|
|
576
|
-
- **
|
|
577
|
-
|
|
653
|
+
- 🚨 **CORRECTED 2026-09-03 — the Core seeds ARE on production.** The line below said "nothing has
|
|
654
|
+
been run on production"; that was true on 2026-08-31 and is **stale**. Verified live on
|
|
655
|
+
`reader1.core.database.togahub.com`: `Core.Surfaces` = 53 rows and
|
|
656
|
+
`slug LIKE 'transfer-order%' OR 'talos%'` returns **`transfer-orders-list`,
|
|
657
|
+
`transfer-orders-list-actions`, `transfer-order-record-header`, `transfer-order-details`,
|
|
658
|
+
`transfer-order-summary`, `transfer-order-items`, `transfer-order-create-items`** and
|
|
659
|
+
`talos-assistant`. Someone ran them on prod between 2026-08-31 and 2026-09-03. Note
|
|
660
|
+
**`transfer-order-create-items` is a surface this doc's table never listed.** Prod `Client_Nychh`
|
|
661
|
+
also carries **12 `SurfaceOverrides`** rows and **1,930 `TransferOrders`** (1,927 on 2026-08-31 —
|
|
662
|
+
the tenant is live). *A migration's run status in this doc or in `dbchanges2` is never evidence
|
|
663
|
+
about an environment — read the environment.*
|
|
664
|
+
- **Run state as of 2026-08-31 (superseded for production by the line above).** On **sandbox-client**
|
|
665
|
+
the three `Client_Nychh` layers are already satisfied — the
|
|
578
666
|
`transfer-orders` TableView (**22**, 4 fields) plus **4 `AclRecordPermissions` and 34
|
|
579
667
|
`AclFieldPermissions`** for records **312/313** — and `TransferOrderItems` already carries
|
|
580
668
|
`dtCreated` / `dtUpdated` / `qtyCommitted`, all pulled back in by the reset from prod's hand-applied
|
|
@@ -665,6 +753,14 @@ deliberate, separate exception — see [surface-frontend](./surface-frontend.md)
|
|
|
665
753
|
|
|
666
754
|
- **⚠ An empty dropdown with no request in the network tab is a numeric `where` value or a
|
|
667
755
|
non-zero `staleTime`** — see the section above. Neither logs anything.
|
|
756
|
+
- **⚠ A bordered scroll box must be `flex: 0 1 auto`, never `flex: 1`.** `flex: 1` on the element
|
|
757
|
+
that carries the border stretches the *visible* table to the bottom of the modal, so one row draws
|
|
758
|
+
a box full of white. Put `flex: 1` on the wrapper that draws nothing.
|
|
759
|
+
- **⚠ Closing a modal and navigating in the same tick races two navigations.** `onClose` clears the
|
|
760
|
+
URL with `setSearchParams`; if you also `navigate()`, the loser strips the params your link is made
|
|
761
|
+
of. Navigate **and return** — skip `onClose()` entirely.
|
|
762
|
+
- **A deep link's filter key uses the table-meta COLUMN id, not the record field name.** Renaming
|
|
763
|
+
the `TableViewFields` column breaks the link silently.
|
|
668
764
|
- **A missing `AclRecordPermissions` grant on a joined record deletes a column, not a request.** The
|
|
669
765
|
Status column would have silently vanished; record 351 had no grants at all.
|
|
670
766
|
- **A missing `AclFieldPermissions` grant on the DEFAULT-SORT column is a 500, not a 403.** The
|
|
@@ -706,6 +802,22 @@ deliberate, separate exception — see [surface-frontend](./surface-frontend.md)
|
|
|
706
802
|
earlier migration without names, so a name-based lookup on them finds nothing.
|
|
707
803
|
|
|
708
804
|
## Change history
|
|
805
|
+
- 2026-09-03 — **CORRECTED the production run state:** the Core transfer-order surfaces (all six
|
|
806
|
+
record/list surfaces **plus `transfer-order-create-items`**) and `talos-assistant` **are on
|
|
807
|
+
production** — verified live on `reader1.core.database.togahub.com` (`Surfaces` = 53). The
|
|
808
|
+
2026-08-31 "nothing has been run on production" line is stale and is now marked so. Record-modal
|
|
809
|
+
work, all **uncommitted on branch `TRUE-80852`**: (1) the items table now **hugs its rows** —
|
|
810
|
+
`.tableScroll` moved from `flex: 1` to `flex: 0 1 auto` so a one-line transfer stops drawing ~400px
|
|
811
|
+
of empty bordered white (ground truth: the design's `TransferItemsTable` passes no `fillerHeight`);
|
|
812
|
+
(2) the header's **Purchase Order value now deep-links into Inventory**, scoped to that PO —
|
|
813
|
+
`grouping=posByItems` pinned, a `slug_column_filter=includes:<number>` chip built through the same
|
|
814
|
+
serializer as `useServerTableUrlState`, and `purchase-orders=<uuid>` (the active-row param, named
|
|
815
|
+
exactly the slug) to expand the child table on load; (3) **fixed a race in that first pass** —
|
|
816
|
+
`onClose()` before `navigate()` queues two navigations in one tick and the loser strips the link's
|
|
817
|
+
params, so the navigation now returns early instead of closing; (4) the record modal's **Item
|
|
818
|
+
column no longer blanks when `Items.title` is NULL** (NYCHH) — added `item.description` to the
|
|
819
|
+
fetch and a flat `COLUMN_FALLBACK_KEYS` map keyed by `columnKey`, deliberately not string surgery
|
|
820
|
+
on the surface `valueKey`. (apeterson)
|
|
709
821
|
- 2026-09-02 — **Finished the Create Transfer Order modal end to end** (front end + framework model +
|
|
710
822
|
Core metadata + client schema) and **closed both required POST changes**: the transfer now writes
|
|
711
823
|
header and every line in **ONE `POST /transfer-orders`** with the lines nested under
|
|
@@ -75,13 +75,30 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
75
75
|
seeds a **Developer-role (role 2)** record ACL grant chain (`AclRecordPermissions` →
|
|
76
76
|
`AclLogicGroups` → `AclLogicGroupExpressions`, reusing record 17's `all` expression) and reads
|
|
77
77
|
`AclFieldPermissions` for the displayed fields, resolving `roleId` by role **name**. Written
|
|
78
|
-
idempotent / uuid-keyed; applied to **prod
|
|
78
|
+
idempotent / uuid-keyed; applied to **prod**.
|
|
79
|
+
🚨 **client-sandbox was NOT rebased on 2026-09-03** (an earlier note here claiming it was is
|
|
80
|
+
**stale**). Verified live: prod `Client_Nychh.TableViews` id **15** `purchase-orders` sits at
|
|
81
|
+
`recordId` **17** with `apiWhereClause (vendorId:eq:172)`, while sandbox-client still rendered the
|
|
82
|
+
pre-rebase column set *Purchase Order · Order Date · Order · Est. Total · Ship To* — the rebased
|
|
83
|
+
view is **number · dateOrder · total** (the rebase drops the sales-order "Order" column and omits
|
|
84
|
+
Ship To on purpose). **The symptom was a uuid mismatch, not a visibly wrong grid:** the PO row uuid
|
|
85
|
+
on the sandbox Inventory grid (`9056ea7b-…`) did not match the transfer order's
|
|
86
|
+
`purchaseOrder.uuid` (`ea8419cd-…`) — same PO number displayed, two different records — so a deep
|
|
87
|
+
link by uuid expanded the wrong row or none. Fixed by **resetting sandbox-client (Core +
|
|
88
|
+
`Client_Nychh`) from production** on 2026-09-03; see
|
|
89
|
+
[targeting the right database](../../2.0/apps/dbchanges2/workflows/targeting-the-right-database.md).
|
|
90
|
+
🚩 **OPEN, not investigated:** that prod view's `apiWhereClause (vendorId:eq:172)` means a PO from
|
|
91
|
+
any other vendor **does not appear in the Inventory grid at all**, so a deep link to it lands on an
|
|
92
|
+
empty list no matter how correct the uuid is.
|
|
79
93
|
- **NYCHH is the ONLY tenant granted the Talos AI assistant (2026-09-02).** Talos is seeded **off
|
|
80
94
|
in Core for every client** and enabled per tenant with 5 `SurfaceOverrides` rows on the
|
|
81
95
|
`talos-assistant` surface; `Client_Nychh/2026-09-02a - TalosAssistantEnable.sql` is the only grant
|
|
82
96
|
file. The grants are **client-wide** (`roleId` NULL), so every NYCHH user including devteam gets
|
|
83
|
-
it — matching how this tenant already grants navigation.
|
|
84
|
-
|
|
97
|
+
it — matching how this tenant already grants navigation. ~~Applied on LOCAL only; beta and prod are
|
|
98
|
+
not run~~ — **STALE. Corrected 2026-09-03: the `talos-assistant` surface IS on production**
|
|
99
|
+
(verified live on `reader1.core.database.togahub.com`; `Core.Surfaces` = 53 rows and
|
|
100
|
+
`talos-assistant` is among them), and prod `Client_Nychh` carries **12 `SurfaceOverrides`** rows.
|
|
101
|
+
Someone ran it between 2026-08-31 and 2026-09-03. Element ids are still per-environment. Their Aegra `assistant_id` is
|
|
85
102
|
`c7981164-1fe9-5878-b115-85c38dadad43` (`TOGa Supply`), and thread auto-titles are broken
|
|
86
103
|
**server-side** on that assistant. See
|
|
87
104
|
[NYCHH Talos enablement](./features/talos-assistant-enablement.md).
|
|
@@ -97,10 +114,12 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
97
114
|
items 1129/1129, VendorItems 2414/2414) so both stay INNER. Seeds Developer-role record grants for
|
|
98
115
|
records 18 and 19 (both had only Base+API) plus Developer read on `PurchaseOrderItems.quantity`
|
|
99
116
|
(field 94); roles resolved by name, uuid-keyed, idempotent. Mechanism:
|
|
100
|
-
[tableview-joins](../../2.0/apps/_underscore/features/tableview-joins.md). **Applied to prod
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
[tableview-joins](../../2.0/apps/_underscore/features/tableview-joins.md). **Applied to prod** — prod re-verified
|
|
118
|
+
2026-09-03 on `reader1.client.database.togahub.com` (view 16 at base `recordId` **18**, no
|
|
119
|
+
`apiWhereClause`). Its client-sandbox state has flipped in the notes more than once; the parent
|
|
120
|
+
`purchase-orders` view was found **stale** on sandbox-client the same day, which is why
|
|
121
|
+
sandbox-client was reset from production on 2026-09-03. **Read the environment before trusting
|
|
122
|
+
either claim.** **OPEN:** the parent `purchase-orders` view filters to vendor 172
|
|
104
123
|
(`apiWhereClause
|
|
105
124
|
(vendorId:eq:172)`); this item view has no `apiWhereClause`, so it lists items for all POs — left
|
|
106
125
|
unfiltered (mirrors prior state), flagged as a decision for the dev. A "3L-2250" error reported
|
|
@@ -214,6 +233,11 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
214
233
|
[NYCHH PO Number direction](./features/po-number-upstream-direction.md).
|
|
215
234
|
|
|
216
235
|
## Notes
|
|
236
|
+
- **`Items.title` is NULL on most NYCHH rows — `description` is the populated label field.** Confirmed
|
|
237
|
+
2026-09-03 on client-sandbox (a `transfer-order-items` line returned `"title": null` with a real
|
|
238
|
+
`partNumber`). Every screen that labels an item for this tenant needs the
|
|
239
|
+
`title || description` chain — the Transfer Order record modal and the Create modal's item picker
|
|
240
|
+
both carry it. The real fix is populating `Items.title`.
|
|
217
241
|
- **Role ids in `Client_Nychh` (verified 2026-09-01):** `1 = Base`, `2 = Developer`, `3 = API`.
|
|
218
242
|
Role ids differ per client DB — resolve by **name** in migrations, never hard-code these.
|
|
219
243
|
- **PO Number is upstream-sourced** (`PurchaseOrders_SalesOrders`), which is why the downstream
|
package/package.json
CHANGED