You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 withinapps/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
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
Context
apps/loopover-miner-ui/src/routes/run-history.tsxrenders one<Table>(Repository / StateBadge/ Last updated) sourced fromfetchRunStates()inapps/loopover-miner-ui/src/lib/run-history.ts, which hitsGET /api/run-stateand reads theminer_run_statetable (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 inportfolio.tsxandledgers.tsxwith no shared component. There are no skeleton placeholders anywhere in the app:@loopover/ui-kitshipspackages/loopover-ui-kit/src/components/skeleton.tsx, but a grep ofapps/loopover-miner-ui/srcconfirms it's imported nowhere. Likewisepackages/loopover-ui-kit/src/components/pagination.tsxexists 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.tsxslice 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/*.tsfetchers and poll cadence stay untouched everywhere, including here.The reusable state-boundary components do not live in
@loopover/ui-kityet.LoadingState/EmptyState/ErrorState/StateBoundary(+ a bareSpinner) currently live app-local atapps/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 withinapps/loopover-ui.apps/loopover-miner-uiis a separate app in the workspace that only ever imports shared UI from@loopover/ui-kit— it already consumesButton/Card/Tabledirectly from@loopover/ui-kit/components/*and has no dependency onapps/loopover-uiat all. So this route cannot reachstate-views.tsxacross the app boundary until those four exports are ported into@loopover/ui-kitproper. 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.Requirements
LoadingState/ErrorState/StateBoundaryare available as importable@loopover/ui-kitcomponents (ported fromapps/loopover-ui/src/components/site/state-views.tsx). Do not fork or hand-copy that file intoapps/loopover-miner-ui/src/as a substitute.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) withStateBoundarywrappingLoadingState/ErrorStateimported from@loopover/ui-kit.StateBoundary's ported API also covers the empty-array case (no tracked repos), route the existing plain-text empty branch through it too viaEmptyState, 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>.fetchRunStates()is loading, render@loopover/ui-kit'sSkeleton(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.@loopover/ui-kit'spagination.tsx(packages/loopover-ui-kit/src/components/pagination.tsx) for the run-state table once the array returned byfetchRunStates()exceeds 20 rows. Below that threshold, render the full table unpaginated exactly as today (no pagination controls shown).GET /api/run-state's response shape or to anything inapps/loopover-miner-ui/src/lib/run-history.ts.apps/loopover-miner-ui/src/lib/run-history.ts— its fetch logic and the existing 10s poll cadence (usePolledFetch) are untouched by this issue.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.Deliverables
apps/loopover-miner-ui/src/routes/run-history.tsxusingStateBoundary/LoadingState/ErrorState(andEmptyStateif covered by the ported API) from@loopover/ui-kitin place of the inline<p>branches@loopover/ui-kit'spagination.tsx, active only once run-state rows exceed 20apps/loopover-miner-ui/src/lib/run-history.tsleft byte-for-byte unchangedTest Coverage Requirements
apps/loopover-miner-uiis underapps/**, outside Codecov'scoverage.include(onlysrc/**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 underapps/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:Skeletonrows, not textErrorStatewith the existing error copyEmptyStateis wired infetchRunStates()returns 20 or fewer rowsExpected Outcome
run-history.tsxshows shimmering skeleton rows instead of plain loading text on initial load; its error and empty states render through the same sharedStateBoundary/ErrorState/LoadingStatecomponents 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 restyledapps/loopover-miner-ui/src/lib/run-history.ts— read-only reference; not to be modifiedapps/loopover-ui/src/components/site/state-views.tsx— source ofLoadingState/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 componentpackages/loopover-ui-kit/src/components/pagination.tsx— existing, currently-unused Pagination componentLoadingState/EmptyState/ErrorState/StateBoundaryfromapps/loopover-uiinto@loopover/ui-kit) — hard blocking prerequisite for this issue, filed separately