Skip to content

feat(ui): refresh-status widget on auto-polling pages#821

Open
arlenvasconcelos wants to merge 1 commit into
mainfrom
refresh-pr
Open

feat(ui): refresh-status widget on auto-polling pages#821
arlenvasconcelos wants to merge 1 commit into
mainfrom
refresh-pr

Conversation

@arlenvasconcelos
Copy link
Copy Markdown
Contributor

@arlenvasconcelos arlenvasconcelos commented May 28, 2026

What

Adds a small "Updated 12s ago · ↻" widget to the header of the pages that auto-poll — Home (cluster status + certs), Audit (Issues), and GitOps.

Why

These pages refresh on a timer but gave the user no signal: no last-updated timestamp and no way to force a refresh. The widget surfaces data freshness and puts a manual refresh within reach.

Details

  • New reusable RefreshStatus component (web/src/components/ui/RefreshStatus.tsx):
    • Relative "Updated Ns ago" label, ticks every second
    • Refresh button that spins while fetching (also reflects background auto-refetches)
    • Tooltip showing the auto-refresh cadence ("Auto-refreshes every 30s")
  • Wired into HomeView (30s), AuditView (1m), and GitOpsView table (2m), matching each query's existing refetchInterval.
  • No paused/countdown state (intentionally out of scope).

Follow-up

packages/k8s-ui already has an equivalent inline pattern inside ResourcesView (LastUpdatedLabel + refresh button). A future change should extract this widget into k8s-ui and refactor ResourcesView onto it to avoid drift.

🤖 Generated with Claude Code


Note

Low Risk
UI-only freshness and refresh wiring on existing React Query hooks; no auth, data model, or API changes.

Overview
Adds a shared Updated N ago freshness indicator and tighter refresh behavior on GitOps and Checks (Audit) surfaces that poll in the background.

In k8s-ui, a new UpdatedAtLabel uses bucketed time helpers moved into format.ts (formatLastUpdatedBucket, msToNextBucket, formatUpdatedAgo) so labels tick only when the displayed bucket changes, not every second. ResourcesView drops its duplicate local helpers and keeps its existing LastUpdatedLabel wired to the shared utilities.

GitOpsTableView accepts optional dataUpdatedAt and isFetching: the toolbar shows the label beside refresh, and the refresh icon spins during initial load or background refetches. GitOpsView passes React Query timestamps, combines row/count fetching for the spinner, and routes manual refresh through refetchTable so counts stay aligned with rows.

AuditView surfaces the same label plus a manual refresh control using useRefreshAnimation and query refetch, with the icon spinning while fetches run.

Reviewed by Cursor Bugbot for commit 1f3388b. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2cdb3bc. Configure here.

Comment thread web/src/components/gitops/GitOpsView.tsx Outdated
Copy link
Copy Markdown
Contributor

@nadaverell nadaverell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed + visual-tested against a live cluster (Home / GitOps / Audit). The component itself is solid — useRefreshAnimation/Tooltip wired correctly, 1s ticker is widget-local so it doesn't re-render the page, refresh resets freshness as expected, tsc clean, zero console errors. The interaction works.

One blocking concern: the widget must not cost vertical space. A freshness indicator is low-value chrome; it's only worth shipping if it slots into space we already have. Right now it's inconsistent across the three surfaces:

  • Audit ✅ — slotted into the existing header control cluster (next to "N namespaces hidden" + settings). Zero added height. This is the pattern. Keep it.
  • GitOps ❌ — adds a brand-new bordered toolbar band (border-b ... px-4 py-1.5) above the table, eating ~32px of table height. Worse, SharedGitOpsTableView already has an onRefresh button in its own header (GitOpsTableView.tsx:636), so this partly duplicates an existing affordance while adding a band on top of it.
  • Home ❌ — adds a near-empty right-aligned row before the first card (the only content is the widget; the rest of the band is whitespace).

There's already an in-app precedent for doing this right: ResourcesView renders LastUpdatedLabel inline in its existing filter/toolbar row (ResourcesView.tsx:3858), next to Clear-filters and the column picker — no new band. That's the bar.

Asks:

  1. GitOps — drop the new toolbar. Slot freshness into the shared table's existing header instead: add an optional prop to GitOpsTableViewProps (e.g. lastUpdatedAt/headerSlot) and render the label next to the existing refresh button, or fold it into that button. Don't stack a band.
  2. Home — there's no existing header row here, so slot it into the ClusterHealthCard header (which already shows cluster name + auto-refresh info) rather than minting a band. If there's no clean slot, drop Home — an empty strip isn't worth it.
  3. Keep Audit as-is.

Net rule: no surface should grow vertically just to host this. If we can slot it in everywhere cleanly, great; if a surface can't take it without a new band, that surface is better off without the widget.

@roylibman
Copy link
Copy Markdown
Contributor

@arlenvasconcelos showing how many seconds ago is a bit too much distraction. Let's do:
0 – 59s: Just Now

1m – 59m: Xm ago (e.g., 14m ago)

1h – 23h: Xh ago (e.g., 2h ago)

24h: over a day ago

@nadaverell
Copy link
Copy Markdown
Contributor

@arlenvasconcelos showing how many seconds ago is a bit too much distraction. Let's do: 0 – 59s: Just Now

1m – 59m: Xm ago (e.g., 14m ago)

1h – 23h: Xh ago (e.g., 2h ago)

24h: over a day ago

We have logic for this already somewhere in the repo, reuse it

Adds a live freshness label + manual refresh to the pages that auto-poll,
slotted into each surface's existing header (no new bands, no duplicate
refresh buttons).

- New shared UpdatedAtLabel (k8s-ui) — a dumb "Updated N ago" label that
  re-renders exactly on each bucket boundary. Each surface owns its own
  refresh button.
- Centralized bucket/format logic (formatLastUpdatedBucket, msToNextBucket,
  formatUpdatedAgo) in utils/format.ts; ResourcesView reuses it.
- GitOpsTableView gains optional dataUpdatedAt/isFetching props and renders
  the label in its existing toolbar; GitOps refresh refetches rows + counts.
- Audit renders an inline label + button in its existing header.
- Wording: just now / Xm ago / Xh ago / over a day ago.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@arlenskh
Copy link
Copy Markdown
Contributor

arlenskh commented May 29, 2026

reworked the initial solution:

  • Home: dropped it.
  • Shared label component. Extracted just the "Updated N ago" label into a reusable component (UpdatedAtLabel in k8s-ui). I went label-only on purpose: GitOps already has its own refresh button, and its design differs from the one I'd added in Audit.
    Since each surface owns its own button, it's cleaner to reuse only the label and let each place keep its own button. We can extract a shared button later if it's worth it.
  • GitOps: label + existing button
  • Audit: label + an inline refresh button in the existing header.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants