Skip to content

Commit 7a864bd

Browse files
refactor(site): Refactor template settings (#6239)
1 parent e161c45 commit 7a864bd

12 files changed

+549
-272
lines changed
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/components/TemplateLayout/TemplateLayout.tsx

+11-109
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,8 @@
1-
import Button from "@material-ui/core/Button"
2-
import Link from "@material-ui/core/Link"
31
import { makeStyles } from "@material-ui/core/styles"
4-
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
5-
import SettingsOutlined from "@material-ui/icons/SettingsOutlined"
62
import { useMachine } from "@xstate/react"
7-
import {
8-
PageHeader,
9-
PageHeaderSubtitle,
10-
PageHeaderTitle,
11-
} from "components/PageHeader/PageHeader"
123
import { useOrganizationId } from "hooks/useOrganizationId"
134
import { createContext, FC, Suspense, useContext } from "react"
14-
import {
15-
Link as RouterLink,
16-
NavLink,
17-
Outlet,
18-
useParams,
19-
} from "react-router-dom"
5+
import { NavLink, Outlet, useNavigate, useParams } from "react-router-dom"
206
import { combineClasses } from "util/combineClasses"
217
import {
228
TemplateContext,
@@ -27,14 +13,7 @@ import { Stack } from "components/Stack/Stack"
2713
import { Permissions } from "xServices/auth/authXService"
2814
import { Loader } from "components/Loader/Loader"
2915
import { usePermissions } from "hooks/usePermissions"
30-
import { Avatar } from "components/Avatar/Avatar"
31-
32-
const Language = {
33-
settingsButton: "Settings",
34-
editButton: "Edit",
35-
createButton: "Create workspace",
36-
noDescription: "",
37-
}
16+
import { TemplatePageHeader } from "./TemplatePageHeader"
3817

3918
const useTemplateName = () => {
4019
const { template } = useParams()
@@ -65,38 +44,10 @@ export const useTemplateLayoutContext = (): TemplateLayoutContextValue => {
6544
return context
6645
}
6746

68-
const TemplateSettingsButton: FC<{ templateName: string }> = ({
69-
templateName,
70-
}) => (
71-
<Link
72-
underline="none"
73-
component={RouterLink}
74-
to={`/templates/${templateName}/settings`}
75-
>
76-
<Button variant="outlined" startIcon={<SettingsOutlined />}>
77-
{Language.settingsButton}
78-
</Button>
79-
</Link>
80-
)
81-
82-
const CreateWorkspaceButton: FC<{
83-
templateName: string
84-
className?: string
85-
}> = ({ templateName, className }) => (
86-
<Link
87-
underline="none"
88-
component={RouterLink}
89-
to={`/templates/${templateName}/workspace`}
90-
>
91-
<Button className={className ?? ""} startIcon={<AddCircleOutline />}>
92-
{Language.createButton}
93-
</Button>
94-
</Link>
95-
)
96-
9747
export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
9848
children = <Outlet />,
9949
}) => {
50+
const navigate = useNavigate()
10051
const styles = useStyles()
10152
const organizationId = useOrganizationId()
10253
const templateName = useTemplateName()
@@ -108,58 +59,20 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
10859
})
10960
const { template, permissions: templatePermissions } = templateState.context
11061
const permissions = usePermissions()
111-
const hasIcon = template && template.icon && template.icon !== ""
11262

113-
if (!template) {
63+
if (!template || !templatePermissions) {
11464
return <Loader />
11565
}
11666

117-
const generatePageHeaderActions = (): JSX.Element[] => {
118-
const pageActions: JSX.Element[] = []
119-
120-
if (templatePermissions?.canUpdateTemplate) {
121-
pageActions.push(<TemplateSettingsButton templateName={template.name} />)
122-
}
123-
124-
pageActions.push(<CreateWorkspaceButton templateName={template.name} />)
125-
126-
return pageActions
127-
}
128-
12967
return (
13068
<>
131-
<Margins>
132-
<PageHeader
133-
actions={
134-
<>
135-
{generatePageHeaderActions().map((action, i) => (
136-
<div key={i}>{action}</div>
137-
))}
138-
</>
139-
}
140-
>
141-
<Stack direction="row" spacing={3} className={styles.pageTitle}>
142-
{hasIcon ? (
143-
<Avatar size="xl" src={template.icon} variant="square" fitImage />
144-
) : (
145-
<Avatar size="xl">{template.name}</Avatar>
146-
)}
147-
148-
<div>
149-
<PageHeaderTitle>
150-
{template.display_name.length > 0
151-
? template.display_name
152-
: template.name}
153-
</PageHeaderTitle>
154-
<PageHeaderSubtitle condensed>
155-
{template.description === ""
156-
? Language.noDescription
157-
: template.description}
158-
</PageHeaderSubtitle>
159-
</div>
160-
</Stack>
161-
</PageHeader>
162-
</Margins>
69+
<TemplatePageHeader
70+
template={template}
71+
permissions={templatePermissions}
72+
onDeleteTemplate={() => {
73+
navigate("/templates")
74+
}}
75+
/>
16376

16477
<div className={styles.tabs}>
16578
<Margins>
@@ -204,17 +117,6 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
204117

205118
export const useStyles = makeStyles((theme) => {
206119
return {
207-
pageTitle: {
208-
alignItems: "center",
209-
},
210-
iconWrapper: {
211-
width: theme.spacing(6),
212-
height: theme.spacing(6),
213-
"& img": {
214-
width: "100%",
215-
},
216-
},
217-
218120
tabs: {
219121
borderBottom: `1px solid ${theme.palette.divider}`,
220122
marginBottom: theme.spacing(5),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import { MockTemplate } from "testHelpers/entities"
3+
import {
4+
TemplatePageHeader,
5+
TemplatePageHeaderProps,
6+
} from "./TemplatePageHeader"
7+
8+
export default {
9+
title: "Components/TemplatePageHeader",
10+
component: TemplatePageHeader,
11+
argTypes: {
12+
template: {
13+
defaultValue: MockTemplate,
14+
},
15+
permissions: {
16+
defaultValue: {
17+
canUpdateTemplate: true,
18+
},
19+
},
20+
},
21+
} as ComponentMeta<typeof TemplatePageHeader>
22+
23+
const Template: Story<TemplatePageHeaderProps> = (args) => (
24+
<TemplatePageHeader {...args} />
25+
)
26+
27+
export const CanUpdate = Template.bind({})
28+
CanUpdate.args = {}
29+
30+
export const CanNotUpdate = Template.bind({})
31+
CanNotUpdate.args = {
32+
permissions: {
33+
canUpdateTemplate: false,
34+
},
35+
}

0 commit comments

Comments
 (0)