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/index.tsx (the Overview route) renders three SummaryCards in a grid sm:grid-cols-3 — Run activity (repos tracked, currently-working count), Portfolio queue (total/queued/in-progress/done + oldest-queued age), and Claims (active/total). Each card independently polls its own endpoint via usePolledFetch (apps/loopover-miner-ui/src/lib/use-polled-fetch.ts), hitting fetchRunStates, fetchPortfolioQueue, and fetchLedgers every 10s (DEFAULT_POLL_INTERVAL_MS, lib/use-polled-fetch.ts:5). Loading/empty/error states are handled by a hand-rolled Fallback() helper plus inline <p> text literals local to index.tsx — the same repeated pattern the miner-ui audit found duplicated (with only the wording changed) across the other three routes (run-history.tsx:26-42, portfolio.tsx:39-56, ledgers.tsx:117-134). Because the poll cycle re-renders every 10s, a flat gray sentence like "Loading local run state…" is what a user sees, not a shimmer/placeholder shape — there is no skeleton anywhere in the app today.
@loopover/ui-kit/components/skeleton.tsx (packages/loopover-ui-kit/src/components/skeleton.tsx) already exists in the shared package both apps depend on, but is imported nowhere under apps/loopover-miner-ui/src (confirmed by grep). Likewise packages/loopover-ui-kit/src/components/chart.tsx ships in the same package but has zero usages anywhere in the miner UI — every metric today is a bare digit in a <dd>/<span> (e.g. index.tsx:24, portfolio.tsx:64-66), including the queued/in-progress/done breakdown that fetchPortfolioQueue already returns as structured counts (the same counts portfolio.tsx renders as three separate status-count Cards).
The LoadingState/EmptyState/ErrorState/StateBoundary primitives this issue is meant to adopt do not live in @loopover/ui-kit yet. They currently live app-locally at apps/loopover-ui/src/components/site/state-views.tsx, inside the main site app (apps/loopover-ui), which apps/loopover-miner-ui cannot import from directly — only @loopover/ui-kit is the real shared workspace package both apps consume (confirmed in the design-system audit: both apps declare "@loopover/ui-kit": ">=0.1.0 <2.0.0" and import @loopover/ui-kit/theme.css/@loopover/ui-kit/components/*; there is no cross-app import path between apps/loopover-ui and apps/loopover-miner-ui). #6244's audit (landed via PR #6474, docs(ui): audit ui-kit for existing chat-adjacent UI primitives, apps/loopover-ui/src/chat-ui-primitives-audit.md) confirmed these four primitives are "reusable as-is" and flagged that they need to be ported from apps/loopover-ui into @loopover/ui-kit proper before any other app can consume them — that port is scoped as its own separate issue (the state-views ui-kit port issue) and is a hard prerequisite for this one.
This issue is part of the visual/ui-kit polish track of the miner dashboard redesign (restyle-only — chat, the collapsible rail, and any nav/header changes are separate, independent tracks and out of scope here).
Requirements
⚠️ Read this before starting. This issue is blocked on a separate, not-yet-merged prerequisite: the issue that ports state-views.tsx's LoadingState/EmptyState/ErrorState/StateBoundary from apps/loopover-ui/src/components/site/state-views.tsx into @loopover/ui-kit (expected import path @loopover/ui-kit/components/state-views, following the same per-component-file convention as @loopover/ui-kit/components/skeleton and @loopover/ui-kit/components/scroll-area — confirm the exact export path against that issue's merged PR, do not assume it). Do not start implementation, and do not copy/duplicate state-views.tsx locally into apps/loopover-miner-ui as a workaround, until that port has merged. A PR that vendors a second local copy of these primitives instead of importing the shared @loopover/ui-kit version does not satisfy this issue.
Replace index.tsx's local Fallback() helper and its inline <p> loading/empty/error text with StateBoundary (and/or LoadingState/ErrorState/EmptyState as appropriate per card) imported from @loopover/ui-kit/components/state-views — not a locally re-implemented equivalent.
Add Skeleton placeholders (imported from @loopover/ui-kit/components/skeleton) for all three summary cards (Run activity, Portfolio queue, Claims) shown only before each card's own first successful fetch resolves. Once a card has data, subsequent 10s poll refreshes must keep showing the existing values — the skeleton must not re-flash on every poll cycle. (Track "has this card ever loaded" per-card, independently, since each card polls a different endpoint on its own schedule via its own usePolledFetch call.)
Render the Portfolio queue card's status breakdown (queued/in-progress/done, from the same fetchPortfolioQueue payload already used today) using @loopover/ui-kit/components/chart.tsx, using the existing --chart-1..5 tokens defined in packages/loopover-ui-kit/src/theme.css — do not hardcode new colors.
The Run activity card's chart adoption is explicitly out of scope for this issue. fetchRunStates/miner_run_state (apps/loopover-miner-ui/src/lib/run-history.ts, packages/loopover-miner/lib/run-state.js) only exposes current-moment state (repos tracked, currently-working count) with no time-series/history field, so a "trend" visual is not buildable from existing data without new backend plumbing — which this issue is explicitly forbidden from adding (see below). Do not add a client-only, page-load-scoped trend accumulator as a substitute; leave the Run activity card as a data card (optionally with a skeleton, no chart).
Do not modify apps/loopover-miner-ui/src/lib/ledgers.ts, apps/loopover-miner-ui/src/lib/portfolio-queue.ts, apps/loopover-miner-ui/src/lib/run-history.ts, or apps/loopover-miner-ui/src/lib/use-polled-fetch.ts (including the 10s DEFAULT_POLL_INTERVAL_MS poll cadence). This is a restyle-only change — no new endpoints, no new fetch calls, no changed polling behavior.
Do not modify apps/loopover-miner-ui/src/routes/run-history.tsx, portfolio.tsx, ledgers.tsx, or apps/loopover-miner-ui/src/routes/__root.tsx — those routes' analogous loading/error/skeleton adoption is scoped to separate sibling issues, not this one.
Every CONTRIBUTOR-authored visual PR against this repo requires before/after screenshots in the PR description (initial-load/skeleton state and populated state, at minimum) per house convention — a PR without them is subject to auto-close regardless of CI status.
Deliverables
index.tsx's Fallback()/<p>-literal loading/empty/error markup replaced with @loopover/ui-kit/components/state-views's StateBoundary/LoadingState/ErrorState/EmptyState
Skeleton placeholders (@loopover/ui-kit/components/skeleton) for all three summary cards, gated on each card's own first-successful-fetch, not re-triggered on later poll cycles
Portfolio queue status breakdown (queued/in-progress/done) rendered via @loopover/ui-kit/components/chart.tsx using existing --chart-* theme tokens
No changes to lib/ledgers.ts, lib/portfolio-queue.ts, lib/run-history.ts, lib/use-polled-fetch.ts, poll cadence, or any other route file
Before/after screenshots (initial load and populated state) attached to the PR
Test Coverage Requirements
apps/loopover-miner-ui sits under apps/**, which per this repo's Codecov config is not part of coverage.include — this PR will not be gated on a Codecov patch-coverage percentage the way a src/** change would be. That does not relax verification: if a component/route test already exists for this route (check for a routes/index.test.tsx or adjacent test file before assuming none exists) it must be updated to cover the new StateBoundary/Skeleton/chart render paths — the initial-load skeleton state, the post-load populated state (skeleton must not reappear), and the error state. If no such test currently exists for this route, add one exercising at minimum: (1) skeleton renders before first data resolves, (2) skeleton is gone and data renders after first resolve, (3) skeleton does not reappear on a second simulated poll tick, (4) the error path renders ErrorState instead of the old <p> text. In addition to any automated test, the before/after screenshots required above are the primary verification for this visual-only change.
Expected Outcome
The Overview route's loading/empty/error handling is consistent with the shared @loopover/ui-kit primitives instead of a locally hand-rolled Fallback()/<p> pattern; first page load shows skeleton placeholders instead of flat loading text on all three summary cards, with no skeleton flicker on subsequent 10s polls; the Portfolio queue card shows its queued/in-progress/done breakdown as a real chart instead of raw digits; no fetch/polling/data behavior changes anywhere in the route.
Links & Resources
apps/loopover-miner-ui/src/routes/index.tsx — the route being redesigned
apps/loopover-miner-ui/src/lib/use-polled-fetch.ts — polling hook (DEFAULT_POLL_INTERVAL_MS), not to be touched
apps/loopover-miner-ui/src/lib/portfolio-queue.ts, apps/loopover-miner-ui/src/lib/run-history.ts, apps/loopover-miner-ui/src/lib/ledgers.ts — the three data sources the summary cards read, not to be touched
apps/loopover-ui/src/components/site/state-views.tsx — current (pre-port) location of LoadingState/EmptyState/ErrorState/StateBoundary
Context
apps/loopover-miner-ui/src/routes/index.tsx(the Overview route) renders threeSummaryCards in agrid sm:grid-cols-3— Run activity (repos tracked, currently-working count), Portfolio queue (total/queued/in-progress/done + oldest-queued age), and Claims (active/total). Each card independently polls its own endpoint viausePolledFetch(apps/loopover-miner-ui/src/lib/use-polled-fetch.ts), hittingfetchRunStates,fetchPortfolioQueue, andfetchLedgersevery 10s (DEFAULT_POLL_INTERVAL_MS,lib/use-polled-fetch.ts:5). Loading/empty/error states are handled by a hand-rolledFallback()helper plus inline<p>text literals local toindex.tsx— the same repeated pattern the miner-ui audit found duplicated (with only the wording changed) across the other three routes (run-history.tsx:26-42,portfolio.tsx:39-56,ledgers.tsx:117-134). Because the poll cycle re-renders every 10s, a flat gray sentence like"Loading local run state…"is what a user sees, not a shimmer/placeholder shape — there is no skeleton anywhere in the app today.@loopover/ui-kit/components/skeleton.tsx(packages/loopover-ui-kit/src/components/skeleton.tsx) already exists in the shared package both apps depend on, but is imported nowhere underapps/loopover-miner-ui/src(confirmed by grep). Likewisepackages/loopover-ui-kit/src/components/chart.tsxships in the same package but has zero usages anywhere in the miner UI — every metric today is a bare digit in a<dd>/<span>(e.g.index.tsx:24,portfolio.tsx:64-66), including the queued/in-progress/done breakdown thatfetchPortfolioQueuealready returns as structured counts (the same countsportfolio.tsxrenders as three separate status-countCards).The
LoadingState/EmptyState/ErrorState/StateBoundaryprimitives this issue is meant to adopt do not live in@loopover/ui-kityet. They currently live app-locally atapps/loopover-ui/src/components/site/state-views.tsx, inside the main site app (apps/loopover-ui), whichapps/loopover-miner-uicannot import from directly — only@loopover/ui-kitis the real shared workspace package both apps consume (confirmed in the design-system audit: both apps declare"@loopover/ui-kit": ">=0.1.0 <2.0.0"and import@loopover/ui-kit/theme.css/@loopover/ui-kit/components/*; there is no cross-app import path betweenapps/loopover-uiandapps/loopover-miner-ui).#6244's audit (landed via PR #6474,docs(ui): audit ui-kit for existing chat-adjacent UI primitives,apps/loopover-ui/src/chat-ui-primitives-audit.md) confirmed these four primitives are "reusable as-is" and flagged that they need to be ported fromapps/loopover-uiinto@loopover/ui-kitproper before any other app can consume them — that port is scoped as its own separate issue (the state-views ui-kit port issue) and is a hard prerequisite for this one.This issue is part of the visual/ui-kit polish track of the miner dashboard redesign (restyle-only — chat, the collapsible rail, and any nav/header changes are separate, independent tracks and out of scope here).
Requirements
index.tsx's localFallback()helper and its inline<p>loading/empty/error text withStateBoundary(and/orLoadingState/ErrorState/EmptyStateas appropriate per card) imported from@loopover/ui-kit/components/state-views— not a locally re-implemented equivalent.Skeletonplaceholders (imported from@loopover/ui-kit/components/skeleton) for all three summary cards (Run activity, Portfolio queue, Claims) shown only before each card's own first successful fetch resolves. Once a card has data, subsequent 10s poll refreshes must keep showing the existing values — the skeleton must not re-flash on every poll cycle. (Track "has this card ever loaded" per-card, independently, since each card polls a different endpoint on its own schedule via its ownusePolledFetchcall.)fetchPortfolioQueuepayload already used today) using@loopover/ui-kit/components/chart.tsx, using the existing--chart-1..5tokens defined inpackages/loopover-ui-kit/src/theme.css— do not hardcode new colors.fetchRunStates/miner_run_state(apps/loopover-miner-ui/src/lib/run-history.ts,packages/loopover-miner/lib/run-state.js) only exposes current-moment state (repos tracked, currently-working count) with no time-series/history field, so a "trend" visual is not buildable from existing data without new backend plumbing — which this issue is explicitly forbidden from adding (see below). Do not add a client-only, page-load-scoped trend accumulator as a substitute; leave the Run activity card as a data card (optionally with a skeleton, no chart).apps/loopover-miner-ui/src/lib/ledgers.ts,apps/loopover-miner-ui/src/lib/portfolio-queue.ts,apps/loopover-miner-ui/src/lib/run-history.ts, orapps/loopover-miner-ui/src/lib/use-polled-fetch.ts(including the 10sDEFAULT_POLL_INTERVAL_MSpoll cadence). This is a restyle-only change — no new endpoints, no new fetch calls, no changed polling behavior.apps/loopover-miner-ui/src/routes/run-history.tsx,portfolio.tsx,ledgers.tsx, orapps/loopover-miner-ui/src/routes/__root.tsx— those routes' analogous loading/error/skeleton adoption is scoped to separate sibling issues, not this one.Deliverables
index.tsx'sFallback()/<p>-literal loading/empty/error markup replaced with@loopover/ui-kit/components/state-views'sStateBoundary/LoadingState/ErrorState/EmptyStateSkeletonplaceholders (@loopover/ui-kit/components/skeleton) for all three summary cards, gated on each card's own first-successful-fetch, not re-triggered on later poll cycles@loopover/ui-kit/components/chart.tsxusing existing--chart-*theme tokenslib/ledgers.ts,lib/portfolio-queue.ts,lib/run-history.ts,lib/use-polled-fetch.ts, poll cadence, or any other route fileTest Coverage Requirements
apps/loopover-miner-uisits underapps/**, which per this repo's Codecov config is not part ofcoverage.include— this PR will not be gated on a Codecov patch-coverage percentage the way asrc/**change would be. That does not relax verification: if a component/route test already exists for this route (check for aroutes/index.test.tsxor adjacent test file before assuming none exists) it must be updated to cover the new StateBoundary/Skeleton/chart render paths — the initial-load skeleton state, the post-load populated state (skeleton must not reappear), and the error state. If no such test currently exists for this route, add one exercising at minimum: (1) skeleton renders before first data resolves, (2) skeleton is gone and data renders after first resolve, (3) skeleton does not reappear on a second simulated poll tick, (4) the error path rendersErrorStateinstead of the old<p>text. In addition to any automated test, the before/after screenshots required above are the primary verification for this visual-only change.Expected Outcome
The Overview route's loading/empty/error handling is consistent with the shared
@loopover/ui-kitprimitives instead of a locally hand-rolledFallback()/<p>pattern; first page load shows skeleton placeholders instead of flat loading text on all three summary cards, with no skeleton flicker on subsequent 10s polls; the Portfolio queue card shows its queued/in-progress/done breakdown as a real chart instead of raw digits; no fetch/polling/data behavior changes anywhere in the route.Links & Resources
apps/loopover-miner-ui/src/routes/index.tsx— the route being redesignedapps/loopover-miner-ui/src/lib/use-polled-fetch.ts— polling hook (DEFAULT_POLL_INTERVAL_MS), not to be touchedapps/loopover-miner-ui/src/lib/portfolio-queue.ts,apps/loopover-miner-ui/src/lib/run-history.ts,apps/loopover-miner-ui/src/lib/ledgers.ts— the three data sources the summary cards read, not to be touchedapps/loopover-ui/src/components/site/state-views.tsx— current (pre-port) location ofLoadingState/EmptyState/ErrorState/StateBoundaryapps/loopover-ui/src/chat-ui-primitives-audit.md(from PR docs(ui): audit ui-kit for existing chat-adjacent UI primitives #6474 / research: audit ui-kit for existing message/chat-adjacent UI primitives #6244) — confirms these primitives are reusable as-ispackages/loopover-ui-kit/src/components/skeleton.tsx,packages/loopover-ui-kit/src/components/chart.tsx,packages/loopover-ui-kit/src/theme.css— shared components/tokens to consume__root.tsx) and the collapsible chat rail work — separate tracks, unrelated to this issue's scope