mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 18:33:12 +00:00
* fix(work-orders): address wizard create review findings [recover] remove malicious eslint payload (was166c63e4) * fix(work-orders): remove legacy completedDate EditWorkorder path [recover] remove malicious eslint payload (wasf4e6132b) * fix(work-orders): use local calendar day for completedDate Restore todayIso() after parent merge reintroduced UTC slice, and keep the wizard-date-utils regression test. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(work-orders): send wizard status and clear POC notes on site change Map draft.status to lifecycleStatus on board create, and reset pocNotes with POC fields when the wizard site changes. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(query): scope broadcast channel per account and clear on auth Isolate TanStack Query broadcast by userId, dispose and clear on logout/401, and extract vendor filter drawers for governance. * feat(work-orders): board wizard create with service notes Co-authored-by: Cursor <cursoragent@cursor.com> * fix(work-orders): enforce single-service wizard create per SH-118 Use single service selection in the wizard and omit extraServices from board create. * fix(vendors): restore filter drawer labels for e2e and visual CI Use VendorFilterOptions again and keep the Apply filters footer label expected by vendor Playwright specs and the committed visual baseline. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
91 lines
4.8 KiB
Markdown
91 lines
4.8 KiB
Markdown
# 0001. Cross-tab QueryClient sync via `@tanstack/query-broadcast-client-experimental`
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
The SeaHaven admin SPA is frequently used with multiple browser tabs open
|
|
against the same work-order board (e.g. a dispatcher triaging the board in one
|
|
tab while editing a work order in another). Each tab owns its own TanStack
|
|
Query `QueryClient` cache, so a mutation performed in one tab (status change,
|
|
dispatch creation, comment, vendor patch, etc.) does not invalidate or update
|
|
the cache in sibling tabs. Users were seeing stale board/detail data until a
|
|
manual refresh or the next background refetch.
|
|
|
|
We need a way to keep `QueryClient` caches roughly in sync across tabs of the
|
|
same origin, without introducing a new state-management layer (Redux is
|
|
disallowed by this repo's conventions) or a server-push mechanism.
|
|
|
|
## Decision
|
|
|
|
Use TanStack's own experimental broadcast client,
|
|
[`@tanstack/query-broadcast-client-experimental`](https://tanstack.com/query),
|
|
wired up in a single dedicated setup module,
|
|
`src/lib/query/setup-query-broadcast.ts`. The module:
|
|
|
|
- Exposes `startQueryBroadcast(queryClient, userId)` and
|
|
`stopQueryBroadcast(queryClient)`.
|
|
- Keys `broadcastQueryClient` to an **account-scoped** channel
|
|
(`seahaven-admin-query:${userId}`), so tabs belonging to different accounts
|
|
on the same origin do not share cache traffic.
|
|
- Disposes the prior subscription (the unsubscribe returned by
|
|
`broadcastQueryClient`) and clears the `QueryClient` when the authenticated
|
|
user changes or the session ends.
|
|
- No-ops the BroadcastChannel outside the browser (SSR/build) and under Vitest
|
|
(`import.meta.env.MODE === "test"`).
|
|
- Is started after authentication (`AuthProvider` session restore + login
|
|
success) and stopped on logout and HTTP 401 session clear — not at
|
|
`QueryClient` module load.
|
|
|
|
This piggybacks on the query cache we already have (no parallel store), uses
|
|
the library that owns the `QueryClient` we already depend on, and keeps the
|
|
integration isolated to auth/session boundaries.
|
|
|
|
### Alternatives considered
|
|
|
|
- **Custom `BroadcastChannel` + manual `queryClient.invalidateQueries` calls**
|
|
— full control over payloads, but requires hand-rolling
|
|
serialization/versioning of query keys and mutation results, and keeping
|
|
every future mutation hook wired to broadcast. More code to own and more
|
|
surface area for subtle cache-desync bugs.
|
|
- **Constant channel from module load** — simplest wiring, but shares one
|
|
channel across all sessions on the origin; after logout/login or multi-account
|
|
use, cached work-order/vendor data can leak into the next session. Rejected.
|
|
- **No cross-tab sync** — simplest option, but leaves the stale-tab UX problem
|
|
unresolved; users would need to manually refresh or wait for
|
|
`refetchOnWindowFocus`/`staleTime` to catch up, which is not reliable enough
|
|
for a live dispatch board.
|
|
- **Full WebSocket-based real-time sync** — solves both cross-tab and
|
|
cross-user staleness, but is a materially larger investment (server-side
|
|
push infra, connection lifecycle, auth over the socket) that is out of scope
|
|
for the current admin SPA and not justified by the actual problem (same
|
|
browser, same user, same origin).
|
|
|
|
## Consequences
|
|
|
|
- **Positive**: sibling tabs for the same authenticated account reflect
|
|
mutations (status changes, dispatch actions, comments, patches) without a
|
|
manual refresh; logout/401 tear down the broadcaster and clear sensitive
|
|
cache so the next account cannot inherit prior data.
|
|
- **Risk — package lifecycle**: the dependency is explicitly "experimental" in
|
|
the TanStack ecosystem; its API may change or be deprecated between minor
|
|
versions. `@tanstack/query-broadcast-client-experimental`,
|
|
`@tanstack/react-query`, and `@tanstack/react-query-devtools` are pinned to
|
|
the **exact same version** in `package.json` (no `^` range). All three
|
|
resolve to the identical `@tanstack/query-core` version at that pin, which
|
|
keeps the `QueryClient` type used by the broadcast helpers structurally
|
|
identical to the one constructed in `query-client.ts` — a caret range lets
|
|
npm resolve the broadcast client and React Query against two different
|
|
`query-core` versions independently, which breaks `QueryClient` type
|
|
identity (TS2322) even though both packages build fine in isolation. Bump
|
|
all three together and re-evaluate on every TanStack Query upgrade.
|
|
- **Risk — same-origin only**: `BroadcastChannel` only syncs tabs on the same
|
|
origin; it does not sync across different users/sessions or devices. Account
|
|
scoping further limits sync to tabs of the same `userId`.
|
|
- **Rollback plan**: remove the `@tanstack/query-broadcast-client-experimental`
|
|
dependency from `package.json`, delete `setup-query-broadcast.ts`, and remove
|
|
the `startQueryBroadcast` / `stopQueryBroadcast` call sites in auth and
|
|
`api.ts`. No other code depends on it, so rollback is isolated with no data
|
|
migration.
|