Skip to content

Commit d1d459c

Browse files
feat(site): add advanced schedule settings to UI (#7061)
* feat: add advanced schedule settings * Update site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx Co-authored-by: Dean Sheather <dean@deansheather.com> * rename variable * fix optionText css --------- Co-authored-by: Dean Sheather <dean@deansheather.com>
1 parent 264093a commit d1d459c

File tree

7 files changed

+166
-46
lines changed

7 files changed

+166
-46
lines changed

site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,26 +105,28 @@ const defaultInitialValues: CreateTemplateData = {
105105
// you are not licensed. We hide the form value based on entitlements.
106106
max_ttl_hours: 24 * 7,
107107
allow_user_cancel_workspace_jobs: false,
108+
allow_user_autostart: false,
109+
allow_user_autostop: false,
108110
}
109111

110112
type GetInitialValuesParams = {
111113
fromExample?: TemplateExample
112114
fromCopy?: Template
113115
parameters?: ParameterSchema[]
114116
variables?: TemplateVersionVariable[]
115-
canSetMaxTTL: boolean
117+
allowAdvancedScheduling: boolean
116118
}
117119

118120
const getInitialValues = ({
119121
fromExample,
120122
fromCopy,
121-
canSetMaxTTL,
123+
allowAdvancedScheduling,
122124
variables,
123125
parameters,
124126
}: GetInitialValuesParams) => {
125127
let initialValues = defaultInitialValues
126128

127-
if (!canSetMaxTTL) {
129+
if (!allowAdvancedScheduling) {
128130
initialValues = {
129131
...initialValues,
130132
max_ttl_hours: 0,
@@ -188,7 +190,7 @@ export interface CreateTemplateFormProps {
188190
error?: unknown
189191
jobError?: string
190192
logs?: ProvisionerJobLog[]
191-
canSetMaxTTL: boolean
193+
allowAdvancedScheduling: boolean
192194
copiedTemplate?: Template
193195
}
194196

@@ -204,12 +206,12 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
204206
error,
205207
jobError,
206208
logs,
207-
canSetMaxTTL,
209+
allowAdvancedScheduling,
208210
}) => {
209211
const styles = useStyles()
210212
const form = useFormik<CreateTemplateData>({
211213
initialValues: getInitialValues({
212-
canSetMaxTTL,
214+
allowAdvancedScheduling,
213215
fromExample: starterTemplate,
214216
fromCopy: copiedTemplate,
215217
variables,
@@ -319,7 +321,7 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
319321
<TextField
320322
{...getFieldHelpers(
321323
"max_ttl_hours",
322-
canSetMaxTTL ? (
324+
allowAdvancedScheduling ? (
323325
<TTLHelperText
324326
translationName="form.helperText.maxTTLHelperText"
325327
ttl={form.values.max_ttl_hours}
@@ -334,13 +336,62 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
334336
</>
335337
),
336338
)}
337-
disabled={isSubmitting || !canSetMaxTTL}
339+
disabled={isSubmitting || !allowAdvancedScheduling}
338340
fullWidth
339341
label={t("form.fields.maxTTL")}
340342
variant="outlined"
341343
type="number"
342344
/>
343345
</Stack>
346+
<Stack direction="column">
347+
<Stack direction="row" alignItems="center">
348+
<Checkbox
349+
id="allow_user_autostart"
350+
size="small"
351+
color="primary"
352+
disabled={isSubmitting || !allowAdvancedScheduling}
353+
onChange={async () => {
354+
await form.setFieldValue(
355+
"allow_user_autostart",
356+
!form.values.allow_user_autostart,
357+
)
358+
}}
359+
name="allow_user_autostart"
360+
checked={form.values.allow_user_autostart}
361+
/>
362+
<Stack spacing={0.5}>
363+
<strong>
364+
Allow users to autostart workspaces on a schedule.
365+
</strong>
366+
</Stack>
367+
</Stack>
368+
<Stack direction="row" alignItems="center">
369+
<Checkbox
370+
id="allow-user-autostop"
371+
size="small"
372+
color="primary"
373+
disabled={isSubmitting || !allowAdvancedScheduling}
374+
onChange={async () => {
375+
await form.setFieldValue(
376+
"allow_user_autostop",
377+
!form.values.allow_user_autostop,
378+
)
379+
}}
380+
name="allow-user-autostop"
381+
checked={form.values.allow_user_autostop}
382+
/>
383+
<Stack spacing={0.5}>
384+
<strong>
385+
Allow users to customize autostop duration for workspaces.
386+
</strong>
387+
<span className={styles.optionHelperText}>
388+
Workspaces will always use the default TTL if this is set.
389+
Regardless of this setting, workspaces can only stay on for
390+
the max TTL.
391+
</span>
392+
</Stack>
393+
</Stack>
394+
</Stack>
344395
</FormFields>
345396
</FormSection>
346397

site/src/pages/CreateTemplatePage/CreateTemplatePage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const CreateTemplatePage: FC = () => {
4444
} = state.context
4545
const shouldDisplayForm = !state.hasTag("loading")
4646
const { entitlements } = useDashboard()
47-
const canSetMaxTTL =
47+
const allowAdvancedScheduling =
4848
entitlements.features["advanced_template_scheduling"].enabled
4949

5050
const onCancel = () => {
@@ -70,7 +70,7 @@ const CreateTemplatePage: FC = () => {
7070
{shouldDisplayForm && (
7171
<CreateTemplateForm
7272
copiedTemplate={state.context.copiedTemplate}
73-
canSetMaxTTL={canSetMaxTTL}
73+
allowAdvancedScheduling={allowAdvancedScheduling}
7474
error={error}
7575
starterTemplate={starterTemplate}
7676
isSubmitting={state.hasTag("submitting")}

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

Lines changed: 96 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import TextField from "@material-ui/core/TextField"
22
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
3-
import { FormikContextType, FormikTouched, useFormik } from "formik"
3+
import { FormikTouched, useFormik } from "formik"
44
import { FC } from "react"
55
import { getFormHelpers } from "util/formUtils"
66
import * as Yup from "yup"
@@ -11,6 +11,7 @@ import { FormSection, HorizontalForm, FormFooter } from "components/Form/Form"
1111
import { Stack } from "components/Stack/Stack"
1212
import { makeStyles } from "@material-ui/core/styles"
1313
import Link from "@material-ui/core/Link"
14+
import Checkbox from "@material-ui/core/Checkbox"
1415

1516
const TTLHelperText = ({
1617
ttl,
@@ -48,6 +49,8 @@ export const getValidationSchema = (): Yup.AnyObjectSchema =>
4849
24 * MAX_TTL_DAYS /* 7 days in hours */,
4950
i18next.t("maxTTLMaxError", { ns: "templateSettingsPage" }),
5051
),
52+
allow_user_autostart: Yup.boolean(),
53+
allow_user_autostop: Yup.boolean(),
5154
})
5255

5356
export interface TemplateScheduleForm {
@@ -56,7 +59,7 @@ export interface TemplateScheduleForm {
5659
onCancel: () => void
5760
isSubmitting: boolean
5861
error?: unknown
59-
canSetMaxTTL: boolean
62+
allowAdvancedScheduling: boolean
6063
// Helpful to show field errors on Storybook
6164
initialTouched?: FormikTouched<UpdateTemplateMeta>
6265
}
@@ -66,35 +69,40 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
6669
onSubmit,
6770
onCancel,
6871
error,
69-
canSetMaxTTL,
72+
allowAdvancedScheduling,
7073
isSubmitting,
7174
initialTouched,
7275
}) => {
7376
const { t: commonT } = useTranslation("common")
7477
const validationSchema = getValidationSchema()
75-
const form: FormikContextType<UpdateTemplateMeta> =
76-
useFormik<UpdateTemplateMeta>({
77-
initialValues: {
78-
// on display, convert from ms => hours
79-
default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION,
80-
// the API ignores this value, but to avoid tripping up validation set
81-
// it to zero if the user can't set the field.
82-
max_ttl_ms: canSetMaxTTL ? template.max_ttl_ms / MS_HOUR_CONVERSION : 0,
83-
},
84-
validationSchema,
85-
onSubmit: (formData) => {
86-
// on submit, convert from hours => ms
87-
onSubmit({
88-
default_ttl_ms: formData.default_ttl_ms
89-
? formData.default_ttl_ms * MS_HOUR_CONVERSION
90-
: undefined,
91-
max_ttl_ms: formData.max_ttl_ms
92-
? formData.max_ttl_ms * MS_HOUR_CONVERSION
93-
: undefined,
94-
})
95-
},
96-
initialTouched,
97-
})
78+
const form = useFormik<UpdateTemplateMeta>({
79+
initialValues: {
80+
// on display, convert from ms => hours
81+
default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION,
82+
// the API ignores this value, but to avoid tripping up validation set
83+
// it to zero if the user can't set the field.
84+
max_ttl_ms: allowAdvancedScheduling
85+
? template.max_ttl_ms / MS_HOUR_CONVERSION
86+
: 0,
87+
allow_user_autostart: template.allow_user_autostart,
88+
allow_user_autostop: template.allow_user_autostop,
89+
},
90+
validationSchema,
91+
onSubmit: (formData) => {
92+
// on submit, convert from hours => ms
93+
onSubmit({
94+
default_ttl_ms: formData.default_ttl_ms
95+
? formData.default_ttl_ms * MS_HOUR_CONVERSION
96+
: undefined,
97+
max_ttl_ms: formData.max_ttl_ms
98+
? formData.max_ttl_ms * MS_HOUR_CONVERSION
99+
: undefined,
100+
allow_user_autostart: formData.allow_user_autostart,
101+
allow_user_autostop: formData.allow_user_autostop,
102+
})
103+
},
104+
initialTouched,
105+
})
98106
const getFieldHelpers = getFormHelpers<UpdateTemplateMeta>(form, error)
99107
const { t } = useTranslation("templateSettingsPage")
100108
const styles = useStyles()
@@ -128,7 +136,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
128136
<TextField
129137
{...getFieldHelpers(
130138
"max_ttl_ms",
131-
canSetMaxTTL ? (
139+
allowAdvancedScheduling ? (
132140
<TTLHelperText
133141
translationName="maxTTLHelperText"
134142
ttl={form.values.max_ttl_ms}
@@ -143,7 +151,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
143151
</>
144152
),
145153
)}
146-
disabled={isSubmitting || !canSetMaxTTL}
154+
disabled={isSubmitting || !allowAdvancedScheduling}
147155
fullWidth
148156
inputProps={{ min: 0, step: 1 }}
149157
label={t("maxTtlLabel")}
@@ -153,13 +161,72 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
153161
</Stack>
154162
</FormSection>
155163

164+
<FormSection
165+
title="Allow users scheduling"
166+
description="Allow users to set custom autostart and autostop scheduling options for workspaces created from this template."
167+
>
168+
<Stack direction="column">
169+
<Stack direction="row" alignItems="center">
170+
<Checkbox
171+
id="allow_user_autostart"
172+
size="small"
173+
color="primary"
174+
disabled={isSubmitting || !allowAdvancedScheduling}
175+
onChange={async () => {
176+
await form.setFieldValue(
177+
"allow_user_autostart",
178+
!form.values.allow_user_autostart,
179+
)
180+
}}
181+
name="allow_user_autostart"
182+
checked={form.values.allow_user_autostart}
183+
/>
184+
<Stack spacing={0.5}>
185+
<strong>
186+
Allow users to autostart workspaces on a schedule.
187+
</strong>
188+
</Stack>
189+
</Stack>
190+
<Stack direction="row" alignItems="center">
191+
<Checkbox
192+
id="allow-user-autostop"
193+
size="small"
194+
color="primary"
195+
disabled={isSubmitting || !allowAdvancedScheduling}
196+
onChange={async () => {
197+
await form.setFieldValue(
198+
"allow_user_autostop",
199+
!form.values.allow_user_autostop,
200+
)
201+
}}
202+
name="allow_user_autostop"
203+
checked={form.values.allow_user_autostop}
204+
/>
205+
<Stack spacing={0.5}>
206+
<strong>
207+
Allow users to customize autostop duration for workspaces.
208+
</strong>
209+
<span className={styles.optionDescription}>
210+
Workspaces will always use the default TTL if this is set.
211+
Regardless of this setting, workspaces can only stay on for the
212+
max lifetime.
213+
</span>
214+
</Stack>
215+
</Stack>
216+
</Stack>
217+
</FormSection>
218+
156219
<FormFooter onCancel={onCancel} isLoading={isSubmitting} />
157220
</HorizontalForm>
158221
)
159222
}
160223

161-
const useStyles = makeStyles(() => ({
224+
const useStyles = makeStyles((theme) => ({
162225
ttlFields: {
163226
width: "100%",
164227
},
228+
optionDescription: {
229+
fontSize: 12,
230+
color: theme.palette.text.secondary,
231+
},
165232
}))

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const TemplateSchedulePage: FC = () => {
1515
const navigate = useNavigate()
1616
const { template } = useTemplateSettingsContext()
1717
const { entitlements } = useDashboard()
18-
const canSetMaxTTL =
18+
const allowAdvancedScheduling =
1919
entitlements.features["advanced_template_scheduling"].enabled
2020
const {
2121
mutate: updateTemplate,
@@ -36,7 +36,7 @@ const TemplateSchedulePage: FC = () => {
3636
<title>{pageTitle([template.name, "Schedule"])}</title>
3737
</Helmet>
3838
<TemplateSchedulePageView
39-
canSetMaxTTL={canSetMaxTTL}
39+
allowAdvancedScheduling={allowAdvancedScheduling}
4040
isSubmitting={isSubmitting}
4141
template={template}
4242
submitError={submitError}

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePageView.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default {
1010
title: "pages/TemplateSchedulePageView",
1111
component: TemplateSchedulePageView,
1212
args: {
13-
canSetMaxTTL: true,
13+
allowAdvancedScheduling: true,
1414
template: MockTemplate,
1515
onSubmit: action("onSubmit"),
1616
onCancel: action("cancel"),
@@ -26,5 +26,5 @@ Example.args = {}
2626

2727
export const CantSetMaxTTL = Template.bind({})
2828
CantSetMaxTTL.args = {
29-
canSetMaxTTL: false,
29+
allowAdvancedScheduling: false,
3030
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ export interface TemplateSchedulePageViewProps {
1111
isSubmitting: boolean
1212
submitError?: unknown
1313
initialTouched?: ComponentProps<typeof TemplateScheduleForm>["initialTouched"]
14-
canSetMaxTTL: boolean
14+
allowAdvancedScheduling: boolean
1515
}
1616

1717
export const TemplateSchedulePageView: FC<TemplateSchedulePageViewProps> = ({
1818
template,
1919
onCancel,
2020
onSubmit,
2121
isSubmitting,
22-
canSetMaxTTL,
22+
allowAdvancedScheduling,
2323
submitError,
2424
initialTouched,
2525
}) => {
@@ -32,7 +32,7 @@ export const TemplateSchedulePageView: FC<TemplateSchedulePageViewProps> = ({
3232
</PageHeader>
3333

3434
<TemplateScheduleForm
35-
canSetMaxTTL={canSetMaxTTL}
35+
allowAdvancedScheduling={allowAdvancedScheduling}
3636
initialTouched={initialTouched}
3737
isSubmitting={isSubmitting}
3838
template={template}

0 commit comments

Comments
 (0)