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

Skip to content

Commit 88e24db

Browse files
refactor(site): Group template permissions, settings and variables under a settings layout (#6737)
1 parent cb73754 commit 88e24db

39 files changed

+1042
-769
lines changed

site/e2e/tests/listTemplates.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ test.use({ storageState: getStatePath("authState") })
55

66
test("list templates", async ({ page, baseURL }) => {
77
await page.goto(`${baseURL}/templates`, { waitUntil: "networkidle" })
8-
await expect(page).toHaveTitle("Templates Coder")
8+
await expect(page).toHaveTitle("Templates - Coder")
99
})

site/src/AppRouter.tsx

Lines changed: 27 additions & 8 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(
@@ -120,7 +121,10 @@ const CreateTemplatePage = lazy(
120121
() => import("./pages/CreateTemplatePage/CreateTemplatePage"),
121122
)
122123
const TemplateVariablesPage = lazy(
123-
() => import("./pages/TemplateVariablesPage/TemplateVariablesPage"),
124+
() =>
125+
import(
126+
"./pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage"
127+
),
124128
)
125129
const WorkspaceSettingsPage = lazy(
126130
() => import("./pages/WorkspaceSettingsPage/WorkspaceSettingsPage"),
@@ -129,7 +133,13 @@ const CreateTokenPage = lazy(
129133
() => import("./pages/CreateTokenPage/CreateTokenPage"),
130134
)
131135
const TemplateFilesPage = lazy(
132-
() => import("./pages/TemplateFilesPage/TemplateFilesPage"),
136+
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
137+
)
138+
const TemplateSchedulePage = lazy(
139+
() =>
140+
import(
141+
"./pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage"
142+
),
133143
)
134144

135145
export const AppRouter: FC = () => {
@@ -160,16 +170,25 @@ export const AppRouter: FC = () => {
160170
<Route path=":template">
161171
<Route element={<TemplateLayout />}>
162172
<Route index element={<TemplateSummaryPage />} />
173+
174+
<Route path="files" element={<TemplateFilesPage />} />
175+
</Route>
176+
177+
<Route path="workspace" element={<CreateWorkspacePage />} />
178+
179+
<Route path="settings" element={<TemplateSettingsLayout />}>
180+
<Route index element={<TemplateSettingsPage />} />
163181
<Route
164182
path="permissions"
165183
element={<TemplatePermissionsPage />}
166184
/>
167-
<Route path="files" element={<TemplateFilesPage />} />
185+
<Route
186+
path="variables"
187+
element={<TemplateVariablesPage />}
188+
/>
189+
<Route path="schedule" element={<TemplateSchedulePage />} />
168190
</Route>
169191

170-
<Route path="workspace" element={<CreateWorkspacePage />} />
171-
<Route path="settings" element={<TemplateSettingsPage />} />
172-
<Route path="variables" element={<TemplateVariablesPage />} />
173192
<Route path="versions">
174193
<Route path=":version">
175194
<Route index element={<TemplateVersionPage />} />

site/src/components/GoBackButton/GoBackButton.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

site/src/components/IconField/LazyIconField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const IconField = lazy(() => import("./IconField"))
55

66
export const LazyIconField: FC<IconFieldProps> = (props) => {
77
return (
8-
<Suspense>
8+
<Suspense fallback={<div role="progressbar" data-testid="loader" />}>
99
<IconField {...props} />
1010
</Suspense>
1111
)

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,

site/src/components/TemplateLayout/TemplateLayout.tsx

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ const fetchTemplate = async (orgId: string, templateName: string) => {
4242
}
4343
}
4444

45-
const useTemplateData = (orgId: string, templateName: string) => {
46-
return useQuery({
47-
queryKey: ["template", templateName],
48-
queryFn: () => fetchTemplate(orgId, templateName),
49-
})
50-
}
51-
5245
type TemplateLayoutContextValue = Awaited<ReturnType<typeof fetchTemplate>>
5346

5447
const TemplateLayoutContext = createContext<
@@ -71,28 +64,31 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
7164
const navigate = useNavigate()
7265
const styles = useStyles()
7366
const orgId = useOrganizationId()
74-
const { template } = useParams() as { template: string }
75-
const templateData = useTemplateData(orgId, template)
67+
const { template: templateName } = useParams() as { template: string }
68+
const { data, error, isLoading } = useQuery({
69+
queryKey: ["template", templateName],
70+
queryFn: () => fetchTemplate(orgId, templateName),
71+
})
7672
const dashboard = useDashboard()
7773

78-
if (templateData.error) {
74+
if (error) {
7975
return (
8076
<div className={styles.error}>
81-
<AlertBanner severity="error" error={templateData.error} />
77+
<AlertBanner severity="error" error={error} />
8278
</div>
8379
)
8480
}
8581

86-
if (templateData.isLoading || !templateData.data) {
82+
if (isLoading || !data) {
8783
return <Loader />
8884
}
8985

9086
return (
9187
<>
9288
<TemplatePageHeader
93-
template={templateData.data.template}
94-
activeVersion={templateData.data.activeVersion}
95-
permissions={templateData.data.permissions}
89+
template={data.template}
90+
activeVersion={data.activeVersion}
91+
permissions={data.permissions}
9692
canEditFiles={dashboard.experiments.includes("template_editor")}
9793
onDeleteTemplate={() => {
9894
navigate("/templates")
@@ -104,7 +100,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
104100
<Stack direction="row" spacing={0.25}>
105101
<NavLink
106102
end
107-
to={`/templates/${template}`}
103+
to={`/templates/${templateName}`}
108104
className={({ isActive }) =>
109105
combineClasses([
110106
styles.tabItem,
@@ -115,18 +111,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
115111
Summary
116112
</NavLink>
117113
<NavLink
118-
to={`/templates/${template}/permissions`}
119-
className={({ isActive }) =>
120-
combineClasses([
121-
styles.tabItem,
122-
isActive ? styles.tabItemActive : undefined,
123-
])
124-
}
125-
>
126-
Permissions
127-
</NavLink>
128-
<NavLink
129-
to={`/templates/${template}/files`}
114+
to={`/templates/${templateName}/files`}
130115
className={({ isActive }) =>
131116
combineClasses([
132117
styles.tabItem,
@@ -141,7 +126,7 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
141126
</div>
142127

143128
<Margins>
144-
<TemplateLayoutContext.Provider value={templateData.data}>
129+
<TemplateLayoutContext.Provider value={data}>
145130
<Suspense fallback={<Loader />}>{children}</Suspense>
146131
</TemplateLayoutContext.Provider>
147132
</Margins>

site/src/components/TemplateLayout/TemplatePageHeader.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,6 @@ const TemplateMenu: FC<{
6767
>
6868
{Language.settingsButton}
6969
</MenuItem>
70-
<MenuItem
71-
component={RouterLink}
72-
to={`/templates/${templateName}/variables`}
73-
onClick={handleClose}
74-
>
75-
{Language.variablesButton}
76-
</MenuItem>
7770
{canEditFiles && (
7871
<MenuItem
7972
component={RouterLink}

site/src/i18n/en/templateSettingsPage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"title": "Template settings",
2+
"title": "General Settings",
33
"nameLabel": "Name",
44
"displayNameLabel": "Display name",
55
"descriptionLabel": "Description",
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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 { Link, 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+
<Link className={styles.name} to={`/templates/${template.name}`}>
56+
{template.display_name !== ""
57+
? template.display_name
58+
: template.name}
59+
</Link>
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="variables"
75+
icon={<SidebarNavItemIcon icon={VariablesIcon} />}
76+
>
77+
Variables
78+
</SidebarNavItem>
79+
<SidebarNavItem
80+
href="schedule"
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+
color: theme.palette.text.primary,
141+
textDecoration: "none",
142+
},
143+
secondary: {
144+
color: theme.palette.text.secondary,
145+
fontSize: 12,
146+
overflow: "hidden",
147+
textOverflow: "ellipsis",
148+
},
149+
}))

0 commit comments

Comments
 (0)