Skip to content

Commit a92282d

Browse files
committed
refactor(site): Refactor template settings
1 parent bc56fb1 commit a92282d

File tree

4 files changed

+245
-69
lines changed

4 files changed

+245
-69
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import {
3+
FormFooterProps as BaseFormFooterProps,
4+
FormFooter as BaseFormFooter,
5+
} from "components/FormFooter/FormFooter"
6+
import { Stack } from "components/Stack/Stack"
7+
import { FC, HTMLProps, PropsWithChildren } from "react"
8+
9+
export const HorizontalForm: FC<
10+
PropsWithChildren & HTMLProps<HTMLFormElement>
11+
> = ({ children, ...formProps }) => {
12+
const styles = useStyles()
13+
14+
return (
15+
<form {...formProps}>
16+
<Stack direction="column" spacing={10} className={styles.formSections}>
17+
{children}
18+
</Stack>
19+
</form>
20+
)
21+
}
22+
23+
export const FormSection: FC<
24+
PropsWithChildren & { title: string; description: string | JSX.Element }
25+
> = ({ children, title, description }) => {
26+
const styles = useStyles()
27+
28+
return (
29+
<div className={styles.formSection}>
30+
<div className={styles.formSectionInfo}>
31+
<h2 className={styles.formSectionInfoTitle}>{title}</h2>
32+
<div className={styles.formSectionInfoDescription}>{description}</div>
33+
</div>
34+
35+
{children}
36+
</div>
37+
)
38+
}
39+
40+
export const FormFields: FC<PropsWithChildren> = ({ children }) => {
41+
const styles = useStyles()
42+
return (
43+
<Stack direction="column" className={styles.formSectionFields}>
44+
{children}
45+
</Stack>
46+
)
47+
}
48+
49+
export const FormFooter: FC<BaseFormFooterProps> = (props) => {
50+
const formFooterStyles = useFormFooterStyles()
51+
return (
52+
<BaseFormFooter
53+
{...props}
54+
styles={{ ...formFooterStyles, ...props.styles }}
55+
/>
56+
)
57+
}
58+
59+
const useStyles = makeStyles((theme) => ({
60+
formSections: {
61+
[theme.breakpoints.down("sm")]: {
62+
gap: theme.spacing(8),
63+
},
64+
},
65+
66+
formSection: {
67+
display: "flex",
68+
alignItems: "flex-start",
69+
gap: theme.spacing(15),
70+
71+
[theme.breakpoints.down("sm")]: {
72+
flexDirection: "column",
73+
gap: theme.spacing(2),
74+
},
75+
},
76+
77+
formSectionInfo: {
78+
width: 312,
79+
flexShrink: 0,
80+
position: "sticky",
81+
top: theme.spacing(3),
82+
83+
[theme.breakpoints.down("sm")]: {
84+
width: "100%",
85+
position: "initial",
86+
},
87+
},
88+
89+
formSectionInfoTitle: {
90+
fontSize: 20,
91+
color: theme.palette.text.primary,
92+
fontWeight: 400,
93+
margin: 0,
94+
marginBottom: theme.spacing(1),
95+
},
96+
97+
formSectionInfoDescription: {
98+
fontSize: 14,
99+
color: theme.palette.text.secondary,
100+
lineHeight: "160%",
101+
margin: 0,
102+
},
103+
104+
formSectionFields: {
105+
width: "100%",
106+
},
107+
}))
108+
109+
const useFormFooterStyles = makeStyles((theme) => ({
110+
button: {
111+
minWidth: theme.spacing(23),
112+
113+
[theme.breakpoints.down("sm")]: {
114+
width: "100%",
115+
},
116+
},
117+
footer: {
118+
display: "flex",
119+
alignItems: "center",
120+
justifyContent: "flex-start",
121+
flexDirection: "row-reverse",
122+
gap: theme.spacing(2),
123+
124+
[theme.breakpoints.down("sm")]: {
125+
flexDirection: "column",
126+
gap: theme.spacing(1),
127+
},
128+
},
129+
}))

site/src/i18n/en/templateForm.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"generalInfo": {
3+
"title": "General info",
4+
"description": "The name is used to identify the template in URLs and the API. It must be unique within your organization."
5+
},
6+
"displayInfo": {
7+
"title": "Display info",
8+
"description": "Give your template a friendly name, description, and icon."
9+
},
10+
"schedule": {
11+
"title": "Schedule",
12+
"description": "Define when workspaces created from this template automatically stop."
13+
},
14+
"operations": {
15+
"title": "Operations",
16+
"description": "Regulate actions allowed on workspaces created from this template."
17+
},
18+
"parameters": {
19+
"title": "Template params",
20+
"description": "These params are provided by your template's Terraform configuration."
21+
},
22+
"fields": {
23+
"name": "Name",
24+
"displayName": "Display name",
25+
"description": "Description",
26+
"icon": "Icon",
27+
"autoStop": "Auto-stop default",
28+
"allowUsersToCancel": "Allow users to cancel in-progress workspace jobs"
29+
},
30+
"helperText": {
31+
"autoStop": "Time in hours",
32+
"allowUsersToCancel": "If checked, users may be able to corrupt their workspace."
33+
},
34+
"upload": {
35+
"removeTitle": "Remove file",
36+
"title": "Upload template"
37+
},
38+
"tooltip": {
39+
"allowUsersToCancel": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases."
40+
}
41+
}

site/src/i18n/en/templateSettingsPage.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
"ttlHelperText_other": "Workspaces created from this template will default to stopping after {{count}} hours.",
1616
"allowUserCancelWorkspaceJobsLabel": "Allow users to cancel in-progress workspace jobs.",
1717
"allowUserCancelWorkspaceJobsNotice": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases.",
18-
"dangerZone": {
19-
"dangerZoneHeader": "Danger Zone",
20-
"deleteTemplateHeader": "Delete this template",
21-
"deleteTemplateCaption": "Do you want to permanently delete this template?",
22-
"deleteCta": "Delete Template"
18+
"generalInfo": {
19+
"title": "General info",
20+
"description": "The name is used to identify the template in URLs and the API. It must be unique within your organization."
21+
},
22+
"displayInfo": {
23+
"title": "Display info",
24+
"description": "Give your template a friendly name, description, and icon."
25+
},
26+
"schedule": {
27+
"title": "Schedule",
28+
"description": "Define when workspaces created from this template automatically stop."
2329
}
2430
}

site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import Box from "@material-ui/core/Box"
2-
import Checkbox from "@material-ui/core/Checkbox"
3-
import Typography from "@material-ui/core/Typography"
41
import TextField from "@material-ui/core/TextField"
52
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
6-
import { FormFooter } from "components/FormFooter/FormFooter"
7-
import { Stack } from "components/Stack/Stack"
83
import { FormikContextType, FormikTouched, useFormik } from "formik"
94
import { FC } from "react"
105
import {
@@ -18,6 +13,12 @@ import i18next from "i18next"
1813
import { useTranslation } from "react-i18next"
1914
import { Maybe } from "components/Conditionals/Maybe"
2015
import { LazyIconField } from "components/IconField/LazyIconField"
16+
import {
17+
FormFields,
18+
FormSection,
19+
HorizontalForm,
20+
FormFooter,
21+
} from "components/HorizontalForm/HorizontalForm"
2122

2223
const TTLHelperText = ({ ttl }: { ttl?: number }) => {
2324
const { t } = useTranslation("templateSettingsPage")
@@ -103,46 +104,66 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
103104
const { t } = useTranslation("templateSettingsPage")
104105

105106
return (
106-
<form onSubmit={form.handleSubmit} aria-label={t("formAriaLabel")}>
107-
<Stack>
108-
<TextField
109-
{...getFieldHelpers("name")}
110-
disabled={isSubmitting}
111-
onChange={onChangeTrimmed(form)}
112-
autoFocus
113-
fullWidth
114-
label={t("nameLabel")}
115-
variant="outlined"
116-
/>
107+
<HorizontalForm
108+
onSubmit={form.handleSubmit}
109+
aria-label={t("formAriaLabel")}
110+
>
111+
<FormSection
112+
title={t("generalInfo.title")}
113+
description={t("generalInfo.description")}
114+
>
115+
<FormFields>
116+
<TextField
117+
{...getFieldHelpers("name")}
118+
disabled={isSubmitting}
119+
onChange={onChangeTrimmed(form)}
120+
autoFocus
121+
fullWidth
122+
label={t("nameLabel")}
123+
variant="outlined"
124+
/>
125+
</FormFields>
126+
</FormSection>
117127

118-
<TextField
119-
{...getFieldHelpers("display_name")}
120-
disabled={isSubmitting}
121-
fullWidth
122-
label={t("displayNameLabel")}
123-
variant="outlined"
124-
/>
128+
<FormSection
129+
title={t("displayInfo.title")}
130+
description={t("displayInfo.description")}
131+
>
132+
<FormFields>
133+
<TextField
134+
{...getFieldHelpers("display_name")}
135+
disabled={isSubmitting}
136+
fullWidth
137+
label={t("displayNameLabel")}
138+
variant="outlined"
139+
/>
125140

126-
<TextField
127-
{...getFieldHelpers("description")}
128-
multiline
129-
disabled={isSubmitting}
130-
fullWidth
131-
label={t("descriptionLabel")}
132-
variant="outlined"
133-
rows={2}
134-
/>
141+
<TextField
142+
{...getFieldHelpers("description")}
143+
multiline
144+
disabled={isSubmitting}
145+
fullWidth
146+
label={t("descriptionLabel")}
147+
variant="outlined"
148+
rows={2}
149+
/>
135150

136-
<LazyIconField
137-
{...getFieldHelpers("icon")}
138-
disabled={isSubmitting}
139-
onChange={onChangeTrimmed(form)}
140-
fullWidth
141-
label={t("form.fields.icon")}
142-
variant="outlined"
143-
onPickEmoji={(value) => form.setFieldValue("icon", value)}
144-
/>
151+
<LazyIconField
152+
{...getFieldHelpers("icon")}
153+
disabled={isSubmitting}
154+
onChange={onChangeTrimmed(form)}
155+
fullWidth
156+
label={t("form.fields.icon")}
157+
variant="outlined"
158+
onPickEmoji={(value) => form.setFieldValue("icon", value)}
159+
/>
160+
</FormFields>
161+
</FormSection>
145162

163+
<FormSection
164+
title={t("schedule.title")}
165+
description={t("schedule.description")}
166+
>
146167
<TextField
147168
{...getFieldHelpers(
148169
"default_ttl_ms",
@@ -155,30 +176,9 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
155176
variant="outlined"
156177
type="number"
157178
/>
158-
159-
<Box display="flex">
160-
<div>
161-
{/*"getFieldHelpers" can't be used as it requires "helperText" property to be present.*/}
162-
<Checkbox
163-
id="allow_user_cancel_workspace_jobs"
164-
name="allow_user_cancel_workspace_jobs"
165-
disabled={isSubmitting}
166-
checked={form.values.allow_user_cancel_workspace_jobs}
167-
onChange={form.handleChange}
168-
/>
169-
</div>
170-
<Box>
171-
<Typography variant="h6" style={{ fontSize: 14 }}>
172-
{t("allowUserCancelWorkspaceJobsLabel")}
173-
</Typography>
174-
<Typography variant="caption" color="textSecondary">
175-
{t("allowUserCancelWorkspaceJobsNotice")}
176-
</Typography>
177-
</Box>
178-
</Box>
179-
</Stack>
179+
</FormSection>
180180

181181
<FormFooter onCancel={onCancel} isLoading={isSubmitting} />
182-
</form>
182+
</HorizontalForm>
183183
)
184184
}

0 commit comments

Comments
 (0)