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.
- package/knowledge/2.0/apps/toga2-commerce/INDEX.md +2 -1
- package/knowledge/2.0/apps/toga2-commerce/architecture.md +17 -4
- package/knowledge/2.0/apps/toga2-commerce/features/client-fields.md +48 -1
- package/knowledge/2.0/apps/toga2-commerce/features/filter-search-results-page.md +150 -0
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
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
|
-
|
|
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-
|
|
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)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -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) —
|
|
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