feat(appkit-ui): generic ResourceStatusProvider + indicator (with analytics adapter)#416
Merged
Merged
Conversation
1097ca3 to
6b5f703
Compare
fjakobs
approved these changes
Jun 5, 2026
…ytics adapter
Adds a small, plugin-agnostic readiness aggregator and a drop-in indicator
so that any plugin (SQL warehouses today, Lakebase / model-serving / etc.
tomorrow) can publish "I'm warming up" status into a shared provider, and
a single global affordance reflects the worst pending state.
Public API in @databricks/appkit-ui/react:
- ResourceStatusProvider — aggregator context (mount once near root).
- useResourceStatus(filter?) — read the aggregate; filter by kind.
- useResourceStatusPublisher(id, label, { kindHint? }) — plugins push
ResourceStatus snapshots; severity ("pending" | "warning" | "error")
drives cross-kind ordering.
- ResourceStatusIndicator — floating card (default bottom-right) with
built-in copy for known kinds, kind-specific overrides via renderers,
and a render prop for full UI replacement.
useAnalyticsQuery now exposes warehouseStatus and auto-publishes into
the provider so every chart's warmup is reflected globally without
per-chart wiring. Existing analytics-specific helpers
(AnalyticsWarehouseStatusProvider, AnalyticsWarehouseBanner,
useAnalyticsWarehouseStatus) are kept as thin adapters over the generic
API for back-compat.
UX: ChartWrapper detects the warmup state and renders a quiet
ResourceWaitingPlaceholder ("Waiting for warehouse…") instead of a
shimmering skeleton — a 30s–2min cold start no longer pretends results
are "any second now". The skeleton returns once the warehouse is
RUNNING and the actual SQL fetch begins.
The provider + indicator are wired into:
- template/client/src/main.tsx — every new app gets it by default.
- apps/dev-playground/client/src/routes/__root.tsx — playground.
Backend SSE wiring for the warehouseStatus events ships separately
(see #415); this PR works dormant until that lands — warehouseStatus
simply stays null.
The store is event-driven; with the backend now de-duplicating equal successive states, the indicator can go tens of seconds without a new notification during a cold start. Without an internal tick, the displayed `elapsedMs` froze at the value derived on the first publish. The indicator now drives its own setInterval at 1Hz while a wait is active and recomputes the live elapsed locally from `worst.startedAt`, shadowing the stale snapshot field. The store stays pure and event-driven; only the visible stopwatch consumer ticks. Pinned by a new test that publishes a STARTING status, advances fake timers by 3.5s without any further publishes, and asserts the rendered description advances from "0s" to "3s".
The card less often overlaps with chart legends, footers, or floating action buttons (which conventionally sit bottom-right) when anchored to the top-right corner. Consumers can still override via the `position` prop. Docs updated to match.
92c5f5e to
f52ca39
Compare
The indicator no longer renders its own floating card. Instead it mounts a sonner `<Toaster />` and drives a single sticky toast (`toast.loading` for cold starts, `toast.error` for unrecoverable states) that mirrors the worst pending status. Sonner handles animation, theming, stacking, and portal placement, so the indicator becomes a thin effects-only bridge. API changes: - `<ResourceStatusIndicator />` now embeds `<Toaster />` and forwards its props (`position`, `theme`, `richColors`, `expand`, …). `position` defaults to `top-right`. - New `useResourceStatusToaster()` hook for apps that already render their own `<Toaster />` for unrelated app toasts and want resource-status toasts to share it. - `className` was renamed to `toastClassName` (the toast itself) to free `className` for the Toaster wrapper via the inherited `ToasterProps`. Drive-by simplification of `ResourceStatusStore`: `recompute()` always ran after a `version` bump, so `snapshotsEqual()` (and its `recordsEqual()` helper) were unreachable dead code. Folded into a single `bump()` method and unified `aggregate()`'s return type. The starter template and dev-playground now mount only `<ResourceStatusIndicator />` instead of `<Toaster />` + indicator.
Cut multi-paragraph rationale chains down to single-line invariants and gotchas, drop redundant JSDoc that restated field names, and fix the now- incorrect 'renders nothing' line on ResourceStatusIndicator (it always renders the embedded Toaster). Net -167 lines across 10 files, no behavioral change.
ResourceStatusStore: replace arrow class fields with regular methods; wrap subscribe/getSnapshot in useMemo so useSyncExternalStore keeps a stable bound reference. Extract aggregateForKind() from the kind-filter path in useResourceStatus. useAnalyticsQuery: extract handleAnalyticsSseMessage() and a warehouse status type guard from the inline onMessage callback; share the generic load-error string.
Synthesize pending from kindHint-only slots, gate chart placeholders on warehouse state, include severity in toast ids, and export the toaster hook. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Drop synthetic kindHint pending so RUNNING clears the indicator, and only show chart placeholders after SSE reports a non-RUNNING warehouse state. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Open
5 tasks
atilafassina
approved these changes
Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a plugin-agnostic readiness aggregator and a drop-in Sonner toast indicator so any plugin (SQL warehouses today; Lakebase / model-serving later) can publish "I'm warming up" status into a shared provider, and a single global affordance surfaces the worst pending state.
Also fixes the misleading shimmering chart skeleton during a 30s–2min warehouse cold start — charts and the global indicator only react once SSE reports a real warehouse state.
What's new in
@databricks/appkit-ui/reactResourceStatusProvider— aggregator context (mount once near root).useResourceStatus(filter?)— read the aggregate; optional{ kind }filter.useResourceStatusPublisher(id, label, { kindHint? })— plugins publishResourceStatussnapshots. Severity ("pending" | "warning" | "error") drives cross-kind ordering.kindHintregisters slots for kind-scopedactiveCountbut does not drive the indicator until a real status arrives.ResourceStatusIndicator— mounts its own<Toaster />and drives a sticky Sonner toast (toast.loading/toast.error, default top-right). Per-kind copy viarenderers={...}, fullrenderescape hatch.useResourceStatusToaster— hook-only variant when the app already mounts its own<Toaster />. Exported fromhooksandreact.Hook surface change
useAnalyticsQuerynow returnswarehouseStatusand auto-publishes to the provider via an internaluseAnalyticsWarehousePublisheradapter — every chart's warmup shows up globally with zero per-chart wiring.UX behavior
ChartWrapper)RUNNINGSTARTING/STOPPED/ etc.ResourceWaitingPlaceholderRUNNING(SQL still fetching)ChartWrapper(used byBarChart,LineChart, etc.) only shows the quiet placeholder after SSE explicitly reports a non-RUNNINGwarehouse state. Error states (DELETED/DELETING) skip the placeholder.Wiring
template/client/src/main.tsx— every newly-scaffolded app ships with the provider + indicator.apps/dev-playground/client/src/main.tsx— same wiring in the playground.Relationship to #415
#415 (merged) ships the backend SSE that drives
warehouseStatus. This PR consumes those events. Without SSE,warehouseStatusstaysnulland both the toast and chart placeholders stay silent (shimmer only).Test plan
ResourceStatusProvider/useResourceStatus/useResourceStatusPublisher/ResourceStatusIndicator(cross-kind aggregation, severity ranking, kind filter,kindHintslot registration, loading→error toast morph, custom render, 1Hz elapsed tick).RUNNING.useAnalyticsQuerytests covering thewarehouseStatusfield onSTARTING/RUNNING/resultevent ordering.pnpm vitest runpassing.appkit-uianddev-playground/client.RUNNING→ no toast, no "waiting for warehouse" on charts.RUNNING).