Skip to content

Commit 2355361

Browse files
authored
feat: add surge links and loading state to test status table (#72)
* feat: add surge links and loading state to test status table * update skeleton rows
1 parent fa1daa3 commit 2355361

File tree

6 files changed

+118
-5
lines changed

6 files changed

+118
-5
lines changed

src/app/TestStatusTable.tsx

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import React from "react";
22

33
import {
4+
Bullseye,
45
Button,
6+
EmptyState,
7+
EmptyStateBody,
8+
EmptyStateVariant,
59
PageSection,
610
Toolbar,
711
ToolbarContent,
812
ToolbarGroup,
913
ToolbarItem,
1014
} from "@patternfly/react-core";
1115
import { Table, Thead, Tr, Th, Tbody, Td } from "@patternfly/react-table";
16+
import SkeletonTable from "@patternfly/react-component-groups/dist/cjs/SkeletonTable";
17+
import ExclamationCircleIcon from "@patternfly/react-icons/dist/dynamic/icons/exclamation-circle-icon";
1218
import { repoStatus } from "@/getters";
1319
import { useSession } from "next-auth/react";
1420

@@ -21,13 +27,17 @@ export interface TestStatusItem extends Omit<repoStatus, "workflowStatus"> {
2127

2228
export interface TestStatusTableProps {
2329
statusItems: TestStatusItem[];
30+
isLoading: boolean;
31+
hasError: boolean;
2432
refresh: () => Promise<void>;
2533
submit: () => Promise<void>;
2634
renewBumps: () => Promise<void>;
2735
}
2836

2937
export const TestStatusTable: React.FunctionComponent<TestStatusTableProps> = ({
3038
statusItems,
39+
isLoading,
40+
hasError,
3141
refresh,
3242
submit,
3343
renewBumps,
@@ -102,7 +112,51 @@ export const TestStatusTable: React.FunctionComponent<TestStatusTableProps> = ({
102112
</Toolbar>
103113
) : null;
104114

105-
const columns = ["Name", "Status", "Synced with upstream?"];
115+
const columns = ["Name", "Status", "Synced with upstream?", "Preview"];
116+
117+
if (isLoading) {
118+
return (
119+
<PageSection isWidthLimited>
120+
{toolbar}
121+
<SkeletonTable rowsCount={statusItems.length || 8} columns={columns} />
122+
</PageSection>
123+
);
124+
}
125+
126+
if (hasError) {
127+
return (
128+
<PageSection isWidthLimited>
129+
{toolbar}
130+
<Table aria-label="Testing status error">
131+
<Thead>
132+
<Tr>
133+
{columns.map((column) => (
134+
<Th key={column}>{column}</Th>
135+
))}
136+
</Tr>
137+
</Thead>
138+
<Tbody>
139+
<Tr>
140+
<Td colSpan={columns.length}>
141+
<Bullseye>
142+
<EmptyState
143+
icon={ExclamationCircleIcon}
144+
titleText="Unable to connect"
145+
headingLevel="h2"
146+
variant={EmptyStateVariant.sm}
147+
>
148+
<EmptyStateBody>
149+
There was an error retrieving data. Check your connection and reload the page.
150+
</EmptyStateBody>
151+
</EmptyState>
152+
</Bullseye>
153+
</Td>
154+
</Tr>
155+
</Tbody>
156+
</Table>
157+
</PageSection>
158+
);
159+
}
106160

107161
return (
108162
<PageSection isWidthLimited>
@@ -116,17 +170,24 @@ export const TestStatusTable: React.FunctionComponent<TestStatusTableProps> = ({
116170
</Tr>
117171
</Thead>
118172
<Tbody>
119-
{statusItems.map((item, rowIndex) => (
173+
{statusItems.map((item) => (
120174
<Tr key={item.name}>
121175
<Td dataLabel={columns[0]} width={30}>
122176
{<a href={item.upstreamOwnerLink} aria-label={`upstream repo ${item.name}`}>{item.name}</a>}
123177
</Td>
124178
<Td dataLabel={columns[1]} width={30}>
125179
{<a href={item.bumpPRLink} aria-label={`dependency bump PR, status ${item.status}`}>{item.status}</a>}
126180
</Td>
127-
<Td dataLabel={columns[1]} width={40}>
181+
<Td dataLabel={columns[2]} width={30}>
128182
{item.syncStatus.toString()}
129183
</Td>
184+
<Td dataLabel={columns[3]} width={10}>
185+
{item.previewUrl ? (
186+
<a href={item.previewUrl} aria-label={`preview for ${item.name}`}>Preview</a>
187+
) : (
188+
"-"
189+
)}
190+
</Td>
130191
</Tr>
131192
))}
132193
</Tbody>

src/getters/getPreviewUrl.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import octokit from "../octokit";
2+
3+
export async function getPreviewUrl(
4+
prNumber: number,
5+
repo: string,
6+
owner: string = "patternfly-extension-testing"
7+
): Promise<string> {
8+
if (prNumber === 0) {
9+
return "";
10+
}
11+
12+
const res = await octokit
13+
.request("GET /repos/{owner}/{repo}/issues/{issue_number}/comments", {
14+
owner,
15+
repo,
16+
issue_number: prNumber,
17+
})
18+
.catch((err) => {
19+
console.error(err);
20+
return { status: err.status, data: [] };
21+
});
22+
23+
if (res.status !== 200) {
24+
return "";
25+
}
26+
27+
for (const comment of res.data) {
28+
const match = comment.body?.match(/https?:\/\/[^\s)]*\.surge\.sh[^\s)]*/);
29+
if (match) {
30+
return match[0];
31+
}
32+
}
33+
34+
return "";
35+
}

src/getters/getStatus.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import {
44
getWorkflowResult,
55
getSyncStatus,
66
getUpstreamOwner,
7+
getPreviewUrl,
78
} from "./index";
89

910
export interface repoStatus {
1011
workflowStatus: string;
1112
syncStatus: string;
1213
bumpPRLink: string;
1314
upstreamOwnerLink: string;
15+
previewUrl: string;
1416
}
1517

1618
export type Statuses = {
@@ -37,12 +39,14 @@ export async function getStatus(
3739

3840
const syncStatus = await getSyncStatus(repo, "main", owner, upstreamOwner);
3941
const workflowStatus = await getWorkflowResult(bumpNumber, repo, owner);
42+
const previewUrl = await getPreviewUrl(bumpNumber, repo, owner);
4043

4144
return {
4245
workflowStatus,
4346
syncStatus,
4447
bumpPRLink,
4548
upstreamOwnerLink,
49+
previewUrl,
4650
};
4751
});
4852

src/getters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./getWorkflowResult";
44
export * from "./getStatus";
55
export * from "./getSyncStatus";
66
export * from "./getUpstreamOwner";
7+
export * from "./getPreviewUrl";

src/pages/api/status.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export default async function handler(
2121
syncStatus: "ERROR owner must be of type string if passed",
2222
bumpPRLink: "ERROR owner must be of type string if passed",
2323
upstreamOwnerLink: "ERROR owner must be of type string if passed",
24+
previewUrl: "",
2425
},
2526
},
2627
});

src/pages/index.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ export default function Home() {
1515
repos: [],
1616
statuses: {},
1717
});
18+
const [isLoading, setIsLoading] = useState(true);
19+
const [hasError, setHasError] = useState(false);
1820

19-
const refresh = () =>
20-
axios.get("/api/status").then((res) => setRepoStatuses(res.data));
21+
const refresh = () => {
22+
setIsLoading(true);
23+
setHasError(false);
24+
return axios
25+
.get("/api/status")
26+
.then((res) => setRepoStatuses(res.data))
27+
.catch(() => setHasError(true))
28+
.finally(() => setIsLoading(false));
29+
};
2130

2231
useEffect(() => {
2332
refresh();
@@ -63,6 +72,8 @@ export default function Home() {
6372
<Tab eventKey={0} title={<TabTitleText>Test Status</TabTitleText>}>
6473
<TestStatusTable
6574
statusItems={statusItems}
75+
isLoading={isLoading}
76+
hasError={hasError}
6677
refresh={refresh}
6778
submit={syncRepos}
6879
renewBumps={renewBumps}

0 commit comments

Comments
 (0)