Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5ea1d23

Browse files
committed
Add template settings
1 parent e85a17b commit 5ea1d23

16 files changed

+325
-264
lines changed

site/src/AppRouter.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import AuditPage from "pages/AuditPage/AuditPage"
66
import GroupsPage from "pages/GroupsPage/GroupsPage"
77
import LoginPage from "pages/LoginPage/LoginPage"
88
import { SetupPage } from "pages/SetupPage/SetupPage"
9-
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage"
9+
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage"
1010
import TemplatesPage from "pages/TemplatesPage/TemplatesPage"
1111
import UsersPage from "pages/UsersPage/UsersPage"
1212
import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage"
@@ -16,6 +16,7 @@ import { DashboardLayout } from "./components/Dashboard/DashboardLayout"
1616
import { RequireAuth } from "./components/RequireAuth/RequireAuth"
1717
import { SettingsLayout } from "./components/SettingsLayout/SettingsLayout"
1818
import { DeploySettingsLayout } from "components/DeploySettingsLayout/DeploySettingsLayout"
19+
import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout"
1920

2021
// Lazy load pages
2122
// - Pages that are secondary, not in the main navigation or not usually accessed
@@ -50,7 +51,7 @@ const TerminalPage = lazy(() => import("./pages/TerminalPage/TerminalPage"))
5051
const TemplatePermissionsPage = lazy(
5152
() =>
5253
import(
53-
"./pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage"
54+
"./pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage"
5455
),
5556
)
5657
const TemplateSummaryPage = lazy(
@@ -129,7 +130,7 @@ const CreateTokenPage = lazy(
129130
() => import("./pages/CreateTokenPage/CreateTokenPage"),
130131
)
131132
const TemplateFilesPage = lazy(
132-
() => import("./pages/TemplateFilesPage/TemplateFilesPage"),
133+
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
133134
)
134135

135136
export const AppRouter: FC = () => {
@@ -160,15 +161,20 @@ export const AppRouter: FC = () => {
160161
<Route path=":template">
161162
<Route element={<TemplateLayout />}>
162163
<Route index element={<TemplateSummaryPage />} />
164+
165+
<Route path="files" element={<TemplateFilesPage />} />
166+
</Route>
167+
168+
<Route path="workspace" element={<CreateWorkspacePage />} />
169+
170+
<Route path="settings" element={<TemplateSettingsLayout />}>
171+
<Route index element={<TemplateSettingsPage />} />
163172
<Route
164173
path="permissions"
165174
element={<TemplatePermissionsPage />}
166175
/>
167-
<Route path="files" element={<TemplateFilesPage />} />
168176
</Route>
169177

170-
<Route path="workspace" element={<CreateWorkspacePage />} />
171-
<Route path="settings" element={<TemplateSettingsPage />} />
172178
<Route path="variables" element={<TemplateVariablesPage />} />
173179
<Route path="versions">
174180
<Route path=":version">

site/src/components/SettingsLayout/Sidebar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ const useStyles = makeStyles((theme) => ({
130130
fontWeight: 600,
131131
overflow: "hidden",
132132
textOverflow: "ellipsis",
133+
whiteSpace: "nowrap",
133134
},
134135
email: {
135136
color: theme.palette.text.secondary,
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import ScheduleIcon from "@material-ui/icons/TimerOutlined"
3+
import VariablesIcon from "@material-ui/icons/CodeOutlined"
4+
import { Template } from "api/typesGenerated"
5+
import { Stack } from "components/Stack/Stack"
6+
import { FC, ElementType, PropsWithChildren, ReactNode } from "react"
7+
import { NavLink } from "react-router-dom"
8+
import { combineClasses } from "util/combineClasses"
9+
import GeneralIcon from "@material-ui/icons/SettingsOutlined"
10+
import SecurityIcon from "@material-ui/icons/LockOutlined"
11+
import { Avatar } from "components/Avatar/Avatar"
12+
13+
const SidebarNavItem: FC<
14+
PropsWithChildren<{ href: string; icon: ReactNode }>
15+
> = ({ children, href, icon }) => {
16+
const styles = useStyles()
17+
return (
18+
<NavLink
19+
end
20+
to={href}
21+
className={({ isActive }) =>
22+
combineClasses([
23+
styles.sidebarNavItem,
24+
isActive ? styles.sidebarNavItemActive : undefined,
25+
])
26+
}
27+
>
28+
<Stack alignItems="center" spacing={1.5} direction="row">
29+
{icon}
30+
{children}
31+
</Stack>
32+
</NavLink>
33+
)
34+
}
35+
36+
const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({
37+
icon: Icon,
38+
}) => {
39+
const styles = useStyles()
40+
return <Icon className={styles.sidebarNavItemIcon} />
41+
}
42+
43+
export const Sidebar: React.FC<{ template: Template }> = ({ template }) => {
44+
const styles = useStyles()
45+
46+
return (
47+
<nav className={styles.sidebar}>
48+
<Stack
49+
direction="row"
50+
alignItems="center"
51+
className={styles.templateInfo}
52+
>
53+
<Avatar src={template.icon} variant="square" fitImage />
54+
<Stack spacing={0} className={styles.templateData}>
55+
<span className={styles.name}>
56+
{template.display_name !== ""
57+
? template.display_name
58+
: template.name}
59+
</span>
60+
<span className={styles.secondary}>{template.name}</span>
61+
</Stack>
62+
</Stack>
63+
64+
<SidebarNavItem href="" icon={<SidebarNavItemIcon icon={GeneralIcon} />}>
65+
General
66+
</SidebarNavItem>
67+
<SidebarNavItem
68+
href="permissions"
69+
icon={<SidebarNavItemIcon icon={SecurityIcon} />}
70+
>
71+
Permissions
72+
</SidebarNavItem>
73+
<SidebarNavItem
74+
href="ssh-keys"
75+
icon={<SidebarNavItemIcon icon={VariablesIcon} />}
76+
>
77+
Variables
78+
</SidebarNavItem>
79+
<SidebarNavItem
80+
href="tokens"
81+
icon={<SidebarNavItemIcon icon={ScheduleIcon} />}
82+
>
83+
Schedule
84+
</SidebarNavItem>
85+
</nav>
86+
)
87+
}
88+
89+
const useStyles = makeStyles((theme) => ({
90+
sidebar: {
91+
width: 245,
92+
flexShrink: 0,
93+
},
94+
sidebarNavItem: {
95+
color: "inherit",
96+
display: "block",
97+
fontSize: 14,
98+
textDecoration: "none",
99+
padding: theme.spacing(1.5, 1.5, 1.5, 2),
100+
borderRadius: theme.shape.borderRadius / 2,
101+
transition: "background-color 0.15s ease-in-out",
102+
marginBottom: 1,
103+
position: "relative",
104+
105+
"&:hover": {
106+
backgroundColor: theme.palette.action.hover,
107+
},
108+
},
109+
sidebarNavItemActive: {
110+
backgroundColor: theme.palette.action.hover,
111+
112+
"&:before": {
113+
content: '""',
114+
display: "block",
115+
width: 3,
116+
height: "100%",
117+
position: "absolute",
118+
left: 0,
119+
top: 0,
120+
backgroundColor: theme.palette.secondary.dark,
121+
borderTopLeftRadius: theme.shape.borderRadius,
122+
borderBottomLeftRadius: theme.shape.borderRadius,
123+
},
124+
},
125+
sidebarNavItemIcon: {
126+
width: theme.spacing(2),
127+
height: theme.spacing(2),
128+
},
129+
templateInfo: {
130+
marginBottom: theme.spacing(2),
131+
},
132+
templateData: {
133+
overflow: "hidden",
134+
},
135+
name: {
136+
fontWeight: 600,
137+
overflow: "hidden",
138+
textOverflow: "ellipsis",
139+
whiteSpace: "nowrap",
140+
},
141+
secondary: {
142+
color: theme.palette.text.secondary,
143+
fontSize: 12,
144+
overflow: "hidden",
145+
textOverflow: "ellipsis",
146+
},
147+
}))

site/src/pages/TemplateSettingsPage/TemplateSettingsPage.tsx renamed to site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,36 @@
1-
import { useMachine } from "@xstate/react"
1+
import { useMutation } from "@tanstack/react-query"
2+
import { updateTemplateMeta } from "api/api"
3+
import { UpdateTemplateMeta } from "api/typesGenerated"
24
import { useDashboard } from "components/Dashboard/DashboardProvider"
3-
import { useOrganizationId } from "hooks/useOrganizationId"
5+
import { displaySuccess } from "components/GlobalSnackbar/utils"
46
import { FC } from "react"
57
import { Helmet } from "react-helmet-async"
68
import { useTranslation } from "react-i18next"
79
import { useNavigate, useParams } from "react-router-dom"
810
import { pageTitle } from "util/page"
9-
import { templateSettingsMachine } from "xServices/templateSettings/templateSettingsXService"
11+
import { useTemplateSettingsContext } from "../TemplateSettingsLayout"
1012
import { TemplateSettingsPageView } from "./TemplateSettingsPageView"
1113

1214
export const TemplateSettingsPage: FC = () => {
1315
const { template: templateName } = useParams() as { template: string }
1416
const { t } = useTranslation("templateSettingsPage")
1517
const navigate = useNavigate()
16-
const organizationId = useOrganizationId()
17-
const [state, send] = useMachine(templateSettingsMachine, {
18-
context: { templateName, organizationId },
19-
actions: {
20-
onSave: (_, { data }) => {
21-
// Use the data.name because the template name can be changed. Since the
22-
// API can return 304 if the template name is not changed, we use the
23-
// templateName from the URL as default.
24-
navigate(`/templates/${data.name ?? templateName}`)
25-
},
26-
},
27-
})
28-
const {
29-
templateSettings: template,
30-
saveTemplateSettingsError,
31-
getTemplateError,
32-
} = state.context
18+
const template = useTemplateSettingsContext()
3319
const { entitlements } = useDashboard()
3420
const canSetMaxTTL =
3521
entitlements.features["advanced_template_scheduling"].enabled
22+
const {
23+
mutate: updateTemplate,
24+
isLoading: isSubmitting,
25+
error: submitError,
26+
} = useMutation(
27+
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
28+
{
29+
onSuccess: () => {
30+
displaySuccess("Template updated successfully")
31+
},
32+
},
33+
)
3634

3735
return (
3836
<>
@@ -41,17 +39,14 @@ export const TemplateSettingsPage: FC = () => {
4139
</Helmet>
4240
<TemplateSettingsPageView
4341
canSetMaxTTL={canSetMaxTTL}
44-
isSubmitting={state.hasTag("submitting")}
42+
isSubmitting={isSubmitting}
4543
template={template}
46-
errors={{
47-
getTemplateError,
48-
saveTemplateSettingsError,
49-
}}
44+
submitError={submitError}
5045
onCancel={() => {
5146
navigate(`/templates/${templateName}`)
5247
}}
5348
onSubmit={(templateSettings) => {
54-
send({ type: "SAVE", templateSettings })
49+
updateTemplate(templateSettings)
5550
}}
5651
/>
5752
</>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
2+
import { ComponentProps, FC } from "react"
3+
import { TemplateSettingsForm } from "./TemplateSettingsForm"
4+
import { useTranslation } from "react-i18next"
5+
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"
6+
import { makeStyles } from "@material-ui/core/styles"
7+
8+
export interface TemplateSettingsPageViewProps {
9+
template: Template
10+
onSubmit: (data: UpdateTemplateMeta) => void
11+
onCancel: () => void
12+
isSubmitting: boolean
13+
submitError?: unknown
14+
initialTouched?: ComponentProps<typeof TemplateSettingsForm>["initialTouched"]
15+
canSetMaxTTL: boolean
16+
}
17+
18+
export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
19+
template,
20+
onCancel,
21+
onSubmit,
22+
isSubmitting,
23+
canSetMaxTTL,
24+
submitError,
25+
initialTouched,
26+
}) => {
27+
const { t } = useTranslation("templateSettingsPage")
28+
const styles = useStyles()
29+
30+
return (
31+
<>
32+
<PageHeader className={styles.pageHeader}>
33+
<PageHeaderTitle>{t("title")}</PageHeaderTitle>
34+
</PageHeader>
35+
36+
<TemplateSettingsForm
37+
canSetMaxTTL={canSetMaxTTL}
38+
initialTouched={initialTouched}
39+
isSubmitting={isSubmitting}
40+
template={template}
41+
onSubmit={onSubmit}
42+
onCancel={onCancel}
43+
error={submitError}
44+
/>
45+
</>
46+
)
47+
}
48+
49+
const useStyles = makeStyles(() => ({
50+
pageHeader: {
51+
paddingTop: 0,
52+
},
53+
}))

0 commit comments

Comments
 (0)