Skip to content
Closed
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions src/api/models/Dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ export type MinerEvaluation = {
lifetimeAlpha?: number;
lifetimeTao?: number;
lifetimeUsd?: number;
// Metagraph standing — present on the leaderboard list (`GET /miners`); used
// to surface a miner's authoritative subnet rank. Absent on the single-miner
// endpoint, so always optional.
metagraphRank?: number;
metagraphTrust?: number;
metagraphConsensus?: number;
metagraphIncentive?: number;
metagraphEmission?: number;
};

export type GithubMinerData = {
Expand Down
90 changes: 90 additions & 0 deletions src/components/common/SegmentedToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { Box, ToggleButton, ToggleButtonGroup } from '@mui/material';

export interface SegmentedToggleOption<T extends string> {
value: T;
label: string;
/** Optional muted count shown after the label. */
count?: number;
}

interface SegmentedToggleProps<T extends string> {
value: T;
onChange: (value: T) => void;
options: ReadonlyArray<SegmentedToggleOption<T>>;
ariaLabel?: string;
}

/**
* Segmented pill toggle — a bordered container with one elevated active pill,
* each segment optionally showing a muted count. Shared by the dashboard filter
* switches (eligibility, PR status, issue status) so they read identically.
*/
function SegmentedToggle<T extends string>({
value,
onChange,
options,
ariaLabel,
}: SegmentedToggleProps<T>) {
return (
<ToggleButtonGroup
value={value}
exclusive
onChange={(_e, v: T | null) => v && onChange(v)}
size="small"
aria-label={ariaLabel}
sx={{
border: '1px solid',
borderColor: 'border.light',
borderRadius: 2,
p: 0.5,
gap: 0.5,
flexWrap: 'wrap',
// Each segment reads as its own pill, not a merged button group.
'& .MuiToggleButtonGroup-grouped': {
m: 0,
border: '1px solid transparent',
borderRadius: '6px !important',
},
'& .MuiToggleButton-root': {
textTransform: 'none',
fontSize: '0.78rem',
fontWeight: 500,
color: 'text.secondary',
px: 1.25,
py: 0.4,
lineHeight: 1.2,
transition: 'all 0.2s',
'&:hover': {
color: 'text.primary',
backgroundColor: 'surface.light',
},
'&.Mui-selected': {
color: 'text.primary',
fontWeight: 700,
backgroundColor: 'surface.elevated',
borderColor: 'border.medium',
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.45)',
'&:hover': { backgroundColor: 'surface.elevated' },
},
},
}}
>
{options.map((o) => (
<ToggleButton key={o.value} value={o.value}>
{o.label}
{o.count !== undefined && (
<Box
component="span"
sx={{ ml: 0.6, color: 'text.tertiary', fontWeight: 600 }}
>
{o.count}
</Box>
)}
</ToggleButton>
))}
</ToggleButtonGroup>
);
}

export default SegmentedToggle;
2 changes: 1 addition & 1 deletion src/components/common/TablePagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function parseMinerExplorerPageParam(raw: string | null): number {
export function getMinerExplorerPaging<T>(
items: readonly T[],
page: number,
rows: MinerExplorerRowsOption,
rows: number | 'all',
): {
totalPages: number;
safePage: number;
Expand Down
18 changes: 2 additions & 16 deletions src/components/leaderboard/MinersLeaderTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import theme, {
} from '../../theme';
import { getRepositoryOwnerAvatarSrc } from '../../utils/avatar';
import { parseNumber } from '../../utils/ExplorerUtils';
import { formatRelativeTimeAgo as formatLastActive } from '../../utils/format';
import { paginateItems } from '../../utils';
import { useDataTableParams } from '../../hooks/useDataTableParams';
import { useWatchlist } from '../../hooks/useWatchlist';
Expand Down Expand Up @@ -160,21 +161,6 @@ const fmtUsd = (n: number): string => {
return `$${Math.round(n).toLocaleString()}`;
};

const formatLastActive = (iso: string | null | undefined): string => {
if (!iso) return '—';
const t = Date.parse(iso);
if (!Number.isFinite(t)) return '—';
const mins = Math.floor((Date.now() - t) / 60_000);
if (mins < 1) return 'just now';
if (mins < 60) return `${mins}m ago`;
const hours = Math.floor(mins / 60);
if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24);
if (days < 30) return `${days}d ago`;
const months = Math.floor(days / 30);
return `${months}mo ago`;
};

const SparklineButton: React.FC<{
username: string;
githubId?: string;
Expand Down Expand Up @@ -1717,7 +1703,7 @@ interface ToolbarProps {
const SORT_FIELD_LABELS: Record<SortField, string> = {
score: 'Score',
usd: '$/Day',
credibility: 'Success rate',
credibility: 'Credibility',
volume: 'Volume (PRs + issues)',
active: 'Last active',
movement: 'Rank movement',
Expand Down
187 changes: 0 additions & 187 deletions src/components/miners/CredibilityChart.tsx

This file was deleted.

Loading
Loading