toga-ai 1.0.466 → 1.0.467

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,9 @@
6
6
  | [Cart Bundle Submission & the bundleUuid Identity Contract](features/cart-bundle-submission-and-identity.md) | How cart **bundles** (kits) are turned into `SalesOrderItems` when a cart is submitted or an existing order is edited, and the **identity-field contract** every | src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts, src/utils/formatSalesOrderBundlesFromApi.ts, src/stores/useCartStoreZu.ts, src/pages/OrderDetails/helpers/formatSalesOrderDataFromLocalStorage.ts, src/pages/OrderDetails/view/components/OrderItems.tsx |
7
7
  | [Cart Notification Emails — duplicate prevention](features/cart-notification-emails.md) | On the cart "Notifications" section a user can add CC email addresses to an order. | src/pages/Cart/CartPage.tsx, src/pages/Cart/view/cartForm/CartForm.tsx, src/stores/useEmailOptionsStore.ts, src/stores/useCartSalesQuoteZu.ts, src/pages/Cart/viewModel/FIELDS/*/*/*/CARTPAGE.ts |
8
8
  | [Cart Page — config-driven form architecture (current state + planned refactor)](features/cart-page-config-architecture.md) | The Cart page (`src/pages/Cart/`) is the most config-heavy page in `toga2-commerce`. | src/pages/Cart/CartPage.tsx, src/pages/Cart/view/cartForm/CartForm.tsx, src/pages/Cart/view/cartForm/CartFormSection.tsx, src/pages/Cart/view/cartForm/CartFormRenderer.tsx, src/pages/Cart/view/EditCart.tsx, src/pages/Cart/view/EditOrder.tsx, src/pages/Cart/viewModel/useEditOrderOrEditCartViewModel.ts, src/pages/Cart/viewModel/FIELDS/*/*/*/CARTPAGE.ts, src/hooks/useAssignClientFields.ts |
9
- | [Client Fields — per-tenant / language / role content & config](features/client-fields.md) | Almost no user-facing text, field layout, or page config is hard-coded in `toga2-commerce`. | src/pages/Account/view/MySettingsView.tsx, src/fieldsConfig/index.ts, src/fieldsConfig/getClientLoginFields.ts, src/fieldsConfig/clientFields/COMPASS.json, src/fieldsConfig/clientFields/COMPASSCANADA.json, src/fieldsConfig/clientFields/QUAD.json, src/pages/Cart/api/CartApi.ts, src/hooks/useAuthenticationFlow.ts, src/contexts/AuthContext.tsx, src/pages/Login/viewModel/useLoginPageViewModel.ts, src/hooks/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx, src/components/NavIcons/NavIconItem.tsx, src/components/Submenus/AlertSubmenu.tsx, src/components/Submenus/types.ts, src/pages/Account/AccountPage.tsx, src/pages/Account/view/MyOrdersView.tsx, src/pages/GetSupport/GetSupportPage.tsx, src/pages/GetSupport/viewModel/useGetSupportViewModel.ts, src/queries/queries.ts |
9
+ | [Client Fields — per-tenant / language / role content & config](features/client-fields.md) | Almost no user-facing text, field layout, or page config is hard-coded in `toga2-commerce`. | src/pages/Account/view/MySettingsView.tsx, src/fieldsConfig/index.ts, src/fieldsConfig/getClientLoginFields.ts, src/fieldsConfig/clientFields/COMPASS.json, src/fieldsConfig/clientFields/COMPASSCANADA.json, src/fieldsConfig/clientFields/QUAD.json, src/pages/Cart/api/CartApi.ts, src/hooks/useAuthenticationFlow.ts, src/contexts/AuthContext.tsx, src/pages/Login/viewModel/useLoginPageViewModel.ts, src/hooks/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx, src/components/NavIcons/NavIconItem.tsx, src/components/Submenus/AlertSubmenu.tsx, src/components/Submenus/types.ts, src/pages/Account/AccountPage.tsx, src/pages/Account/view/MyOrdersView.tsx, src/pages/GetSupport/GetSupportPage.tsx, src/pages/GetSupport/viewModel/useGetSupportViewModel.ts, src/queries/queries.ts, src/App.tsx, src/pages/Filter/FilterPage.tsx, src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json |
10
10
  | [Config-Driven Expedited Shipping Gating (Cart)](features/expedited-shipping-gating.md) | On the toga2-commerce **Cart** page, expedited shipping options (**"2nd Day EOB"** and **"Next Day Air"**) are only offered in the *Shipping Method* dropdown wh | toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/viewModel/FIELDS/shared/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/CartPage.tsx |
11
+ | [Filter / Search-Results Page & the Two Search Entry Points](features/filter-search-results-page.md) | The storefront has **two distinct search entry points that render the same card component through completely different code paths and different FIELDS files**. | src/pages/Filter/FilterPage.tsx, src/pages/Filter/viewModel/useFilterViewModel.ts, src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json, src/pages/Filter/viewModel/FIELDS/COMPASSCANADA/FRENCH/USER/FILTERPAGEFIELDS.json, src/pages/Filter/viewModel/FIELDS/QUAD/ENGLISH/BUYER/FILTERPAGEFIELDS.json, src/components/Header/Header.tsx, src/pages/Home/view/components/BundlesSection.tsx, src/pages/Home/viewModel/FIELDS/COMPASS/ENGLISH/USER/HOMEPAGEFIELDS.json, src/components/Cards/BundleViewCard.tsx, src/utils/renderBadge.tsx, src/hooks/useAssignClientFields.ts |
11
12
  | [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 |
12
13
  | [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 |
13
14
  | [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 |
@@ -6,8 +6,8 @@ project: TOGa Commerce
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-06-23
10
- owners: ["apeterson"]
9
+ updated: 2026-07-28
10
+ owners: ["apeterson", "tcox"]
11
11
  files:
12
12
  - src/main.tsx
13
13
  - src/App.tsx
@@ -26,6 +26,7 @@ related:
26
26
  - 2.0/apps/toga2-commerce/features/client-fields.md
27
27
  - 2.0/apps/toga2-commerce/workflows/amplify-build-and-deploy.md
28
28
  - 2.0/apps/api2/architecture.md
29
+ - 2.0/apps/toga2-commerce/features/filter-search-results-page.md
29
30
  ---
30
31
 
31
32
  ## Summary
@@ -236,11 +237,23 @@ generic CRUD helpers live in `src/api/genericApi.ts` (paginated `getData`, `save
236
237
  - **`AuthLayout` never remounts** → `refetchOnMount` fires once per session; wire explicit
237
238
  invalidation for data that must refresh on navigation.
238
239
  - **24h `staleTime` + persisted cache** → users can see stale catalog/pricing; invalidate on the
239
- events that should bust it.
240
+ events that should bust it. **This also masks config/copy deploys:** `FIELDS` is served *through*
241
+ the `["clientFields", …]` query, so a returning user rehydrates the old labels from
242
+ `localStorage["commerce"]` and sees stale copy for up to 24h after a build that contains the new
243
+ JSON. Verify any FIELDS change after `localStorage.removeItem('commerce')` or a logout, never on a
244
+ warm browser. A build-version `buster` in `persistOptions` would fix this at deploy time but
245
+ invalidates every persisted query app-wide — **open team decision, not implemented.**
240
246
  - **Tenant resolution depends on the hostname.** On `localhost` with no `*.togacommerce` host the
241
247
  first DNS label won't match a tenant, so config falls back (`getClientLoginFields` → COMPASS,
242
248
  theme → DEFAULT). Use the per-tenant dev scripts.
243
249
  - **Theme vs. fields language keys differ** — theme/field *folder* names are uppercase
244
250
  (`COMPASSCANADA`, `ENGLISH`/`FRENCH`) but the runtime `FIELDS` registry keys language as
245
- lowercase `en`/`fr`. See the client-fields doc.
251
+ `en` / **`fr-CA`** (not `fr`). Any language branch must use `startsWith("fr")` — a `=== "fr"`
252
+ comparison silently never matches. See the client-fields doc.
253
+
254
+ ## Change history
255
+ - 2026-07-28 — Gotchas: recorded that the 24h persisted React Query cache also masks `FIELDS`
256
+ config/copy deploys (verify after clearing `localStorage["commerce"]`; `persistOptions.buster` is
257
+ an open team decision), and corrected the runtime language key to `fr-CA` requiring
258
+ `startsWith("fr")` (tcox)
246
259
  </content>
@@ -6,7 +6,7 @@ project: TOGa Commerce
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-23
9
+ updated: 2026-07-28
10
10
  owners: ["apeterson", "tcox", "bala"]
11
11
  files:
12
12
  - src/pages/Account/view/MySettingsView.tsx
@@ -31,11 +31,15 @@ files:
31
31
  - src/pages/GetSupport/GetSupportPage.tsx
32
32
  - src/pages/GetSupport/viewModel/useGetSupportViewModel.ts
33
33
  - src/queries/queries.ts
34
+ - src/App.tsx
35
+ - src/pages/Filter/FilterPage.tsx
36
+ - src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json
34
37
  related:
35
38
  - 2.0/apps/toga2-commerce/architecture.md
36
39
  - 2.0/apps/toga2-commerce/features/multi-tenant-theming.md
37
40
  - 2.0/apps/toga2-commerce/features/cart-notification-emails.md
38
41
  - 2.0/apps/toga2-commerce/features/cart-page-config-architecture.md
42
+ - 2.0/apps/toga2-commerce/features/filter-search-results-page.md
39
43
  - 2.0/apps/_underscore/features/persona-name-translation.md
40
44
  ---
41
45
 
@@ -226,6 +230,26 @@ Any code that branches on the current language must compare against **`"fr-CA"`*
226
230
  `language.startsWith("fr")`, **never `language === "fr"`** (that comparison silently fails and was the
227
231
  root cause of the Get Support image bug — see gotchas).
228
232
 
233
+ ### FIELDS ride the persisted React Query cache — config fixes can appear not to deploy
234
+
235
+ `FIELDS` is statically bundled, but it is **served through a React Query query**
236
+ (`["clientFields", clientName, language, role]`). `src/App.tsx` sets the QueryClient default
237
+ `staleTime` to **24h** and persists the entire cache to `localStorage` under the key **`"commerce"`**
238
+ via `createSyncStoragePersister` + `PersistQueryClientProvider`. Consequence: a **returning** user
239
+ rehydrates the *old* fields object from localStorage and keeps seeing **stale labels for up to 24
240
+ hours after a deploy**, even though the new JSON is in the shipped bundle.
241
+
242
+ This is the single most common false negative when verifying a FIELDS/copy change — QA reports "your
243
+ fix didn't ship" on a browser that simply replayed the persisted cache.
244
+
245
+ - **Clear it manually:** `localStorage.removeItem('commerce')` then refresh.
246
+ - **Logging out also clears it** — both `src/contexts/AuthContext.tsx` (logout path) and
247
+ `src/api/axiosInstance.ts` call `localStorage.removeItem("commerce")`.
248
+ - **Proper deploy-time fix — not implemented, team decision pending:** pass a build-version `buster`
249
+ string in `persistOptions` in `App.tsx`. It is one line, but it invalidates **every** persisted query
250
+ app-wide on each deploy (losing all warm cache, not just fields), so it needs a deliberate call
251
+ rather than a drive-by change.
252
+
229
253
  ## What's inside a FIELDS JSON
230
254
 
231
255
  Shape varies by page, but common forms:
@@ -322,6 +346,21 @@ on switch.
322
346
  untranslated on purpose (the dropdown *contents* are translated). It is not a localization gap.
323
347
  - **No fallback** in `FIELDS[client][language][role]` — a missing tenant/role/language combo returns
324
348
  `null` and the page renders empty. Keep all role variants in sync.
349
+ - **No fallback for an individual *key* either — and that failure is silent.** The "no fallback" rule
350
+ applies one level deeper than the section lookup: a key missing from an otherwise-present page JSON
351
+ just yields `undefined`, which the consuming component happily interpolates. Real symptoms shipped
352
+ 2026-07-28: `renderBadge(undefined)` rendered a structurally-present but **empty, uncolored,
353
+ icon-less** badge, and a card template `` `${count} ${label}s` `` rendered **"3 undefineds"**. Nothing
354
+ throws, nothing logs, TypeScript is clean (the lookups are optional-chained `any`). When adding a
355
+ card/label key, add it to **every** tenant × language × role file for that page — 12 for Compass
356
+ (COMPASS/ENGLISH × 4 roles + COMPASSCANADA/{ENGLISH,FRENCH} × 4), 3 for QUAD. See
357
+ [filter-search-results-page](filter-search-results-page.md).
358
+ - **Key names are per page, not global — the same UI element can have different key names in two
359
+ pages' JSON.** The kit badge is `bundleBadge` in `HOMEPAGEFIELDS` but `kitBadge` in
360
+ `FILTERPAGEFIELDS`. Never assume a label added for one page covers another; grep the consuming
361
+ component for the exact `fields?.<key>` it reads.
362
+ - **A stale persisted cache masks FIELDS deploys** — see the persisted-cache section above; verify
363
+ copy changes after `localStorage.removeItem('commerce')` or a logout, never on a warm browser.
325
364
  - Role is derived from string `"1"` flags on the user (`user?._isAdmin === "1"`), not booleans.
326
365
  - `getClientLoginFields` defaults to **COMPASS** for an unknown host, which can mask a
327
366
  misconfigured tenant in local dev.
@@ -339,6 +378,14 @@ on switch.
339
378
  slip. Do not "reconcile" the two lists.
340
379
 
341
380
  ## Change history
381
+ - 2026-07-28 — Documented two failure modes found while fixing the header-search kit card: (1) there is
382
+ **no fallback for an individual key** inside a present page JSON — a missing key silently yields
383
+ `undefined` (empty uncolored badge, `"3 undefineds"`), and key names differ per page
384
+ (`bundleBadge` in HOMEPAGEFIELDS vs `kitBadge` in FILTERPAGEFIELDS); (2) FIELDS are served *through*
385
+ the `["clientFields", …]` React Query, whose cache is persisted to `localStorage["commerce"]` with a
386
+ 24h `staleTime`, so returning users see **stale labels for up to 24h after a deploy** — clear with
387
+ `localStorage.removeItem('commerce')` or a logout. Recorded the pending team decision on adding a
388
+ build-version `buster` to `persistOptions` in `App.tsx`. (tcox)
342
389
  - 2026-07-23 — **Correction:** clarified that `LANGUAGE_SENSITIVE_QUERY_KEYS` alone does **not**
343
390
  refresh the persona name on language switch — `user._personaOptions` lives in the persisted Zustand
344
391
  `user` store (populated once at login), not a live React Query, so nothing in the allow-list owns it.
@@ -0,0 +1,150 @@
1
+ ---
2
+ title: Filter / Search-Results Page & the Two Search Entry Points
3
+ framework: "2.0"
4
+ repo: toga2-commerce
5
+ project: TOGa Commerce
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-07-28
10
+ owners: ["tcox"]
11
+ files:
12
+ - src/pages/Filter/FilterPage.tsx
13
+ - src/pages/Filter/viewModel/useFilterViewModel.ts
14
+ - src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json
15
+ - src/pages/Filter/viewModel/FIELDS/COMPASSCANADA/FRENCH/USER/FILTERPAGEFIELDS.json
16
+ - src/pages/Filter/viewModel/FIELDS/QUAD/ENGLISH/BUYER/FILTERPAGEFIELDS.json
17
+ - src/components/Header/Header.tsx
18
+ - src/pages/Home/view/components/BundlesSection.tsx
19
+ - src/pages/Home/viewModel/FIELDS/COMPASS/ENGLISH/USER/HOMEPAGEFIELDS.json
20
+ - src/components/Cards/BundleViewCard.tsx
21
+ - src/utils/renderBadge.tsx
22
+ - src/hooks/useAssignClientFields.ts
23
+ related:
24
+ - 2.0/apps/toga2-commerce/architecture.md
25
+ - 2.0/apps/toga2-commerce/features/client-fields.md
26
+ - 2.0/apps/toga2-commerce/features/multi-tenant-theming.md
27
+ ---
28
+
29
+ ## Summary
30
+
31
+ The storefront has **two distinct search entry points that render the same card component through
32
+ completely different code paths and different FIELDS files**. That split is the single most important
33
+ fact about this page: a search-results defect almost always reproduces on **one** path only, and the
34
+ first diagnostic question is *"which search box did you use?"*
35
+
36
+ | Entry point | Component | Route | FIELDS section read |
37
+ |---|---|---|---|
38
+ | **Home page lower search bar** | `src/pages/Home/view/components/BundlesSection.tsx` | stays on `/home`, filters **in place** | `HOMEPAGEFIELDS` |
39
+ | **Header search bar** (global, every authenticated page) | `src/components/Header/Header.tsx` → `src/pages/Filter/FilterPage.tsx` | **navigates** to `/filter?search=<text>&category=all` | `FILTERPAGEFIELDS` |
40
+
41
+ Both then render the shared **`src/components/Cards/BundleViewCard.tsx`**. Because the two callers
42
+ populate that shared card's props from two different JSON files, the card can render correctly from
43
+ one entry point and be broken from the other — which is exactly the 2026-07-28 "3 undefineds" kit-card
44
+ bug (see Change history).
45
+
46
+ ## How it works
47
+
48
+ ### Header search → `/filter`
49
+
50
+ `Header.tsx` navigates to `` `/filter?search=${encodeURIComponent(inputValue)}` + "&category=all" ``.
51
+ `FilterPage.tsx` reads the query string, and `useFilterViewModel.ts` runs the catalog query and calls
52
+ `useAssignClientFields(fieldKey, language, user)` to resolve `FILTERPAGEFIELDS` for the current
53
+ tenant × language × role (see [client-fields](client-fields.md) for the resolver). `FilterPage`
54
+ `renderBundles` maps each result to a `BundleViewCard`.
55
+
56
+ ### Home search → in-place filter
57
+
58
+ The Home page's own lower search bar never leaves `/home`. `BundlesSection.tsx` reads
59
+ `HOMEPAGEFIELDS` (already loaded by the Home ViewModel) and renders the same `BundleViewCard`.
60
+
61
+ ### The shared card contract (`BundleViewCard`)
62
+
63
+ The card is a dumb presentational component — every label is a prop. Two props matter for kit cards:
64
+
65
+ - `includedItems` — the count.
66
+ - `includedItemsText` — the **singular** noun, e.g. `"included item"`.
67
+
68
+ The card composes them itself, in **two places** (mobile and desktop blocks, ~lines 112 and 191):
69
+
70
+ ```tsx
71
+ text={`${includedItems} ${includedItemsText}${includedItems == 1 ? "" : "s"}`}
72
+ ```
73
+
74
+ So a caller that forgets `includedItemsText` renders **"3 undefineds"** — the literal string
75
+ `undefined` plus the appended `s`. There is no default and no runtime guard.
76
+
77
+ ### Badges are keyed off the badge *text*
78
+
79
+ `src/utils/renderBadge.tsx` takes the resolved label string and derives **everything** from it:
80
+
81
+ - `badgeTextMapping` / `badgeTextMappingFrench` — display text (French is looked up by the **English**
82
+ key, e.g. `Kit → "Trousse"`), falling back to the raw string.
83
+ - `getBadgeClasses(badgeText)` — a `switch` on the text for background/foreground colors (it accepts
84
+ both English and French spellings: `"Kit"`, `"Trousse"`, `"Ensemble"`).
85
+ - `getBadgeIcon(badgeText)` — a `switch` on the text for the FontAwesome icon; only `"Kit"` and
86
+ `"VIP"` match.
87
+
88
+ `renderBadge(undefined, "")` therefore renders a **structurally present but empty and uncolored
89
+ badge**: no text, no icon, and the `default:` class branch (no color). Nothing throws and nothing
90
+ logs — the bug looks like a CSS problem and is actually a missing config key.
91
+
92
+ ### The badge key is named differently on each page
93
+
94
+ Same visual badge, two different FIELDS key names — do not assume one:
95
+
96
+ - Home / `HOMEPAGEFIELDS` → **`bundleBadge`** (`fields?.bundleBadge?.label`)
97
+ - Filter / `FILTERPAGEFIELDS` → **`kitBadge`** (`fields?.kitBadge?.label`)
98
+
99
+ ## Gotchas
100
+
101
+ - **A missing *key* inside a present FIELDS file fails silently.** The known "no fallback" rule for
102
+ `FIELDS[tenant][language][role]` also applies one level deeper: an individual key absent from a
103
+ page's JSON yields `undefined`, which renders as an empty badge or `"N undefineds"`. Any card-label
104
+ key must exist in **every** tenant × language × role file for that page — 12 files for Compass
105
+ (COMPASS/ENGLISH × 4 roles, COMPASSCANADA/ENGLISH × 4, COMPASSCANADA/FRENCH × 4) and 3 for QUAD
106
+ (QUAD/ENGLISH/{GLOBALADMIN,BUYER,ITSHOPPER}).
107
+ - **Adding a label to one page's FIELDS does not cover the other page.** `HOMEPAGEFIELDS` and
108
+ `FILTERPAGEFIELDS` are independent files with independent key sets; a label added for the Home card
109
+ must be added again (possibly under a different key name) for the Filter card.
110
+ - **French pluralization is broken by design in the shared card.** `BundleViewCard` blindly appends
111
+ `"s"`, so `"articles inclus"` renders as **"3 articles incluss"** in fr-CA. This pre-dates the
112
+ Filter-page fix (the Compass Canada *home* page has always shown it) and the Filter page now
113
+ mirrors it for parity. The real fix is pre-pluralized per-language labels (or a singular/plural pair)
114
+ in the card contract — **follow-up ticket, not yet filed.**
115
+ - **French kit badge loses its icon.** The FRENCH JSONs store the label `"Trousse"`, so
116
+ `getBadgeClasses` still colors it but `getBadgeIcon` (which only matches `"Kit"`) returns `null` — no
117
+ BoxesStacked icon in French. Storing the English `"Kit"` in the French file would get both the icon
118
+ *and* auto-translated text via `badgeTextMappingFrench`. Current behavior matches the Home page, so
119
+ it was left alone; treat it as a known cosmetic gap, not a regression.
120
+ - **Persisted React Query cache hides FIELDS fixes for up to 24h after deploy.** FIELDS are served
121
+ *through* a query (`["clientFields", tenant, language, role]`) and the whole cache is persisted to
122
+ `localStorage["commerce"]` with a 24h `staleTime`, so a returning user rehydrates the **old** labels
123
+ even though the new JSON is in the bundle. When QA says "your config fix didn't ship", have them run
124
+ `localStorage.removeItem('commerce')` and refresh (logging out also clears it). See
125
+ [client-fields](client-fields.md).
126
+
127
+ ## Known open items
128
+
129
+ - **QUAD has the same missing-key bug, unfixed.** All three
130
+ `src/pages/Filter/viewModel/FIELDS/QUAD/ENGLISH/{GLOBALADMIN,BUYER,ITSHOPPER}/FILTERPAGEFIELDS.json`
131
+ still lack `kitBadge` and `includedItemsText`, so a header-search kit card on QUAD renders the empty
132
+ badge + `"3 undefineds"`. The fix is the identical two-entry JSON addition; it was left out because
133
+ the 2026-07-28 session was scoped to Compass.
134
+ - French pluralization (`"3 articles incluss"`) — needs a card-contract change, see gotchas.
135
+
136
+ ## Change history
137
+ - 2026-07-28 — Fixed header-search kit cards for Compass USA + Compass Canada. Two defects in the
138
+ Filter path: (1) `FilterPage.tsx` rendered `BundleViewCard` without passing `includedItemsText`
139
+ → `"3 undefineds"` (added `const includedItemsText = fields?.includedItemsText?.label;` beside the
140
+ existing `kitBadgeLabel` read and passed it through in `renderBundles`); (2) the `kitBadge` key was
141
+ missing from every `FILTERPAGEFIELDS.json` except COMPASSCANADA/ENGLISH/ADMIN, so
142
+ `renderBadge(undefined)` produced an empty uncolored badge. Added `kitBadge` ("Kit"/"Trousse") and
143
+ `includedItemsText` ("included item"/"articles inclus") to all **12** Compass FILTERPAGEFIELDS
144
+ files, copied from the working HOMEPAGEFIELDS labels so both search paths render identically. The
145
+ code change is a no-op for tenants whose config lacks the keys, so QUAD is unchanged (still buggy —
146
+ see Known open items). `npx tsc --noEmit` clean. (tcox)
147
+ - 2026-07-28 — Initial doc: recorded the two search entry points (Home in-place vs. header →
148
+ `/filter`), the shared `BundleViewCard` label contract, text-keyed badge rendering in
149
+ `renderBadge.tsx`, the `bundleBadge` vs `kitBadge` key-name split, and the silent-`undefined`
150
+ failure mode for a missing per-key FIELDS entry. (tcox)
@@ -28,7 +28,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
28
28
  - **talos** (TOGa IQ) — 7 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
29
29
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
30
30
  - **ai-bdr** (AI-BDR) — 8 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
31
- - **toga2-commerce** (TOGa Commerce) — 10 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
31
+ - **toga2-commerce** (TOGa Commerce) — 11 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
32
32
  - **toga25-supply** (TOGa 2.5 Supply) — 11 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
33
33
  - **toga-blox** (TOGa Blox) — 8 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
34
34
  - **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.466",
3
+ "version": "1.0.467",
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",