Skip to content

fix(ui): content-shaped loading skeletons for dead-letter/maintainer/miner panels (#6178) - #6452

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
nghetienhiep:fix/issue-6178
Jul 16, 2026
Merged

fix(ui): content-shaped loading skeletons for dead-letter/maintainer/miner panels (#6178)#6452
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
nghetienhiep:fix/issue-6178

Conversation

@nghetienhiep

Copy link
Copy Markdown
Contributor

Summary

StateBoundary's loadingSkeleton prop exists to swap the generic centered spinner for a
content-shaped placeholder so a list/table doesn't visibly jump when its data mounts (#793). Only
app.runs.tsx's RunsListSkeleton used it; three structurally similar list/table dashboards still
fell through to the generic LoadingState and jumped on first paint.

This adds a content-shaped skeleton for each, following RunsListSkeleton's approach (approximate the
real row/column structure), and wires it as loadingSkeleton:

  • dead-letter-queue-panel.tsxDeadLetterQueueSkeleton: the bordered table container (header
    strip + row placeholders) plus the range-text / pagination footer row.
  • maintainer-panel.tsx (drives ContributorQualityTable) — MaintainerDashboardSkeleton:
    mirrors the dashboard's top-level layout (refresh line, onboarding preview, 4-metric grid, the
    two-column install-health/settings row, and the reviewability table block).
  • miner-panel.tsxMinerDashboardSkeleton: the refresh line, 4-metric grid, the
    next-actions/scoreability two-column row, and the blockers/repo-fit row.

The generic LoadingState component is unchanged — it stays correct for boundaries with no fixed
content shape. UI-only, under apps/**.

Closes #6178

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (e.g. Closes #123) — a linked open issue is required for every contributor PR.

Validation

  • git diff --check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm --workspace @loopover/ui run test (276 passed, incl. the 3 new skeleton tests)
  • npm audit --audit-level=moderate
  • New behavior has unit tests (each panel: content-shaped skeleton shows instead of the generic spinner while loading)

If any required check was skipped, explain why:

  • This is a UI-only change under apps/** (Codecov-ignored). The backend/MCP checks
    (actionlint, typecheck, test:coverage, test:workers, build:mcp, test:mcp-pack,
    ui:openapi:check) don't cover this diff — no src/**, worker, MCP, or OpenAPI/schema surface is
    touched — so they were not run locally. The UI lint/typecheck/build/test gates that do cover this
    diff are all green.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. — N/A: no auth/session/CORS surface touched.
  • API/OpenAPI/MCP behavior is updated and tested where needed. — N/A: no API/OpenAPI/MCP change.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. — this improves the real loading state; no mock/demo data added.
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. — N/A: no doc/changelog change needed.

UI Evidence

Content-shaped loading skeletons (captured with the data endpoint held pending so the loading branch
stays on screen). Each mirrors its panel's real row/column structure, so no layout jump when data
arrives.

State / title JPG/PNG evidence
Dead-letter queue — skeleton (desktop) Dead-letter queue skeleton, desktop
Dead-letter queue — skeleton (mobile) Dead-letter queue skeleton, mobile
Maintainer console — skeleton (desktop) Maintainer dashboard skeleton, desktop
Maintainer console — skeleton (mobile) Maintainer dashboard skeleton, mobile
Miner dashboard — skeleton (desktop) Miner dashboard skeleton, desktop
Miner dashboard — skeleton (mobile) Miner dashboard skeleton, mobile

Notes

  • Follows the existing RunsListSkeleton pattern: local component, aria-hidden wrapper, Skeleton
    from @/components/ui/skeleton. Row/section counts are approximate — just enough to fill the
    viewport and match the loaded layout's shape.

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 16, 2026
@loopover-orb

loopover-orb Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-16 09:00:38 UTC

6 files · 1 AI reviewer · no blockers · readiness 88/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds content-shaped skeleton placeholders (mirroring RunsListSkeleton's pattern) for the dead-letter queue, maintainer, and miner panels, wiring each into StateBoundary's `loadingSkeleton` prop so loading no longer swaps to the generic spinner. The change is additive, UI-only, and each panel gets a matching test asserting the generic spinner/title is absent and `.animate-pulse` blocks are present. All three skeletons plausibly approximate their real layouts and the PR closes the linked issue #6178.

Nits — 5 non-blocking
  • The `Array.from({ length: 4 }, ...)` / `length: 5` row/metric counts in maintainer-panel.tsx, miner-panel.tsx, and dead-letter-queue-panel.tsx are magic numbers with no named constant explaining why 4 metrics or 5 rows was chosen.
  • The miner-panel.test.tsx comment references an sr-only status live-region to justify not using a role query, but doesn't assert against it directly, making the reasoning a little hard to verify from the test alone.
  • JSDoc-style block comments above each skeleton component repeat similar phrasing ("so the panel doesn't jump...") that could be trimmed to a single line per the repo's usual comment density.
  • Consider extracting a shared row-count constant (e.g. `SKELETON_ROW_COUNT = 5`) if these skeletons are likely to be tuned together later, though this is optional given each panel's shape differs.
  • If `RunsListSkeleton` already exports a reusable skeleton-row primitive, these three new skeletons could dedupe against it instead of each hand-rolling `Array.from` blocks.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #6178
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ❌ 8/20 High review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 135 registered-repo PR(s), 79 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor nghetienhiep; Gittensor profile; 135 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The PR adds content-shaped skeleton components for all three named panels (DeadLetterQueueSkeleton, MaintainerDashboardSkeleton, MinerDashboardSkeleton) that approximate each dashboard's actual row/column layout, and wires each as the loadingSkeleton prop on its StateBoundary, matching the RunsListSkeleton pattern without touching the generic LoadingState component.

Review context
  • Author: nghetienhiep
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, Vue, C#, JavaScript, C, C++, Go, Objective-C
  • Official Gittensor activity: 135 PR(s), 0 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Add a concise scope and risk note.
  • Then work through the remaining 1 step in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/ desktop before /
before /
after /
after /
diff /
diff /
/ mobile before / (mobile)
before / (mobile)
after / (mobile)
after / (mobile)

Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy · Diff highlights exactly what changed.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot 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.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit b09f262 into JSONbored:main Jul 16, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ui): content-shaped loadingSkeleton (#793) adopted in only 1 of several list/table StateBoundary sites, causing layout jump

1 participant