Skip to content

Redesign: Ledgers route — StateBoundary + skeletons + governor section restyle #6512

Description

@JSONbored

Context

apps/loopover-miner-ui/src/routes/ledgers.tsx (lines 60-266) renders the "Ledgers" route in two structurally separate pieces that currently share nothing but a copy-pasted loading/error JSX shape:

  • A read-only ledger summary: claims count cards, a governor-events CountTable, an events-by-type CountTable, and a recent-events Table, all sourced from a single GET /api/ledgers call via apps/loopover-miner-ui/src/lib/ledgers.ts.
  • A separate "Governor control" section: an Input + Button pair wired to POST /api/governor/{pause,resume} via apps/loopover-miner-ui/src/lib/governor.ts, with its own independent fetch/action loop (fetching current governor state, then submitting the pause/resume request) distinct from the ledger-summary fetch above.

Per the miner-ui audit, this route's loading state today is the literal string "Loading local ledgers…" (ledgers.tsx:118) — plain gray text, re-rendered on every 10s poll cycle, with no shimmer/placeholder shape — even though @loopover/ui-kit/components/skeleton.tsx (packages/loopover-ui-kit/src/components/skeleton.tsx) already ships in the shared package and is imported nowhere under apps/loopover-miner-ui/src. The same audit found this exact 4-branch loading/error/empty/success pattern hand-duplicated near-identically across all four routes (run-history.tsx, portfolio.tsx, ledgers.tsx, index.tsx) with no shared component factoring it out. This issue scopes only ledgers.tsx; the other three routes are covered by separate sibling issues in the same visual/ui-kit polish track and are out of scope here.

The shared-design-system audit confirms apps/loopover-miner-ui already consumes @loopover/ui-kit first-class and directly (e.g. Button/Card/Table imported from @loopover/ui-kit/components/* in portfolio.tsx:4-6) — it never imports from apps/loopover-ui, which is a separate app with its own local src/components/* tree. The reusable LoadingState/EmptyState/ErrorState/StateBoundary primitives (plus a bare Spinner) that this issue needs currently live only at apps/loopover-ui/src/components/site/state-views.tsx — confirmed reusable as-is by the ui-kit chat-primitives audit (#6244, landed via PR #6474, merged 2026-07-16) — but that audit also established these primitives must first be ported from that app-local file into @loopover/ui-kit proper before any other app (including apps/loopover-miner-ui) can consume them. This issue is blocked on that port landing first — see the Requirements callout below.

This is a pure visual/restyle change with no new attack surface: the write-capable governor pause/resume request path (lib/governor.ts, the Button's click handler, and the underlying POST /api/governor/{pause,resume} call) is explicitly left untouched by this issue. Only the rendering layer — what's shown while those calls are pending, what's shown if they fail, and how the governor-control section is laid out — changes.

Requirements

⚠️ Read this before starting. StateBoundary, LoadingState, and ErrorState must be imported from @loopover/ui-kit once that package exports them (following the existing @loopover/ui-kit/components/<name> convention already used for Skeleton, Card, Table, etc. — e.g. @loopover/ui-kit/components/state-views). Do not import apps/loopover-ui/src/components/site/state-views.tsx directly from apps/loopover-miner-ui — these are separate apps with no cross-app dependency, and no other file in apps/loopover-miner-ui does this. Do not hand-roll a second local copy of LoadingState/ErrorState/StateBoundary inside apps/loopover-miner-ui as a workaround either. If @loopover/ui-kit does not yet export these primitives when you pick this up, this issue is still blocked — stop and say so in the issue/PR rather than reimplementing them locally; do not treat "the ui-kit export doesn't exist yet" as license to invent a parallel version.

  • The ledger-summary fetch (claims counts, both CountTables, the recent-events Table — everything currently gated by the "Loading local ledgers…" branch at ledgers.tsx:118 and its adjacent error branch) must be wrapped in StateBoundary, rendering LoadingState while pending and ErrorState on fetch failure.
  • The governor pause/resume fetch/action loop must be wrapped in its own, separate StateBoundary/LoadingState/ErrorState — it is a distinct async flow from the ledger-summary fetch above and must not be merged into a single shared boundary that would, for example, block the whole page on a governor-state fetch failure.
  • Skeleton placeholders (@loopover/ui-kit/components/skeleton) must replace the plain-text loading branches, shaped to approximate the final layout (count-card grid, table rows) rather than a single generic bar.
  • The "Governor control" section's layout must be restyled — reorganize the existing Input + Button pair's spacing/grouping/visual hierarchy using existing @loopover/ui-kit primitives and design tokens (the same tokens already in use elsewhere in this app, e.g. text-token-sm, rounded-token-sm, hover-surface). Do not introduce a new component library, new ad-hoc CSS, or new design tokens.
  • Do not modify apps/loopover-miner-ui/src/lib/ledgers.ts.
  • Do not modify apps/loopover-miner-ui/src/lib/governor.ts.
  • Do not modify the pause/resume Button's existing click handlers or its call into lib/governor.ts — the action logic itself is out of scope; only what wraps it visually changes. A PR that touches the pause/resume request logic, even incidentally, does not satisfy this issue.
  • This is a contributor-facing visual PR: include before/after screenshots (or a short screen recording) in the PR description showing, for both async flows independently: the skeleton loading state, the populated/success state, and an error state (e.g. via devtools request blocking or throttling). Per house convention, a visual PR without this evidence is closed rather than merged.

Deliverables

  • Ledger-summary section (claims cards, both CountTables, recent-events Table) wrapped in StateBoundary using ui-kit's LoadingState/ErrorState, replacing the current plain-text loading/error branches.
  • Skeleton placeholders shown during the ledger-summary LoadingState, shaped to the final card/table layout.
  • Governor pause/resume fetch/action loop wrapped in its own, independently-triggered StateBoundary/LoadingState/ErrorState.
  • Governor-control section visually restyled (layout/spacing/hierarchy only) using existing @loopover/ui-kit primitives and tokens.
  • git diff confirms zero changes to lib/ledgers.ts, lib/governor.ts, and the pause/resume Button's click-handler wiring.
  • Before/after screenshots (or recording) for both async flows' loading/success/error states included in the PR description.

Test Coverage Requirements

apps/loopover-miner-ui/** is outside src/**, so Codecov's patch-coverage gate (the 99%+ branch-counted requirement in CLAUDE.md) does not numerically gate this change — note this explicitly in the PR so a reviewer isn't confused about why no Codecov check appears. That does not relax the bar for actually verifying the change: if apps/loopover-miner-ui already has an existing test pattern for its routes (check for *.test.tsx alongside src/routes/*.tsx before starting), extend it to cover the new StateBoundary-driven loading/error/populated branches for both the ledger-summary and governor flows. If no such test harness exists yet for this app, say so explicitly in the PR rather than inventing a new one from scratch — the before/after screenshot requirement above is the mandatory verification either way, and stands regardless of what automated coverage exists.

Expected Outcome

  • The Ledgers route no longer shows raw "Loading local ledgers…" text on every poll cycle; loading is a Skeleton shaped to the eventual content.
  • Both the ledger-summary read and the governor pause/resume flow render through the shared StateBoundary/LoadingState/ErrorState primitives instead of hand-rolled inline JSX, matching the pattern the sibling route-redesign issues apply to run-history.tsx/portfolio.tsx/index.tsx.
  • The governor-control section reads as an organized, visually distinct panel instead of a bare Input+Button row.
  • lib/ledgers.ts, lib/governor.ts, and the pause/resume action wiring are byte-for-byte unchanged — verifiable directly from the diff — so there is exactly one write path into the governor (the existing one) both before and after this PR.

Links & Resources

  • apps/loopover-miner-ui/src/routes/ledgers.tsx (lines 60-266) — the file this issue touches.
  • apps/loopover-miner-ui/src/lib/ledgers.ts — untouched; GET /api/ledgers fetcher.
  • apps/loopover-miner-ui/src/lib/governor.ts — untouched; POST /api/governor/{pause,resume} actions.
  • packages/loopover-ui-kit/src/components/skeleton.tsx — existing, currently-unused Skeleton component this issue adopts.
  • apps/loopover-ui/src/components/site/state-views.tsx — source of LoadingState/EmptyState/ErrorState/StateBoundary, confirmed reusable as-is by research: audit ui-kit for existing message/chat-adjacent UI primitives #6244 (PR docs(ui): audit ui-kit for existing chat-adjacent UI primitives #6474, merged 2026-07-16); this issue is blocked on the separate issue that ports these primitives from this app-local file into @loopover/ui-kit proper.
  • apps/loopover-miner-ui/src/routes/portfolio.tsx:4-6 — existing precedent for importing shared components directly from @loopover/ui-kit/components/*.
  • Sibling issues in the same visual/ui-kit polish track (out of scope here, reference only): the Overview route redesign issue, the Run history route redesign issue, and the Portfolio route redesign issue — each applies the same StateBoundary/Skeleton adoption pattern to its own route.
  • The broader miner dashboard redesign epic (routed dashboard + persistent chat rail) this issue is part of — see that epic for the full split between this visual/ui-kit polish track and the separately-scoped, higher-safety-scope chat track.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    Status
    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions