Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 2 additions & 2 deletions codersdk/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ func (c *Client) getWorkspace(ctx context.Context, id uuid.UUID, opts ...Request
}

type WorkspaceBuildsRequest struct {
WorkspaceID uuid.UUID
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid" typescript:"-"`
Pagination
Since time.Time
Since time.Time `json:"since,omitempty" format:"date-time"`
}

func (c *Client) WorkspaceBuilds(ctx context.Context, req WorkspaceBuildsRequest) ([]WorkspaceBuild, error) {
Expand Down
6 changes: 3 additions & 3 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -775,10 +775,10 @@ export const regenerateUserSSHKey = async (

export const getWorkspaceBuilds = async (
workspaceId: string,
since: Date,
): Promise<TypesGen.WorkspaceBuild[]> => {
req?: TypesGen.WorkspaceBuildsRequest,
) => {
const response = await axios.get<TypesGen.WorkspaceBuild[]>(
`/api/v2/workspaces/${workspaceId}/builds?since=${since.toISOString()}`,
getURLWithSearchParams(`/api/v2/workspaces/${workspaceId}/builds`, req),
);
return response.data;
};
Expand Down
24 changes: 23 additions & 1 deletion site/src/api/queries/workspaceBuilds.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
import { UseInfiniteQueryOptions } from "@tanstack/react-query";
import * as API from "api/api";
import { WorkspaceBuild, WorkspaceBuildsRequest } from "api/typesGenerated";

export const workspaceBuildByNumber = (
username: string,
workspaceName: string,
buildNumber: number,
) => {
return {
queryKey: [username, workspaceName, "workspaceBuild", buildNumber],
queryKey: ["workspaceBuild", username, workspaceName, buildNumber],
queryFn: () =>
API.getWorkspaceBuildByNumber(username, workspaceName, buildNumber),
};
};

export const infiniteWorkspaceBuilds = (
workspaceId: string,
req?: WorkspaceBuildsRequest,
): UseInfiniteQueryOptions<WorkspaceBuild[]> => {
const limit = req?.limit ?? 25;

return {
queryKey: ["workspaceBuilds", workspaceId, req],
getNextPageParam: (lastPage, pages) => {
return pages.length + 1;
},
queryFn: ({ pageParam = 0 }) => {
return API.getWorkspaceBuilds(workspaceId, {
limit,
offset: pageParam <= 0 ? 0 : (pageParam - 1) * limit,
});
},
};
};
3 changes: 1 addition & 2 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions site/src/pages/WorkspaceBuildPage/WorkspaceBuildPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ export const WorkspaceBuildPage: FC = () => {
keepPreviousData: true,
});
const build = wsBuildQuery.data;
const { data: builds } = useQuery({
const buildsQuery = useQuery({
queryKey: ["builds", username, build?.workspace_id],
queryFn: () => {
return getWorkspaceBuilds(
build?.workspace_id ?? "",
dayjs().add(-30, "day").toDate(),
);
return getWorkspaceBuilds(build?.workspace_id ?? "", {
since: dayjs().add(-30, "day").toISOString(),
});
},
enabled: Boolean(build),
});
Expand All @@ -50,7 +49,7 @@ export const WorkspaceBuildPage: FC = () => {
<WorkspaceBuildPageView
logs={logs}
build={build}
builds={builds}
builds={buildsQuery.data}
activeBuildNumber={buildNumber}
/>
</>
Expand Down
8 changes: 8 additions & 0 deletions site/src/pages/WorkspacePage/BuildsTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Story = StoryObj<typeof BuildsTable>;
export const Example: Story = {
args: {
builds: MockBuilds,
hasMoreBuilds: true,
},
};

Expand All @@ -21,3 +22,10 @@ export const Empty: Story = {
builds: [],
},
};

export const NoMoreBuilds: Story = {
args: {
builds: MockBuilds,
hasMoreBuilds: false,
},
};
77 changes: 52 additions & 25 deletions site/src/pages/WorkspacePage/BuildsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,70 @@ import * as TypesGen from "api/typesGenerated";
import { EmptyState } from "components/EmptyState/EmptyState";
import { TableLoader } from "components/TableLoader/TableLoader";
import { BuildRow } from "./BuildRow";
import { Stack } from "components/Stack/Stack";
import LoadingButton from "@mui/lab/LoadingButton";
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";

export const Language = {
emptyMessage: "No builds found",
};

export interface BuildsTableProps {
builds?: TypesGen.WorkspaceBuild[];
builds: TypesGen.WorkspaceBuild[] | undefined;
onLoadMoreBuilds: () => void;
isLoadingMoreBuilds: boolean;
hasMoreBuilds: boolean;
}

export const BuildsTable: FC<React.PropsWithChildren<BuildsTableProps>> = ({
builds,
onLoadMoreBuilds,
isLoadingMoreBuilds,
hasMoreBuilds,
}) => {
return (
<TableContainer>
<Table data-testid="builds-table" aria-describedby="builds table">
<TableBody>
{builds ? (
<Timeline
items={builds}
getDate={(build) => new Date(build.created_at)}
row={(build) => <BuildRow key={build.id} build={build} />}
/>
) : (
<TableLoader />
)}
<Stack>
<TableContainer>
<Table data-testid="builds-table" aria-describedby="builds table">
<TableBody>
{builds ? (
<Timeline
items={builds}
getDate={(build) => new Date(build.created_at)}
row={(build) => <BuildRow key={build.id} build={build} />}
/>
) : (
<TableLoader />
)}

{builds && builds.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<Box p={4}>
<EmptyState message={Language.emptyMessage} />
</Box>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{builds && builds.length === 0 && (
<TableRow>
<TableCell colSpan={999}>
<Box p={4}>
<EmptyState message={Language.emptyMessage} />
</Box>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<LoadingButton
disabled={!hasMoreBuilds}
onClick={onLoadMoreBuilds}
loading={isLoadingMoreBuilds}
loadingPosition="start"
variant="outlined"
color="neutral"
startIcon={<ArrowDownwardOutlined />}
css={{
display: "inline-flex",
margin: "auto",
borderRadius: "9999px",
}}
>
Load previous builds
</LoadingButton>
</Stack>
);
};
15 changes: 13 additions & 2 deletions site/src/pages/WorkspacePage/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export interface WorkspaceProps {
isRestarting: boolean;
workspace: TypesGen.Workspace;
resources?: TypesGen.WorkspaceResource[];
builds?: TypesGen.WorkspaceBuild[];
templateWarnings?: TypesGen.TemplateVersionWarning[];
canUpdateWorkspace: boolean;
updateMessage?: string;
Expand All @@ -70,6 +69,10 @@ export interface WorkspaceProps {
quotaBudget?: number;
handleBuildRetry: () => void;
buildLogs?: React.ReactNode;
builds: TypesGen.WorkspaceBuild[] | undefined;
onLoadMoreBuilds: () => void;
isLoadingMoreBuilds: boolean;
hasMoreBuilds: boolean;
}

/**
Expand Down Expand Up @@ -105,6 +108,9 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
handleBuildRetry,
templateWarnings,
buildLogs,
onLoadMoreBuilds,
isLoadingMoreBuilds,
hasMoreBuilds,
}) => {
const styles = useStyles();
const navigate = useNavigate();
Expand Down Expand Up @@ -345,7 +351,12 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
/>
) : (
<BuildsTable builds={builds} />
<BuildsTable
builds={builds}
onLoadMoreBuilds={onLoadMoreBuilds}
isLoadingMoreBuilds={isLoadingMoreBuilds}
hasMoreBuilds={hasMoreBuilds}
/>
)}
</Stack>
</Margins>
Expand Down
19 changes: 18 additions & 1 deletion site/src/pages/WorkspacePage/WorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { useOrganizationId } from "hooks";
import { isAxiosError } from "axios";
import { Margins } from "components/Margins/Margins";
import { workspaceQuota } from "api/queries/workspaceQuota";
import { useQuery } from "@tanstack/react-query";
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { infiniteWorkspaceBuilds } from "api/queries/workspaceBuilds";

export const WorkspacePage: FC = () => {
const params = useParams() as {
Expand All @@ -26,10 +27,19 @@ export const WorkspacePage: FC = () => {
workspaceName,
username,
},
actions: {
refreshBuilds: async () => {
await buildsQuery.refetch();
},
},
});
const { workspace, error } = workspaceState.context;
const quotaQuery = useQuery(workspaceQuota(username));
const pageError = error ?? quotaQuery.error;
const buildsQuery = useInfiniteQuery({
...infiniteWorkspaceBuilds(workspace?.id ?? ""),
enabled: Boolean(workspace),
});

if (pageError) {
return (
Expand All @@ -53,6 +63,13 @@ export const WorkspacePage: FC = () => {
workspaceState={workspaceState}
quota={quotaQuery.data}
workspaceSend={workspaceSend}
builds={buildsQuery.data?.pages.flat()}
buildsError={buildsQuery.error}
isLoadingMoreBuilds={buildsQuery.isFetchingNextPage}
onLoadMoreBuilds={async () => {
await buildsQuery.fetchNextPage();
}}
hasMoreBuilds={Boolean(buildsQuery.hasNextPage)}
/>
</RequirePermission>
);
Expand Down
17 changes: 14 additions & 3 deletions site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,22 @@ interface WorkspaceReadyPageProps {
workspaceState: StateFrom<typeof workspaceMachine>;
workspaceSend: (event: WorkspaceEvent) => void;
quota?: TypesGen.WorkspaceQuota;
builds: TypesGen.WorkspaceBuild[] | undefined;
buildsError: unknown;
onLoadMoreBuilds: () => void;
isLoadingMoreBuilds: boolean;
hasMoreBuilds: boolean;
}

export const WorkspaceReadyPage = ({
workspaceState,
workspaceSend,
quota,
builds,
buildsError,
onLoadMoreBuilds,
isLoadingMoreBuilds,
hasMoreBuilds,
}: WorkspaceReadyPageProps): JSX.Element => {
const [_, bannerSend] = useActor(
workspaceState.children["scheduleBannerMachine"],
Expand All @@ -56,8 +66,6 @@ export const WorkspaceReadyPage = ({
template,
templateVersion: currentVersion,
deploymentValues,
builds,
getBuildsError,
buildError,
cancellationError,
sshPrefix,
Expand Down Expand Up @@ -168,14 +176,17 @@ export const WorkspaceReadyPage = ({
handleDormantActivate={() => workspaceSend({ type: "ACTIVATE" })}
resources={workspace.latest_build.resources}
builds={builds}
onLoadMoreBuilds={onLoadMoreBuilds}
isLoadingMoreBuilds={isLoadingMoreBuilds}
hasMoreBuilds={hasMoreBuilds}
canUpdateWorkspace={canUpdateWorkspace}
updateMessage={latestVersion?.message}
canRetryDebugMode={canRetryDebugMode}
canChangeVersions={canUpdateTemplate}
hideSSHButton={featureVisibility["browser_only"]}
hideVSCodeDesktopButton={featureVisibility["browser_only"]}
workspaceErrors={{
[WorkspaceErrors.GET_BUILDS_ERROR]: getBuildsError,
[WorkspaceErrors.GET_BUILDS_ERROR]: buildsError,
[WorkspaceErrors.BUILD_ERROR]: buildError || restartBuildError,
[WorkspaceErrors.CANCELLATION_ERROR]: cancellationError,
}}
Expand Down
Loading