toga-ai 1.0.875 → 1.0.877
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.
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: nychh
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-09-23
|
|
10
|
+
owners: ["bala", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Client/Nychh/TransferOrderEmails.php
|
|
13
13
|
- _underscore/Model/Nychh/TransferOrder.php
|
|
@@ -211,9 +211,23 @@ Cost of the split, stated so nobody is surprised later:
|
|
|
211
211
|
templates and the tracking line are Plus Jakarta Sans. Outlook gets Arial inside an
|
|
212
212
|
`<!--[if mso]-->` block because it cannot load web fonts.
|
|
213
213
|
- **Detail box:** *Site Contact* falls back to the person who raised the order — 3 of the 6 supply
|
|
214
|
-
orders in prod have no contact
|
|
215
|
-
`CONCAT_WS` skips NULL but **not** an empty string, so
|
|
216
|
-
`451 Clarkson Ave , Brooklyn`; every part is `NULLIF`'d before
|
|
214
|
+
orders in prod have no contact. (The greeting used to share that fallback; since 2026-09-23 it
|
|
215
|
+
runs the other way round — see below.) And `CONCAT_WS` skips NULL but **not** an empty string, so
|
|
216
|
+
a blank address line 2 produced `451 Clarkson Ave , Brooklyn`; every part is `NULLIF`'d before
|
|
217
|
+
joining.
|
|
218
|
+
- **The greeting addresses the SUBMITTER, the detail box shows the CONTACT.** They are two different
|
|
219
|
+
people and the email shows both:
|
|
220
|
+
|
|
221
|
+
| Slot | Source column | Shape |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `greetingName` ("Hi …,") | `CreatedByUsers.firstName` → `submittedByFirstName`, falling back to `Contacts.firstName` | **first name** |
|
|
224
|
+
| *Site Contact* row | `Contacts.firstName`+`lastName` → `siteContactName`, falling back to `submittedByName` | full name |
|
|
225
|
+
| *Submitted by* row | `CreatedByUsers.firstName`+`lastName` → `submittedByName` | full name |
|
|
226
|
+
|
|
227
|
+
Both greeting sources must be **`firstName` columns**. The original fallback was
|
|
228
|
+
`contactFirstName ?: submittedByName`, and `submittedByName` is the `CONCAT_WS` **full** name — so
|
|
229
|
+
an order with no contact greeted *"Hi Carol Ann Rosado,"*. Never fall back from a first-name slot
|
|
230
|
+
to a `…Name` full-name alias.
|
|
217
231
|
|
|
218
232
|
## Gotchas / known issues
|
|
219
233
|
|
|
@@ -247,6 +261,15 @@ unescaped values and both detail-box defects were all found there, not in produc
|
|
|
247
261
|
|
|
248
262
|
## Change history
|
|
249
263
|
|
|
264
|
+
- 2026-09-23 — **The greeting now addresses whoever raised the order, not the site contact.**
|
|
265
|
+
`readTransferOrderForEmail()` selects `CreatedByUsers.firstName AS submittedByFirstName`, and
|
|
266
|
+
`greetingName` flipped from `contactFirstName ?: submittedByName` to
|
|
267
|
+
`submittedByFirstName ?: contactFirstName`. Found live: an order raised by Carol Ann Rosado
|
|
268
|
+
opened *"Hi Shehu,"* — the site contact. Fixing the order of preference also fixed a second
|
|
269
|
+
latent bug: the old fallback reached for `submittedByName`, the **full** name, so it could render
|
|
270
|
+
*"Hi Carol Ann Rosado,"*; both sides of the fallback are now first-name columns. *Site Contact*
|
|
271
|
+
and *Submitted by* in the details box are unchanged. (worker2 `ed6d566` on `_production`)
|
|
272
|
+
(rgirish)
|
|
250
273
|
- 2026-09-18 — **Moved the whole feature out of `_underscore` into worker2.** Deleted
|
|
251
274
|
`_Trait_Nychh_TransferOrderEmail`; `worker2/Worker/Client/Nychh/TransferOrderEmails.php` now holds
|
|
252
275
|
the queued `SendSubmitted` / `SendFulfillment` entry points, the `SendPending` cron, every query
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: bundles-page-bugs
|
|
4
|
+
title: TRUE-82138 — Bundles page view/edit/create bugs
|
|
5
|
+
author: apeterson
|
|
6
|
+
repos: [toga25-supply, toga2-supply, _underscore, api2]
|
|
7
|
+
framework: "2.0"
|
|
8
|
+
client: compass-usa
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-09-23
|
|
11
|
+
updated: 2026-09-23
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: bundles-page-bugs
|
|
15
|
+
**Date:** 2026-09-23
|
|
16
|
+
**Project/Repo:** toga25-supply (2.0), branch `TRUE-82138`
|
|
17
|
+
**Task:** Fix three reported bugs on the toga25-supply Bundles page for Compass USA / Compass Canada — description not editable on an inactive bundle, the "< Bundle" back button not working, and an edited bundle/kit price not showing in Supply (shows correctly in Commerce).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
|
|
23
|
+
- **Bug 1 root cause found and fixed.** blox `BaseInput.js` switches on `case "textArea"` and ends `default: return null`; the label is rendered OUTSIDE the switch. The bundle/vendor-item field JSONs said `"textarea"` (lower-case `a`), so the field rendered a label with no input. Evidence: live DOM on bundle 170 showed `<div class="_inputAndLabelWrapper_"><label for="description">Description</label></div>` with no `_inputContainer_`, while `Name` had both. Further evidence: a temporary `console.log` in `BundleRecordEdit.renderField` printed `DBG renderField description "textarea"` vs `DBG renderField name "text"`. Fixed in 6 files (`"textarea"` → `"textArea"`). **The bug has nothing to do with the bundle being inactive** — that is just how it was reported.
|
|
24
|
+
- **Bug 2 root cause found and fixed.** `BundleRecordModalLayout.tsx` passed `onBack={bundleItemDiscard.handleClose}`. `useDiscardCancelGuardrail.handleClose` only RETURNS a boolean — it never calls the `onClose` (`closeBundleItem`) it was given. The modal's X works because `TableRecordModal.onRequestClose` acts on the return value; the back button has no shell, so a clean form did nothing. Fixed with a new `handleBundleItemBack` that clears the `bundle-items` param when the guardrail returns true.
|
|
25
|
+
- **Bug 3 root-caused (not fixed).** Not a caching bug. `useBundleItemEditForm.tsx:75-77` PUTs `bundleItemPrices: [{ priceType: {uuid}, price }]` with **no `uuid` on the price row**, so api2 creates a new `BundleItemPrices` row on every save (per the nested-relationship-writes rule: no identifier → create). Supply then reads `bundleItemPrices?.[0]?.price` — the oldest row. Commerce reads the current one.
|
|
26
|
+
- **Bug 3 measured on production `Client_Compass`** (read-only SELECTs via `reader1.client.database.togahub.com`):
|
|
27
|
+
- **5,123 `BundleItemPrices` rows for 1,575 bundle items** (~3.3 each).
|
|
28
|
+
- Bundle item **266**: 15 rows, all `priceTypeId` 3, all `8.00`.
|
|
29
|
+
- Bundle item **434**: 7 rows — id 1005 (`priceType` 3, 554.35) … id 5422 (`priceType` 1, **533.34**, the real current price). Supply shows the oldest.
|
|
30
|
+
- `Logs_Compass.Api` `PUT /v2/bundle-items/%` by month starts at **2025-09 (2448 calls)** — 7 months before toga2-supply's Bundles page shipped, so a third, unidentified writer exists.
|
|
31
|
+
- **Same bug confirmed in `toga2-supply`** — `src/pages/Bundles/view/modals/viewEdit/BundleItemModal.tsx:151` builds the identical payload and line 106 reads `[0]`. First commit `d95d0520e`, 2026-04-07. toga25-supply inherited it 2026-06-25 (`a788081`).
|
|
32
|
+
- **Add Bundle Item modal layout fixed.** `.colRight` used `justify-content: space-between` across the full 1026px body, pushing two narrow columns to opposite ends. Changed to `flex-start`, and added a `.colSplit` modifier (57% / 43%, 5rem gap) applied only by `BundleItemRecordEdit.tsx`. Measured live: 539px / 407px, gap 80px.
|
|
33
|
+
- `npx tsc --noEmit -p tsconfig.app.json` exits 0 after every change.
|
|
34
|
+
- Two commits pushed: `9c4cfe9` (bugs 1+2), `c5f92d8` (modal layout).
|
|
35
|
+
|
|
36
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
37
|
+
|
|
38
|
+
- **Do NOT re-verify the bug-1 fix by reloading the dev page — the browser will still show the OLD behaviour.** After changing the JSON, the running page kept rendering `"textarea"`. Everything below was tried and did NOT clear it:
|
|
39
|
+
- `location.reload()` and `navigate_page` to the same URL — still stale.
|
|
40
|
+
- Restarting Vite with `--force` (twice, ports 5175 and 5180) — still stale.
|
|
41
|
+
- `fetch(url, {cache:'reload'})` on `/src/fieldsConfig/index.ts` and the JSON module, then `location.reload()` — still stale.
|
|
42
|
+
- A fresh page with a cache-busting `?cb=2` document query — still stale.
|
|
43
|
+
- Temporarily setting `inputType` to `"text"` to isolate — the description field STILL did not render, proving the page was not reading the changed file at all.
|
|
44
|
+
- Meanwhile `fetch` of the exact module URL the app imports returned `"inputType":"textArea"` (correct), and a `.tsx` edit in the same file tree DID hot-reload. So the server and the build are fine; only the JSON module is stuck in the page.
|
|
45
|
+
- **Do NOT restart Vite on a different port to get a clean cache.** Port 5180 is a different origin, so `localStorage` is empty and the app lands on `/login` with no session.
|
|
46
|
+
- **Ruled out as causes of the stale JSON (do not re-investigate):** wrong file (single import at `src/fieldsConfig/index.ts:38` → the DEFAULT JSON; `DEFAULT_BUNDLE_RECORD_EDIT_FIELDS` at line 121-122 is just `.DEFAULT` off that import; no client override), wrong server (PID on :5175 has cwd `/Users/alexpeterson/Agilant/toga25-supply`), stale Vite deps, and a stale `.tsx`.
|
|
47
|
+
- **Do NOT treat duplicate `BundleItemPrices` rows as intended price history.** This was proposed and checked against the docs, and the evidence is against it: `SalesOrderItems` carries its own `price`/`priceTypeId` snapshot; `_Model_Client_BundleItemPrice` has **no** `dtCreated`/`dtUpdated`/effective date/`isActive`, so nothing marks which row is current; and `clients/compass-usa/workflows/changing-a-kit-line-item.md` calls the duplicates a hazard ("do not blindly reproduce the duplicates either"). The real history mechanism is at the `BundleItems` level — mirror the row, set the old one `isActive = 0`, never delete, because `SalesOrderItems.bundleItemId` points at historical lines.
|
|
48
|
+
- **Do NOT blanket-dedupe to one row per bundle item.** Multiple rows per bundle item ARE legitimate — one per `(priceType, currency, plan)`. Item 434 genuinely has a `priceType` 3 row and a `priceType` 1 row. Only repeats WITHIN one combination are junk.
|
|
49
|
+
|
|
50
|
+
## Not tried yet (candidates for next session)
|
|
51
|
+
|
|
52
|
+
- Identify the third writer behind the 2025-09 `PUT /v2/bundle-items/` traffic — read `requestPayload` on an early `Logs_Compass.Api` row; it names the app. This decides whether the fix belongs in each frontend or platform-side (a `MATCH_UPSERT` child policy on `BundleItems.bundleItemPrices`).
|
|
53
|
+
- Fix the bug-3 WRITE: send the existing price row's `uuid`, or PUT `/bundle-item-prices/{uuid}` directly. Direct is safer — a nested child failure on a parent PUT still returns 200. Needed in BOTH `toga25-supply` and `toga2-supply`.
|
|
54
|
+
- Fix the bug-3 READ: the frontends use `bundleItemPrices?.[0]` and ignore price type entirely, so even with clean data they can show the lease price where the purchase price belongs. Should select by the bundle item's `defaultPriceType`. Three sites: `BundleItemsTable.tsx:27`, `BundleItemRecordView.tsx:141`, `useBundleItemEditForm.tsx:18`.
|
|
55
|
+
- Write the `dbchanges2` cleanup migration for the duplicate prod rows (keep newest per price-type group). **Hand to the developer to run — never execute.**
|
|
56
|
+
- Add a unique key on `BundleItemPrices` so this cannot come back. Needs the key-shape answer first (see Blockers).
|
|
57
|
+
- Regression tests. The pre-commit check flagged `BundleRecordModalLayout` and `BundleItemRecordEdit` as untested. The back-button fix is easy to cover with a Vitest case.
|
|
58
|
+
- Apply the same 57/43 `.colSplit` to the read-only `BundleItemRecordView` if wanted — it still uses plain `.colRight`.
|
|
59
|
+
|
|
60
|
+
## Current file state
|
|
61
|
+
|
|
62
|
+
| File | Status | Notes |
|
|
63
|
+
|------|--------|-------|
|
|
64
|
+
| `src/layout/BundleRecordModalLayout/BundleRecordModalLayout.tsx` | committed `9c4cfe9` | New `handleBundleItemBack`; `onBack` now wired to it |
|
|
65
|
+
| `src/layout/BundleRecordModalLayout/viewModel/FIELDS/DEFAULT/bundleRecordEditFields.json` | committed `9c4cfe9` | `"textarea"` → `"textArea"` (line 48) |
|
|
66
|
+
| `src/layout/BundleRecordModalLayout/viewModel/FIELDS/DEFAULT/bundleRecordViewFields.json` | committed `9c4cfe9` | same casing fix (line 75) |
|
|
67
|
+
| `src/layout/VendorItemRecordModalLayout/viewModel/FIELDS/{DEFAULT,COMPASS,COMPASSCANADA,NYCHH}/vendorItemRecordViewFields.json` | committed `9c4cfe9` | same casing fix, 4 files |
|
|
68
|
+
| `src/layout/BundleRecordModalLayout/BundleRecordModal.module.css` | committed `c5f92d8` | `.colRight` → `justify-content: flex-start`; new `.colSplit` (5rem gap, `flex: 57/43 1 0`) |
|
|
69
|
+
| `src/layout/BundleItemRecordModalLayout/components/BundleItemRecordEdit.tsx` | committed `c5f92d8` | applies `colSplit` alongside `colRight` |
|
|
70
|
+
| `.env.development` | **modified, NOT mine, left unstaged** | Working tree has the sandbox `VITE_API` line active; the committed version has it commented. Do not stage it. |
|
|
71
|
+
| `src/layout/BundleItemRecordModalLayout/hooks/useBundleItemEditForm.tsx` | **unchanged — bug 3 not started** | Line 75-77 is the create-instead-of-update write |
|
|
72
|
+
|
|
73
|
+
## Decisions made
|
|
74
|
+
|
|
75
|
+
- **`.colSplit` as a scoped modifier, not a change to `.colRight`.** `.colRight` is shared with the bundle record modal (Overview / Restrictions). Forcing 57/43 there would be wrong. Only the `flex-start` change reaches it, which removes the same gutter without imposing a ratio. Rejected: changing `.colRight` outright.
|
|
76
|
+
- **`flex: 57 1 0` / `flex: 43 1 0` rather than percentages or a grid.** `flex-basis: 0` makes the ratio exact regardless of content width, and the flex numbers read as the percentages, so retuning is a one-number edit. Rejected: `width: 57%` (breaks with the gap), CSS grid (more churn for no gain).
|
|
77
|
+
- **Bug 3 fix deferred pending the unique-key shape.** It touches 13 months of prod data across three apps, so the key decision comes before any code. Rejected: patching toga25-supply alone, which would leave toga2-supply still writing duplicates.
|
|
78
|
+
- **Prefer a direct `PUT /bundle-item-prices/{uuid}` over a nested child write.** api2's update path returns 200 even when a nested CHILD write failed, so a nested price write gives no signal about whether it landed.
|
|
79
|
+
|
|
80
|
+
## Blockers
|
|
81
|
+
|
|
82
|
+
- **Bug 3 is blocked on one answer from the developer:** should `BundleItemPrices` be unique on `(bundleItemId, priceTypeId)`, or also include `currencyId` and `planId`? Compass has both NULL, but Quad's per-currency pricing needs the wider key. This decides both the write fix and whether a real unique constraint can be added.
|
|
83
|
+
- **Bug 1's fix is not visually confirmed.** The mechanism is proven by DOM + runtime log, but the dev page never picked up the changed JSON (see the DO-NOT-RETRY list). Someone needs to confirm the textarea renders before the PR merges.
|
|
84
|
+
|
|
85
|
+
## Exact next step
|
|
86
|
+
|
|
87
|
+
> Open the Bundles page in the app with DevTools "Disable cache" ticked and hard-refresh (Cmd+Shift+R), then open bundle 170 → Edit and confirm the **Description textarea now renders**. If it does, bug 1 is done and the only thing left is the developer's answer on the `BundleItemPrices` unique-key shape before starting bug 3.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
_Saved by /session-save on 2026-09-23_
|
package/package.json
CHANGED