Skip to content

feat(dashboard): redesign dashboard — site selector heading, sites gallery, fleet health panel#600

Merged
flesher merged 9 commits into
mainfrom
redesign-dashboard-page
Jun 26, 2026
Merged

feat(dashboard): redesign dashboard — site selector heading, sites gallery, fleet health panel#600
flesher merged 9 commits into
mainfrom
redesign-dashboard-page

Conversation

@flesher

@flesher flesher commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Reviewable diff: +887/-61 across 19 files (excludes generated, test, and story files).

Summary

Redesigns the Dashboard's Overview area into a site-aware layout. The page now carries its own heading-style site selector (the global topbar picker is hidden here), and the content below it changes shape with the selection: All Sites shows a flat fleet-health metric list plus a horizontally-sliding gallery of per-site cards; a single selected site shows a Fleet health module (metrics + health bar) with a Buildings / Racks / Components selector underneath. The Performance section and the rest of the page are unchanged.

How it works

Site selector as the page heading. The Dashboard renders the existing SitePicker at the top of the Overview section using a new heading-size variant. Selecting a site sets the active site (store + route, exactly as the topbar picker does) and navigates to the scoped dashboard. Because the dashboard now owns a selector, the global topbar SitePicker is hidden whenever the current route resolves to /dashboard (covers /dashboard, /:site/dashboard, /unassigned/dashboard).

All-Sites mode. Below the heading, FleetHealthMetrics renders the five fleet-health tiles (Your fleet / Health / Needs attention / Offline / Sleeping) as a bare list — no card chrome, no composition bar. Underneath, SitesSection renders one SiteCard per site in a horizontal track that overflows the content width. The number of cards visible at once is responsive (desktop 3, laptop/tablet 2, phone 1); chevrons slide the track one card at a time and the offset is clamped so the last card right-aligns with the page content. Each SiteCard polls GetSiteStats and shows hashrate (auto-scaled units), power, an integer temperature range, a full-width HealthBar, a "needs attention" badge linking to the site-scoped, status-filtered miner list, and an arrow link to the site detail page.

Single-site mode. FleetHealthSection renders a module card: a performance subheading built from GetSiteStats (e.g. 922.6 PH/s, 3.5 MW (29% of 12 MW), 3.8 J/TH, each metric gated on its own reporting count), the fleet-health metric tiles, a full-width HealthBar + legend, and below a divider, SiteResourcePanel. That panel uses the duration-selector button row to switch between Buildings, Racks, and Components:

  • BuildingslistBuildingsBySiteBuildingSummaryCard gallery.
  • RacksuseDeviceSetListState scoped to the site → simplified RackCards (telemetry footer hidden) linking to the rack detail page.
  • ComponentsuseComponentErrors for the site → the FleetErrors breakdown.

Each tab's data fetch is gated so only the active tab does work: Buildings fetches in an effect gated by the tab, Racks resolves to a matchNone site filter (which short-circuits the request inside useDeviceSetListState) until selected, and Components passes enabled: tab === "Components". Buildings/Racks render in an overflowing, animated gallery driven by a shared useCardCarousel hook; chevrons appear in the toolbar only when the row overflows. The gallery clips at the card's edges (so cards stay visible bleeding through the card padding as they slide) but the slide clamps against content width (so the last card lands flush with the content edge). A per-tab "View all" links to the matching site-scoped fleet page (Components → the needs-attention-filtered miner list).

Diagrams

flowchart TD
  Picker["Site selector heading (SitePicker, heading-300)"] --> Mode{"activeSite.kind"}
  Mode -->|"all / unassigned"| AllMode["FleetHealthMetrics (bare metric list)"]
  AllMode --> Sites["SitesSection: animated SiteCard gallery"]
  Mode -->|"site"| SiteMode["FleetHealthSection module"]
  SiteMode --> Health["Metric tiles + HealthBar + legend"]
  SiteMode --> Panel["SiteResourcePanel"]
Loading
flowchart LR
  Sel["Buildings / Racks / Components selector"] --> T{"active tab"}
  T -->|"Buildings"| B["listBuildingsBySite -> BuildingSummaryCard"]
  T -->|"Racks"| R["useDeviceSetListState (site-scoped) -> RackCard (showMetrics=false)"]
  T -->|"Components"| C["useComponentErrors -> FleetErrors"]
  B --> Car["useCardCarousel: overflow detect + chevron slide"]
  R --> Car
Loading

Areas of the code involved

Area / file What changed Why it matters for review
features/dashboard/pages/Dashboard.tsx Overview section rebuilt: heading selector + mode branch; removed FleetErrors/Overview title from this page Main orchestration + mode split
features/dashboard/components/FleetHealthMetrics/ (new) Bare 5-tile fleet-health metric list Shared by both modes
features/dashboard/components/SitesSection/ (new) All-Sites animated gallery + SiteCard Carousel geometry, per-site polling, deep links
features/dashboard/components/FleetHealthSection/ (new) Single-site module, SiteResourcePanel, useCardCarousel Tab gating, measurement-based carousel, FleetErrors reuse
components/PageHeader/PageHeader.tsx Hide global SitePicker on the dashboard route Route-conditional rendering
components/PageHeader/SitePicker/SitePicker.tsx New triggerClassName prop (default unchanged) Heading variant; shared component
features/fleetManagement/components/RackCard/RackCard.tsx New showMetrics prop (default true) to hide the telemetry footer Shared with fleet racks page (keeps full card)
shared/components/Metric/Metric.tsx New compact variant (smaller value, tighter gap) Shared primitive; default behavior preserved
features/kpis/components/ComponentErrors/ComponentErrors.tsx Contrasting card surface + always-critical icon background Shared via FleetErrors (also affects GroupOverviewPage)
features/groupManagement/components/FleetHealth/ (moved) Legacy FleetHealth card moved from features/dashboard; GroupOverviewPage + dashboard barrel updated Pure relocation (its only remaining consumer is groups)

Key technical decisions & trade-offs

  • Two carousel strategies. The All-Sites gallery uses a CSS-variable / fixed-visible-count model (responsive 3/2/1); the in-card Buildings/Racks gallery uses a measurement-based useCardCarousel (ResizeObserver) because card widths are fixed and overflow is content-driven. Chose measurement there over a fixed count so arrows appear only on real overflow.
  • Tab-gated fetching. Racks reuse useDeviceSetListState but stay mounted; an inactive tab returns a matchNone filter that skips the network call, rather than conditionally mounting the hook. Keeps the carousel/toolbar state in one component at the cost of the hook staying mounted.
  • Shared-component edits are global. Metric (compact variant, additive), RackCard (showMetrics, default true), and SitePicker (triggerClassName, default unchanged) are backward-compatible. ComponentErrors surface/icon changes are not gated — they also change FleetErrors on GroupOverviewPage (intentional, for consistency).
  • Heading owns the selector. Hiding the global picker on the dashboard means the dashboard must always render its own selector (both modes) so the user can still switch scope.

Testing & validation

  • Unit tests added for every new component (FleetHealthMetrics, SitesSection, SiteCard, FleetHealthSection, SiteResourcePanel, RackCard variant) and the existing FleetHealth test moved with the component. tsc, ESLint, and Prettier are clean; the touched suites pass (237 tests across the affected dirs).
  • Carousel overflow/arrow math relies on real layout measurement, so it is exercised by the tab/content orchestration tests rather than pixel assertions (jsdom has no layout).
  • Not covered / follow-up: the Playwright dashboard.spec still asserts the old "Overview" section title and a Control Boards issue-card navigation step that no longer exist on this page — those e2e steps need updating before they go green.

… gallery, and fleet health panel

Reworks the Dashboard's Overview section into two modes driven by the site
selection:

- All Sites: a bare fleet-health metrics list (no card/health bar) followed
  by a horizontally-sliding Sites gallery of per-site cards (hashrate, power,
  temperature, health bar, needs-attention badge).
- Single site: a Fleet health module with the health metrics + HealthBar +
  legend, and a Buildings / Racks / Components selector below. Buildings and
  Racks render as an overflowing, animated card gallery; Components renders the
  FleetErrors breakdown. Per-tab "View all" links to the matching fleet pages.

Other changes:
- Render a heading-style site selector at the top of the dashboard and hide the
  global topbar SitePicker on the dashboard route.
- Add a `triggerClassName` prop to SitePicker for the heading variant.
- Add a `compact` variant to the shared Metric component.
- Add a `showMetrics` prop to RackCard for a simplified (no telemetry footer)
  card used by the dashboard; the fleet page keeps the full card.
- Component error cards use the contrasting surface + always-critical icon bg.
- Move the legacy FleetHealth card from features/dashboard to
  features/groupManagement (its only remaining consumer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@flesher flesher requested a review from a team as a code owner June 26, 2026 17:23
Copilot AI review requested due to automatic review settings June 26, 2026 17:23
@github-actions github-actions Bot added javascript Pull requests that update javascript code client labels Jun 26, 2026
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (6c65c044969d26705a1a34ef053a488eb9bf0767...a3e370c2f3b3f0b150693436890575c712a82e49, exact PR three-dot diff)
  • Model: gpt-5.5

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: NONE

Findings

No findings.

Notes

  • .git/codex-review.diff only changes client dashboard/site UI, shared UI components, and e2e/tests. No server, SQL/migration, plugin, protobuf, Docker/Nginx, command execution, or pool/wallet configuration changes were in scope.
  • I did not find high-impact auth, XSS, command injection, pool hijack, protobuf compatibility, or client reliability issues in the changed hunks.

Generated by Codex Security Review |
Triggered by: @flesher |
Review workflow run

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the ProtoFleet dashboard UX by replacing the old “Overview” layout with a heading-style site selector, a new fleet health module (single-site vs all-sites modes), and an all-sites “Sites” card gallery, plus supporting component tweaks and test coverage.

Changes:

  • Add new dashboard sections/components: FleetHealthSection (single-site), FleetHealthMetrics (shared metrics tiles), and SitesSection + SiteCard (all-sites gallery).
  • Move/reuse fleet health UI in group management and adjust dashboard imports/exports accordingly.
  • Add UI flexibility knobs (Metric variant, RackCard showMetrics) and introduce new unit tests/stories.

Reviewed changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
client/src/shared/components/Metric/Metric.tsx Adds variant support (default/compact) and adjusts skeleton sizing/spacing.
client/src/protoFleet/features/kpis/components/ComponentErrors/ComponentErrors.tsx Updates icon styling and card surface styling for component error tiles.
client/src/protoFleet/features/groupManagement/pages/GroupOverviewPage.tsx Re-points FleetHealth import to the new groupManagement location.
client/src/protoFleet/features/groupManagement/components/FleetHealth/index.ts New barrel export for groupManagement FleetHealth.
client/src/protoFleet/features/groupManagement/components/FleetHealth/FleetHealth.tsx Updates ChartWidget import path to dashboard component.
client/src/protoFleet/features/groupManagement/components/FleetHealth/FleetHealth.test.tsx Adds coverage for fleet health rendering, loading, pluralization, and links.
client/src/protoFleet/features/groupManagement/components/FleetHealth/FleetHealth.stories.tsx Adds Storybook stories for fleet health states.
client/src/protoFleet/features/fleetManagement/components/RackCard/RackCard.tsx Adds showMetrics toggle to optionally hide telemetry footer.
client/src/protoFleet/features/fleetManagement/components/RackCard/RackCard.test.tsx Adds tests for showMetrics default/false behavior.
client/src/protoFleet/features/dashboard/pages/Dashboard.tsx Reworks the dashboard header/overview area and adds fleet health + sites section logic.
client/src/protoFleet/features/dashboard/index.ts Removes FleetHealth export from the dashboard barrel.
client/src/protoFleet/features/dashboard/components/SitesSection/SitesSection.tsx New all-sites “Sites” section with responsive horizontal gallery + pagination.
client/src/protoFleet/features/dashboard/components/SitesSection/SitesSection.test.tsx Tests gallery sizing, pagination behavior, and loading skeletons.
client/src/protoFleet/features/dashboard/components/SitesSection/SiteCard.tsx New per-site card with polled stats, compact metrics, health bar, and deep links.
client/src/protoFleet/features/dashboard/components/SitesSection/SiteCard.test.tsx Tests SiteCard links, displayed metrics, badge behavior, and loading skeletons.
client/src/protoFleet/features/dashboard/components/SitesSection/index.ts New barrel export for SitesSection.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/useCardCarousel.ts New reusable carousel hook for card-track measurement and stepping.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/SiteResourcePanel.tsx New tabbed panel (Buildings/Racks/Components) with galleries and FleetErrors integration.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/SiteResourcePanel.test.tsx Tests tab switching, empty state, and site-scoped “View all” linking.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/index.ts New barrel export for FleetHealthSection.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/FleetHealthSection.tsx New single-site Fleet health section: performance subheading, metrics, health bar, legend, resources panel.
client/src/protoFleet/features/dashboard/components/FleetHealthSection/FleetHealthSection.test.tsx Tests scoping, subheading construction, and loading fallbacks.
client/src/protoFleet/features/dashboard/components/FleetHealthMetrics/index.ts New barrel export for FleetHealthMetrics (+ props type).
client/src/protoFleet/features/dashboard/components/FleetHealthMetrics/FleetHealthMetrics.tsx New shared metric-tiles component for fleet health counts/percentages.
client/src/protoFleet/features/dashboard/components/FleetHealthMetrics/FleetHealthMetrics.test.tsx Tests percentages, loading skeletons, em-dash state, and empty fleet handling.
client/src/protoFleet/components/PageHeader/SitePicker/SitePicker.tsx Adds triggerClassName to support heading-style picker typography.
client/src/protoFleet/components/PageHeader/PageHeader.tsx Hides the topbar SitePicker on the Dashboard route to avoid duplicate selectors.

Comment thread client/src/protoFleet/features/dashboard/components/SitesSection/SiteCard.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c71b485ff9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread client/src/protoFleet/features/dashboard/pages/Dashboard.tsx Outdated
flesher and others added 2 commits June 26, 2026 10:53
…etry, e2e

- Viewport-gate each SiteCard's GetSiteStats poll (useInViewport) so the
  all-sites gallery no longer fans out one poll per site every tick; only
  on-screen cards poll. useSiteStats now keys scope on siteId only (matching
  useBuildingStats) so suspend/resume keeps last-good stats without a flash.
- FleetHealthMetrics: label the healthy bucket "Healthy" (was "Health") to
  match the legend and FleetHealth copy.
- Dashboard: surface ListSites failures through the heading SitePicker's
  retry affordance instead of an indefinite loading skeleton.
- FleetHealthSection: key SiteResourcePanel by siteId so switching sites
  remounts it and clears the previous site's building/rack cards.
- e2e: update dashboard.spec ("Overview" title -> "Sites"), and drop the two
  issue-card navigation tests whose all-sites affordance moved to the
  single-site Components tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Revert the always-critical icon background — at 0 issues or while loading the
icon now uses the neutral surface, so a healthy card no longer reads as an
error state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52eae992f2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread client/src/protoFleet/features/dashboard/pages/Dashboard.tsx
Document that the index reset uses React's endorsed adjust-state-on-prop-change
render pattern (converges, runs before paint) and why the useLayoutEffect
alternative is intentionally avoided (repo react-hooks/set-state-in-effect rule).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 147e72c8d1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Swap the dashboard Buildings tab from BuildingSummaryCard (health bar) to the
floor-plan BuildingCard, and add a showMetrics prop to BuildingCard that omits
the telemetry footer — mirroring the simplified RackCard. The dashboard renders
showMetrics={false}; the fleet/sites pages keep the full card.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 32350ba338

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread client/src/protoFleet/features/dashboard/pages/Dashboard.tsx Outdated
flesher and others added 2 commits June 26, 2026 11:44
…ction to all-sites

- Keep knownSiteIds unknown (not an empty loaded set) when ListSites errors,
  mirroring the picker, so a transient failure on /:site/dashboard no longer
  makes useActiveSite treat the route site as stale and fall back to all-sites.
- Render the Sites gallery only when activeSite.kind === 'all'; on
  /unassigned/dashboard it no longer shows org-wide site cards while the rest
  of the page is scoped to unassigned miners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ng flash

- Buildings tab: record listBuildingsBySite failures and show an error/retry
  instead of collapsing to 'No buildings'; keep last-good rows.
- Racks tab: surface useDeviceSetListState errors with a retry instead of a
  permanent skeleton.
- Gate the racks loading state on the real fetch having started so switching
  to Racks no longer flashes 'No racks in this site yet.' from the stale
  matchNone state before the scoped fetch runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7eb47db9e3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…d CTAs

Adds an optional `to` prop to the shared Button: when set it renders a
react-router Link (an <a>) with the exact same styling, so navigation CTAs
no longer nest a <button> inside a <Link> (invalid DOM / duplicate focus
stops). Disabled link CTAs render an inert styled <span>. Swaps every
dashboard CTA (SitesSection, SiteCard, SiteResourcePanel, FleetHealthSection)
to <Button to=...>, removing the Link wrappers and styling duplication.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd61aff67e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

When useComponentErrors' first request fails the counts stay undefined, so
the FleetErrors grid showed permanent skeletons with no retry. Render an
error/retry state (gated on the hook's error + !hasLoaded) before the grid,
wired to the hook's refetch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@flesher flesher merged commit 8b30e02 into main Jun 26, 2026
81 checks passed
@flesher flesher deleted the redesign-dashboard-page branch June 26, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants