Skip to content

Commit 6caeb00

Browse files
committed
Few refactorings for ttl fields
1 parent d54d798 commit 6caeb00

8 files changed

+177
-68
lines changed

site/src/i18n/en/common.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@
3939
"updateCheck": {
4040
"message": "Coder {{version}} is now available. View the <4>release notes</4> and <7>upgrade instructions</7> for more information.",
4141
"error": "Coder update check failed."
42-
}
42+
},
43+
"licenseFieldTextHelper": "You need an enterprise license to use it.",
44+
"learnMore": "Learn more"
4345
}

site/src/i18n/en/createTemplatePage.json

+15-4
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@
2626
"displayName": "Display name",
2727
"description": "Description",
2828
"icon": "Icon",
29-
"autoStop": "Auto-stop default",
30-
"maxTTL": "Maximum lifetime of workspaces",
29+
"autoStop": "Default auto-stop",
30+
"maxTTL": "Max. Lifetime",
3131
"allowUsersToCancel": "Allow users to cancel in-progress workspace jobs"
3232
},
3333
"helperText": {
34-
"autoStop": "Time in hours",
35-
"maxTTL": "Time in hours",
34+
"defaultTTLHelperText_zero": "Workspaces will run until stopped manually.",
35+
"defaultTTLHelperText_one": "Workspaces will default to stopping after {{count}} hour.",
36+
"defaultTTLHelperText_other": "Workspaces will default to stopping after {{count}} hours.",
37+
"maxTTLHelperText_zero": "Workspaces may run indefinitely.",
38+
"maxTTLHelperText_one": "Workspaces must stop within 1 hour of starting.",
39+
"maxTTLHelperText_other": "Workspaces must stop within {{count}} hours of starting.",
3640
"allowUsersToCancel": "If checked, users may be able to corrupt their workspace."
3741
},
3842
"upload": {
@@ -41,6 +45,13 @@
4145
},
4246
"tooltip": {
4347
"allowUsersToCancel": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases."
48+
},
49+
"error": {
50+
"descriptionMax": "Please enter a description that is less than or equal to 128 characters.",
51+
"defaultTTLMax": "Please enter a limit that is less than or equal to 168 hours (7 days).",
52+
"defaultTTLMin": "Default time until auto-stop must not be less than 0.",
53+
"maxTTLMax": "Please enter a limit that is less than or equal to 168 hours (7 days).",
54+
"maxTTLMin": "Maximum time until auto-stop must not be less than 0."
4455
}
4556
}
4657
}

site/src/i18n/en/templateSettingsPage.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
"displayNameLabel": "Display name",
55
"descriptionLabel": "Description",
66
"descriptionMaxError": "Please enter a description that is less than or equal to 128 characters.",
7-
"defaultTtlLabel": "Auto-stop default",
8-
"maxTtlLabel": "Maximum lifetime for started workspaces",
7+
"defaultTtlLabel": "Default auto-stop",
8+
"maxTtlLabel": "Max. Lifetime",
99
"iconLabel": "Icon",
1010
"formAriaLabel": "Template settings form",
1111
"selectEmoji": "Select emoji",
1212
"defaultTTLMaxError": "Please enter a limit that is less than or equal to 168 hours (7 days).",
1313
"defaultTTLMinError": "Default time until auto-stop must not be less than 0.",
14-
"defaultTTLHelperText_zero": "Workspaces created from this template will run until stopped manually.",
15-
"defaultTTLHelperText_one": "Workspaces created from this template will default to stopping after {{count}} hour.",
16-
"defaultTTLHelperText_other": "Workspaces created from this template will default to stopping after {{count}} hours.",
14+
"defaultTTLHelperText_zero": "Workspaces will run until stopped manually.",
15+
"defaultTTLHelperText_one": "Workspaces will default to stopping after {{count}} hour.",
16+
"defaultTTLHelperText_other": "Workspaces will default to stopping after {{count}} hours.",
1717
"maxTTLMaxError": "Please enter a limit that is less than or equal to 168 hours (7 days).",
1818
"maxTTLMinError": "Maximum time until auto-stop must not be less than 0.",
19-
"maxTTLHelperText_zero": "Workspaces created from this template may run indefinitely.",
20-
"maxTTLHelperText_one": "Workspaces created from this template must stop within {{count}} hour of starting.",
21-
"maxTTLHelperText_other": "Workspaces created from this template must stop within {{count}} hours of starting.",
19+
"maxTTLHelperText_zero": "Workspaces may run indefinitely.",
20+
"maxTTLHelperText_one": "Workspaces must stop within 1 hour of starting.",
21+
"maxTTLHelperText_other": "Workspaces must stop within {{count}} hours of starting.",
2222
"allowUserCancelWorkspaceJobsLabel": "Allow users to cancel in-progress workspace jobs.",
2323
"allowUserCancelWorkspaceJobsNotice": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases.",
2424
"allowUsersCancelHelperText": "If checked, users may be able to corrupt their workspace.",

site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx

+94-21
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,73 @@ import { useFormik } from "formik"
1717
import { SelectedTemplate } from "pages/CreateWorkspacePage/SelectedTemplate"
1818
import { FC } from "react"
1919
import { useTranslation } from "react-i18next"
20-
import { nameValidator, getFormHelpers, onChangeTrimmed } from "util/formUtils"
20+
import {
21+
nameValidator,
22+
getFormHelpers,
23+
onChangeTrimmed,
24+
templateDisplayNameValidator,
25+
} from "util/formUtils"
2126
import { CreateTemplateData } from "xServices/createTemplate/createTemplateXService"
2227
import * as Yup from "yup"
2328
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs"
2429
import { HelpTooltip, HelpTooltipText } from "components/Tooltips/HelpTooltip"
2530
import { LazyIconField } from "components/IconField/LazyIconField"
2631
import { Maybe } from "components/Conditionals/Maybe"
2732
import { useDashboard } from "components/Dashboard/DashboardProvider"
33+
import i18next from "i18next"
34+
import Link from "@material-ui/core/Link"
35+
36+
const MAX_DESCRIPTION_CHAR_LIMIT = 128
37+
const MAX_TTL_DAYS = 7
38+
39+
const TTLHelperText = ({
40+
ttl,
41+
translationName,
42+
}: {
43+
ttl?: number
44+
translationName: string
45+
}) => {
46+
const { t } = useTranslation("createTemplatePage")
47+
const count = typeof ttl !== "number" ? 0 : ttl
48+
return (
49+
// no helper text if ttl is negative - error will show once field is considered touched
50+
<Maybe condition={count >= 0}>
51+
<span>{t(translationName, { count })}</span>
52+
</Maybe>
53+
)
54+
}
2855

2956
const validationSchema = Yup.object({
30-
name: nameValidator("Name"),
31-
display_name: Yup.string().optional(),
32-
description: Yup.string().optional(),
57+
name: nameValidator(
58+
i18next.t("form.fields.name", { ns: "createTemplatePage" }),
59+
),
60+
display_name: templateDisplayNameValidator(
61+
i18next.t("form.fields.displayName", {
62+
ns: "createTemplatePage",
63+
}),
64+
),
65+
description: Yup.string().max(
66+
MAX_DESCRIPTION_CHAR_LIMIT,
67+
i18next.t("form.error.descriptionMax", { ns: "createTemplatePage" }),
68+
),
3369
icon: Yup.string().optional(),
34-
default_ttl_hours: Yup.number(),
35-
max_ttl_hours: Yup.number(),
70+
default_ttl_hours: Yup.number()
71+
.integer()
72+
.min(
73+
0,
74+
i18next.t("form.error.defaultTTLMin", { ns: "templateSettingsPage" }),
75+
)
76+
.max(
77+
24 * MAX_TTL_DAYS /* 7 days in hours */,
78+
i18next.t("form.error.defaultTTLMax", { ns: "templateSettingsPage" }),
79+
),
80+
max_ttl_hours: Yup.number()
81+
.integer()
82+
.min(0, i18next.t("form.error.maxTTLMin", { ns: "templateSettingsPage" }))
83+
.max(
84+
24 * MAX_TTL_DAYS /* 7 days in hours */,
85+
i18next.t("form.error.maxTTLMax", { ns: "templateSettingsPage" }),
86+
),
3687
allow_user_cancel_workspace_jobs: Yup.boolean(),
3788
parameter_values_by_name: Yup.object().optional(),
3889
})
@@ -110,6 +161,7 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
110161
})
111162
const getFieldHelpers = getFormHelpers<CreateTemplateData>(form, error)
112163
const { t } = useTranslation("createTemplatePage")
164+
const { t: commonT } = useTranslation("common")
113165

114166
return (
115167
<form onSubmit={form.handleSubmit}>
@@ -197,30 +249,47 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
197249
</p>
198250
</div>
199251

200-
<Stack direction="column" className={styles.formSectionFields}>
252+
<Stack direction="row" className={styles.ttlFields}>
201253
<TextField
202-
{...getFieldHelpers("default_ttl_hours")}
254+
{...getFieldHelpers(
255+
"default_ttl_hours",
256+
<TTLHelperText
257+
translationName="form.helperText.defaultTTLHelperText"
258+
ttl={form.values.default_ttl_hours}
259+
/>,
260+
)}
203261
disabled={isSubmitting}
204262
onChange={onChangeTrimmed(form)}
205263
fullWidth
206264
label={t("form.fields.autoStop")}
207265
variant="outlined"
208266
type="number"
209-
helperText={t("form.helperText.autoStop")}
210267
/>
211268

212-
<Maybe condition={canSetMaxTTL}>
213-
<TextField
214-
{...getFieldHelpers("max_ttl_hours")}
215-
disabled={isSubmitting}
216-
onChange={onChangeTrimmed(form)}
217-
fullWidth
218-
label={t("form.fields.maxTTL")}
219-
variant="outlined"
220-
type="number"
221-
helperText={t("form.helperText.maxTTL")}
222-
/>
223-
</Maybe>
269+
<TextField
270+
{...getFieldHelpers(
271+
"max_ttl_hours",
272+
canSetMaxTTL ? (
273+
<TTLHelperText
274+
translationName="form.helperText.maxTTLHelperText"
275+
ttl={form.values.max_ttl_hours}
276+
/>
277+
) : (
278+
<>
279+
{commonT("licenseFieldTextHelper")}{" "}
280+
<Link href="https://coder.com/docs/v2/latest/enterprise">
281+
{commonT("learnMore")}
282+
</Link>
283+
.
284+
</>
285+
),
286+
)}
287+
disabled={isSubmitting || !canSetMaxTTL}
288+
fullWidth
289+
label={t("form.fields.maxTTL")}
290+
variant="outlined"
291+
type="number"
292+
/>
224293
</Stack>
225294
</div>
226295

@@ -346,6 +415,10 @@ const useStyles = makeStyles((theme) => ({
346415
},
347416
},
348417

418+
ttlFields: {
419+
width: "100%",
420+
},
421+
349422
formSectionInfo: {
350423
width: 312,
351424
flexShrink: 0,

site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx

+38-25
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { Stack } from "components/Stack/Stack"
2323
import Checkbox from "@material-ui/core/Checkbox"
2424
import { HelpTooltip, HelpTooltipText } from "components/Tooltips/HelpTooltip"
2525
import { makeStyles } from "@material-ui/core/styles"
26-
import { useDashboard } from "components/Dashboard/DashboardProvider"
26+
import Link from "@material-ui/core/Link"
2727

2828
const TTLHelperText = ({
2929
ttl,
@@ -81,6 +81,7 @@ export interface TemplateSettingsForm {
8181
onCancel: () => void
8282
isSubmitting: boolean
8383
error?: unknown
84+
canSetMaxTTL: boolean
8485
// Helpful to show field errors on Storybook
8586
initialTouched?: FormikTouched<UpdateTemplateMeta>
8687
}
@@ -90,13 +91,11 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
9091
onSubmit,
9192
onCancel,
9293
error,
94+
canSetMaxTTL,
9395
isSubmitting,
9496
initialTouched,
9597
}) => {
96-
const { entitlements } = useDashboard()
97-
const canSetMaxTTL =
98-
entitlements.features["advanced_template_scheduling"].enabled
99-
98+
const { t: commonT } = useTranslation("common")
10099
const validationSchema = getValidationSchema()
101100
const form: FormikContextType<UpdateTemplateMeta> =
102101
useFormik<UpdateTemplateMeta>({
@@ -193,39 +192,49 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
193192
title={t("schedule.title")}
194193
description={t("schedule.description")}
195194
>
196-
<TextField
197-
{...getFieldHelpers(
198-
"default_ttl_ms",
199-
<TTLHelperText
200-
translationName="defaultTTLHelperText"
201-
ttl={form.values.default_ttl_ms}
202-
/>,
203-
)}
204-
disabled={isSubmitting}
205-
fullWidth
206-
inputProps={{ min: 0, step: 1 }}
207-
label={t("defaultTtlLabel")}
208-
variant="outlined"
209-
type="number"
210-
/>
211-
212-
<Maybe condition={canSetMaxTTL}>
195+
<Stack direction="row" className={styles.ttlFields}>
213196
<TextField
214197
{...getFieldHelpers(
215-
"max_ttl_ms",
198+
"default_ttl_ms",
216199
<TTLHelperText
217-
translationName="maxTTLHelperText"
200+
translationName="defaultTTLHelperText"
218201
ttl={form.values.default_ttl_ms}
219202
/>,
220203
)}
221204
disabled={isSubmitting}
222205
fullWidth
223206
inputProps={{ min: 0, step: 1 }}
207+
label={t("defaultTtlLabel")}
208+
variant="outlined"
209+
type="number"
210+
/>
211+
212+
<TextField
213+
{...getFieldHelpers(
214+
"max_ttl_ms",
215+
canSetMaxTTL ? (
216+
<TTLHelperText
217+
translationName="maxTTLHelperText"
218+
ttl={form.values.max_ttl_ms}
219+
/>
220+
) : (
221+
<>
222+
{commonT("licenseFieldTextHelper")}{" "}
223+
<Link href="https://coder.com/docs/v2/latest/enterprise">
224+
{commonT("learnMore")}
225+
</Link>
226+
.
227+
</>
228+
),
229+
)}
230+
disabled={isSubmitting || !canSetMaxTTL}
231+
fullWidth
232+
inputProps={{ min: 0, step: 1 }}
224233
label={t("maxTtlLabel")}
225234
variant="outlined"
226235
type="number"
227236
/>
228-
</Maybe>
237+
</Stack>
229238
</FormSection>
230239

231240
<FormSection
@@ -281,4 +290,8 @@ const useStyles = makeStyles((theme) => ({
281290
fontSize: theme.spacing(1.5),
282291
color: theme.palette.text.secondary,
283292
},
293+
294+
ttlFields: {
295+
width: "100%",
296+
},
284297
}))

site/src/pages/TemplateSettingsPage/TemplateSettingsPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useMachine } from "@xstate/react"
2+
import { useDashboard } from "components/Dashboard/DashboardProvider"
23
import { useOrganizationId } from "hooks/useOrganizationId"
34
import { FC } from "react"
45
import { Helmet } from "react-helmet-async"
@@ -27,13 +28,17 @@ export const TemplateSettingsPage: FC = () => {
2728
saveTemplateSettingsError,
2829
getTemplateError,
2930
} = state.context
31+
const { entitlements } = useDashboard()
32+
const canSetMaxTTL =
33+
entitlements.features["advanced_template_scheduling"].enabled
3034

3135
return (
3236
<>
3337
<Helmet>
3438
<title>{pageTitle(t("title"))}</title>
3539
</Helmet>
3640
<TemplateSettingsPageView
41+
canSetMaxTTL={canSetMaxTTL}
3742
isSubmitting={state.hasTag("submitting")}
3843
template={template}
3944
errors={{

0 commit comments

Comments
 (0)