Skip to content

Commit 0312c16

Browse files
committed
Move danger zone
1 parent 81c29c0 commit 0312c16

File tree

5 files changed

+151
-93
lines changed

5 files changed

+151
-93
lines changed

site/src/components/TemplateLayout/TemplateLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
110110
const permissions = usePermissions()
111111
const hasIcon = template && template.icon && template.icon !== ""
112112

113-
if (!template) {
113+
if (!template || !templatePermissions) {
114114
return <Loader />
115115
}
116116

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import Button from "@material-ui/core/Button/Button"
2+
import Link from "@material-ui/core/Link/Link"
3+
import RemoveOutlined from "@material-ui/icons/RemoveOutlined"
4+
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
5+
import SettingsOutlined from "@material-ui/icons/SettingsOutlined"
6+
import { AuthorizationResponse, Template } from "api/typesGenerated"
7+
import { Avatar } from "components/Avatar/Avatar"
8+
import { Maybe } from "components/Conditionals/Maybe"
9+
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"
10+
import {
11+
PageHeader,
12+
PageHeaderTitle,
13+
PageHeaderSubtitle,
14+
} from "components/PageHeader/PageHeader"
15+
import { Stack } from "components/Stack/Stack"
16+
import { FC } from "react"
17+
import { Link as RouterLink } from "react-router-dom"
18+
import { useDeleteTemplate } from "./delete"
19+
20+
const TemplateSettingsButton: FC<{ templateName: string }> = ({
21+
templateName,
22+
}) => (
23+
<Link
24+
underline="none"
25+
component={RouterLink}
26+
to={`/templates/${templateName}/settings`}
27+
>
28+
<Button variant="outlined" startIcon={<SettingsOutlined />}>
29+
Settings
30+
</Button>
31+
</Link>
32+
)
33+
34+
const CreateWorkspaceButton: FC<{
35+
templateName: string
36+
className?: string
37+
}> = ({ templateName, className }) => (
38+
<Link
39+
underline="none"
40+
component={RouterLink}
41+
to={`/templates/${templateName}/workspace`}
42+
>
43+
<Button className={className ?? ""} startIcon={<AddCircleOutline />}>
44+
Create workspace
45+
</Button>
46+
</Link>
47+
)
48+
49+
const DeleteTemplateButton: FC<{ onClick: () => void }> = ({ onClick }) => (
50+
<Button startIcon={<RemoveOutlined />} onClick={onClick}>
51+
Delete
52+
</Button>
53+
)
54+
55+
export const TemplatePageHeader: FC<{
56+
template: Template
57+
permissions: AuthorizationResponse
58+
}> = ({ template, permissions }) => {
59+
const hasIcon = template.icon && template.icon !== ""
60+
const deleteTemplate = useDeleteTemplate(template)
61+
62+
return (
63+
<>
64+
<PageHeader
65+
actions={
66+
<>
67+
<Maybe condition={permissions.canUpdateTemplate}>
68+
<DeleteTemplateButton
69+
onClick={deleteTemplate.openDeleteConfirmation}
70+
/>
71+
<TemplateSettingsButton templateName={template.name} />
72+
</Maybe>
73+
<CreateWorkspaceButton templateName={template.name} />
74+
</>
75+
}
76+
>
77+
<Stack direction="row" spacing={3} alignItems="center">
78+
{hasIcon ? (
79+
<Avatar size="xl" src={template.icon} variant="square" fitImage />
80+
) : (
81+
<Avatar size="xl">{template.name}</Avatar>
82+
)}
83+
84+
<div>
85+
<PageHeaderTitle>
86+
{template.display_name.length > 0
87+
? template.display_name
88+
: template.name}
89+
</PageHeaderTitle>
90+
<PageHeaderSubtitle condensed>
91+
{template.description === ""
92+
? "No description"
93+
: template.description}
94+
</PageHeaderSubtitle>
95+
</div>
96+
</Stack>
97+
</PageHeader>
98+
99+
<DeleteDialog
100+
isOpen={deleteTemplate.isDeleteDialogOpen}
101+
confirmLoading={deleteTemplate.state.status === "deleting"}
102+
onConfirm={deleteTemplate.confirmDelete}
103+
onCancel={deleteTemplate.cancelDeleteConfirmation}
104+
entity="template"
105+
name={template.name}
106+
/>
107+
</>
108+
)
109+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { deleteTemplate } from "api/api"
2+
import { Template } from "api/typesGenerated"
3+
import { useState } from "react"
4+
import { useNavigate } from "react-router-dom"
5+
6+
type DeleteTemplateState =
7+
| { status: "idle" }
8+
| { status: "confirming" }
9+
| { status: "deleting" }
10+
11+
export const useDeleteTemplate = (template: Template) => {
12+
const navigate = useNavigate()
13+
const [state, setState] = useState<DeleteTemplateState>({ status: "idle" })
14+
const isDeleteDialogOpen =
15+
state.status === "confirming" || state.status === "deleting"
16+
17+
const openDeleteConfirmation = () => {
18+
setState({ status: "confirming" })
19+
}
20+
21+
const cancelDeleteConfirmation = () => {
22+
setState({ status: "idle" })
23+
}
24+
25+
const confirmDelete = async () => {
26+
setState({ status: "deleting" })
27+
await deleteTemplate(template.id)
28+
navigate("/templates")
29+
}
30+
31+
return {
32+
state,
33+
isDeleteDialogOpen,
34+
openDeleteConfirmation,
35+
cancelDeleteConfirmation,
36+
confirmDelete,
37+
}
38+
}

site/src/pages/TemplateSettingsPage/TemplateSettingsPage.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export const TemplateSettingsPage: FC = () => {
2626
templateSettings: template,
2727
saveTemplateSettingsError,
2828
getTemplateError,
29-
deleteTemplateError,
3029
} = state.context
3130

3231
return (
@@ -40,22 +39,13 @@ export const TemplateSettingsPage: FC = () => {
4039
errors={{
4140
getTemplateError,
4241
saveTemplateSettingsError,
43-
deleteTemplateError,
4442
}}
4543
onCancel={() => {
4644
navigate(`/templates/${templateName}`)
4745
}}
4846
onSubmit={(templateSettings) => {
4947
send({ type: "SAVE", templateSettings })
5048
}}
51-
onDelete={() => {
52-
send("DELETE")
53-
}}
54-
onConfirmDelete={() => send("CONFIRM_DELETE")}
55-
onCancelDelete={() => send("CANCEL_DELETE")}
56-
isConfirmingDelete={state.matches("confirmingDelete")}
57-
isDeleting={state.matches("deleting")}
58-
isDeleted={state.matches("deleted")}
5949
/>
6050
</>
6151
)
Lines changed: 3 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
11
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
22
import { AlertBanner } from "components/AlertBanner/AlertBanner"
3-
import { FullPageForm } from "components/FullPageForm/FullPageForm"
43
import { Loader } from "components/Loader/Loader"
54
import { ComponentProps, FC } from "react"
65
import { TemplateSettingsForm } from "./TemplateSettingsForm"
76
import { Stack } from "components/Stack/Stack"
8-
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"
97
import { makeStyles } from "@material-ui/core/styles"
10-
import { colors } from "theme/colors"
11-
import Button from "@material-ui/core/Button"
128
import { useTranslation } from "react-i18next"
13-
import { Navigate } from "react-router-dom"
9+
import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm"
1410

1511
export interface TemplateSettingsPageViewProps {
1612
template?: Template
1713
onSubmit: (data: UpdateTemplateMeta) => void
1814
onCancel: () => void
19-
onDelete: () => void
20-
onConfirmDelete: () => void
21-
onCancelDelete: () => void
22-
isConfirmingDelete: boolean
23-
isDeleting: boolean
24-
isDeleted: boolean
2515
isSubmitting: boolean
2616
errors?: {
2717
getTemplateError?: unknown
2818
saveTemplateSettingsError?: unknown
29-
deleteTemplateError?: unknown
3019
}
3120
initialTouched?: ComponentProps<typeof TemplateSettingsForm>["initialTouched"]
3221
}
@@ -35,12 +24,6 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
3524
template,
3625
onCancel,
3726
onSubmit,
38-
onDelete,
39-
onConfirmDelete,
40-
onCancelDelete,
41-
isConfirmingDelete,
42-
isDeleting,
43-
isDeleted,
4427
isSubmitting,
4528
errors = {},
4629
initialTouched,
@@ -49,22 +32,13 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
4932
const isLoading = !template && !errors.getTemplateError
5033
const { t } = useTranslation("templateSettingsPage")
5134

52-
if (isDeleted) {
53-
return <Navigate to="/templates" />
54-
}
55-
5635
return (
57-
<FullPageForm title={t("title")}>
36+
<FullPageHorizontalForm title={t("title")} onCancel={onCancel}>
5837
{Boolean(errors.getTemplateError) && (
5938
<Stack className={classes.errorContainer}>
6039
<AlertBanner severity="error" error={errors.getTemplateError} />
6140
</Stack>
6241
)}
63-
{Boolean(errors.deleteTemplateError) && (
64-
<Stack className={classes.errorContainer}>
65-
<AlertBanner severity="error" error={errors.deleteTemplateError} />
66-
</Stack>
67-
)}
6842
{isLoading && <Loader />}
6943
{template && (
7044
<>
@@ -76,67 +50,14 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
7650
onCancel={onCancel}
7751
error={errors.saveTemplateSettingsError}
7852
/>
79-
<Stack className={classes.dangerContainer}>
80-
<div className={classes.dangerHeader}>
81-
{t("dangerZone.dangerZoneHeader")}
82-
</div>
83-
84-
<Stack className={classes.dangerBorder}>
85-
<Stack spacing={0}>
86-
<p className={classes.deleteTemplateHeader}>
87-
{t("dangerZone.deleteTemplateHeader")}
88-
</p>
89-
<span>{t("dangerZone.deleteTemplateCaption")}</span>
90-
</Stack>
91-
<Button
92-
className={classes.deleteButton}
93-
onClick={onDelete}
94-
aria-label={t("dangerZone.deleteCta")}
95-
>
96-
{t("dangerZone.deleteCta")}
97-
</Button>
98-
</Stack>
99-
</Stack>
100-
101-
<DeleteDialog
102-
isOpen={isConfirmingDelete}
103-
confirmLoading={isDeleting}
104-
onConfirm={onConfirmDelete}
105-
onCancel={onCancelDelete}
106-
entity="template"
107-
name={template.name}
108-
/>
10953
</>
11054
)}
111-
</FullPageForm>
55+
</FullPageHorizontalForm>
11256
)
11357
}
11458

11559
const useStyles = makeStyles((theme) => ({
11660
errorContainer: {
11761
marginBottom: theme.spacing(2),
11862
},
119-
dangerContainer: {
120-
marginTop: theme.spacing(4),
121-
},
122-
dangerHeader: {
123-
fontSize: theme.typography.h5.fontSize,
124-
color: theme.palette.text.secondary,
125-
},
126-
dangerBorder: {
127-
border: `1px solid ${colors.red[13]}`,
128-
borderRadius: theme.shape.borderRadius,
129-
padding: theme.spacing(2),
130-
131-
"& p": {
132-
marginTop: "0px",
133-
},
134-
},
135-
deleteTemplateHeader: {
136-
fontSize: theme.typography.h6.fontSize,
137-
fontWeight: "bold",
138-
},
139-
deleteButton: {
140-
color: colors.red[8],
141-
},
14263
}))

0 commit comments

Comments
 (0)