Skip to content

Commit 73b8b49

Browse files
committed
add frontend
1 parent 4359ecf commit 73b8b49

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

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

+61-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const MS_HOUR_CONVERSION = 3600000
2525
const MS_DAY_CONVERSION = 86400000
2626
const FAILURE_CLEANUP_DEFAULT = 7
2727
const INACTIVITY_CLEANUP_DEFAULT = 180
28+
const LOCKED_CLEANUP_DEFAULT = 30
2829

2930
export interface TemplateScheduleForm {
3031
template: Template
@@ -65,13 +66,18 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
6566
inactivity_ttl_ms: allowAdvancedScheduling
6667
? template.inactivity_ttl_ms / MS_DAY_CONVERSION
6768
: 0,
69+
locked_ttl_ms: allowAdvancedScheduling
70+
? template.locked_ttl_ms / MS_DAY_CONVERSION
71+
: 0,
6872

6973
allow_user_autostart: template.allow_user_autostart,
7074
allow_user_autostop: template.allow_user_autostop,
7175
failure_cleanup_enabled:
7276
allowAdvancedScheduling && Boolean(template.failure_ttl_ms),
7377
inactivity_cleanup_enabled:
7478
allowAdvancedScheduling && Boolean(template.inactivity_ttl_ms),
79+
locked_cleanup_enabled:
80+
allowAdvancedScheduling && Boolean(template.locked_ttl_ms),
7581
},
7682
validationSchema,
7783
onSubmit: () => {
@@ -114,6 +120,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
114120
inactivity_ttl_ms: form.values.inactivity_ttl_ms
115121
? form.values.inactivity_ttl_ms * MS_DAY_CONVERSION
116122
: undefined,
123+
locked_ttl_ms: form.values.locked_ttl_ms
124+
? form.values.locked_ttl_ms * MS_DAY_CONVERSION
125+
: undefined,
117126

118127
allow_user_autostart: form.values.allow_user_autostart,
119128
allow_user_autostop: form.values.allow_user_autostop,
@@ -158,6 +167,25 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
158167
}
159168
}
160169

170+
const handleToggleLockedCleanup = async (e: ChangeEvent) => {
171+
form.handleChange(e)
172+
if (!form.values.locked_cleanup_enabled) {
173+
// fill failure_ttl_ms with defaults
174+
await form.setValues({
175+
...form.values,
176+
locked_cleanup_enabled: true,
177+
locked_ttl_ms: LOCKED_CLEANUP_DEFAULT,
178+
})
179+
} else {
180+
// clear failure_ttl_ms
181+
await form.setValues({
182+
...form.values,
183+
locked_cleanup_enabled: false,
184+
locked_ttl_ms: 0,
185+
})
186+
}
187+
}
188+
161189
return (
162190
<HorizontalForm
163191
onSubmit={form.handleSubmit}
@@ -298,7 +326,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
298326
</FormSection>
299327
<FormSection
300328
title="Inactivity Cleanup"
301-
description="When enabled, Coder will automatically delete workspaces that are in an inactive state after a specified number of days."
329+
description="When enabled, Coder will lock workspaces that have not been accessed after a specified number of days."
302330
>
303331
<FormFields>
304332
<FormControlLabel
@@ -330,6 +358,38 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
330358
/>
331359
</FormFields>
332360
</FormSection>
361+
<FormSection
362+
title="Locked Cleanup"
363+
description="When enabled, Coder will permanently delete workspaces that have been locked for a specified number of days."
364+
>
365+
<FormFields>
366+
<FormControlLabel
367+
control={
368+
<Switch
369+
name="lockedCleanupEnabled"
370+
checked={form.values.locked_cleanup_enabled}
371+
onChange={handleToggleLockedCleanup}
372+
/>
373+
}
374+
label="Enable Locked Cleanup"
375+
/>
376+
<TextField
377+
{...getFieldHelpers(
378+
"locked_ttl_ms",
379+
<TTLHelperText
380+
translationName="lockedTTLHelperText"
381+
ttl={form.values.locked_ttl_ms}
382+
/>,
383+
)}
384+
disabled={isSubmitting || !form.values.locked_cleanup_enabled}
385+
fullWidth
386+
inputProps={{ min: 0, step: "any" }}
387+
label="Time until cleanup (days)"
388+
type="number"
389+
aria-label="Locked Cleanup"
390+
/>
391+
</FormFields>
392+
</FormSection>
333393
</>
334394
)}
335395
<InactivityDialog

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

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import i18next from "i18next"
55
export interface TemplateScheduleFormValues extends UpdateTemplateMeta {
66
failure_cleanup_enabled: boolean
77
inactivity_cleanup_enabled: boolean
8+
locked_cleanup_enabled: boolean
89
}
910

1011
const MAX_TTL_DAYS = 7
@@ -63,6 +64,20 @@ export const getValidationSchema = (): Yup.AnyObjectSchema =>
6364
}
6465
},
6566
),
67+
locked_ttl_ms: Yup.number()
68+
.min(0, "Locked cleanup days must not be less than 0.")
69+
.test(
70+
"positive-if-enabled",
71+
"Locked cleanup days must be greater than zero when enabled.",
72+
function (value) {
73+
const parent = this.parent as TemplateScheduleFormValues
74+
if (parent.locked_cleanup_enabled) {
75+
return Boolean(value)
76+
} else {
77+
return true
78+
}
79+
},
80+
),
6681
allow_user_autostart: Yup.boolean(),
6782
allow_user_autostop: Yup.boolean(),
6883
})

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

+39-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const validFormValues = {
2222
max_ttl_ms: 2,
2323
failure_ttl_ms: 7,
2424
inactivity_ttl_ms: 180,
25+
locked_ttl_ms: 30,
2526
}
2627

2728
const renderTemplateSchedulePage = async () => {
@@ -37,11 +38,13 @@ const fillAndSubmitForm = async ({
3738
max_ttl_ms,
3839
failure_ttl_ms,
3940
inactivity_ttl_ms,
41+
locked_ttl_ms,
4042
}: {
4143
default_ttl_ms: number
4244
max_ttl_ms: number
4345
failure_ttl_ms: number
4446
inactivity_ttl_ms: number
47+
locked_ttl_ms: number
4548
}) => {
4649
const user = userEvent.setup()
4750
const defaultTtlLabel = t("defaultTtlLabel", { ns: "templateSettingsPage" })
@@ -64,6 +67,11 @@ const fillAndSubmitForm = async ({
6467
})
6568
await user.type(inactivityTtlField, inactivity_ttl_ms.toString())
6669

70+
const lockedTtlField = screen.getByRole("checkbox", {
71+
name: /Locked Cleanup/i,
72+
})
73+
await user.type(lockedTtlField, locked_ttl_ms.toString())
74+
6775
const submitButton = await screen.findByText(
6876
FooterFormLanguage.defaultSubmitLabel,
6977
)
@@ -111,7 +119,7 @@ describe("TemplateSchedulePage", () => {
111119
)
112120
})
113121

114-
test("failure and inactivity ttl converted to and from days", async () => {
122+
test("failure, inactivity, and locked ttl converted to and from days", async () => {
115123
await renderTemplateSchedulePage()
116124

117125
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
@@ -127,6 +135,7 @@ describe("TemplateSchedulePage", () => {
127135
expect.objectContaining({
128136
failure_ttl_ms: validFormValues.failure_ttl_ms * 86400000,
129137
inactivity_ttl_ms: validFormValues.inactivity_ttl_ms * 86400000,
138+
locked_ttl_ms: validFormValues.locked_ttl_ms * 86400000,
130139
}),
131140
),
132141
)
@@ -218,4 +227,33 @@ describe("TemplateSchedulePage", () => {
218227
"Inactivity cleanup days must not be less than 0.",
219228
)
220229
})
230+
231+
it("allows a locked ttl of 7 days", () => {
232+
const values: UpdateTemplateMeta = {
233+
...validFormValues,
234+
locked_ttl_ms: 86400000 * 7,
235+
}
236+
const validate = () => getValidationSchema().validateSync(values)
237+
expect(validate).not.toThrowError()
238+
})
239+
240+
it("allows a locked ttl of 0", () => {
241+
const values: UpdateTemplateMeta = {
242+
...validFormValues,
243+
locked_ttl_ms: 0,
244+
}
245+
const validate = () => getValidationSchema().validateSync(values)
246+
expect(validate).not.toThrowError()
247+
})
248+
249+
it("disallows a negative inactivity ttl", () => {
250+
const values: UpdateTemplateMeta = {
251+
...validFormValues,
252+
locked_ttl_ms: -1,
253+
}
254+
const validate = () => getValidationSchema().validateSync(values)
255+
expect(validate).toThrowError(
256+
"Locked cleanup days must not be less than 0.",
257+
)
258+
})
221259
})

site/src/testHelpers/entities.ts

+1
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ export const MockTemplate: TypesGen.Template = {
388388
allow_user_cancel_workspace_jobs: true,
389389
failure_ttl_ms: 0,
390390
inactivity_ttl_ms: 0,
391+
locked_ttl_ms: 0,
391392
allow_user_autostart: false,
392393
allow_user_autostop: false,
393394
}

0 commit comments

Comments
 (0)