Skip to content

Commit cc4806d

Browse files
committed
feat(site): load previous builds
1 parent fc41851 commit cc4806d

File tree

7 files changed

+120
-40
lines changed

7 files changed

+120
-40
lines changed

site/src/api/queries/workspaceBuilds.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { UseInfiniteQueryOptions } from "@tanstack/react-query";
12
import * as API from "api/api";
2-
import { WorkspaceBuildsRequest } from "api/typesGenerated";
3+
import { WorkspaceBuild, WorkspaceBuildsRequest } from "api/typesGenerated";
34

45
export const workspaceBuildByNumber = (
56
username: string,
@@ -13,14 +14,22 @@ export const workspaceBuildByNumber = (
1314
};
1415
};
1516

16-
export const workspaceBuilds = (
17+
export const infiniteWorkspaceBuilds = (
1718
workspaceId: string,
1819
req?: WorkspaceBuildsRequest,
19-
) => {
20+
): UseInfiniteQueryOptions<WorkspaceBuild[]> => {
21+
const limit = req?.limit ?? 25;
22+
2023
return {
2124
queryKey: ["workspaceBuilds", workspaceId, req],
22-
queryFn: () => {
23-
return API.getWorkspaceBuilds(workspaceId, req);
25+
getNextPageParam: (lastPage, pages) => {
26+
return pages.length + 1;
27+
},
28+
queryFn: ({ pageParam = 0 }) => {
29+
return API.getWorkspaceBuilds(workspaceId, {
30+
limit,
31+
offset: pageParam <= 0 ? 0 : (pageParam - 1) * limit,
32+
});
2433
},
2534
};
2635
};

site/src/pages/WorkspacePage/BuildsTable.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Story = StoryObj<typeof BuildsTable>;
1313
export const Example: Story = {
1414
args: {
1515
builds: MockBuilds,
16+
hasMoreBuilds: true,
1617
},
1718
};
1819

@@ -21,3 +22,10 @@ export const Empty: Story = {
2122
builds: [],
2223
},
2324
};
25+
26+
export const NoMoreBuilds: Story = {
27+
args: {
28+
builds: MockBuilds,
29+
hasMoreBuilds: false,
30+
},
31+
};

site/src/pages/WorkspacePage/BuildsTable.tsx

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,70 @@ import * as TypesGen from "api/typesGenerated";
1010
import { EmptyState } from "components/EmptyState/EmptyState";
1111
import { TableLoader } from "components/TableLoader/TableLoader";
1212
import { BuildRow } from "./BuildRow";
13+
import { Stack } from "components/Stack/Stack";
14+
import LoadingButton from "@mui/lab/LoadingButton";
15+
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
1316

1417
export const Language = {
1518
emptyMessage: "No builds found",
1619
};
1720

1821
export interface BuildsTableProps {
19-
builds?: TypesGen.WorkspaceBuild[];
22+
builds: TypesGen.WorkspaceBuild[] | undefined;
23+
onLoadMoreBuilds: () => void;
24+
isLoadingMoreBuilds: boolean;
25+
hasMoreBuilds: boolean;
2026
}
2127

2228
export const BuildsTable: FC<React.PropsWithChildren<BuildsTableProps>> = ({
2329
builds,
30+
onLoadMoreBuilds,
31+
isLoadingMoreBuilds,
32+
hasMoreBuilds,
2433
}) => {
2534
return (
26-
<TableContainer>
27-
<Table data-testid="builds-table" aria-describedby="builds table">
28-
<TableBody>
29-
{builds ? (
30-
<Timeline
31-
items={builds}
32-
getDate={(build) => new Date(build.created_at)}
33-
row={(build) => <BuildRow key={build.id} build={build} />}
34-
/>
35-
) : (
36-
<TableLoader />
37-
)}
35+
<Stack>
36+
<TableContainer>
37+
<Table data-testid="builds-table" aria-describedby="builds table">
38+
<TableBody>
39+
{builds ? (
40+
<Timeline
41+
items={builds}
42+
getDate={(build) => new Date(build.created_at)}
43+
row={(build) => <BuildRow key={build.id} build={build} />}
44+
/>
45+
) : (
46+
<TableLoader />
47+
)}
3848

39-
{builds && builds.length === 0 && (
40-
<TableRow>
41-
<TableCell colSpan={999}>
42-
<Box p={4}>
43-
<EmptyState message={Language.emptyMessage} />
44-
</Box>
45-
</TableCell>
46-
</TableRow>
47-
)}
48-
</TableBody>
49-
</Table>
50-
</TableContainer>
49+
{builds && builds.length === 0 && (
50+
<TableRow>
51+
<TableCell colSpan={999}>
52+
<Box p={4}>
53+
<EmptyState message={Language.emptyMessage} />
54+
</Box>
55+
</TableCell>
56+
</TableRow>
57+
)}
58+
</TableBody>
59+
</Table>
60+
</TableContainer>
61+
<LoadingButton
62+
disabled={!hasMoreBuilds}
63+
onClick={onLoadMoreBuilds}
64+
loading={isLoadingMoreBuilds}
65+
loadingPosition="start"
66+
variant="outlined"
67+
color="neutral"
68+
startIcon={<ArrowDownwardOutlined />}
69+
css={{
70+
display: "inline-flex",
71+
margin: "auto",
72+
borderRadius: "9999px",
73+
}}
74+
>
75+
Load previous builds
76+
</LoadingButton>
77+
</Stack>
5178
);
5279
};

site/src/pages/WorkspacePage/Workspace.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ export interface WorkspaceProps {
5555
isRestarting: boolean;
5656
workspace: TypesGen.Workspace;
5757
resources?: TypesGen.WorkspaceResource[];
58-
builds?: TypesGen.WorkspaceBuild[];
5958
templateWarnings?: TypesGen.TemplateVersionWarning[];
6059
canUpdateWorkspace: boolean;
6160
updateMessage?: string;
@@ -70,6 +69,10 @@ export interface WorkspaceProps {
7069
quotaBudget?: number;
7170
handleBuildRetry: () => void;
7271
buildLogs?: React.ReactNode;
72+
builds: TypesGen.WorkspaceBuild[] | undefined;
73+
onLoadMoreBuilds: () => void;
74+
isLoadingMoreBuilds: boolean;
75+
hasMoreBuilds: boolean;
7376
}
7477

7578
/**
@@ -105,6 +108,9 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
105108
handleBuildRetry,
106109
templateWarnings,
107110
buildLogs,
111+
onLoadMoreBuilds,
112+
isLoadingMoreBuilds,
113+
hasMoreBuilds,
108114
}) => {
109115
const styles = useStyles();
110116
const navigate = useNavigate();
@@ -345,7 +351,12 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
345351
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
346352
/>
347353
) : (
348-
<BuildsTable builds={builds} />
354+
<BuildsTable
355+
builds={builds}
356+
onLoadMoreBuilds={onLoadMoreBuilds}
357+
isLoadingMoreBuilds={isLoadingMoreBuilds}
358+
hasMoreBuilds={hasMoreBuilds}
359+
/>
349360
)}
350361
</Stack>
351362
</Margins>

site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { useOrganizationId } from "hooks";
1010
import { isAxiosError } from "axios";
1111
import { Margins } from "components/Margins/Margins";
1212
import { workspaceQuota } from "api/queries/workspaceQuota";
13-
import { useQuery } from "@tanstack/react-query";
14-
import { workspaceBuilds } from "api/queries/workspaceBuilds";
13+
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
14+
import { infiniteWorkspaceBuilds } from "api/queries/workspaceBuilds";
1515

1616
export const WorkspacePage: FC = () => {
1717
const params = useParams() as {
@@ -36,8 +36,8 @@ export const WorkspacePage: FC = () => {
3636
const { workspace, error } = workspaceState.context;
3737
const quotaQuery = useQuery(workspaceQuota(username));
3838
const pageError = error ?? quotaQuery.error;
39-
const buildsQuery = useQuery({
40-
...workspaceBuilds(workspace?.id ?? ""),
39+
const buildsQuery = useInfiniteQuery({
40+
...infiniteWorkspaceBuilds(workspace?.id ?? ""),
4141
enabled: Boolean(workspace),
4242
});
4343

@@ -63,8 +63,13 @@ export const WorkspacePage: FC = () => {
6363
workspaceState={workspaceState}
6464
quota={quotaQuery.data}
6565
workspaceSend={workspaceSend}
66-
builds={buildsQuery.data}
66+
builds={buildsQuery.data?.pages.flat()}
6767
buildsError={buildsQuery.error}
68+
isLoadingMoreBuilds={buildsQuery.isFetchingNextPage}
69+
onLoadMoreBuilds={async () => {
70+
await buildsQuery.fetchNextPage();
71+
}}
72+
hasMoreBuilds={Boolean(buildsQuery.hasNextPage)}
6873
/>
6974
</RequirePermission>
7075
);

site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ interface WorkspaceReadyPageProps {
4141
quota?: TypesGen.WorkspaceQuota;
4242
builds: TypesGen.WorkspaceBuild[] | undefined;
4343
buildsError: unknown;
44+
onLoadMoreBuilds: () => void;
45+
isLoadingMoreBuilds: boolean;
46+
hasMoreBuilds: boolean;
4447
}
4548

4649
export const WorkspaceReadyPage = ({
@@ -49,6 +52,9 @@ export const WorkspaceReadyPage = ({
4952
quota,
5053
builds,
5154
buildsError,
55+
onLoadMoreBuilds,
56+
isLoadingMoreBuilds,
57+
hasMoreBuilds,
5258
}: WorkspaceReadyPageProps): JSX.Element => {
5359
const [_, bannerSend] = useActor(
5460
workspaceState.children["scheduleBannerMachine"],
@@ -170,6 +176,9 @@ export const WorkspaceReadyPage = ({
170176
handleDormantActivate={() => workspaceSend({ type: "ACTIVATE" })}
171177
resources={workspace.latest_build.resources}
172178
builds={builds}
179+
onLoadMoreBuilds={onLoadMoreBuilds}
180+
isLoadingMoreBuilds={isLoadingMoreBuilds}
181+
hasMoreBuilds={hasMoreBuilds}
173182
canUpdateWorkspace={canUpdateWorkspace}
174183
updateMessage={latestVersion?.message}
175184
canRetryDebugMode={canRetryDebugMode}

site/src/xServices/workspace/workspaceXService.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,10 +641,21 @@ export const workspaceMachine = createMachine(
641641
}
642642

643643
context.eventSource.addEventListener("data", (event) => {
644+
const newWorkspaceData = JSON.parse(event.data) as TypesGen.Workspace;
644645
// refresh our workspace with each SSE
645-
send({ type: "REFRESH_WORKSPACE", data: JSON.parse(event.data) });
646-
// refresh our timeline
647-
send({ type: "REFRESH_TIMELINE" });
646+
send({ type: "REFRESH_WORKSPACE", data: newWorkspaceData });
647+
648+
const currentWorkspace = context.workspace!;
649+
const hasNewBuild =
650+
newWorkspaceData.latest_build.id !==
651+
currentWorkspace.latest_build.id;
652+
const lastBuildHasChanged =
653+
newWorkspaceData.latest_build.status !==
654+
currentWorkspace.latest_build.status;
655+
656+
if (hasNewBuild || lastBuildHasChanged) {
657+
send({ type: "REFRESH_TIMELINE" });
658+
}
648659
});
649660

650661
// handle any error events returned by our sse

0 commit comments

Comments
 (0)