Skip to content

Commit 43f94a7

Browse files
authored
Add url param support to runners page (#7127)
When you filter the /runners/[org] results, it now updates the url so that you can save/book mark frequent queries For example, when you for "b200" the url will get updated to "/runners/pytorch?search=b200"
1 parent a51d4f0 commit 43f94a7

File tree

3 files changed

+73
-24
lines changed

3 files changed

+73
-24
lines changed

torchci/lib/ParamSelector.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,37 @@ export function ParamSelector({
1515
return (
1616
<form
1717
className={styles.branchForm}
18-
onSubmit={(e) => {
18+
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
1919
e.preventDefault();
20-
// @ts-ignore
21-
handleSubmit(e.target[0].value);
20+
const form = e.currentTarget;
21+
const input = form.querySelector(
22+
'input[type="text"]'
23+
) as HTMLInputElement;
24+
if (input) {
25+
handleSubmit(input.value);
26+
}
2227
}}
2328
>
2429
<input
25-
onChange={(e) => {
26-
// @ts-ignore
30+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
2731
setVal(e.target.value);
2832
}}
29-
onFocus={(e) => {
33+
onFocus={(e: React.FocusEvent<HTMLInputElement>) => {
3034
e.target.select();
31-
console.log(e.target.value);
3235
}}
33-
onBlur={(e) => {
36+
onBlur={(e: React.FocusEvent<HTMLInputElement>) => {
3437
if (e.target.value !== value && e.target.value.length > 0) {
3538
e.preventDefault();
3639
handleSubmit(e.target.value);
3740
}
3841
}}
39-
onKeyDown={(e) => {
42+
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
4043
if (e.key === "Escape") {
4144
e.preventDefault();
42-
// @ts-ignore
43-
e.target.value = value;
45+
const input = e.currentTarget;
46+
input.value = value;
4447
setVal(value);
45-
// @ts-ignore
46-
e.target.blur();
48+
input.blur();
4749
}
4850
}}
4951
size={val.length || 0}

torchci/pages/api/runners/[org].ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ export const ALLOWED_ORGS = ["pytorch", "meta-pytorch"];
1818

1919
// Shared function to map GitHub API runner response to our format
2020
function mapRunnerFromGitHubAPI(runner: any): RunnerData {
21-
// Debug: Log full runner object for runners with no labels
22-
if (!runner.labels || runner.labels.length === 0) {
23-
console.log("Runner with no labels:", JSON.stringify(runner, null, 2));
24-
}
25-
2621
return {
2722
id: runner.id,
2823
name: runner.name,
@@ -187,9 +182,7 @@ export default async function handler(
187182
// Check if org is allowed
188183
if (!ALLOWED_ORGS.includes(org.toLowerCase())) {
189184
return res.status(403).json({
190-
error: `Access denied. Only ${ALLOWED_ORGS.join(
191-
", "
192-
)} organizations are supported.`,
185+
error: "Access denied. This organization is not supported.",
193186
});
194187
}
195188

torchci/pages/runners/[org].tsx

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ import {
4040
import { RunnerGroupCard } from "components/runners/RunnerGroupCard";
4141
import { ParamSelector } from "lib/ParamSelector";
4242
import { RunnersApiResponse, unknownGoesLast } from "lib/runnerUtils";
43+
import debounce from "lodash/debounce";
4344
import { useSession } from "next-auth/react";
4445
import { useRouter } from "next/router";
45-
import { useMemo, useState } from "react";
46+
import type { ParsedUrlQuery } from "querystring";
47+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4648
import useSWR from "swr";
4749

4850
// Define sort order constants to prevent typos
@@ -71,10 +73,58 @@ export default function RunnersPage() {
7173
const orgParam = typeof org === "string" ? org : null;
7274

7375
const { data: _session, status: _status } = useSession();
74-
const [searchTerm, setSearchTerm] = useState("");
76+
77+
// Utility function to extract search term from URL query
78+
const getSearchFromQuery = (query: ParsedUrlQuery): string => {
79+
return typeof query.search === "string" ? query.search : "";
80+
};
81+
82+
// Get search term from URL parameters
83+
const [searchTerm, setSearchTerm] = useState(
84+
getSearchFromQuery(router.query)
85+
);
7586
const [sortOrder, setSortOrder] = useState<SortOrder>(SORT_ALPHABETICAL);
7687
const [expandedGroup, setExpandedGroup] = useState<string | null>(null);
7788

89+
// Use ref to access current router.query without causing debounce recreations
90+
const routerQueryRef = useRef(router.query);
91+
routerQueryRef.current = router.query;
92+
93+
// Sync search state with URL changes
94+
useEffect(() => {
95+
const searchParam = router.query.search;
96+
const newSearchTerm = getSearchFromQuery({ search: searchParam });
97+
setSearchTerm(newSearchTerm);
98+
}, [router.query.search]);
99+
100+
// Debounced function to update search in URL
101+
// Debouncing lets us limit the rate at which the
102+
// function is called, so that it doesn't get called on every
103+
// keystroke. Instead, it'll wait for DEBOUNCE_DELAY_MS
104+
// before executing.
105+
const DEBOUNCE_DELAY_MS = 300;
106+
const updateSearchInUrl = useCallback(
107+
debounce((newSearchTerm: string) => {
108+
const query = { ...routerQueryRef.current };
109+
if (newSearchTerm) {
110+
query.search = newSearchTerm;
111+
} else {
112+
delete query.search;
113+
}
114+
router.push({ pathname: router.pathname, query }, undefined, {
115+
shallow: true,
116+
});
117+
}, DEBOUNCE_DELAY_MS),
118+
[router.pathname]
119+
);
120+
121+
// Cleanup debounced function on unmount or when it changes
122+
useEffect(() => {
123+
return () => {
124+
updateSearchInUrl.cancel();
125+
};
126+
}, [updateSearchInUrl]);
127+
78128
// Handle URL editing for organization
79129
const handleOrgSubmit = (newOrg: string) => {
80130
if (!newOrg) return;
@@ -192,7 +242,11 @@ export default function RunnersPage() {
192242
variant="outlined"
193243
placeholder="Search runners by name, ID, OS, or labels..."
194244
value={searchTerm}
195-
onChange={(e) => setSearchTerm(e.target.value)}
245+
onChange={(e) => {
246+
const value = e.target.value;
247+
setSearchTerm(value); // Immediate UI update
248+
updateSearchInUrl(value); // Debounced URL update
249+
}}
196250
sx={{ maxWidth: 600, mb: 2 }}
197251
/>
198252

0 commit comments

Comments
 (0)