Skip to content

Commit 966ee7a

Browse files
authored
OOT HUD: Add frontend components — summary, dashboard, PR section (Part 2/3) (#8111)
## Summary This is **Part 2 of 3** of the OOT HUD pipeline, split from #8069 per reviewer request. **Depends on:** #8110 (Part 1 — queries + utils + tests) This PR adds the frontend pages and components for the OOT HUD: - **OOT Summary page** (`torchci/pages/oot/index.tsx`) — aggregated view of all registered OOT backends with latest status, PR count, and success rates - **Per-backend dashboard** (`torchci/pages/oot/[org]/[repo].tsx`) — detailed job history per downstream repo with filtering - **OotPrSection component** (`torchci/components/oot/OotPrSection.tsx`) — shows OOT CI results inline on pytorch/pytorch PR pages All components consume the ClickHouse queries and utility library added in Part 1. ### PR Stack 1. #8110 — OOT HUD: Add ClickHouse queries, utility library, and unit tests (Part 1/3) 2. **This PR** — OOT HUD: Add frontend components — summary, dashboard, PR section (Part 2/3) 3. #8112 — OOT HUD: Add API endpoint, PR page integration, and replicator mapping (Part 3/3) ## Test Plan - Frontend pages can be verified via Vercel preview once dummy data is populated in the `oot_workflow_job` ClickHouse table (see #8105) - Components import from `ootUtils.ts` (Part 1) — no runtime dependencies on the API endpoint (Part 3)
1 parent 9880ebf commit 966ee7a

3 files changed

Lines changed: 621 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
2+
import {
3+
Accordion,
4+
AccordionDetails,
5+
AccordionSummary,
6+
Chip,
7+
Link,
8+
Stack,
9+
Table,
10+
TableBody,
11+
TableCell,
12+
TableContainer,
13+
TableHead,
14+
TableRow,
15+
Typography,
16+
} from "@mui/material";
17+
import { durationDisplay } from "components/common/TimeUtils";
18+
import { fetcher } from "lib/GeneralUtils";
19+
import { conclusionColor, conclusionLabel } from "lib/oot/ootUtils";
20+
import useSWR from "swr";
21+
22+
interface OotPrResult {
23+
downstream_repo: string;
24+
workflow_name: string;
25+
job_name: string;
26+
check_run_id: string;
27+
run_id: string;
28+
run_attempt: number;
29+
status: string;
30+
conclusion: string;
31+
duration_seconds: number;
32+
workflow_run_url: string;
33+
artifact_url: string;
34+
started_at: string;
35+
queue_time: number | null;
36+
execution_time: number | null;
37+
}
38+
39+
export default function OotPrSection({ prNumber }: { prNumber: number }) {
40+
const url = `/api/clickhouse/oot_pr_results?parameters=${encodeURIComponent(
41+
JSON.stringify({ pr: String(prNumber) })
42+
)}`;
43+
const { data, error } = useSWR<OotPrResult[]>(url, fetcher, {
44+
refreshInterval: 60_000,
45+
});
46+
47+
if (error || !data || data.length === 0) return null;
48+
49+
const successCount = data.filter(
50+
(r) => r.status === "completed" && r.conclusion === "success"
51+
).length;
52+
const totalCompleted = data.filter((r) => r.status === "completed").length;
53+
const inProgress = data.filter((r) => r.status === "in_progress").length;
54+
55+
const summaryText = [
56+
totalCompleted > 0 ? `${successCount}/${totalCompleted} passed` : null,
57+
inProgress > 0 ? `${inProgress} running` : null,
58+
]
59+
.filter(Boolean)
60+
.join(", ");
61+
62+
return (
63+
<Accordion defaultExpanded={false} sx={{ mt: 2 }}>
64+
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
65+
<Stack direction="row" spacing={1} alignItems="center">
66+
<Typography variant="subtitle1">
67+
<strong>Out-of-Tree Backends</strong>
68+
</Typography>
69+
<Typography variant="body2" color="text.secondary">
70+
({summaryText})
71+
</Typography>
72+
</Stack>
73+
</AccordionSummary>
74+
<AccordionDetails>
75+
<TableContainer>
76+
<Table size="small">
77+
<TableHead>
78+
<TableRow>
79+
<TableCell>
80+
<strong>Backend</strong>
81+
</TableCell>
82+
<TableCell>
83+
<strong>Job</strong>
84+
</TableCell>
85+
<TableCell align="center">
86+
<strong>Status</strong>
87+
</TableCell>
88+
<TableCell align="right">
89+
<strong>Duration</strong>
90+
</TableCell>
91+
<TableCell>
92+
<strong>Links</strong>
93+
</TableCell>
94+
</TableRow>
95+
</TableHead>
96+
<TableBody>
97+
{data.map((row, i) => (
98+
<TableRow key={i} hover>
99+
<TableCell>{row.downstream_repo}</TableCell>
100+
<TableCell>{row.job_name}</TableCell>
101+
<TableCell align="center">
102+
<Chip
103+
label={conclusionLabel(row.status, row.conclusion)}
104+
color={conclusionColor(row.status, row.conclusion)}
105+
size="small"
106+
/>
107+
</TableCell>
108+
<TableCell align="right">
109+
{row.duration_seconds
110+
? durationDisplay(Math.round(row.duration_seconds))
111+
: "–"}
112+
</TableCell>
113+
<TableCell>
114+
<Stack direction="row" spacing={1}>
115+
{row.workflow_run_url && (
116+
<Link
117+
href={row.workflow_run_url}
118+
target="_blank"
119+
rel="noopener"
120+
variant="body2"
121+
>
122+
Run
123+
</Link>
124+
)}
125+
{row.artifact_url && (
126+
<Link
127+
href={row.artifact_url}
128+
target="_blank"
129+
rel="noopener"
130+
variant="body2"
131+
>
132+
Artifacts
133+
</Link>
134+
)}
135+
</Stack>
136+
</TableCell>
137+
</TableRow>
138+
))}
139+
</TableBody>
140+
</Table>
141+
</TableContainer>
142+
</AccordionDetails>
143+
</Accordion>
144+
);
145+
}

0 commit comments

Comments
 (0)