toga-ai 1.0.654 → 1.0.656
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/features/acl-permission-chain.md +13 -0
- package/knowledge/2.0/apps/_underscore/features/apirequest-json-content-type.md +28 -2
- package/knowledge/2.0/apps/toga-blox/INDEX.md +2 -1
- package/knowledge/2.0/apps/toga-blox/features/primary-table-templates.md +32 -1
- package/knowledge/2.0/apps/toga-blox/features/table.md +29 -1
- package/knowledge/2.0/apps/toga-blox/features/toaster.md +104 -0
- package/knowledge/2.0/apps/toga-blox/workflows/dynamic-publish-pipeline.md +63 -1
- package/knowledge/2.0/apps/toga2-commerce/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-commerce/features/order-submit-sync-sequencing.md +15 -2
- package/knowledge/2.0/apps/toga2-commerce/workflows/amplify-build-and-deploy.md +50 -6
- package/knowledge/2.0/apps/toga2-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-supply/features/currency-amount-lines-editor.md +141 -2
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -0
- package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +17 -2
- package/knowledge/2.0/apps/worker2/features/netsuite-opportunity-client-labels.md +138 -0
- package/knowledge/2.0/apps/worker2/features/netsuite-opportunity-sync.md +66 -7
- package/knowledge/2.0/apps/worker2/features/notification-email.md +19 -2
- package/knowledge/2.0/standards/frontend-deploy.md +8 -3
- package/knowledge/2.0/standards/frontend.md +29 -6
- package/knowledge/INDEX.md +2 -2
- package/knowledge/clients/quad/features/multi-currency-item-pricing.md +47 -3
- package/knowledge/clients/quad/profile.md +6 -0
- package/knowledge/sessions/2026-08-26-blox-toaster-peer-deps-apeterson.md +86 -0
- package/knowledge/standalone/apps/claude/workflows/mcp-tool-usage.md +19 -2
- package/package.json +1 -1
|
@@ -65,6 +65,13 @@ writable). Without it the field is rejected on write **and** is unreadable:
|
|
|
65
65
|
VISIBILITY too.** Concrete symptom (TRUE-79191): `Measures.measureType` was registered but ungranted,
|
|
66
66
|
so it came back absent and the frontend unit dropdowns were empty. When a field renders blank with no
|
|
67
67
|
error, check for a missing `AclFieldPermissions` grant before suspecting the selector or the data.
|
|
68
|
+
- **⚠ Sibling columns of the SAME table routinely carry DIFFERENT role sets, so verify per field
|
|
69
|
+
before you add one to a `fields=` list — and verify as the LEAST-privileged role.** Concrete case
|
|
70
|
+
(`Client_Quad.Currencies`, 2026-08-26): `code` (1424) and `symbol` (1425) are granted to roles
|
|
71
|
+
1/7/8/9, but `position` (1997) only to 7/8/9. Adding `currency.position` to an existing read would
|
|
72
|
+
have 403'd the **whole** request for every user holding only role 1 — while working perfectly for a
|
|
73
|
+
developer whose own account also carries role 7. **A dev/admin account hides this class of bug
|
|
74
|
+
completely**; a "just one more field" addition is a permission change, not a display change.
|
|
68
75
|
- **`AclFieldPermissions` is `UNIQUE (recordFieldId, roleId)`** (Client blank DDL) — guard inserts
|
|
69
76
|
with `NOT EXISTS`. For a **read-only, server-written** field (one populated by a `postPost`
|
|
70
77
|
interceptor rather than the API caller), grant `isWritable = 0`.
|
|
@@ -403,6 +410,12 @@ hardcoded `Core.RecordFields` id literals instead of a subselect.
|
|
|
403
410
|
and every repo is on the **same branch** so the generated model matches the DB.
|
|
404
411
|
|
|
405
412
|
## Change history
|
|
413
|
+
- 2026-08-26 - Added the **verify-per-sibling-field, verify-as-the-least-privileged-role** rule:
|
|
414
|
+
columns of the same table often carry different role sets, so adding one more field to a `fields=`
|
|
415
|
+
list is a permission change. `Client_Quad.Currencies` grants `code` (1424) and `symbol` (1425) to
|
|
416
|
+
roles 1/7/8/9 but `position` (1997) only to 7/8/9, so requesting `currency.position` would have
|
|
417
|
+
403'd the whole read for role-1-only users while looking fine to a developer who also holds role 7 -
|
|
418
|
+
a dev/admin account hides this class of bug entirely. (bala)
|
|
406
419
|
- 2026-08-26 - Added the **action-flag gate** (`Core.AclActions` + `Client_*.AclActionPermissions`),
|
|
407
420
|
a third gate alongside record CRUD and record scripts: `Page.php` (~L1285-1302) defaults every
|
|
408
421
|
action flag to `false` and only a matching `AclActionPermissions` row flips it true, so a missing
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-08-26
|
|
10
|
+
owners: ["jcardinal", "ajean"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/ApiRequest.php
|
|
13
13
|
related:
|
|
@@ -74,7 +74,33 @@ case-insensitive scan of already-set headers, so a caller-supplied `Content-Type
|
|
|
74
74
|
save) lives **inside `execute()`** and would affect every framework caller, so it is deferred
|
|
75
75
|
pending architecture review. Do **not** work around it per-caller.
|
|
76
76
|
|
|
77
|
+
- **Hand-rolled `Logs.Api` inserts must set `isAuthRequest`, `instanceId` and `hostname` — they are
|
|
78
|
+
NOT NULL with no defaults.** `_ApiRequest`'s own logging sets all three (`ApiRequest.php:260-264`),
|
|
79
|
+
so a helper that writes its own log row while only setting `direction`/`source`/`method`/`route`
|
|
80
|
+
throws `Column 'X' cannot be null` on **every** insert. Because such helpers are usually a
|
|
81
|
+
*wrapper* around the real call, the throw propagates out of the wrapped call and, under a
|
|
82
|
+
`catch(Throwable)`, turns the whole feature into a **silent no-op that still reports success**.
|
|
83
|
+
Two worker2 handlers shipped with exactly this bug and one went unnoticed for months — see
|
|
84
|
+
[netsuite-opportunity-sync](../../worker2/features/netsuite-opportunity-sync.md#gotchas--known-issues).
|
|
85
|
+
Prefer letting `_ApiRequest` log rather than writing `Logs.Api` by hand.
|
|
86
|
+
- **⚠ Auto-logging persists the `Authorization` header in PLAINTEXT.** `ApiRequest.php:267` writes
|
|
87
|
+
the full request headers to `Logs.Api.requestHeaders`, so any component that sends a bearer
|
|
88
|
+
token/API key and does not `setLogging(false)` is depositing that credential into the logs DB on
|
|
89
|
+
every call. Known live instance: `_underscore/Component/Api/Clickup/Clickup.php` (which also
|
|
90
|
+
**hardcodes** its token in committed source — flagged for a rotate-and-move-to-config ticket; 11
|
|
91
|
+
worker2 files use it). When adding a new component, decide deliberately: log and accept the header
|
|
92
|
+
exposure, strip/redact the auth header before the call is logged, or `setLogging(false)`.
|
|
93
|
+
- **`Logs.Api` is 1.4M+ rows and `source` is NOT indexed.** Any diagnostic query must be bounded by
|
|
94
|
+
`dtStamp` (indexed) or it times out — `WHERE source = '…'` alone will not return.
|
|
95
|
+
|
|
77
96
|
## Change history
|
|
97
|
+
- 2026-08-26 — Documented the **manual-insert column contract** (`isAuthRequest`, `instanceId`,
|
|
98
|
+
`hostname` are NOT NULL with no defaults, and `_ApiRequest` sets them at `ApiRequest.php:260-264`),
|
|
99
|
+
after a hand-rolled `logged()` helper in two worker2 handlers threw on every insert and silently
|
|
100
|
+
no-op'd an entire feature behind a `catch(Throwable)`. Also recorded that auto-logging persists the
|
|
101
|
+
**`Authorization` header in plaintext** into `Logs.Api.requestHeaders` (`ApiRequest.php:267`) — the
|
|
102
|
+
reason `_Component_Api_Clickup`'s hardcoded token needs rotation — and that `Logs.Api.source` is
|
|
103
|
+
unindexed at 1.4M+ rows, so queries must be bounded by `dtStamp`. (ajean)
|
|
78
104
|
- 2026-08-05 — Documented (no code change) that the `direction = 'OUT'` api-log row is
|
|
79
105
|
**committed immediately** inside `execute()` (`transactionCommit(DB_CLIENT_LOGS)`), so it
|
|
80
106
|
survives a later rollback and `Logs_<Client>.Api` is definitive evidence of whether an outbound
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
| [AdvancedSelect (virtualized single/multi select)](features/advanced-select.md) | `AdvancedSelect<T>` is a **fully controlled, virtualized** single/multi select built from scratch (not react-select) on **`@tanstack/react-virtual`**, for large | toga-blox/src/components/AdvancedSelect/AdvancedSelect.tsx, toga-blox/src/components/AdvancedSelect/AdvancedSelect.types.ts, toga-blox/src/components/AdvancedSelect/AdvancedSelect.module.css |
|
|
8
8
|
| [API client (axios wrapper, auth, table-data fetchers)](features/api-client.md) | `src/api/` is a thin **axios** wrapper that standardizes the **2.0 API envelope**, manages auth (Bearer + refresh), serializes complex query options, and provid | toga-blox/src/reactQuery/queryHelpers.ts, toga-blox/src/api/index.ts, toga-blox/src/api/axiosInstance.ts, toga-blox/src/api/apiFunctions.ts, toga-blox/src/api/auth.ts, toga-blox/src/api/genericApi.ts, toga-blox/src/api/types.ts, toga-blox/src/api/tableData |
|
|
9
9
|
| [BaseInput (react-hook-form field factory)](features/base-input.md) | `BaseInput` is a **form-field factory** driven by react-hook-form. | toga-blox/src/components/BaseInput/BaseInput.tsx, toga-blox/src/components/BaseInput/BaseInput.types.ts, toga-blox/src/components/BaseInput/BaseInput.module.css, toga-blox/src/components/BaseInput/components |
|
|
10
|
-
| [Primary Table templates (server/client, sizing, virtualization)](features/primary-table-templates.md) | `src/templates/PrimaryTable/` is the **production, wired-up table** built on the [Table component](table.md). | toga-blox/src/templates/PrimaryTable/PrimaryTable.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableServerTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableClientTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableHeaderCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableBodyCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableRow.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableExpandableRow.tsx, toga-blox/src/templates/PrimaryTable/types.ts |
|
|
10
|
+
| [Primary Table templates (server/client, sizing, virtualization)](features/primary-table-templates.md) | `src/templates/PrimaryTable/` is the **production, wired-up table** built on the [Table component](table.md). | toga-blox/src/components/Table/themeConfig/toga.module.css, toga-blox/src/templates/PrimaryTable/PrimaryTable.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableServerTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableClientTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableHeaderCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableBodyCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableRow.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableExpandableRow.tsx, toga-blox/src/templates/PrimaryTable/types.ts |
|
|
11
11
|
| [TableRecordModal (record-detail modal shell)](features/table-record-modal.md) | `TableRecordModal` is a **generic, presentational modal shell** for showing a single table record (row) in detail — typically opened from a table row click. | toga-blox/src/components/TableRecordModal/TableRecordModal.tsx, toga-blox/src/components/TableRecordModal/index.ts, toga-blox/src/components/TableRecordModal/tableRecordModal.module.css |
|
|
12
12
|
| [Table component (cells, action cells, filters & sorts, hooks, theming)](features/table.md) | The `Table` component (`src/components/Table/`) is the **TanStack Table v8** building block behind the [Primary Table templates](primary-table-templates.md). | toga-blox/src/components/Table/hooks/useFetchTablePageMeta.ts, toga-blox/src/api/types.ts, toga-blox/src/components/Table/index.ts, toga-blox/src/components/Table/types.ts, toga-blox/src/components/Table/utils/buildTanstackColumns.tsx, toga-blox/src/components/Table/utils/resolveCellType.tsx, toga-blox/src/components/Table/components/cellTypes, toga-blox/src/components/Table/components/actionCells, toga-blox/src/components/Table/components/columnFiltersAndSorts, toga-blox/src/components/Table/hooks, toga-blox/src/components/Table/themeConfig |
|
|
13
13
|
| [Talos AI-Assistant Component (launcher + slide-out chat panel)](features/talos-assistant.md) | `Talos` is a **shared, pure-UI** AI-assistant component in `@agilant/toga-blox`: a header launcher button (`TalosLauncher`) plus a slide-out chat panel (`TalosP | toga-blox/src/components/Talos/TalosLauncher.tsx, toga-blox/src/components/Talos/TalosPanel.tsx, toga-blox/src/components/Talos/TalosMessage.tsx, toga-blox/src/components/Talos/types.ts, toga-blox/src/components/Talos/theme.ts, toga-blox/src/components/Talos/helpers.tsx, toga-blox/src/components/Talos/stub.ts, toga-blox/src/components/Talos/Talos.module.css, toga-blox/src/components/Talos/index.ts, toga-blox/src/components/index.ts |
|
|
14
|
+
| [Toaster — host-token styling contract](features/toaster.md) | The blox `Toaster` is a CSS-Module component whose every visual property reads a `--toaster-*` custom property supplied by the **host app**. | toga-blox/src/components/Toaster/Toaster.module.css, toga-blox/src/components/Toaster/Toaster.tsx, toga-blox/src/components/Toaster/ToasterContext.tsx, toga2-commerce/src/styles/index.css, toga2-commerce/src/components/Toasters/ToasterList.tsx |
|
|
14
15
|
| [Dynamic npm Publish Pipeline (branch → channel)](workflows/dynamic-publish-pipeline.md) | How `@agilant/toga-blox` (checkout folder `toga-blox-npm`, registry repo key `toga-blox`) publishes a per-environment npm **channel** (dist-tag) from a `_<mode> | toga-blox/.github/workflows/publish.yml, toga-blox/package.json, toga-blox/src/utils/getFontAwesomeIcon.tsx |
|
|
@@ -6,9 +6,10 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-26
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
|
+
- toga-blox/src/components/Table/themeConfig/toga.module.css
|
|
12
13
|
- toga-blox/src/templates/PrimaryTable/PrimaryTable.tsx
|
|
13
14
|
- toga-blox/src/templates/PrimaryTable/PrimaryTableServerTemplate.tsx
|
|
14
15
|
- toga-blox/src/templates/PrimaryTable/PrimaryTableClientTemplate.tsx
|
|
@@ -82,8 +83,29 @@ spacer rows computed from the virtualizer item `.start`/`.end` minus `scrollMarg
|
|
|
82
83
|
`supply-modal-table` skin **disables virtualization** (renders all rows, min-height 300px).
|
|
83
84
|
Sticky columns use a `--sticky-offset` CSS variable summed from neighboring column widths.
|
|
84
85
|
|
|
86
|
+
## The "Show more" hover affordance is CSS-driven, not JS-driven
|
|
87
|
+
|
|
88
|
+
A truncated body cell reveals its "Show more" toggle **on hover via CSS only**:
|
|
89
|
+
`.bodyCell:hover .showMoreWrapper:not(.expandedWrapper) button` in
|
|
90
|
+
`themeConfig/toga.module.css` (plus a `:focus-visible` rule so keyboard users can reach it).
|
|
91
|
+
`Cellstyles.module.css` already shipped `.toggle { opacity: 0 }` + a hover rule — the CSS
|
|
92
|
+
hover-reveal was the original design. `PrimaryTableBodyCell` must **not** inline-set the collapsed
|
|
93
|
+
`opacity`; inline styles beat the cascade and hand visibility back to React state. Layout props
|
|
94
|
+
(flex/sizing) stay inline; visibility does not. The React `isTruncated` state is now for
|
|
95
|
+
**capability detection only** (does this cell need a toggle at all), and `clearTruncationHint`
|
|
96
|
+
runs unconditionally — it no longer no-ops while `isExpanded`.
|
|
97
|
+
|
|
85
98
|
## Gotchas
|
|
86
99
|
|
|
100
|
+
- **Never gate a hover affordance on a JS `mouseleave` inside a virtualized table.** `mouseleave`
|
|
101
|
+
is not guaranteed to fire: a virtualized row can translate out from under a stationary cursor on
|
|
102
|
+
wheel-scroll, and any early-return in the clear handler strands the "hovered" flag set. The toggle
|
|
103
|
+
then stays visible on cells the mouse is nowhere near. Drive visibility from CSS `:hover`, which
|
|
104
|
+
**cannot** get stuck, and keep JS state for capability detection.
|
|
105
|
+
- **Residual (accepted):** a stale `isTruncated` still keeps the flex wrapper mounted, so that one
|
|
106
|
+
cell's text ellipsizes marginally earlier until the next `mouseleave`. Cosmetically invisible;
|
|
107
|
+
clearing it properly would need a per-cell scroll listener.
|
|
108
|
+
|
|
87
109
|
- Frozen widths measure only **mounted** rows; virtualized tables size from the first paint set.
|
|
88
110
|
- **Header labels must never abbreviate or overlap.** The full header width is a hard floor
|
|
89
111
|
(above `maxColumnWidth`, `minColumnWidth`, and pinned `columnWidths`). Measure header width
|
|
@@ -98,6 +120,15 @@ Sticky columns use a `--sticky-offset` CSS variable summed from neighboring colu
|
|
|
98
120
|
not the column set.
|
|
99
121
|
|
|
100
122
|
## Change history
|
|
123
|
+
- 2026-08-26 — Fixed: the "Show more" toggle got stuck visible on cells the mouse was not hovering.
|
|
124
|
+
`.toggle` already had `opacity: 0` + a CSS hover rule, but `PrimaryTableBodyCell` inline-set
|
|
125
|
+
`opacity: 1` unconditionally (inline beats the cascade), so visibility depended entirely on the
|
|
126
|
+
`isTruncated` state set on `mouseenter`/cleared on `mouseleave` — and `mouseleave` is not
|
|
127
|
+
guaranteed to fire (virtualized row translating out from under a stationary cursor on wheel-scroll;
|
|
128
|
+
`clearTruncationHint` no-opping while `isExpanded`). Visibility is now CSS `:hover`-driven
|
|
129
|
+
(`.bodyCell:hover .showMoreWrapper:not(.expandedWrapper) button` + `:focus-visible`), the collapsed
|
|
130
|
+
inline `opacity` was removed, and the `isExpanded` guard dropped from `clearTruncationHint`.
|
|
131
|
+
Shipped in `1.0.334-sandbox-client.136`. (apeterson)
|
|
101
132
|
- 2026-08-17 — Fixed clipped/abbreviated headers: full header width is now a hard floor for every
|
|
102
133
|
column (not capped by max-width, not overridden by `minColumnWidth`, applied even to pinned
|
|
103
134
|
`columnWidths` — the old `return def.size` bypass left pinned columns clipped). Added a
|
|
@@ -6,7 +6,7 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-26
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/src/components/Table/hooks/useFetchTablePageMeta.ts
|
|
@@ -60,6 +60,15 @@ alignment (CVA `cellWrapperVariants`), expandable, and copyable behavior. Empty/
|
|
|
60
60
|
render `EmptyCell`. If `imageUrlField` is set, the URL is resolved separately and passed to
|
|
61
61
|
`ImageCell`.
|
|
62
62
|
|
|
63
|
+
**The expandable wrap is destructive, and is now denied for status types.** When the table-meta
|
|
64
|
+
flag `isExpandableCell` is set on a field, the expandable branch does **not** wrap the
|
|
65
|
+
already-rendered `content` — it discards it and re-renders the **raw `value`** inside
|
|
66
|
+
`<ExpandableCell maxLength={40} expandable>`. Any rich cell type loses its component that way
|
|
67
|
+
(a status column rendered as truncated text with a "Show more" toggle instead of a badge).
|
|
68
|
+
`NEVER_EXPANDABLE_TYPES` (`STATUS`, `STATUS_BADGE`) now gates that branch, so status columns keep
|
|
69
|
+
their badge regardless of the meta flag. Fixing it in the renderer covers every client and
|
|
70
|
+
environment instead of chasing per-client `isExpandableCell` data rows.
|
|
71
|
+
|
|
63
72
|
## Cell types (`components/cellTypes/`)
|
|
64
73
|
|
|
65
74
|
| Component | Type | Renders |
|
|
@@ -153,6 +162,16 @@ layer and emits it — see
|
|
|
153
162
|
|
|
154
163
|
## Gotchas
|
|
155
164
|
|
|
165
|
+
- **`isExpandableCell` still clobbers every other rich cell type.** Only `STATUS`/`STATUS_BADGE`
|
|
166
|
+
are protected today. `URGENCY`, `BOOLEAN`, `CURRENCY`, `DATE`, `AVATAR` and `CLIENT` carry the
|
|
167
|
+
**same latent defect** — set the flag on one of those fields and the cell degrades to raw
|
|
168
|
+
truncated text. The fuller fix is inverting the deny-list into an **allowlist** (expandable only
|
|
169
|
+
for free-text/default cells); deliberately not done yet.
|
|
170
|
+
- **A cell-rendering difference between local and a deployed environment is usually DB data, not
|
|
171
|
+
code.** `isExpandableCell` is a per-field flag in the **server's table meta**, so the same blox
|
|
172
|
+
build renders differently against two databases. Diff the branches over the cell-rendering files
|
|
173
|
+
**first** (`git diff <feature> origin/_sandbox-client -- src/components/Table/...`); if that comes
|
|
174
|
+
back empty, stop reading code and go look at the table-meta rows.
|
|
156
175
|
- `MIN_COLUMN_WIDTH = 120` is the shrink floor only for columns with **no** explicit width.
|
|
157
176
|
- **Never truncate header labels.** `truncateCells` (`.truncateCells .headerCellLabel` in
|
|
158
177
|
`toga.module.css`) is for **body** values only; it must apply only `white-space: nowrap` to
|
|
@@ -198,6 +217,15 @@ layer and emits it — see
|
|
|
198
217
|
requires `npm run build` before it takes effect at runtime.
|
|
199
218
|
|
|
200
219
|
## Change history
|
|
220
|
+
- 2026-08-26 — Fixed: status columns rendered as truncated text with a "Show more" toggle instead of
|
|
221
|
+
a badge. Root cause was in `resolveCellType` — the `isExpandableCell` branch **discards** the
|
|
222
|
+
rendered `content` and re-renders the raw `value` in `ExpandableCell`. Added
|
|
223
|
+
`NEVER_EXPANDABLE_TYPES` (`STATUS`, `STATUS_BADGE`) to gate it. Recorded that the same defect is
|
|
224
|
+
latent for `URGENCY`/`BOOLEAN`/`CURRENCY`/`DATE`/`AVATAR`/`CLIENT` (an allowlist is the fuller
|
|
225
|
+
fix, not done). Also recorded the debugging lesson: the "works locally, broken on deployed
|
|
226
|
+
`_sandbox-client`" symptom was **not** a code difference — the branch diff over the cell-rendering
|
|
227
|
+
files was empty; `isExpandableCell` was set in the sandbox-client DB and unset locally. Shipped in
|
|
228
|
+
`1.0.334-sandbox-client.136`. (apeterson)
|
|
201
229
|
- 2026-08-25 — `useFetchTablePageMeta` now prefers **server-supplied `filterOptions`** and skips the
|
|
202
230
|
`recordRoute` fetch for those fields (query list + index lookup share one `needsFetchedOptions`
|
|
203
231
|
predicate so they can't skew); `filterOptions[].uuid` made optional and `surfaceSlug` declared on
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Toaster — host-token styling contract
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga-blox
|
|
5
|
+
project: TOGa Blox
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-26
|
|
10
|
+
owners: [apeterson]
|
|
11
|
+
files:
|
|
12
|
+
- toga-blox/src/components/Toaster/Toaster.module.css
|
|
13
|
+
- toga-blox/src/components/Toaster/Toaster.tsx
|
|
14
|
+
- toga-blox/src/components/Toaster/ToasterContext.tsx
|
|
15
|
+
- toga2-commerce/src/styles/index.css
|
|
16
|
+
- toga2-commerce/src/components/Toasters/ToasterList.tsx
|
|
17
|
+
related:
|
|
18
|
+
- ../architecture.md
|
|
19
|
+
- ../../../standards/frontend.md
|
|
20
|
+
- ../../toga2-commerce/architecture.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Summary
|
|
24
|
+
|
|
25
|
+
The blox `Toaster` is a CSS-Module component whose every visual property reads a
|
|
26
|
+
`--toaster-*` custom property supplied by the **host app**. It is the clearest working example of
|
|
27
|
+
the blox styling contract described in `frontend.md` §13(e) — and of that contract's failure mode:
|
|
28
|
+
a host that defines **zero** tokens gets a component that renders **unstyled**, because every
|
|
29
|
+
declaration is invalid-at-computed-value-time and dropped.
|
|
30
|
+
|
|
31
|
+
As of 2026-08-26 the component ships **default values via `var(--token, fallback)`**, so it renders
|
|
32
|
+
fully styled with no host configuration, while any host-defined `--toaster-*` token still wins.
|
|
33
|
+
The blox-side changes are **merged and published** as of 2026-08-26 in
|
|
34
|
+
`@agilant/toga-blox@1.0.334-sandbox-client.136` (dist-tag `sandbox-client`). The commerce-side
|
|
35
|
+
rewiring (`TRUE-80707`) is still a separate branch — verify that half against the installed app.
|
|
36
|
+
|
|
37
|
+
## How it works
|
|
38
|
+
|
|
39
|
+
### Token layer with fallbacks (the pattern to copy)
|
|
40
|
+
|
|
41
|
+
Every declaration in `Toaster.module.css` is written `var(--toaster-x, <literal default>)`. This was
|
|
42
|
+
chosen over adding a `:root` token block inside blox because:
|
|
43
|
+
|
|
44
|
+
- blox ships **no** `:root` token block anywhere (§13e), and
|
|
45
|
+
- `dist/main.css` is **uncompiled** and must not be imported by consumers (§13f),
|
|
46
|
+
|
|
47
|
+
so a `:root` block would need a stylesheet import that no consumer makes. Fallbacks need nothing
|
|
48
|
+
imported. **This is the generalisable rule for any blox component that reads host tokens: ship
|
|
49
|
+
`var(--token, fallback)`, never a bare `var(--token)`.**
|
|
50
|
+
|
|
51
|
+
### Do NOT declare a property on an element that also receives a host override class
|
|
52
|
+
|
|
53
|
+
`.prefixIcon` and `.closeBtn` deliberately declare **no `color`**. The host's `iconColor` /
|
|
54
|
+
`closeIconColor` classes land on those same elements at **equal specificity**, so a `color`
|
|
55
|
+
declared in the module would be resolved by **stylesheet source order** and could silently beat the
|
|
56
|
+
host's class (supply's mint/crimson icons losing to a grey default). They inherit from `.base`
|
|
57
|
+
instead, which any host class reliably overrides. In-code comments record this so it is not
|
|
58
|
+
"helpfully" re-added.
|
|
59
|
+
|
|
60
|
+
### Host wiring — the variant-remap mechanism
|
|
61
|
+
|
|
62
|
+
The supported way for a host to express toast variants is to define the `--toaster-*` layer once,
|
|
63
|
+
then have each variant class **remap only the `--toaster-default-*` tokens** that blox's `.base`
|
|
64
|
+
already reads. Do **not** style toasts with utility classes on a wrapper — Tailwind utilities and
|
|
65
|
+
blox's `.base` land at equal specificity and compete unpredictably.
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
:root { --toaster-padding: 1.25rem; /* … full token set … */ }
|
|
69
|
+
.commerceToaster--error { --toaster-default-bg: …; --toaster-default-text: …; }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`toga25-supply` defines the full set (~33 tokens) in `src/themeConfig.json`; `toga2-commerce` now
|
|
73
|
+
defines it in `src/styles/index.css` with `.commerceToaster--{success,error,restricted,default}`
|
|
74
|
+
variant classes consumed by `ToasterList.tsx`. Commerce's result was verified by `tsc -b` / `vite
|
|
75
|
+
build` and by inspecting the emitted CSS for the tokens and all four variant classes — **not
|
|
76
|
+
visually in a browser**.
|
|
77
|
+
|
|
78
|
+
## Gotchas
|
|
79
|
+
|
|
80
|
+
- **A missing `var()` wrapper fails silently.** Two declarations read `min-width: (--toaster-min-width)`
|
|
81
|
+
/ `max-width: (--toaster-max-width)` — no `var()`, so the parser dropped both. `toga25-supply`
|
|
82
|
+
masked this by re-declaring `min-width` on `.supplyToaster`; commerce had nothing to mask it, which
|
|
83
|
+
is how it surfaced. A host override can hide a blox CSS bug indefinitely.
|
|
84
|
+
- **`border-width` + `border-color` with no `border-style` renders no border at all.** `.base`
|
|
85
|
+
previously set the first two only; `border-style` (plus `border-radius`, `font-family`,
|
|
86
|
+
`font-size`, `margin-block`) has been added.
|
|
87
|
+
- **Order placement in commerce does not raise a success toast** — do not use it to test toast
|
|
88
|
+
styling. See `../../toga2-commerce/features/order-submit-sync-sequencing.md`.
|
|
89
|
+
- Commerce's `restricted` (orange) variant is commerce-only; supply has no equivalent. Commerce's
|
|
90
|
+
palette has no `crimson-600`, so supply's error icon color maps to `crimson-500`.
|
|
91
|
+
|
|
92
|
+
## Change history
|
|
93
|
+
|
|
94
|
+
- 2026-08-26 — The blox-side fallback work above was committed, merged to `_sandbox-client` and
|
|
95
|
+
**published** as `1.0.334-sandbox-client.136`; superseded the earlier "working-tree, not yet
|
|
96
|
+
merged or published" caveat. (apeterson)
|
|
97
|
+
- 2026-08-26 — Toaster now ships **default styling** via `var(--token, fallback)` on every custom
|
|
98
|
+
property (renders styled with zero host config; host tokens still win) and gained the missing
|
|
99
|
+
`border-style`/`border-radius`/`font-family`/`font-size`/`margin-block` on `.base`. Fixed two
|
|
100
|
+
declarations missing their `var()` wrapper (`min-width`/`max-width`), which supply had been
|
|
101
|
+
masking. Recorded the deliberate **no `color` on `.prefixIcon`/`.closeBtn`** decision (equal
|
|
102
|
+
specificity with the host's `iconColor`/`closeIconColor` classes). `toga2-commerce` rewired to
|
|
103
|
+
supply's variant-remap mechanism and given the full `--toaster-*` token layer, replacing the
|
|
104
|
+
competing Tailwind-utility styling. (apeterson)
|
|
@@ -6,7 +6,7 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-26
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/.github/workflows/publish.yml
|
|
@@ -93,6 +93,52 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
|
|
|
93
93
|
copy on the pushed branch, a new `_<mode>` channel will **not** publish unless the branch you push
|
|
94
94
|
carries the wildcard-trigger version. When standing up a new channel, merge from a branch that has
|
|
95
95
|
the dynamic `publish.yml` (or update it first).
|
|
96
|
+
- **Do NOT judge a blox branch's staleness by its behind-count.** `feature-new-table` reports
|
|
97
|
+
**"0 ahead / 47 behind"** `_sandbox-client` and `git merge-base --is-ancestor` says it is fully
|
|
98
|
+
contained — which reads as "stale, already merged, wrong base". **It is not.** Those 47 commits are
|
|
99
|
+
almost entirely `Merge branch 'feature-new-table' into _sandbox-client` and
|
|
100
|
+
`chore: bump version to 1.0.3xx` — **release plumbing generated ON `_sandbox-client` by the publish
|
|
101
|
+
flow and never merged back**. The actual source difference is **2 files** (`src/global.css`,
|
|
102
|
+
`src/index.ts`); the rest of the 334-file diff is `.claude/knowledge/` docs and `publish.yml`.
|
|
103
|
+
`feature-new-table` is the **active development branch** (tip 2026-08-24, `feat: prefer
|
|
104
|
+
server-supplied filter options in table meta`) and is the correct base. Verified 2026-08-26.
|
|
105
|
+
Corollary: the `publish.yml` divergence noted above is a **non-issue for publishing**, because
|
|
106
|
+
Actions runs the workflow on the **pushed** branch (`_sandbox-client`), which carries the dynamic
|
|
107
|
+
`_*` trigger.
|
|
108
|
+
- **A declared peer can be phantom — check before working around it.** Verified 2026-08-26:
|
|
109
|
+
`react-router-dom` appeared in blox's `peerDependencies` but only three files referenced it, two of
|
|
110
|
+
them tests; the single production reference was a **dead `import { Link }`** on line 1 of
|
|
111
|
+
`src/components/old/BaseButton/BaseButton.tsx` (the component renders an injected `linkComponent`
|
|
112
|
+
prop instead). The peer declaration and the dead import were removed and are **published** as of
|
|
113
|
+
2026-08-26 in `1.0.334-sandbox-client.136`; `react-router-dom` stays a
|
|
114
|
+
**devDependency** (`^6.30.1`) for the tests' `MemoryRouter`. Verified clean build, zero
|
|
115
|
+
`react-router-dom` references in `dist/`, and vitest identical to baseline. This peer was the
|
|
116
|
+
stated reason consumers pass `--legacy-peer-deps` against a router-7 app.
|
|
117
|
+
- **A feature branch that never touches the version line merges without a version conflict —
|
|
118
|
+
and that is why the bump has to happen afterwards.** Observed 2026-08-26: `feature-new-table` sat
|
|
119
|
+
at base `1.0.322` while `_sandbox-client` was at `1.0.332`. The merge auto-resolved the `version`
|
|
120
|
+
line to `1.0.332` with **no conflict**, because only `_sandbox-client` had ever edited it (the
|
|
121
|
+
"47 behind" commits are that same release plumbing — see the gotcha above). The bump to `1.0.333`
|
|
122
|
+
was then made **on `_sandbox-client`**, at the developer's explicit direction. That **deviates
|
|
123
|
+
from the `deploy-sandbox-client` skill's "never commit the bump directly to `_sandbox-client`"
|
|
124
|
+
rule.** It was safe *only* because the feature branch does not carry the version line, so nothing
|
|
125
|
+
could be clobbered — **record this as a one-off, not a new default.** CI then published base+1 →
|
|
126
|
+
`1.0.334-sandbox-client.136`.
|
|
127
|
+
- **`gh` is not installed on every dev machine.** The skill's `gh run watch` monitoring step simply
|
|
128
|
+
does not work there. Fall back to polling the registry:
|
|
129
|
+
`npm view @agilant/toga-blox@sandbox-client version` (or `npm dist-tag ls @agilant/toga-blox`)
|
|
130
|
+
until the new version appears — the registry is the authoritative confirmation anyway.
|
|
131
|
+
- **Consuming apps pin exactly, so a publish is only half the deploy.** After
|
|
132
|
+
`1.0.334-sandbox-client.136` shipped, `toga25-supply` on `_sandbox-client` was still pinned to
|
|
133
|
+
`1.0.333-sandbox-client.134` and needed a pin bump + reinstall. Likewise the feature branch stays
|
|
134
|
+
at its old base version after the bump lands on `_sandbox-client` — expect the two to be out of
|
|
135
|
+
step and don't read it as a problem.
|
|
136
|
+
- **⚠ Credentials have been found in the local git remote URL.** Verified 2026-08-26: the `origin`
|
|
137
|
+
remote for the `toga-blox-npm` checkout had a **GitHub PAT embedded in plaintext in the URL**
|
|
138
|
+
(visible to anything that runs `git remote get-url origin`, and to any tool that reads
|
|
139
|
+
`.git/config`). Check for this and re-point the remote at SSH or a credential helper; treat any
|
|
140
|
+
token found this way as compromised and rotate it. This is the local-config sibling of the
|
|
141
|
+
committed-`.npmrc` violation already recorded in `../../../standards/frontend.md`.
|
|
96
142
|
- **FontAwesome v7 `style`-prop TS2322** (fixed 2026-07-21 in `getFontAwesomeIcon.tsx`, and a
|
|
97
143
|
reusable pattern for any consumer). FA v7 types the `FontAwesomeIcon` `style` prop as
|
|
98
144
|
`CSSProperties & CSSVariables`, where `CSSVariables` requires a `--fa-*` custom-property index
|
|
@@ -122,6 +168,22 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
|
|
|
122
168
|
the pattern the consuming apps should follow (see the commerce workflow's secret-hygiene note).
|
|
123
169
|
|
|
124
170
|
## Change history
|
|
171
|
+
- 2026-08-26 — Deployed `1.0.334-sandbox-client.136` (`feature-new-table` → `_sandbox-client`, 6
|
|
172
|
+
files, no conflicts) and recorded three workflow gotchas from it: (1) a feature branch that never
|
|
173
|
+
touches the `version` line merges without a version conflict, which is why the bump lands on
|
|
174
|
+
`_sandbox-client` afterwards — a documented **deviation** from the `deploy-sandbox-client` skill's
|
|
175
|
+
"never bump on `_sandbox-client`" rule, safe here only because of that, not a new default;
|
|
176
|
+
(2) `gh` is not installed on every machine, so verify the publish by polling
|
|
177
|
+
`npm view @agilant/toga-blox@sandbox-client version` instead of `gh run watch`; (3) consuming apps
|
|
178
|
+
pin exactly and need a separate pin bump + reinstall. Also flagged that a **GitHub PAT was found
|
|
179
|
+
in plaintext in the local `origin` remote URL** (rotate + re-point at SSH/credential helper), and
|
|
180
|
+
marked the phantom `react-router-dom` peer removal as merged and published. (apeterson)
|
|
181
|
+
- 2026-08-26 — Corrected the branch-topology misreading: `feature-new-table`'s "0 ahead / 47 behind"
|
|
182
|
+
is release plumbing generated on `_sandbox-client`, not staleness — it remains the active dev
|
|
183
|
+
branch and correct base (real source delta: 2 files). Noted the `publish.yml` divergence is a
|
|
184
|
+
non-issue because Actions runs the pushed branch's copy. Removed the **phantom
|
|
185
|
+
`react-router-dom` peer** (its only production reference was a dead `Link` import in
|
|
186
|
+
`old/BaseButton`); it is now a devDependency for tests only. Working-tree, uncommitted. (apeterson)
|
|
125
187
|
- 2026-08-04 — Recorded the prod/beta `.scss`-only build gotcha: the `_production`/`_beta`
|
|
126
188
|
`build` copies only `src/**/*.scss`, so a component shipped with a plain `.css` + matching
|
|
127
189
|
`import "./X.css"` publishes JS referencing a stylesheet absent from the tarball, breaking
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
| [Multi-Tenant Resolution & Theming](features/multi-tenant-theming.md) | `toga2-commerce` serves multiple clients from one codebase. | src/themeConfig/themes.json, src/themeConfig/ThemeContext.tsx, src/themeConfig/types.ts, src/components/ThemeSwitcher/ThemeSwitcher.tsx, src/components/AuthLayout/AuthLayout.tsx, src/api/axiosInstance.ts, src/contexts/AuthContext.tsx, tailwind.config.js |
|
|
17
17
|
| [Order-submit sync sequencing (useSubmitOrder) — why these calls must not run in parallel](features/order-submit-sync-sequencing.md) | Submitting an order from the cart fires **two independent sync routines** — one for the sales-order header (`syncSalesOrderData`) and one for the line items (`s | toga2-commerce/src/pages/OrderDetails/hooks/useSubmitOrder.ts, toga2-commerce/src/api/syncSalesOrdersDataFromLocalStorageCartToApi.ts, toga2-commerce/src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts |
|
|
18
18
|
| [Config-Driven Shipping Cost Waiver (Standard Ground free for computer kits)](features/shipping-cost-waiver-gating.md) | On the toga2-commerce **Cart** page, a shipping option's **cost** can be waived by config using the same `PrimaryItemShippingRule` vocabulary that drives expedi | toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/viewModel/FIELDS/shared/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/CartPage.tsx |
|
|
19
|
-
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**. | toga2-commerce/amplify.yml, toga2-commerce/.gitattributes, toga2-commerce/package.json, toga2-commerce/.github/workflows/sync-stage-environments.yml |
|
|
19
|
+
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**. | toga2-commerce/amplify.yml, toga2-commerce/.gitattributes, toga2-commerce/package.json, toga2-commerce/.npmrc, toga2-commerce/vite.config.ts, toga2-commerce/.github/workflows/sync-stage-environments.yml |
|
|
20
20
|
| [Cart e2e — Cypress conventions & harness (toga2-commerce)](workflows/cypress-testing.md) | The Cypress **e2e** convention set for `toga2-commerce`, and the first **active** e2e coverage for the **Cart** page (`cartV2.cy.ts`, slice 1 — 12 tests, verifi | toga2-commerce/cypress/e2e/cartPage/cartV2.cy.ts, toga2-commerce/cypress/fixtures/cart/fetchSingleUserAdmin.json, toga2-commerce/cypress/fixtures/cart/fetchLocations.json, toga2-commerce/cypress/fixtures/cart/fetchUserShippingMethods.json, toga2-commerce/cypress/support/commands.ts, toga2-commerce/cypress/support/e2e.ts, toga2-commerce/src/pages/Cart/CartPage.tsx, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/view/cartForm/CartFormSection.tsx, toga2-commerce/src/pages/Cart/view/cartTable/CartContentsTable.tsx, toga2-commerce/src/pages/Cart/view/cartTable/CartTableItem.tsx, toga2-commerce/src/components/Inputs/AdvancedInput.tsx, toga2-commerce/src/components/BaseButton/BaseButton.tsx |
|
|
21
21
|
| [Diagnosing ERR_HTTP2_PROTOCOL_ERROR (one client fails, everyone else is fine)](workflows/http2-protocol-error-diagnosis.md) | When a Chromium browser (Chrome / Edge) shows **`ERR_HTTP2_PROTOCOL_ERROR`** loading a `*.togacommerce.com` tenant for **one client/network but works for the TO | |
|
|
@@ -6,8 +6,8 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-08-26
|
|
10
|
+
owners: ["bala", "apeterson"]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-commerce/src/pages/OrderDetails/hooks/useSubmitOrder.ts
|
|
13
13
|
- toga2-commerce/src/api/syncSalesOrdersDataFromLocalStorageCartToApi.ts
|
|
@@ -134,9 +134,22 @@ Three changes, in `toga2-commerce`:
|
|
|
134
134
|
- **⚠ Omitting a line from the rebuilt cart DELETES it from a placed order.** `processItems` with
|
|
135
135
|
`getDeleteItems = true` derives the delete set by difference, so "don't re-add this one" and
|
|
136
136
|
"destroy this one" are the same instruction on this path.
|
|
137
|
+
- **There is NO success toaster on order placement — by design, not a bug.** `useSubmitOrder` calls
|
|
138
|
+
`addToaster` **only in its `catch` block** (`status: 'error'`). Both success paths navigate away
|
|
139
|
+
before anything could render: cart → `createNewSalesOrderSubmit()` →
|
|
140
|
+
`navigate("/order-details?uuid=…&type=orderPlaced")` (no `addToaster` anywhere in it); edit-order →
|
|
141
|
+
`window.location.href = getSupplyRedirectLink(orderUuid)`. **Consequence for debugging: placing an
|
|
142
|
+
order is not a valid way to test toaster styling.** Use a path that stays mounted — add/edit
|
|
143
|
+
shipping address (Cart or Account), Account → My Settings, or Cart → add new user; for the error
|
|
144
|
+
variant, force a submit failure to hit the catch block. See
|
|
145
|
+
[blox Toaster](../../toga-blox/features/toaster.md).
|
|
137
146
|
|
|
138
147
|
## Change history
|
|
139
148
|
|
|
149
|
+
- 2026-08-26 — Recorded that order placement raises **no success toaster by design** (`addToaster`
|
|
150
|
+
is only in the `catch`; both success paths navigate away), and listed the mounted-path
|
|
151
|
+
alternatives for testing toaster styling. (apeterson)
|
|
152
|
+
|
|
140
153
|
- 2026-08-14 — Recorded **how order lines are actually written**, learned while adding a
|
|
141
154
|
server-side order guard: the header sync `delete`s `salesOrderItems` from the edit `PUT` (line
|
|
142
155
|
20), and lines are written **one request at a time** — new lines via the nested
|
|
@@ -6,16 +6,19 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-08-26
|
|
10
|
+
owners: ["jcardinal", "apeterson"]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-commerce/amplify.yml
|
|
13
13
|
- toga2-commerce/.gitattributes
|
|
14
14
|
- toga2-commerce/package.json
|
|
15
|
+
- toga2-commerce/.npmrc
|
|
16
|
+
- toga2-commerce/vite.config.ts
|
|
15
17
|
- toga2-commerce/.github/workflows/sync-stage-environments.yml
|
|
16
18
|
related:
|
|
17
19
|
- ../../toga2-supply/workflows/amplify-build-and-deploy.md
|
|
18
20
|
- ../../toga-blox/workflows/dynamic-publish-pipeline.md
|
|
21
|
+
- ../../../standards/frontend.md
|
|
19
22
|
---
|
|
20
23
|
|
|
21
24
|
## Summary
|
|
@@ -69,6 +72,10 @@ full cross-repo publish-then-build model. **The channel must exist before the ap
|
|
|
69
72
|
|
|
70
73
|
## Edge cases & escalation
|
|
71
74
|
|
|
75
|
+
> The 2026-08-26 commerce-side fixes below (`.npmrc` flag, added peers, `resolve.dedupe`, the
|
|
76
|
+
> `--toaster-*` token layer, the `ToasterList` rewiring) are **working-tree on branch `TRUE-80707`** —
|
|
77
|
+
> uncommitted, unmerged, and not in any deployed build.
|
|
78
|
+
|
|
72
79
|
- **All the toga2-supply gotchas apply** (console build spec overriding repo `amplify.yml`;
|
|
73
80
|
CRLF breaking block-scalar parsing; Vite silently falling back to `.env`/beta when a mode's env
|
|
74
81
|
file is missing). See the related doc.
|
|
@@ -77,14 +84,44 @@ full cross-repo publish-then-build model. **The channel must exist before the ap
|
|
|
77
84
|
only triggers on branches listed in `on.push.branches` and only tags branches with an explicit
|
|
78
85
|
`elif` case — creating the branch alone does nothing; the workflow file must be present on that
|
|
79
86
|
branch (GitHub Actions runs the workflow version on the pushed branch).
|
|
80
|
-
- **`--legacy-peer-deps` is required** because blox
|
|
87
|
+
- **`--legacy-peer-deps` is required** because blox declared `react-router-dom@^6` as a peer while
|
|
81
88
|
commerce uses `react-router-dom@7` (npm 7+ `ERESOLVE` otherwise). All non-prod build scripts use it.
|
|
89
|
+
(That peer was **phantom** and was removed from blox on 2026-08-26 — see the blox publish-pipeline
|
|
90
|
+
doc — but the flag stays until a build without it is proven.)
|
|
91
|
+
- **When one app installs a shared lib and a sibling app does not, compare `.npmrc` BEFORE touching
|
|
92
|
+
peer ranges.** A local `npm i` in commerce failed `ERESOLVE` on blox's router peer while
|
|
93
|
+
`toga25-supply` installed cleanly against the **same** blox version and the **same** router major.
|
|
94
|
+
The difference was not the peer range: supply carries `legacy-peer-deps=true` in its committed
|
|
95
|
+
`.npmrc` and commerce did not. Fix was adding `legacy-peer-deps=true` to commerce's `.npmrc` — a
|
|
96
|
+
tracked file, so it also reaches the Amplify build (the amplify.yml flag alone does not cover a
|
|
97
|
+
developer's local install).
|
|
82
98
|
- **`--legacy-peer-deps` disables npm's peer auto-install**, so any blox peer that is NOT in
|
|
83
99
|
commerce's own `dependencies` and NOT in blox's `dependencies` must be added explicitly, or Vite/
|
|
84
100
|
Rollup fails with `failed to resolve import "<pkg>"`. Concretely, commerce had to add the table
|
|
85
|
-
peers: `@tanstack/react-table@^8`, `react-table@^7.8.0`, `react-table-sticky@^1.1.3
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
peers: `@tanstack/react-table@^8`, `react-table@^7.8.0`, `react-table-sticky@^1.1.3`, and on
|
|
102
|
+
2026-08-26 `@fortawesome/free-solid-svg-icons@^6.7.2` + `react-multi-select-component@^4.3.4`.
|
|
103
|
+
Check `npm view @agilant/toga-blox@<tag> peerDependencies` against commerce deps when a resolve
|
|
104
|
+
error appears.
|
|
105
|
+
- **A BARREL import is what drags in peers you never use.** The failure text was
|
|
106
|
+
`Rollup failed to resolve import "@fortawesome/free-solid-svg-icons" from
|
|
107
|
+
node_modules/@agilant/toga-blox/dist/components/Input/Input.js`, triggered by
|
|
108
|
+
`AuthLayout.tsx` doing `import { EnvironmentBadge } from "@agilant/toga-blox"` — the package
|
|
109
|
+
index pulls `Input` and `MultiSelect` along with it. A deep import would not have surfaced it, so
|
|
110
|
+
expect the missing-peer set to be blox's **whole** surface, not the components you reference.
|
|
111
|
+
- **`resolve.dedupe` was missing from commerce's `vite.config.ts`** (a `frontend.md` §11 MUST), which
|
|
112
|
+
made consuming blox via a local symlink hazardous: `toga-blox-npm/node_modules/` contains its own
|
|
113
|
+
`react@18.3.1`/`react-dom@18.3.1`, and because blox ships **unbundled**, imports resolve against
|
|
114
|
+
the nearest `node_modules` — under a symlink that is blox's own, giving a second React copy. This
|
|
115
|
+
is precisely why supply's symlink workflow works (it has always deduped
|
|
116
|
+
`react`/`react-dom`/`@tanstack/react-table`) and commerce's did not. Commerce now dedupes
|
|
117
|
+
`react`, `react-dom`, `react-hook-form`, `react-router-dom`, `framer-motion`,
|
|
118
|
+
`@tanstack/react-query`, `@tanstack/react-table`.
|
|
119
|
+
- **`package.json` pins blox with a CARET** (`"@agilant/toga-blox": "^1.0.322-sandbox-client.117"`),
|
|
120
|
+
violating `frontend.md` §13(a) — some published versions ship an empty `dist`, and a caret can
|
|
121
|
+
float you onto one. Pin exact. Related: the `TRUE-80707` branch pin is **11 versions behind** the
|
|
122
|
+
`_sandbox-client` branch pin (`1.0.333-sandbox-client.134`); reconcile before merging.
|
|
123
|
+
- **Secret hygiene re-confirmed unremediated 2026-08-26** — the live tokens are still in the tracked
|
|
124
|
+
`.npmrc` in **both** `toga2-commerce` and `toga25-supply`. See the remediation below.
|
|
88
125
|
- **Secret hygiene (open remediation):** `toga2-commerce/.npmrc` is **git-tracked** — it is listed
|
|
89
126
|
in `.gitignore` but was committed before that, so git still tracks it, and it holds a live
|
|
90
127
|
FontAwesome registry token and a live npm auth token in plaintext. Remediation: (1) **rotate both
|
|
@@ -95,6 +132,13 @@ full cross-repo publish-then-build model. **The channel must exist before the ap
|
|
|
95
132
|
|
|
96
133
|
## Change history
|
|
97
134
|
|
|
135
|
+
- 2026-08-26 — Dependency-resolution findings from a local commerce build: root-caused an
|
|
136
|
+
`ERESOLVE` to the **missing `legacy-peer-deps=true` in commerce's tracked `.npmrc`** (supply had
|
|
137
|
+
it) rather than blox's peer range; recorded the barrel-import trigger for missing peers (added
|
|
138
|
+
`@fortawesome/free-solid-svg-icons`, `react-multi-select-component`); added the previously absent
|
|
139
|
+
`resolve.dedupe` to `vite.config.ts` and explained why supply can symlink blox safely and commerce
|
|
140
|
+
could not; flagged the caret blox pin and the 11-version pin drift on `TRUE-80707`; re-confirmed
|
|
141
|
+
the committed `.npmrc` tokens are still unremediated. All working-tree, uncommitted. (apeterson)
|
|
98
142
|
- 2026-07-21 — Reworked `amplify.yml` to a **fully dynamic inline build** (no per-branch `case`, no
|
|
99
143
|
`npm run <script>`): derive mode from `$AWS_BRANCH`, dynamic `npm install
|
|
100
144
|
"@agilant/toga-blox@$MODE"`, `npx tsc -b` (composite), `npx vite build --mode "$MODE"`. Re-export
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [TOGa Supply (toga2-supply) Architecture](architecture.md) | `toga2-supply` is the **React + Vite frontend** for TOGa Supply — warehouse fulfillment tooling (shipment selection, fulfill & ship against carrier APIs, NetSui | toga2-supply/src/api/toga.ts, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx |
|
|
6
|
-
| [Per-currency amount lines (CurrencyAmountLines / CurrencyAmountList) and child-row diffing](features/currency-amount-lines-editor.md) | The shared UI for "one amount per currency" on a parent record — an item's prices, a vendor item's costs. | toga2-supply/src/components/ui/CurrencyAmountLines.tsx, toga2-supply/src/pages/Items/api/itemsApi.ts, toga2-supply/src/pages/VendorItems/api/vendorItemsApi.ts |
|
|
6
|
+
| [Per-currency amount lines (CurrencyAmountLines / CurrencyAmountList) and child-row diffing](features/currency-amount-lines-editor.md) | The shared UI for "one amount per currency" on a parent record — an item's prices, a vendor item's costs. | toga2-supply/src/components/ui/CurrencyAmountLines.tsx, toga2-supply/src/pages/Items/api/itemsApi.ts, toga2-supply/src/pages/VendorItems/api/vendorItemsApi.ts, toga2-supply/src/pages/Items/view/modals/viewEdit/ItemsModalContentTemplate.tsx, toga2-supply/src/pages/VendorItems/view/modals/edit/VendorItemsModalContentTemplate.tsx, toga2-supply/src/utils/formatCurrency.tsx |
|
|
7
7
|
| [Fulfill & Ship](features/fulfill-and-ship.md) | Fulfill & Ship lets a warehouse user select sales-order line items, enter serials, pick a carrier/method, and in one action: create the Item Fulfillment records | _underscore/Trait/Netsuite/SalesOrder.php, dbchanges2/Client_Growrk/2026-08-10e - GrowrkUpsServiceMethodCodes.sql, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, _underscore/Model/Client/Measure.php, _underscore/Model/Client/TrackingNumber.php, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/getEditShipmentFormOptions.ts, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/validateFormOnSubmit.ts, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/checkDimensions.ts, toga2-supply/src/components/ui/Tables/BasicTable/BasicTable.tsx, toga2-supply/src/components/ui/Tables/types.ts, toga2-supply/src/components/ui/GoogleMapsLink.tsx, toga2-supply/src/pages/ShipmentItems/view/forms/ShipmentItemsTable.tsx, toga2-supply/src/pages/ShipmentItems/api/ShipmentItemsApi.ts, toga2-supply/src/pages/ShipmentItems/types.ts, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/RETURNLABELFIELDS.json, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/components/ui/BaseInput/UnitSelect.tsx, toga2-supply/src/pages/EditShipment/view/modals/SerialNumbersModal.tsx, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/pages/EditShipment/view/components/SelectedShipmentItemsTable.tsx, toga2-supply/src/pages/EditShipment/view/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/DUMMYUPDATESHIPMENTFIELDS.json, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/tailwind.config.cjs, toga2-supply/src/pages/EditShipment/view/modals/ReturnShippingModal.tsx, toga2-supply/src/styles/index.scss, toga2-supply/src/components/ui/BaseInput/BaseInput.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/EditShipment/viewModel/signatureTypes.ts, toga2-supply/src/pages/EditShipment/view/modals/SelectReturnAddressModal.tsx, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/formatShipmentData.ts, toga2-supply/src/pages/EditShipment/types.ts, toga2-supply/src/pages/Shipments/view/ShipmentsPage.tsx, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx, toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts, toga2-supply/src/pages/Shipments/types.ts, toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx, toga2-supply/src/components/ui/CardTable/CardTable.tsx, toga2-supply/src/components/ui/CardTable/types.ts, toga2-supply/src/assets/pen-line.svg, _underscore/Model/Client/ItemFulfillment.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php |
|
|
8
8
|
| [Order-details field config (valueKey resolution) & the hardcoded Customer name](features/order-detail-field-config-and-customer-name.md) | The supply order-details modal is **config-driven**: each host has a JSON field list under `src/pages/Orders/view/OrderView/viewModel/FIELDS/<HOST>/`, and every | toga2-supply/src/utils/formatCustomerName.tsx, toga2-supply/src/pages/Orders/view/OrderView/components/invoices/OrderContentSection.tsx, toga2-supply/src/pages/Orders/view/OrderView/viewModel/FIELDS/, toga2-supply/src/pages/Orders/view/OrderView/viewModel/useOrderDetailsViewModel.ts, toga2-supply/src/api/toga.ts |
|
|
9
9
|
| [Table-data request filters (`additionalData` → `getDataTableData`)](features/table-data-additional-filters.md) | A listing page adds its own row filters to a table-view data request by passing an **`additionalData`** prop down to the shared `useFetchData` hook, which forwa | toga2-supply/src/components/ui/Tables/hooks/useFetchData.tsx, toga2-supply/src/api/toga.ts, toga2-supply/src/pages/VendorItems/VendorItemsPage.tsx |
|