Skip to content

Commit c569528

Browse files
authored
fix: add front-end fixes for minor workspace action bugs (#8252)
* fix: incorrect copy on inactivity_ttl field * disabling locked fields unless inactivity TTL is set * scoping inactivity dialog message to template * fixed pluralization for inactivity dialog * amending logic gate to show inactivity dialog * fixed pagination bug
1 parent 9f76dab commit c569528

File tree

10 files changed

+72
-22
lines changed

10 files changed

+72
-22
lines changed

site/src/components/WorkspaceDeletion/ImpendingDeletionBanner.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { Workspace } from "api/typesGenerated"
2-
import { displayImpendingDeletion } from "./utils"
2+
import {
3+
displayImpendingDeletion,
4+
IMPENDING_DELETION_DISPLAY_THRESHOLD,
5+
} from "./utils"
36
import { useDashboard } from "components/Dashboard/DashboardProvider"
47
import { Alert } from "components/Alert/Alert"
58
import { formatDistanceToNow, differenceInDays, add, format } from "date-fns"
@@ -48,7 +51,9 @@ export const ImpendingDeletionBanner = ({
4851
new Date(),
4952
)
5053

51-
const plusFourteen = add(new Date(), { days: 14 })
54+
const plusFourteen = add(new Date(), {
55+
days: IMPENDING_DELETION_DISPLAY_THRESHOLD,
56+
})
5257

5358
return (
5459
<Alert

site/src/components/WorkspaceDeletion/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Workspace } from "api/typesGenerated"
22

33
// This const dictates how far out we alert the user that a workspace
44
// has an impending deletion (due to template.InactivityTTL being set)
5-
const IMPENDING_DELETION_DISPLAY_THRESHOLD = 14 // 14 days
5+
export const IMPENDING_DELETION_DISPLAY_THRESHOLD = 14 // 14 days
66

77
/**
88
* Returns a boolean indicating if an impending deletion indicator should be

site/src/i18n/en/templateSettingsPage.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
"failureTTLHelperText_zero": "Coder will not automatically stop failed workspaces",
2323
"failureTTLHelperText_one": "Coder will attempt to stop failed workspaces after {{count}} day.",
2424
"failureTTLHelperText_other": "Coder will attempt to stop failed workspaces after {{count}} days.",
25-
"inactivityTTLHelperText_zero": "Coder will not automatically delete inactive workspaces",
26-
"inactivityTTLHelperText_one": "Coder will automatically delete inactive workspaces after {{count}} day.",
27-
"inactivityTTLHelperText_other": "Coder will automatically delete inactive workspaces after {{count}} days.",
25+
"inactivityTTLHelperText_zero": "Coder will not automatically lock inactive workspaces",
26+
"inactivityTTLHelperText_one": "Coder will automatically lock inactive workspaces after {{count}} day.",
27+
"inactivityTTLHelperText_other": "Coder will automatically lock inactive workspaces after {{count}} days.",
2828
"lockedTTLHelperText_zero": "Coder will not automatically delete locked workspaces",
2929
"lockedTTLHelperText_one": "Coder will automatically delete locked workspaces after {{count}} day.",
3030
"lockedTTLHelperText_other": "Coder will automatically delete locked workspaces after {{count}} days.",
31+
"inactivityDialogDescription_one": "There is {{count}} workspace that already matches this filter and will be deleted upon form submission. Are you sure you want to proceed?",
32+
"inactivityDialogDescription_other": "There are {{count}} workspaces that already match this filter and will be deleted upon form submission. Are you sure you want to proceed?",
3133
"allowUserCancelWorkspaceJobsLabel": "Allow users to cancel in-progress workspace jobs.",
3234
"allowUserCancelWorkspaceJobsNotice": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases.",
3335
"allowUsersCancelHelperText": "If checked, users may be able to corrupt their workspace.",

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ export const OpenDialog: Story = {
1414
submitValues: () => null,
1515
isInactivityDialogOpen: true,
1616
setIsInactivityDialogOpen: () => null,
17-
workspacesToBeDeletedToday: 2,
17+
numberWorkspacesToBeDeletedToday: 2,
1818
},
1919
}
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
2+
import { useTranslation } from "react-i18next"
23

34
export const InactivityDialog = ({
45
submitValues,
56
isInactivityDialogOpen,
67
setIsInactivityDialogOpen,
7-
workspacesToBeDeletedToday,
8+
numberWorkspacesToBeDeletedToday,
89
}: {
910
submitValues: () => void
1011
isInactivityDialogOpen: boolean
1112
setIsInactivityDialogOpen: (arg0: boolean) => void
12-
workspacesToBeDeletedToday: number
13+
numberWorkspacesToBeDeletedToday: number
1314
}) => {
15+
const { t } = useTranslation("templateSettingsPage")
16+
1417
return (
1518
<ConfirmDialog
1619
type="delete"
@@ -22,9 +25,9 @@ export const InactivityDialog = ({
2225
onClose={() => setIsInactivityDialogOpen(false)}
2326
title="Delete inactive workspaces"
2427
confirmText="Delete Workspaces"
25-
description={`There are ${
26-
workspacesToBeDeletedToday ? workspacesToBeDeletedToday : ""
27-
} workspaces that already match this filter and will be deleted upon form submission. Are you sure you want to proceed?`}
28+
description={t("inactivityDialogDescription", {
29+
count: numberWorkspacesToBeDeletedToday,
30+
})}
2831
/>
2932
)
3033
}

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/TemplateScheduleForm.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
8282
validationSchema,
8383
onSubmit: () => {
8484
if (
85-
form.values.inactivity_cleanup_enabled &&
85+
form.values.locked_cleanup_enabled &&
8686
workspacesToBeDeletedToday &&
8787
workspacesToBeDeletedToday.length > 0
8888
) {
@@ -100,7 +100,10 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
100100
const { t } = useTranslation("templateSettingsPage")
101101
const styles = useStyles()
102102

103-
const workspacesToBeDeletedToday = useWorkspacesToBeDeleted(form.values)
103+
const workspacesToBeDeletedToday = useWorkspacesToBeDeleted(
104+
form.values,
105+
template.name,
106+
)
104107

105108
const [isInactivityDialogOpen, setIsInactivityDialogOpen] =
106109
useState<boolean>(false)
@@ -305,6 +308,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
305308
onChange={handleToggleFailureCleanup}
306309
/>
307310
}
311+
disabled={isSubmitting}
308312
label="Enable Failure Cleanup"
309313
/>
310314
<TextField
@@ -337,6 +341,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
337341
onChange={handleToggleInactivityCleanup}
338342
/>
339343
}
344+
disabled={isSubmitting}
340345
label="Enable Inactivity Cleanup"
341346
/>
342347
<TextField
@@ -371,6 +376,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
371376
onChange={handleToggleLockedCleanup}
372377
/>
373378
}
379+
disabled={
380+
isSubmitting || !form.values.inactivity_cleanup_enabled
381+
}
374382
label="Enable Locked Cleanup"
375383
/>
376384
<TextField
@@ -396,7 +404,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
396404
submitValues={submitValues}
397405
isInactivityDialogOpen={isInactivityDialogOpen}
398406
setIsInactivityDialogOpen={setIsInactivityDialogOpen}
399-
workspacesToBeDeletedToday={workspacesToBeDeletedToday?.length ?? 0}
407+
numberWorkspacesToBeDeletedToday={
408+
workspacesToBeDeletedToday?.length ?? 0
409+
}
400410
/>
401411
<FormFooter
402412
onCancel={onCancel}

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/useWorkspacesToBeDeleted.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ const inactiveStatuses: WorkspaceStatus[] = [
1313

1414
export const useWorkspacesToBeDeleted = (
1515
formValues: TemplateScheduleFormValues,
16+
templateName: string,
1617
) => {
1718
const { data: workspacesData } = useQuery({
1819
queryKey: ["workspaces"],
19-
queryFn: () => getWorkspaces({}),
20-
enabled: formValues.inactivity_cleanup_enabled,
20+
queryFn: () => getWorkspaces({ q: `template:${templateName}` }),
21+
enabled: formValues.locked_cleanup_enabled,
2122
})
2223
return workspacesData?.workspaces?.filter((workspace: Workspace) => {
2324
const isInactive = inactiveStatuses.includes(workspace.latest_build.status)

site/src/pages/WorkspacesPage/WorkspacesPage.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { usePagination } from "hooks/usePagination"
22
import { FC } from "react"
33
import { Helmet } from "react-helmet-async"
44
import { pageTitle } from "utils/page"
5-
import { useWorkspacesData, useWorkspaceUpdate } from "./data"
5+
import {
6+
useWorkspacesData,
7+
useWorkspaceUpdate,
8+
useWorkspacesWithImpendingDeletions,
9+
} from "./data"
610
import { WorkspacesPageView } from "./WorkspacesPageView"
711
import { useOrganizationId, usePermissions } from "hooks"
812
import { useTemplateFilterMenu, useStatusFilterMenu } from "./filter/menus"
@@ -29,6 +33,9 @@ const WorkspacesPage: FC = () => {
2933
query: filter.query,
3034
})
3135
const updateWorkspace = useWorkspaceUpdate(queryKey)
36+
const { data: workspacesWithImpendingDeletions } =
37+
useWorkspacesWithImpendingDeletions()
38+
3239
const permissions = usePermissions()
3340
const canFilterByUser = permissions.viewDeploymentValues
3441
const userMenu = useUserFilterMenu({
@@ -57,6 +64,7 @@ const WorkspacesPage: FC = () => {
5764

5865
<WorkspacesPageView
5966
workspaces={data?.workspaces}
67+
workspacesWithDeletions={workspacesWithImpendingDeletions?.workspaces}
6068
error={error}
6169
count={data?.count}
6270
page={pagination.page}

site/src/pages/WorkspacesPage/WorkspacesPageView.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const Language = {
3333
export interface WorkspacesPageViewProps {
3434
error: unknown
3535
workspaces?: Workspace[]
36+
workspacesWithDeletions?: Workspace[]
3637
count?: number
3738
filterProps: ComponentProps<typeof WorkspacesFilter>
3839
page: number
@@ -45,6 +46,7 @@ export const WorkspacesPageView: FC<
4546
React.PropsWithChildren<WorkspacesPageViewProps>
4647
> = ({
4748
workspaces,
49+
workspacesWithDeletions,
4850
error,
4951
limit,
5052
count,
@@ -55,9 +57,9 @@ export const WorkspacesPageView: FC<
5557
}) => {
5658
const { saveLocal, getLocal } = useLocalStorage()
5759

58-
const workspaceIdsWithImpendingDeletions = workspaces
59-
?.filter((workspace) => workspace.deleting_at)
60-
.map((workspace) => workspace.id)
60+
const workspaceIdsWithImpendingDeletions = workspacesWithDeletions?.map(
61+
(workspace) => workspace.id,
62+
)
6163

6264
/**
6365
* Returns a boolean indicating if there are workspaces that have been
@@ -105,7 +107,9 @@ export const WorkspacesPageView: FC<
105107
</Maybe>
106108
{/* <ImpendingDeletionBanner/> determines its own visibility */}
107109
<ImpendingDeletionBanner
108-
workspace={workspaces?.find((workspace) => workspace.deleting_at)}
110+
workspace={workspacesWithDeletions?.find(
111+
(workspace) => workspace.deleting_at,
112+
)}
109113
shouldRedisplayBanner={isNewWorkspacesImpendingDeletion()}
110114
onDismiss={() =>
111115
saveLocal(

site/src/pages/WorkspacesPage/data.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
import { displayError } from "components/GlobalSnackbar/utils"
1515
import { useState } from "react"
1616
import { useTranslation } from "react-i18next"
17+
import { add, format } from "date-fns"
18+
import { IMPENDING_DELETION_DISPLAY_THRESHOLD } from "components/WorkspaceDeletion/utils"
1719

1820
type UseWorkspacesDataParams = {
1921
page: number
@@ -79,6 +81,21 @@ export const useWorkspaceUpdate = (queryKey: QueryKey) => {
7981
})
8082
}
8183

84+
// Returns workspace that will be deleted within 14 days.
85+
// This query is used for the ImpendingDeletionBanner
86+
export const useWorkspacesWithImpendingDeletions = () => {
87+
return useQuery({
88+
queryKey: ["workspacesWithImpendingDeletions"],
89+
queryFn: () =>
90+
getWorkspaces({
91+
q: `deleting_by:"${format(
92+
add(new Date(), { days: IMPENDING_DELETION_DISPLAY_THRESHOLD }),
93+
"yyyy-MM-dd",
94+
)}"`,
95+
}),
96+
})
97+
}
98+
8299
const assignLatestBuild = (
83100
oldResponse: WorkspacesResponse,
84101
build: WorkspaceBuild,

0 commit comments

Comments
 (0)