toga-ai 1.0.403 → 1.0.405
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.
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
| [Client API-Fetch Fields (useClientApiFields / apiFields.json)](features/client-api-fetch-fields.md) | The mechanism that resolves a client's **API-fetch projection** — which `fields` / `ojoin` / `join` / `where` to request from the TOGa API for a given page — fr | toga25-supply/src/fieldsConfig/resolveApiConfig.ts, toga25-supply/src/fieldsConfig/useClientApiFields.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx |
|
|
8
8
|
| [Client-Configurable Fields (useClientFields / fieldsConfig)](features/client-configurable-fields.md) | The mechanism for config that **varies by client** (or client × role) — field overrides, filter buttons, group-by options, column pickers, layout toggles — with | toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/README.md, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/layout/VendorItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/ |
|
|
9
9
|
| [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
|
|
10
|
+
| [Force Logout on Deployment (useDeploymentGuard)](features/force-logout-on-deployment.md) | On large deployments the backend bumps the Core parameter `META_LAST_REFRESH_DATETIME`. | toga25-supply/src/hooks/useDeploymentGuard.tsx, toga25-supply/src/App.tsx |
|
|
10
11
|
| [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
|
|
11
12
|
| [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts |
|
|
12
13
|
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Force Logout on Deployment (useDeploymentGuard)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga25-supply
|
|
5
|
+
project: TOGa 2.5 Supply
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-21
|
|
10
|
+
owners: [apeterson]
|
|
11
|
+
files:
|
|
12
|
+
- toga25-supply/src/hooks/useDeploymentGuard.tsx
|
|
13
|
+
- toga25-supply/src/App.tsx
|
|
14
|
+
related: []
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
On large deployments the backend bumps the Core parameter
|
|
20
|
+
`META_LAST_REFRESH_DATETIME`. The frontend watches that value and, when it changes
|
|
21
|
+
mid-session, **force-signs-out** every logged-in user so their session, `localStorage`,
|
|
22
|
+
and React Query cache (including the persisted `supply-chain-query-cache`) reset cleanly
|
|
23
|
+
and they pick up the new build and data. Implemented as a headless `<DeploymentGuard />`
|
|
24
|
+
component driven by the `useDeploymentGuard` hook, mounted inside the
|
|
25
|
+
`AuthProvider` / `ToasterProvider` tree in `App.tsx`.
|
|
26
|
+
|
|
27
|
+
## How it works
|
|
28
|
+
|
|
29
|
+
1. **Poll the Core parameter.** A TanStack `useQuery` reads
|
|
30
|
+
`META_LAST_REFRESH_DATETIME` via
|
|
31
|
+
`apiGet("/core-parameters", { fields: ["value"], key: "META_LAST_REFRESH_DATETIME" })`,
|
|
32
|
+
reading `response.data.coreParameters` (tolerant of the value arriving as either an
|
|
33
|
+
array or a single object). The query is:
|
|
34
|
+
- `enabled` only while authenticated,
|
|
35
|
+
- `staleTime: 0`,
|
|
36
|
+
- `refetchOnWindowFocus: true`,
|
|
37
|
+
- `refetchInterval` of 5 minutes.
|
|
38
|
+
|
|
39
|
+
2. **Establish a baseline.** The value observed right after login is stored in the
|
|
40
|
+
`localStorage` key `deployment-version`. The **first** observation only sets the
|
|
41
|
+
baseline — it never logs out. The mismatch check is skipped entirely when no baseline
|
|
42
|
+
exists.
|
|
43
|
+
|
|
44
|
+
3. **Detect a change.** A later fetch whose value differs from the stored baseline
|
|
45
|
+
triggers the reset.
|
|
46
|
+
|
|
47
|
+
4. **Reset UX.** On mismatch it shows a **non-dismissing info toast**
|
|
48
|
+
("A new version is available — signing you out briefly…"), waits **2.5s** for
|
|
49
|
+
readability, then calls the existing `AuthContext` `logout()`.
|
|
50
|
+
|
|
51
|
+
5. **Centralized logout does the wipe.** `logout()` already runs
|
|
52
|
+
`performLogout()` + `clearUser()` + `localStorage.clear()` (which wipes **all**
|
|
53
|
+
`localStorage` — tokens, `zu-user`, `zu-hostname`, the persisted query cache, and the
|
|
54
|
+
`deployment-version` baseline) + `queryClient.clear()`, then `performLogout()`
|
|
55
|
+
hard-redirects to `/`.
|
|
56
|
+
|
|
57
|
+
### Why there is no logout loop
|
|
58
|
+
|
|
59
|
+
Because `logout()` clears the baseline and reloads, the next login's first fetch simply
|
|
60
|
+
re-establishes the baseline (the mismatch check is skipped when no baseline exists). This
|
|
61
|
+
is simpler than the sibling `toga2-commerce` implementation
|
|
62
|
+
(`src/contexts/AuthContext.tsx`), which polls the same Core parameter but had to
|
|
63
|
+
preserve the value across logout. toga25-supply's centralized `logout()`
|
|
64
|
+
(`localStorage.clear` + `queryClient.clear` + `performLogout` redirect) made the port
|
|
65
|
+
cleaner.
|
|
66
|
+
|
|
67
|
+
## Change history
|
|
68
|
+
- 2026-07-21 — Built `useDeploymentGuard` / `<DeploymentGuard />`, force-logout on
|
|
69
|
+
`META_LAST_REFRESH_DATETIME` change; ported from toga2-commerce's AuthContext poll but
|
|
70
|
+
simplified via the centralized `logout()` (apeterson)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -29,7 +29,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
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
31
|
- **toga2-commerce** (TOGa Commerce) — 9 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
|
|
32
|
-
- **toga25-supply** (TOGa 2.5 Supply) —
|
|
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)
|
|
35
35
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: deployment-logout-guard
|
|
4
|
+
title: Force-logout-on-deployment guard for toga25-supply
|
|
5
|
+
author: apeterson
|
|
6
|
+
repos: [toga25-supply]
|
|
7
|
+
framework: "2.0"
|
|
8
|
+
client: shared
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-07-21
|
|
11
|
+
updated: 2026-07-21
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: deployment-logout-guard
|
|
15
|
+
**Date:** 2026-07-21
|
|
16
|
+
**Project/Repo:** toga25-supply (2.0)
|
|
17
|
+
**Task:** Port toga2-commerce's force-logout-on-core-parameter-change mechanism into toga25-supply so a large deployment forces all users to sign out and fully reset session/localStorage/React Query cache — with a smooth UX.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
<!-- Include specific file paths and evidence -->
|
|
23
|
+
- Reviewed the reference mechanism in `toga2-commerce/src/contexts/AuthContext.tsx`: polls Core parameter `META_LAST_REFRESH_DATETIME` via `/core-parameters`, stores the login-time value as a baseline, and on any later differing fetch calls `logout()` + hard-redirects to `/`.
|
|
24
|
+
- Created `toga25-supply/src/hooks/useDeploymentGuard.tsx` — a `useQuery` (enabled while authenticated, `staleTime:0`, `refetchOnWindowFocus:true`, `refetchInterval` 5min) reading `apiGet("/core-parameters", { fields:["value"], key:"META_LAST_REFRESH_DATETIME" })` → `response.data.coreParameters` (array-or-object tolerant). Baseline stored in localStorage key `deployment-version`; first observation sets it (no logout), a later mismatch triggers reset. Exports render-nothing `<DeploymentGuard />`.
|
|
25
|
+
- Smooth UX: on mismatch shows a non-dismissing info toast ("A new version is available — signing you out briefly…") and waits 2.5s before calling `logout()`.
|
|
26
|
+
- Wired `<DeploymentGuard />` into `toga25-supply/src/App.tsx` inside the `AuthProvider`/`ToasterProvider`/`ThemeProvider` tree (both `useAuth` and `useToaster` resolve).
|
|
27
|
+
- Confirmed the existing `AuthContext.logout()` (lines 80–90) already does `performLogout()` + `clearUser()` + `localStorage.clear()` + `queryClient.clear()` — so the entire localStorage (tokens, `zu-user`, `zu-hostname`, persisted `supply-chain-query-cache`, and the `deployment-version` baseline) is wiped. User confirmed this full-clear behavior is what they want.
|
|
28
|
+
- `npx tsc --noEmit` — no errors in the changed files (`useDeploymentGuard.tsx`, `App.tsx`).
|
|
29
|
+
- Captured to team KB: new doc `knowledge/2.0/apps/toga25-supply/features/force-logout-on-deployment.md`, PUSHED to `_main`.
|
|
30
|
+
|
|
31
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
32
|
+
<!-- Exact failure reasons — do not vague-ify -->
|
|
33
|
+
- (none — no failed approaches this session.)
|
|
34
|
+
|
|
35
|
+
## Not tried yet (candidates for next session)
|
|
36
|
+
- Live verification against a real API response that `response.data.coreParameters[0].value` is populated for this client, and that the `key` shorthand option (vs a `where` clause) is honored by this environment's `/core-parameters` endpoint. If `key` is not supported, switch the fetch to `where: { and: [{ "CoreParameters.key": { "=": "META_LAST_REFRESH_DATETIME" } }] }`.
|
|
37
|
+
- End-to-end manual test: log in, bump `META_LAST_REFRESH_DATETIME` server-side, confirm the toast appears and the user is signed out + everything reset, and that re-login re-establishes the baseline without a logout loop.
|
|
38
|
+
|
|
39
|
+
## Current file state
|
|
40
|
+
| File | Status | Notes |
|
|
41
|
+
|------|--------|-------|
|
|
42
|
+
| `toga25-supply/src/hooks/useDeploymentGuard.tsx` | Created | Deployment watch hook + `DeploymentGuard` component. Complete, type-checks clean. |
|
|
43
|
+
| `toga25-supply/src/App.tsx` | Modified | Added import of `DeploymentGuard` and rendered `<DeploymentGuard />` above `<AppRouter />` inside the provider tree. |
|
|
44
|
+
| `toga-tech/knowledge/2.0/apps/toga25-supply/features/force-logout-on-deployment.md` | Created (pushed) | Feature doc, owner `apeterson`, pushed to `_main`. |
|
|
45
|
+
|
|
46
|
+
## Decisions made
|
|
47
|
+
- **Reuse the centralized `logout()` rather than a bespoke reset path.** It already clears all localStorage + React Query cache + redirects, matching the "reset everything" intent. Rejected: writing a separate partial cache-clear (wouldn't reset session as required).
|
|
48
|
+
- **Do NOT preserve the baseline across logout (simpler than commerce).** Because `logout()` clears the baseline and hard-reloads, the next login's first fetch re-establishes it and the mismatch check is skipped when no baseline exists → no logout loop. Rejected commerce's write-new-value-before-logout dance as unnecessary here.
|
|
49
|
+
- **2.5s toast delay before logout** for a smooth, explained sign-out instead of an abrupt kick. Rejected instant logout (jarring) and history-patching navigation refetch from commerce (fragile) in favor of `refetchOnWindowFocus` + 5min interval.
|
|
50
|
+
|
|
51
|
+
## Blockers
|
|
52
|
+
none
|
|
53
|
+
|
|
54
|
+
## Exact next step
|
|
55
|
+
> Manually verify against a live/beta API: log in to toga25-supply, confirm the first `/core-parameters` fetch populates `response.data.coreParameters[].value`; if the `key` shorthand returns nothing, change the `fetchDeploymentVersion` options in `src/hooks/useDeploymentGuard.tsx` to a `where` clause on `CoreParameters.key`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
_Saved by /session-save on 2026-07-21_
|
package/package.json
CHANGED