Skip to content

Redesign: Run history route — StateBoundary + skeletons + pagination #6510

Description

@JSONbored

Context

apps/loopover-miner-ui/src/routes/run-history.tsx renders one <Table> (Repository / State Badge / Last updated) sourced from fetchRunStates() in apps/loopover-miner-ui/src/lib/run-history.ts, which hits GET /api/run-state and reads the miner_run_state table (one row per tracked repo: repo_full_name, state, updated_at). Per the current miner-ui audit, the route's loading/error/empty handling is three near-duplicate literal <p> blocks hand-written inline — e.g. "Loading local run state…" — the same pattern independently repeated in portfolio.tsx and ledgers.tsx with no shared component. There are no skeleton placeholders anywhere in the app: @loopover/ui-kit ships packages/loopover-ui-kit/src/components/skeleton.tsx, but a grep of apps/loopover-miner-ui/src confirms it's imported nowhere. Likewise packages/loopover-ui-kit/src/components/pagination.tsx exists in the kit's component set but is unused — every table in the app, including this one, is a static full dump with no paging controls.

This issue is the run-history.tsx slice of the broader miner-dashboard visual/ui-kit-polish track (the sibling route-redesign issues cover the overview, portfolio, and ledgers routes plus the shared header/nav in the same style — reference those by their own titles when they're filed, not this one). That track is explicitly restyle-only: lib/*.ts fetchers and poll cadence stay untouched everywhere, including here.

The reusable state-boundary components do not live in @loopover/ui-kit yet. LoadingState/EmptyState/ErrorState/StateBoundary (+ a bare Spinner) currently live app-local at apps/loopover-ui/src/components/site/state-views.tsx, confirmed reusable as-is by the chat-adjacent UI primitives audit (#6244, landed via PR #6474). That audit scoped reuse within apps/loopover-ui. apps/loopover-miner-ui is a separate app in the workspace that only ever imports shared UI from @loopover/ui-kit — it already consumes Button/Card/Table directly from @loopover/ui-kit/components/* and has no dependency on apps/loopover-ui at all. So this route cannot reach state-views.tsx across the app boundary until those four exports are ported into @loopover/ui-kit proper. That port is shared prerequisite work — the same primitives are also needed by the (separately tracked) chat message-list work — so land it once, not per-consuming-issue.

⚠️ Read this before starting. This issue is blocked until LoadingState/ErrorState/StateBoundary (and ideally EmptyState) exist as real exports of @loopover/ui-kit (e.g. @loopover/ui-kit/components/state-views or an equivalent path chosen by that porting work) — they are not there today. Do not work around this by copy-pasting or re-implementing state-views.tsx locally inside apps/loopover-miner-ui/src/; that produces a second, forked copy of the exact duplication this issue exists to remove. Do not touch apps/loopover-miner-ui/src/lib/run-history.ts — its fetch/poll logic is explicitly out of scope. Do not add a new API route, query param, or change GET /api/run-state's response shape for pagination — paging must be done client-side over the array fetchRunStates() already returns.

Requirements

  • This issue cannot start until LoadingState/ErrorState/StateBoundary are available as importable @loopover/ui-kit components (ported from apps/loopover-ui/src/components/site/state-views.tsx). Do not fork or hand-copy that file into apps/loopover-miner-ui/src/ as a substitute.
  • In apps/loopover-miner-ui/src/routes/run-history.tsx, replace the current inline loading/error <p> branches (the "Loading local run state…" text and its sibling error-state text) with StateBoundary wrapping LoadingState/ErrorState imported from @loopover/ui-kit.
  • If StateBoundary's ported API also covers the empty-array case (no tracked repos), route the existing plain-text empty branch through it too via EmptyState, so all state branches in the file use the shared component consistently rather than three states migrating and a fourth staying as a bare <p>.
  • While fetchRunStates() is loading, render @loopover/ui-kit's Skeleton (packages/loopover-ui-kit/src/components/skeleton.tsx) as placeholder rows inside the same <Table> shape (Repository / State / Last updated columns), replacing the current bare loading text.
  • Adopt @loopover/ui-kit's pagination.tsx (packages/loopover-ui-kit/src/components/pagination.tsx) for the run-state table once the array returned by fetchRunStates() exceeds 20 rows. Below that threshold, render the full table unpaginated exactly as today (no pagination controls shown).
  • Pagination must be implemented entirely client-side over the already-fetched array — no new query params, no new API route, no change to GET /api/run-state's response shape or to anything in apps/loopover-miner-ui/src/lib/run-history.ts.
  • No change of any kind to apps/loopover-miner-ui/src/lib/run-history.ts — its fetch logic and the existing 10s poll cadence (usePolledFetch) are untouched by this issue.
  • The Repository / State (Badge) / Last updated column layout and the data shown stay the same — this is a restyle of loading/error/empty/pagination presentation only, not a change to what data appears in the table.
  • This is a visual/UI PR: the PR description must include before/after screenshots (or a short screen recording) showing the loading-state skeleton rows and, separately, the table once paginated (temporarily seed or mock >20 rows locally to capture this — do not ship a temporary seed change).

Deliverables

  • apps/loopover-miner-ui/src/routes/run-history.tsx using StateBoundary/LoadingState/ErrorState (and EmptyState if covered by the ported API) from @loopover/ui-kit in place of the inline <p> branches
  • Skeleton placeholder rows rendered during the loading state, matching the real table's column layout
  • Client-side pagination via @loopover/ui-kit's pagination.tsx, active only once run-state rows exceed 20
  • apps/loopover-miner-ui/src/lib/run-history.ts left byte-for-byte unchanged
  • Before/after screenshots (loading state + paginated state) attached to the PR description

Test Coverage Requirements

apps/loopover-miner-ui is under apps/**, outside Codecov's coverage.include (only src/** is measured per house convention), so this PR is not gated by the 99% patch-coverage requirement — but that is not a license to skip tests. Add or update tests colocated with the route, matching whatever test convention already exists elsewhere under apps/loopover-miner-ui/src/routes/ (if no per-route test file exists yet for this app, add a new one rather than skip verification), covering:

  • the loading state renders Skeleton rows, not text
  • the error state renders ErrorState with the existing error copy
  • the empty state (zero tracked repos) renders correctly if EmptyState is wired in
  • pagination controls are absent when fetchRunStates() returns 20 or fewer rows
  • pagination controls appear and correctly page the table when it returns more than 20 rows, with no change to the underlying fetch call count or poll cadence

Expected Outcome

run-history.tsx shows shimmering skeleton rows instead of plain loading text on initial load; its error and empty states render through the same shared StateBoundary/ErrorState/LoadingState components the rest of the redesigned app uses, instead of a hand-written <p> unique to this file; and once a install's tracked-repo count grows past 20, the table pages instead of rendering an unbounded list. lib/run-history.ts's fetch/poll behavior is provably unchanged (same endpoint, same cadence, same response shape).

Links & Resources

  • apps/loopover-miner-ui/src/routes/run-history.tsx — file being restyled
  • apps/loopover-miner-ui/src/lib/run-history.ts — read-only reference; not to be modified
  • apps/loopover-ui/src/components/site/state-views.tsx — source of LoadingState/EmptyState/ErrorState/StateBoundary/Spinner, confirmed reusable as-is by research: audit ui-kit for existing message/chat-adjacent UI primitives #6244 (landed via PR docs(ui): audit ui-kit for existing chat-adjacent UI primitives #6474)
  • packages/loopover-ui-kit/src/components/skeleton.tsx — existing, currently-unused Skeleton component
  • packages/loopover-ui-kit/src/components/pagination.tsx — existing, currently-unused Pagination component
  • The ui-kit state-views port issue (porting LoadingState/EmptyState/ErrorState/StateBoundary from apps/loopover-ui into @loopover/ui-kit) — hard blocking prerequisite for this issue, filed separately
  • The sibling route-redesign issues for the overview, portfolio, and ledgers routes plus the shared header/nav — same restyle treatment, filed separately
  • Out of scope: the persistent collapsible chat rail work and Spec: conversational chat interface for loopover (Lovable/Cursor-style) #6230's maintainer-chat scope are unrelated tracks and touch none of these files

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