toga-ai 1.0.876 → 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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.876",
3
+ "version": "1.0.877",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",