Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add template settings
  • Loading branch information
BrunoQuaresma committed Mar 22, 2023
commit 5ea1d233c84e4881bee40a9ea392453584289eaf
18 changes: 12 additions & 6 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import AuditPage from "pages/AuditPage/AuditPage"
import GroupsPage from "pages/GroupsPage/GroupsPage"
import LoginPage from "pages/LoginPage/LoginPage"
import { SetupPage } from "pages/SetupPage/SetupPage"
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage"
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage"
import TemplatesPage from "pages/TemplatesPage/TemplatesPage"
import UsersPage from "pages/UsersPage/UsersPage"
import WorkspacesPage from "pages/WorkspacesPage/WorkspacesPage"
Expand All @@ -16,6 +16,7 @@ import { DashboardLayout } from "./components/Dashboard/DashboardLayout"
import { RequireAuth } from "./components/RequireAuth/RequireAuth"
import { SettingsLayout } from "./components/SettingsLayout/SettingsLayout"
import { DeploySettingsLayout } from "components/DeploySettingsLayout/DeploySettingsLayout"
import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout"

// Lazy load pages
// - Pages that are secondary, not in the main navigation or not usually accessed
Expand Down Expand Up @@ -50,7 +51,7 @@ const TerminalPage = lazy(() => import("./pages/TerminalPage/TerminalPage"))
const TemplatePermissionsPage = lazy(
() =>
import(
"./pages/TemplatePage/TemplatePermissionsPage/TemplatePermissionsPage"
"./pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage"
),
)
const TemplateSummaryPage = lazy(
Expand Down Expand Up @@ -129,7 +130,7 @@ const CreateTokenPage = lazy(
() => import("./pages/CreateTokenPage/CreateTokenPage"),
)
const TemplateFilesPage = lazy(
() => import("./pages/TemplateFilesPage/TemplateFilesPage"),
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
)

export const AppRouter: FC = () => {
Expand Down Expand Up @@ -160,15 +161,20 @@ export const AppRouter: FC = () => {
<Route path=":template">
<Route element={<TemplateLayout />}>
<Route index element={<TemplateSummaryPage />} />

<Route path="files" element={<TemplateFilesPage />} />
</Route>

<Route path="workspace" element={<CreateWorkspacePage />} />

<Route path="settings" element={<TemplateSettingsLayout />}>
<Route index element={<TemplateSettingsPage />} />
<Route
path="permissions"
element={<TemplatePermissionsPage />}
/>
<Route path="files" element={<TemplateFilesPage />} />
</Route>

<Route path="workspace" element={<CreateWorkspacePage />} />
<Route path="settings" element={<TemplateSettingsPage />} />
<Route path="variables" element={<TemplateVariablesPage />} />
<Route path="versions">
<Route path=":version">
Expand Down
1 change: 1 addition & 0 deletions site/src/components/SettingsLayout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const useStyles = makeStyles((theme) => ({
fontWeight: 600,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
email: {
color: theme.palette.text.secondary,
Expand Down
147 changes: 147 additions & 0 deletions site/src/pages/TemplateSettingsPage/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { makeStyles } from "@material-ui/core/styles"
import ScheduleIcon from "@material-ui/icons/TimerOutlined"
import VariablesIcon from "@material-ui/icons/CodeOutlined"
import { Template } from "api/typesGenerated"
import { Stack } from "components/Stack/Stack"
import { FC, ElementType, PropsWithChildren, ReactNode } from "react"
import { NavLink } from "react-router-dom"
import { combineClasses } from "util/combineClasses"
import GeneralIcon from "@material-ui/icons/SettingsOutlined"
import SecurityIcon from "@material-ui/icons/LockOutlined"
import { Avatar } from "components/Avatar/Avatar"

const SidebarNavItem: FC<
PropsWithChildren<{ href: string; icon: ReactNode }>
> = ({ children, href, icon }) => {
const styles = useStyles()
return (
<NavLink
end
to={href}
className={({ isActive }) =>
combineClasses([
styles.sidebarNavItem,
isActive ? styles.sidebarNavItemActive : undefined,
])
}
>
<Stack alignItems="center" spacing={1.5} direction="row">
{icon}
{children}
</Stack>
</NavLink>
)
}

const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({
icon: Icon,
}) => {
const styles = useStyles()
return <Icon className={styles.sidebarNavItemIcon} />
}

export const Sidebar: React.FC<{ template: Template }> = ({ template }) => {
const styles = useStyles()

return (
<nav className={styles.sidebar}>
<Stack
direction="row"
alignItems="center"
className={styles.templateInfo}
>
<Avatar src={template.icon} variant="square" fitImage />
<Stack spacing={0} className={styles.templateData}>
<span className={styles.name}>
{template.display_name !== ""
? template.display_name
: template.name}
</span>
<span className={styles.secondary}>{template.name}</span>
</Stack>
</Stack>

<SidebarNavItem href="" icon={<SidebarNavItemIcon icon={GeneralIcon} />}>
General
</SidebarNavItem>
<SidebarNavItem
href="permissions"
icon={<SidebarNavItemIcon icon={SecurityIcon} />}
>
Permissions
</SidebarNavItem>
<SidebarNavItem
href="ssh-keys"
icon={<SidebarNavItemIcon icon={VariablesIcon} />}
>
Variables
</SidebarNavItem>
<SidebarNavItem
href="tokens"
icon={<SidebarNavItemIcon icon={ScheduleIcon} />}
>
Schedule
</SidebarNavItem>
</nav>
)
}

const useStyles = makeStyles((theme) => ({
sidebar: {
width: 245,
flexShrink: 0,
},
sidebarNavItem: {
color: "inherit",
display: "block",
fontSize: 14,
textDecoration: "none",
padding: theme.spacing(1.5, 1.5, 1.5, 2),
borderRadius: theme.shape.borderRadius / 2,
transition: "background-color 0.15s ease-in-out",
marginBottom: 1,
position: "relative",

"&:hover": {
backgroundColor: theme.palette.action.hover,
},
},
sidebarNavItemActive: {
backgroundColor: theme.palette.action.hover,

"&:before": {
content: '""',
display: "block",
width: 3,
height: "100%",
position: "absolute",
left: 0,
top: 0,
backgroundColor: theme.palette.secondary.dark,
borderTopLeftRadius: theme.shape.borderRadius,
borderBottomLeftRadius: theme.shape.borderRadius,
},
},
sidebarNavItemIcon: {
width: theme.spacing(2),
height: theme.spacing(2),
},
templateInfo: {
marginBottom: theme.spacing(2),
},
templateData: {
overflow: "hidden",
},
name: {
fontWeight: 600,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
secondary: {
color: theme.palette.text.secondary,
fontSize: 12,
overflow: "hidden",
textOverflow: "ellipsis",
},
}))
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
import { useMachine } from "@xstate/react"
import { useMutation } from "@tanstack/react-query"
import { updateTemplateMeta } from "api/api"
import { UpdateTemplateMeta } from "api/typesGenerated"
import { useDashboard } from "components/Dashboard/DashboardProvider"
import { useOrganizationId } from "hooks/useOrganizationId"
import { displaySuccess } from "components/GlobalSnackbar/utils"
import { FC } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import { useNavigate, useParams } from "react-router-dom"
import { pageTitle } from "util/page"
import { templateSettingsMachine } from "xServices/templateSettings/templateSettingsXService"
import { useTemplateSettingsContext } from "../TemplateSettingsLayout"
import { TemplateSettingsPageView } from "./TemplateSettingsPageView"

export const TemplateSettingsPage: FC = () => {
const { template: templateName } = useParams() as { template: string }
const { t } = useTranslation("templateSettingsPage")
const navigate = useNavigate()
const organizationId = useOrganizationId()
const [state, send] = useMachine(templateSettingsMachine, {
context: { templateName, organizationId },
actions: {
onSave: (_, { data }) => {
// Use the data.name because the template name can be changed. Since the
// API can return 304 if the template name is not changed, we use the
// templateName from the URL as default.
navigate(`/templates/${data.name ?? templateName}`)
},
},
})
const {
templateSettings: template,
saveTemplateSettingsError,
getTemplateError,
} = state.context
const template = useTemplateSettingsContext()
const { entitlements } = useDashboard()
const canSetMaxTTL =
entitlements.features["advanced_template_scheduling"].enabled
const {
mutate: updateTemplate,
isLoading: isSubmitting,
error: submitError,
} = useMutation(
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
{
onSuccess: () => {
displaySuccess("Template updated successfully")
},
},
)

return (
<>
Expand All @@ -41,17 +39,14 @@ export const TemplateSettingsPage: FC = () => {
</Helmet>
<TemplateSettingsPageView
canSetMaxTTL={canSetMaxTTL}
isSubmitting={state.hasTag("submitting")}
isSubmitting={isSubmitting}
template={template}
errors={{
getTemplateError,
saveTemplateSettingsError,
}}
submitError={submitError}
onCancel={() => {
navigate(`/templates/${templateName}`)
}}
onSubmit={(templateSettings) => {
send({ type: "SAVE", templateSettings })
updateTemplate(templateSettings)
}}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Template, UpdateTemplateMeta } from "api/typesGenerated"
import { ComponentProps, FC } from "react"
import { TemplateSettingsForm } from "./TemplateSettingsForm"
import { useTranslation } from "react-i18next"
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"
import { makeStyles } from "@material-ui/core/styles"

export interface TemplateSettingsPageViewProps {
template: Template
onSubmit: (data: UpdateTemplateMeta) => void
onCancel: () => void
isSubmitting: boolean
submitError?: unknown
initialTouched?: ComponentProps<typeof TemplateSettingsForm>["initialTouched"]
canSetMaxTTL: boolean
}

export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
template,
onCancel,
onSubmit,
isSubmitting,
canSetMaxTTL,
submitError,
initialTouched,
}) => {
const { t } = useTranslation("templateSettingsPage")
const styles = useStyles()

return (
<>
<PageHeader className={styles.pageHeader}>
<PageHeaderTitle>{t("title")}</PageHeaderTitle>
</PageHeader>

<TemplateSettingsForm
canSetMaxTTL={canSetMaxTTL}
initialTouched={initialTouched}
isSubmitting={isSubmitting}
template={template}
onSubmit={onSubmit}
onCancel={onCancel}
error={submitError}
/>
</>
)
}

const useStyles = makeStyles(() => ({
pageHeader: {
paddingTop: 0,
},
}))
Loading