Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 50 additions & 57 deletions apps/ui/src/routes/subnets.$netuid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1300,74 +1300,67 @@ function CandidatesPanel({ netuid }: { netuid: number }) {
}

function GapsPanel({ netuid, compact }: { netuid: number; compact?: boolean }) {
const { data: gapsResult, isLoading, error } = useQuery(subnetGapsQuery(netuid));
// Match the seven sibling tabs on this page: a genuine fetch error surfaces the
// shared red-bordered ErrorState (with retry) via QueryErrorBoundary, rather
// than reusing the success-case EmptyState look. Loading is the Suspense fallback.
return (
<SectionAnchor
id="gaps"
title={compact ? "Known gaps" : "Gaps"}
subtitle="Missing resources, profile incompleteness, and curation notes."
info="GET /api/v1/subnets/{netuid}/gaps"
>
<QueryErrorBoundary>
<Suspense fallback={<Skeleton className="h-24 w-full" />}>
<GapsList netuid={netuid} />
</Suspense>
</QueryErrorBoundary>
</SectionAnchor>
);
}

function GapsList({ netuid }: { netuid: number }) {
const { data: gapsResult } = useSuspenseQuery(subnetGapsQuery(netuid));
const gaps = gapsResult?.data;
const missing = gaps?.missing_kinds ?? [];
const notes = gaps?.gap_notes ?? [];
if (isLoading) {
return (
<SectionAnchor id="gaps" title={compact ? "Known gaps" : "Gaps"}>
<Skeleton className="h-24 w-full" />
</SectionAnchor>
);
}
if (error) {
return (
<SectionAnchor id="gaps" title={compact ? "Known gaps" : "Gaps"}>
<EmptyState
title="Gaps unavailable"
description="The subnet gaps endpoint did not respond."
action={RECOVERY.gaps}
/>
</SectionAnchor>
);
}
if (missing.length === 0 && notes.length === 0) {
return (
<SectionAnchor id="gaps" title="Gaps">
<EmptyState
title="No outstanding gaps"
description="Profile looks complete."
action={RECOVERY.gaps}
/>
</SectionAnchor>
<EmptyState
title="No outstanding gaps"
description="Profile looks complete."
action={RECOVERY.gaps}
/>
);
}
return (
<SectionAnchor
id="gaps"
title={compact ? "Known gaps" : "Gaps"}
subtitle="Missing resources, profile incompleteness, and curation notes."
info="GET /api/v1/subnets/{netuid}/gaps"
>
<div className="rounded-lg border border-border bg-card p-4 space-y-3">
{missing.length > 0 ? (
<div>
<div className="mg-label mb-1">Missing kinds</div>
<div className="flex flex-wrap gap-1">
{missing.map((k) => (
<span
key={k}
className="rounded border border-dashed border-ink-subtle bg-paper px-1.5 py-0.5 font-mono text-[10px] text-ink-muted"
>
{k}
</span>
))}
</div>
</div>
) : null}
{notes.length > 0 ? (
<ul className="space-y-1 text-[12px] text-ink leading-relaxed">
{notes.map((n, i) => (
<li key={i}>· {n}</li>
<div className="rounded-lg border border-border bg-card p-4 space-y-3">
{missing.length > 0 ? (
<div>
<div className="mg-label mb-1">Missing kinds</div>
<div className="flex flex-wrap gap-1">
{missing.map((k) => (
<span
key={k}
className="rounded border border-dashed border-ink-subtle bg-paper px-1.5 py-0.5 font-mono text-[10px] text-ink-muted"
>
{k}
</span>
))}
</ul>
) : null}
<div className="border-t border-border pt-2 text-[11px] text-ink-muted">
Help close these gaps by opening a PR against the public registry repo.
</div>
</div>
) : null}
{notes.length > 0 ? (
<ul className="space-y-1 text-[12px] text-ink leading-relaxed">
{notes.map((n, i) => (
<li key={i}>· {n}</li>
))}
</ul>
) : null}
<div className="border-t border-border pt-2 text-[11px] text-ink-muted">
Help close these gaps by opening a PR against the public registry repo.
</div>
</SectionAnchor>
</div>
);
}

Expand Down
Loading