Thanks to visit codestin.com
Credit goes to github.com

Skip to content

chore: simplify workspaces data fetching #17703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion site/src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const badgeVariants = cva(
warning:
"border border-solid border-border-warning bg-surface-orange text-content-warning shadow",
destructive:
"border border-solid border-border-destructive bg-surface-red text-content-highlight-red shadow",
"border border-solid border-border-destructive bg-surface-red text-highlight-red shadow",
},
size: {
xs: "text-2xs font-regular h-5 [&_svg]:hidden rounded px-1.5",
Expand Down
6 changes: 1 addition & 5 deletions site/src/hooks/usePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const usePagination = ({
const [searchParams, setSearchParams] = searchParamsResult;
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1;
const limit = DEFAULT_RECORDS_PER_PAGE;
const offset = calcOffset(page, limit);
const offset = page <= 0 ? 0 : (page - 1) * limit;

const goToPage = (page: number) => {
searchParams.set("page", page.toString());
Expand All @@ -23,7 +23,3 @@ export const usePagination = ({
offset,
};
};

export const calcOffset = (page: number, limit: number) => {
return page <= 0 ? 0 : (page - 1) * limit;
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { action } from "@storybook/addon-actions";
import type { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, waitFor, within } from "@storybook/test";
import { MockTemplate, MockTemplateVersion } from "testHelpers/entities";
import {
MockTemplate,
MockTemplateVersion,
MockWorkspace,
} from "testHelpers/entities";
import { withDashboardProvider } from "testHelpers/storybook";
import { WorkspaceOutdatedTooltip } from "./WorkspaceOutdatedTooltip";

Expand All @@ -18,9 +21,11 @@ const meta: Meta<typeof WorkspaceOutdatedTooltip> = {
],
},
args: {
onUpdateVersion: action("onUpdateVersion"),
templateName: MockTemplate.display_name,
latestVersionId: MockTemplateVersion.id,
workspace: {
...MockWorkspace,
template_name: MockTemplate.display_name,
template_active_version_id: MockTemplateVersion.id,
},
},
};

Expand All @@ -29,14 +34,12 @@ type Story = StoryObj<typeof WorkspaceOutdatedTooltip>;

const Example: Story = {
play: async ({ canvasElement, step }) => {
const screen = within(canvasElement);
const body = within(canvasElement.ownerDocument.body);

await step("activate hover trigger", async () => {
await userEvent.hover(screen.getByRole("button"));
await userEvent.hover(body.getByRole("button"));
await waitFor(() =>
expect(
screen.getByText(MockTemplateVersion.message),
).toBeInTheDocument(),
expect(body.getByText(MockTemplateVersion.message)).toBeInTheDocument(),
);
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import InfoIcon from "@mui/icons-material/InfoOutlined";
import RefreshIcon from "@mui/icons-material/Refresh";
import Link from "@mui/material/Link";
import Skeleton from "@mui/material/Skeleton";
import { getErrorDetail, getErrorMessage } from "api/errors";
import { templateVersion } from "api/queries/templates";
import type { Workspace } from "api/typesGenerated";
import { displayError } from "components/GlobalSnackbar/utils";
import {
HelpTooltip,
HelpTooltipAction,
Expand All @@ -17,102 +20,99 @@ import { usePopover } from "components/deprecated/Popover/Popover";
import { linkToTemplate, useLinks } from "modules/navigation";
import type { FC } from "react";
import { useQuery } from "react-query";

const Language = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda like the pattern of putting all the language in a constant but it truly doesn't matter haha.

outdatedLabel: "Outdated",
versionTooltipText:
"This workspace version is outdated and a newer version is available.",
updateVersionLabel: "Update",
};
import {
WorkspaceUpdateDialogs,
useWorkspaceUpdate,
} from "../WorkspaceUpdateDialogs";

interface TooltipProps {
organizationName: string;
templateName: string;
latestVersionId: string;
onUpdateVersion: () => void;
ariaLabel?: string;
workspace: Workspace;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big fan 😎

}

export const WorkspaceOutdatedTooltip: FC<TooltipProps> = (props) => {
return (
<HelpTooltip>
<HelpTooltipTrigger
size="small"
aria-label="More info"
hoverEffect={false}
>
<HelpTooltipTrigger size="small" hoverEffect={false}>
<InfoIcon css={styles.icon} />
<span className="sr-only">Outdated info</span>
</HelpTooltipTrigger>

<WorkspaceOutdatedTooltipContent {...props} />
</HelpTooltip>
);
};

const WorkspaceOutdatedTooltipContent: FC<TooltipProps> = ({
organizationName,
templateName,
latestVersionId,
onUpdateVersion,
ariaLabel,
}) => {
const WorkspaceOutdatedTooltipContent: FC<TooltipProps> = ({ workspace }) => {
const getLink = useLinks();
const theme = useTheme();
const popover = usePopover();
const { data: activeVersion } = useQuery({
...templateVersion(latestVersionId),
...templateVersion(workspace.template_active_version_id),
enabled: popover.open,
});
const updateWorkspace = useWorkspaceUpdate({
workspace,
latestVersion: activeVersion,
onError: (error) => {
displayError(
getErrorMessage(error, "Error updating workspace"),
getErrorDetail(error),
);
},
});

const versionLink = `${getLink(
linkToTemplate(organizationName, templateName),
linkToTemplate(workspace.organization_name, workspace.template_name),
)}`;

return (
<HelpTooltipContent>
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
<>
<HelpTooltipContent disablePortal={false}>
<HelpTooltipTitle>Outdated</HelpTooltipTitle>
<HelpTooltipText>
This workspace version is outdated and a newer version is available.
</HelpTooltipText>

<div css={styles.container}>
<div css={{ lineHeight: "1.6" }}>
<div css={styles.bold}>New version</div>
<div>
{activeVersion ? (
<Link
href={`${versionLink}/versions/${activeVersion.name}`}
target="_blank"
css={{ color: theme.palette.primary.light }}
>
{activeVersion.name}
</Link>
) : (
<Skeleton variant="text" height={20} width={100} />
)}
<div css={styles.container}>
<div css={{ lineHeight: "1.6" }}>
<div css={styles.bold}>New version</div>
<div>
{activeVersion ? (
<Link
href={`${versionLink}/versions/${activeVersion.name}`}
target="_blank"
css={{ color: theme.palette.primary.light }}
>
{activeVersion.name}
</Link>
) : (
<Skeleton variant="text" height={20} width={100} />
)}
</div>
</div>
</div>

<div css={{ lineHeight: "1.6" }}>
<div css={styles.bold}>Message</div>
<div>
{activeVersion ? (
activeVersion.message || "No message"
) : (
<Skeleton variant="text" height={20} width={150} />
)}
<div css={{ lineHeight: "1.6" }}>
<div css={styles.bold}>Message</div>
<div>
{activeVersion ? (
activeVersion.message || "No message"
) : (
<Skeleton variant="text" height={20} width={150} />
)}
</div>
</div>
</div>
</div>

<HelpTooltipLinksGroup>
<HelpTooltipAction
icon={RefreshIcon}
onClick={onUpdateVersion}
ariaLabel={ariaLabel}
>
{Language.updateVersionLabel}
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
<HelpTooltipLinksGroup>
<HelpTooltipAction
icon={RefreshIcon}
onClick={updateWorkspace.update}
>
Update
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
<WorkspaceUpdateDialogs {...updateWorkspace.dialogs} />
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { workspaces } from "api/queries/workspaces";
import type { Template, Workspace } from "api/typesGenerated";
import { compareAsc } from "date-fns";
import { calcOffset } from "hooks/usePagination";
import { useWorkspacesData } from "pages/WorkspacesPage/data";
import { useQuery } from "react-query";
import type { TemplateScheduleFormValues } from "./formHelpers";

export const useWorkspacesToGoDormant = (
template: Template,
formValues: TemplateScheduleFormValues,
fromDate: Date,
) => {
const { data } = useWorkspacesData({
offset: calcOffset(0, 0),
limit: 0,
q: `template:${template.name}`,
});
const { data } = useQuery(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big fan 😎

workspaces({
q: `template:${template.name}`,
}),
);

return data?.workspaces?.filter((workspace: Workspace) => {
if (!formValues.time_til_dormant_ms) {
Expand All @@ -40,11 +40,12 @@ export const useWorkspacesToBeDeleted = (
formValues: TemplateScheduleFormValues,
fromDate: Date,
) => {
const { data } = useWorkspacesData({
offset: calcOffset(0, 0),
limit: 0,
q: `template:${template.name} dormant:true`,
});
const { data } = useQuery(
workspaces({
q: `template:${template.name} dormant:true`,
}),
);

return data?.workspaces?.filter((workspace: Workspace) => {
if (!workspace.dormant_at || !formValues.time_til_dormant_autodelete_ms) {
return false;
Expand Down
18 changes: 9 additions & 9 deletions site/src/pages/WorkspacesPage/WorkspacesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getErrorDetail, getErrorMessage } from "api/errors";
import { workspacePermissionsByOrganization } from "api/queries/organizations";
import { templates } from "api/queries/templates";
import { workspaces } from "api/queries/workspaces";
import type { Workspace } from "api/typesGenerated";
import { useFilter } from "components/Filter/Filter";
import { useUserFilterMenu } from "components/Filter/UserFilter";
Expand All @@ -19,7 +20,6 @@ import { BatchDeleteConfirmation } from "./BatchDeleteConfirmation";
import { BatchUpdateConfirmation } from "./BatchUpdateConfirmation";
import { WorkspacesPageView } from "./WorkspacesPageView";
import { useBatchActions } from "./batchActions";
import { useWorkspaceUpdate, useWorkspacesData } from "./data";
import { useStatusFilterMenu, useTemplateFilterMenu } from "./filter/menus";

function useSafeSearchParams() {
Expand All @@ -45,9 +45,7 @@ const WorkspacesPage: FC = () => {
const pagination = usePagination({ searchParamsResult });
const { permissions, user: me } = useAuthenticated();
const { entitlements } = useDashboard();

const templatesQuery = useQuery(templates());

const workspacePermissionsQuery = useQuery(
workspacePermissionsByOrganization(
templatesQuery.data?.map((template) => template.organization_id),
Expand All @@ -73,12 +71,17 @@ const WorkspacesPage: FC = () => {
onFilterChange: () => pagination.goToPage(1),
});

const { data, error, queryKey, refetch } = useWorkspacesData({
const workspacesQueryOptions = workspaces({
...pagination,
q: filterProps.filter.query,
});
const { data, error, refetch } = useQuery({
...workspacesQueryOptions,
refetchInterval: (_, query) => {
return query.state.error ? false : 5_000;
},
});

const updateWorkspace = useWorkspaceUpdate(queryKey);
const [checkedWorkspaces, setCheckedWorkspaces] = useState<
readonly Workspace[]
>([]);
Expand Down Expand Up @@ -123,17 +126,14 @@ const WorkspacesPage: FC = () => {
limit={pagination.limit}
onPageChange={pagination.goToPage}
filterProps={filterProps}
onUpdateWorkspace={(workspace) => {
updateWorkspace.mutate(workspace);
}}
isRunningBatchAction={batchActions.isLoading}
onDeleteAll={() => setConfirmingBatchAction("delete")}
onUpdateAll={() => setConfirmingBatchAction("update")}
onStartAll={() => batchActions.startAll(checkedWorkspaces)}
onStopAll={() => batchActions.stopAll(checkedWorkspaces)}
onActionSuccess={async () => {
await queryClient.invalidateQueries({
queryKey,
queryKey: workspacesQueryOptions.queryKey,
});
}}
onActionError={(error) => {
Expand Down
3 changes: 0 additions & 3 deletions site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export interface WorkspacesPageViewProps {
page: number;
limit: number;
onPageChange: (page: number) => void;
onUpdateWorkspace: (workspace: Workspace) => void;
onCheckChange: (checkedWorkspaces: readonly Workspace[]) => void;
isRunningBatchAction: boolean;
onDeleteAll: () => void;
Expand All @@ -76,7 +75,6 @@ export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({
count,
filterProps,
onPageChange,
onUpdateWorkspace,
page,
checkedWorkspaces,
onCheckChange,
Expand Down Expand Up @@ -223,7 +221,6 @@ export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({
canCreateTemplate={canCreateTemplate}
workspaces={workspaces}
isUsingFilter={filterProps.filter.used}
onUpdateWorkspace={onUpdateWorkspace}
checkedWorkspaces={checkedWorkspaces}
onCheckChange={onCheckChange}
canCheckWorkspaces={canCheckWorkspaces}
Expand Down
Loading
Loading