* [870467e6] Frontend: page-scoped refresh provider, hook, and navbar button (#347) * [55376b8a] Create page-scoped refresh provider and context (#327) * [55376b8a] feat(panel): add page-scoped refresh context and provider * [55376b8a] docs(frontend): add page-refresh-provider component documentation --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [a0c02d0f] Add public usePageRefresh hook (#332) * [a0c02d0f] test(hooks): assert usePageRefresh is exported from hooks barrel * [a0c02d0f] feat(hooks): add public usePageRefresh hook with provider and tests * [a0c02d0f] fix(panel): move hook test wrappers to components and rename providers.tsx to unshadow barrel * [a0c02d0f] docs(panel): document usePageRefresh hook and PageRefreshProvider API --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Renn F <rennf93@users.noreply.github.com> * [5f28dd9b] Add navbar refresh button and remove inline dashboard refresh buttons (#336) * [5f28dd9b] Align PageRefreshProvider with active hook API and remove inline dashboard refresh buttons * [5f28dd9b] Remove unused scope-keyed PageRefreshProvider, context, and associated tests * [5f28dd9b] Address QA revision: add header refresh tests, page-scoped label, remove dead provider code and .venv symlink, revert formatting-only changes * [5f28dd9b] Remove remaining inline dashboard refresh buttons and committed .venv symlink * [5f28dd9b] docs(frontend): update page-refresh provider docs and panel README for navbar refresh button * [5f28dd9b] fix(panel): remove .venv symlink, ignore root .venv entries, and thin task-detail page data fetch into useTaskDetail hook * [5f28dd9b] Extract GitBrowser data fetching into useGitBrowser hook and add tests; verify .venv cleanup and task-detail thin hook usage * [5f28dd9b] fix(panel): remove root .venv symlink, restore .gitignore anchored rule, and revert lifecycle.json formatting noise * Delete .venv --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Renn F <rennf93@users.noreply.github.com> * [b8e1de1b] Fix navbar refresh button disabled state when registry is empty (#356) (#358) * [b8e1de1b] fix(panel): derive navbar refresh disabled state from registry, not unused prop PageRefreshProvider now computes `disabled` from whether any refresh callback is currently registered (registry size > 0) instead of a static, never-passed `disabled` prop that left the button permanently enabled. header.tsx now destructures `disabled` from usePageRefresh() and disables the button on `disabled || loading`. Updated the tests that asserted the old always-enabled-by-default behavior and added a new header test asserting the button is disabled with zero registered callbacks. * [b8e1de1b] docs(panel): document PageRefreshProvider disabled state derived from registry Updated documentation to reflect the refactored PageRefreshProvider behavior: the `disabled` state is now derived from whether any refresh callbacks are currently registered (empty registry = disabled), rather than a static `disabled` prop. Clarified in both panel/README.md and the full component guide that the navbar refresh button disables when no callbacks are registered and when a refresh cycle is in progress. Updated API documentation to remove the now-removed `disabled` prop from PageRefreshProviderProps and updated code examples and test coverage descriptions to reflect the new callback-driven semantics. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * test(panel): mock usePageRefresh in tests predating the provider Merge-skew: the page-refresh feature makes CommandCenter and the agent detail page call usePageRefresh; three tests merged from master render them without the new provider. Mock the hook module, matching the files' stub-everything style. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Renn F <rennf93@users.noreply.github.com>
4.5 KiB
Frontend hooks
This page documents the public React hooks available under panel/src/hooks.
usePageRefresh
A page-scoped refresh coordinator. Pages and panels register callbacks that refetch their data; UI chrome calls refresh() and reflects the combined loading/disabled state.
When to use it
Use usePageRefresh when several components on the same page need to refresh together from a single trigger, such as a navbar refresh button. It keeps the refresh lifecycle scoped to the current page and avoids invalidating unrelated data.
Setup
Wrap the page (or root layout) with PageRefreshProvider:
import { PageRefreshProvider } from "@/components/providers";
export default function Layout({ children }: { children: React.ReactNode }) {
return <PageRefreshProvider>{children}</PageRefreshProvider>;
}
Basic usage
import { useEffect } from "react";
import { usePageRefresh } from "@/hooks";
import { useTasks } from "@/hooks";
export function TasksPanel() {
const { refetch } = useTasks();
const { register, unregister } = usePageRefresh();
useEffect(() => {
const refresh = () => refetch();
register(refresh);
return () => unregister(refresh);
}, [register, unregister, refetch]);
return <div>{/* task list */}</div>;
}
Triggering a refresh from UI chrome
import { usePageRefresh } from "@/hooks";
export function RefreshButton() {
const { refresh, loading, disabled } = usePageRefresh();
return (
<button onClick={refresh} disabled={disabled || loading}>
{loading ? "Refreshing…" : "Refresh"}
</button>
);
}
Navbar refresh button
The canonical consumer is panel/src/components/layout/header.tsx. The refresh button is rendered between the connection-status badge and the theme toggle. Its accessible label and tooltip read "Refresh only the current page", and it is disabled with a spinning icon while the registered refresh cycle is running.
Dashboard pages no longer include their own inline "Refresh" buttons. Instead, each page registers its refetch callbacks with usePageRefresh and lets the shared header button drive the refresh. See components/page-refresh-provider.md for the full list of wired pages and the registration pattern.
API reference
PageRefreshProvider
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
required | React tree that can consume the context. |
disabled |
boolean |
false |
When true, refresh() is ignored and disabled is exposed as true. |
usePageRefresh
Returns a PageRefreshState object:
| Property | Type | Description |
|---|---|---|
disabled |
boolean |
Whether refresh actions are currently disabled. |
loading |
boolean |
Whether a refresh cycle is currently in progress. |
register |
(callback: RefreshCallback) => void |
Add a callback to invoke on the next refresh. |
unregister |
(callback: RefreshCallback) => void |
Remove a previously registered callback. |
refresh |
() => Promise<void> |
Run every registered callback and update loading. |
RefreshCallback is () => void | Promise<void>. Synchronous and asynchronous callbacks are both supported.
Behavior
usePageRefreshthrows if called outside aPageRefreshProviderso consumers fail fast instead of silently missing refreshes.- Concurrent calls to
refresh()are coalesced: a second call while one is running returns immediately and does not start another cycle. - When
disabledistrue,refresh()is a no-op and callbacks are not invoked. registerandunregisterare stable across renders and can be used asuseEffectdependencies.
Exports
usePageRefreshfrom@/hooksPageRefreshProviderfrom@/components/providers- Types:
PageRefreshState,RefreshCallback,PageRefreshProviderProps
Migration notes
panel/src/components/providers.tsxwas renamed topanel/src/components/app-providers.tsxso that@/components/providerscould be used as a barrel export forPageRefreshProvider. Update any direct import of the root providers component from@/components/providersto@/components/app-providers.- The earlier scope-keyed provider files (
panel/src/components/page-refresh-provider.tsxandpanel/src/store/page-refresh-context.ts) were deleted. The current implementation lives inpanel/src/components/providers/page-refresh-provider.tsxand is consumed throughusePageRefreshfrom@/hooks.