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

Skip to content

Commit 5d4d7a1

Browse files
committed
Add base components for the Settings Page
1 parent bc47d7c commit 5d4d7a1

File tree

4 files changed

+367
-1
lines changed

4 files changed

+367
-1
lines changed

site/src/AppRouter.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useSelector } from "@xstate/react"
22
import { FeatureNames } from "api/types"
33
import { RequirePermission } from "components/RequirePermission/RequirePermission"
4+
import { OIDCSettingsPage } from "pages/DeploySettingsPage/OIDCSettingsPage"
45
import { SetupPage } from "pages/SetupPage/SetupPage"
56
import { TemplateSettingsPage } from "pages/TemplateSettingsPage/TemplateSettingsPage"
67
import { FC, lazy, Suspense, useContext } from "react"
@@ -152,6 +153,17 @@ export const AppRouter: FC = () => {
152153
/>
153154
</Route>
154155

156+
<Route
157+
path="settings/deployment/oidc"
158+
element={
159+
<RequireAuth>
160+
<AuthAndFrame>
161+
<OIDCSettingsPage />
162+
</AuthAndFrame>
163+
</RequireAuth>
164+
}
165+
/>
166+
155167
<Route path="settings" element={<SettingsLayout />}>
156168
<Route path="account" element={<AccountPage />} />
157169
<Route path="security" element={<SecurityPage />} />
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import Button from "@material-ui/core/Button"
2+
import { makeStyles } from "@material-ui/core/styles"
3+
import LaunchOutlined from "@material-ui/icons/LaunchOutlined"
4+
import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined"
5+
import { Margins } from "components/Margins/Margins"
6+
import { Stack } from "components/Stack/Stack"
7+
import React, { ElementType, PropsWithChildren, ReactNode } from "react"
8+
import { NavLink } from "react-router-dom"
9+
import { MONOSPACE_FONT_FAMILY } from "theme/constants"
10+
import { combineClasses } from "util/combineClasses"
11+
12+
const Sidebar: React.FC<PropsWithChildren> = ({ children }) => {
13+
const styles = useStyles()
14+
return <nav className={styles.sidebar}>{children}</nav>
15+
}
16+
17+
const SidebarNavItem: React.FC<PropsWithChildren<{ href: string; icon: ReactNode }>> = ({
18+
children,
19+
href,
20+
icon,
21+
}) => {
22+
const styles = useStyles()
23+
return (
24+
<NavLink
25+
to={href}
26+
className={({ isActive }) =>
27+
combineClasses([styles.sidebarNavItem, isActive ? styles.sidebarNavItemActive : undefined])
28+
}
29+
>
30+
<Stack alignItems="center" spacing={1.5} direction="row">
31+
{icon}
32+
{children}
33+
</Stack>
34+
</NavLink>
35+
)
36+
}
37+
38+
const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({ icon: Icon }) => {
39+
const styles = useStyles()
40+
return <Icon className={styles.sidebarNavItemIcon} />
41+
}
42+
43+
const SidebarCaption: React.FC<PropsWithChildren> = ({ children }) => {
44+
const styles = useStyles()
45+
return <span className={styles.sidebarCaption}>{children}</span>
46+
}
47+
48+
export const SettingsHeader: React.FC<{
49+
title: string
50+
description: string | JSX.Element
51+
isEnterprise?: boolean
52+
docsHref: string
53+
}> = ({ title, description, isEnterprise, docsHref }) => {
54+
const styles = useStyles()
55+
56+
return (
57+
<Stack alignItems="baseline" direction="row" justifyContent="space-between">
58+
<div className={styles.headingGroup}>
59+
<h1 className={styles.title}>
60+
{title}
61+
{isEnterprise ? <span className={styles.enterpriseBadge}>Enterprise</span> : null}
62+
</h1>
63+
<span className={styles.description}>{description}</span>
64+
</div>
65+
66+
<Button
67+
size="small"
68+
startIcon={<LaunchOutlined />}
69+
component="a"
70+
href={docsHref}
71+
target="_blank"
72+
variant="outlined"
73+
>
74+
Read the docs
75+
</Button>
76+
</Stack>
77+
)
78+
}
79+
80+
export const SettingsList: React.FC<PropsWithChildren> = ({ children }) => {
81+
const styles = useStyles()
82+
83+
return <div className={styles.settingsList}>{children}</div>
84+
}
85+
86+
const SettingsValue: React.FC<{ label: string; value: string; type?: "primary" | "secondary" }> = ({
87+
label,
88+
value,
89+
type = "primary",
90+
}) => {
91+
const styles = useStyles()
92+
93+
return (
94+
<div>
95+
<span className={styles.settingsValueLabel}>{label}</span>
96+
<span
97+
className={combineClasses([
98+
styles.settingsValueValue,
99+
type === "secondary" ? styles.settingsValueSecondary : undefined,
100+
])}
101+
>
102+
{value}
103+
</span>
104+
</div>
105+
)
106+
}
107+
108+
export const SettingsItem: React.FC<{
109+
title: string
110+
description: string | JSX.Element
111+
values: { label: string; value: string }[]
112+
}> = ({ title, description, values }) => {
113+
const styles = useStyles()
114+
115+
return (
116+
<section className={styles.settingsItem}>
117+
<div>
118+
<h2 className={styles.settingsItemTitle}>{title}</h2>
119+
<span className={styles.settingsItemDescription}>{description}</span>
120+
</div>
121+
122+
<Stack alignItems="baseline" direction="row" className={styles.settingsValues} spacing={5}>
123+
{values.map(({ value, label }, index) => (
124+
<SettingsValue
125+
key={label}
126+
label={label}
127+
value={value}
128+
// The first value is primary and the other secondary
129+
type={index === 0 ? "primary" : "secondary"}
130+
/>
131+
))}
132+
</Stack>
133+
</section>
134+
)
135+
}
136+
137+
export const DeploySettingsLayout: React.FC<PropsWithChildren> = ({ children }) => {
138+
const styles = useStyles()
139+
140+
return (
141+
<Margins>
142+
<Stack className={styles.wrapper} direction="row" spacing={5}>
143+
<Sidebar>
144+
<SidebarNavItem
145+
href="/settings/deployment/general"
146+
icon={<SidebarNavItemIcon icon={LaunchOutlined} />}
147+
>
148+
Deployment
149+
</SidebarNavItem>
150+
<SidebarCaption>Authentication</SidebarCaption>
151+
<SidebarNavItem
152+
href="/settings/deployment/auth"
153+
icon={<SidebarNavItemIcon icon={VpnKeyOutlined} />}
154+
>
155+
OAuth
156+
</SidebarNavItem>
157+
<SidebarNavItem
158+
href="/settings/deployment/oidc"
159+
icon={<SidebarNavItemIcon icon={VpnKeyOutlined} />}
160+
>
161+
OIDC
162+
</SidebarNavItem>
163+
</Sidebar>
164+
165+
<main className={styles.content}>{children}</main>
166+
</Stack>
167+
</Margins>
168+
)
169+
}
170+
171+
const useStyles = makeStyles((theme) => ({
172+
wrapper: {
173+
padding: theme.spacing(6, 0),
174+
},
175+
176+
sidebar: {
177+
width: 245,
178+
},
179+
180+
sidebarNavItem: {
181+
color: "inherit",
182+
display: "block",
183+
fontSize: 16,
184+
textDecoration: "none",
185+
padding: theme.spacing(1.5, 1.5, 1.5, 3),
186+
borderRadius: theme.shape.borderRadius / 2,
187+
transition: "background-color 0.15s ease-in-out",
188+
marginBottom: 1,
189+
position: "relative",
190+
191+
"&:hover": {
192+
backgroundColor: theme.palette.action.hover,
193+
},
194+
},
195+
196+
sidebarNavItemActive: {
197+
backgroundColor: theme.palette.action.hover,
198+
199+
"&:before": {
200+
content: '""',
201+
display: "block",
202+
width: 3,
203+
height: "100%",
204+
position: "absolute",
205+
left: 0,
206+
top: 0,
207+
backgroundColor: theme.palette.secondary.dark,
208+
borderRadius: theme.shape.borderRadius,
209+
},
210+
},
211+
212+
sidebarNavItemIcon: {
213+
width: theme.spacing(2),
214+
height: theme.spacing(2),
215+
},
216+
217+
sidebarCaption: {
218+
fontSize: 14,
219+
color: theme.palette.text.secondary,
220+
fontWeight: 600,
221+
margin: theme.spacing(2, 0, 1.5, 3),
222+
display: "block",
223+
},
224+
225+
content: {
226+
maxWidth: 800,
227+
width: "100%",
228+
},
229+
230+
headingGroup: {
231+
marginBottom: theme.spacing(3),
232+
maxWidth: 360,
233+
},
234+
235+
title: {
236+
fontSize: 36,
237+
fontWeight: 700,
238+
display: "flex",
239+
alignItems: "center",
240+
lineHeight: "initial",
241+
margin: 0,
242+
marginBottom: theme.spacing(2),
243+
},
244+
245+
description: {
246+
fontSize: 14,
247+
color: theme.palette.text.secondary,
248+
lineHeight: "160%",
249+
},
250+
251+
enterpriseBadge: {
252+
fontSize: 10,
253+
fontWeight: 600,
254+
textTransform: "uppercase",
255+
letterSpacing: "0.085em",
256+
marginLeft: theme.spacing(2),
257+
backgroundColor: theme.palette.success.dark,
258+
padding: theme.spacing(0.5, 2),
259+
borderRadius: 9999,
260+
border: `1px solid ${theme.palette.success.light}`,
261+
lineHeight: "160%",
262+
},
263+
264+
settingsList: {
265+
background: theme.palette.background.paper,
266+
borderRadius: theme.shape.borderRadius,
267+
border: `1px solid ${theme.palette.divider}`,
268+
},
269+
270+
settingsItem: {
271+
padding: theme.spacing(4, 5),
272+
273+
"&:not(:last-child)": {
274+
borderBottom: `1px solid ${theme.palette.divider}`,
275+
},
276+
},
277+
278+
settingsItemTitle: {
279+
fontSize: 20,
280+
fontWeight: 400,
281+
lineHeight: "initial",
282+
margin: 0,
283+
marginBottom: theme.spacing(0.5),
284+
},
285+
286+
settingsItemDescription: {
287+
fontSize: 14,
288+
color: theme.palette.text.secondary,
289+
},
290+
291+
settingsValues: {
292+
marginTop: theme.spacing(3),
293+
},
294+
295+
settingsValueLabel: {
296+
fontSize: 14,
297+
fontWeight: 600,
298+
color: theme.palette.text.secondary,
299+
marginBottom: theme.spacing(0.5),
300+
display: "block",
301+
},
302+
303+
settingsValueValue: {
304+
display: "block",
305+
fontSize: 16,
306+
},
307+
308+
settingsValueSecondary: {
309+
fontFamily: MONOSPACE_FONT_FAMILY,
310+
color: theme.palette.text.secondary,
311+
},
312+
}))

site/src/components/NavbarView/NavbarView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ const useStyles = makeStyles((theme) => ({
181181
fontSize: 16,
182182
padding: `${theme.spacing(1.5)}px ${theme.spacing(2)}px`,
183183
textDecoration: "none",
184-
transition: "background-color 0.3s ease",
184+
transition: "background-color 0.15s ease-in-out",
185185

186186
"&:hover": {
187187
backgroundColor: theme.palette.action.hover,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
DeploySettingsLayout,
3+
SettingsHeader,
4+
SettingsItem,
5+
SettingsList,
6+
} from "components/DeploySettingsLayout/DeploySettingsLayout"
7+
import React from "react"
8+
9+
export const OIDCSettingsPage: React.FC = () => {
10+
return (
11+
<DeploySettingsLayout>
12+
<SettingsHeader
13+
title="OIDC"
14+
description="Configure these options at your deployment level with environment variables or command-line flags."
15+
docsHref="https://coder.com/docs/coder-oss/latest"
16+
isEnterprise
17+
/>
18+
19+
<SettingsList>
20+
<SettingsItem
21+
title="Access URL"
22+
description="Specifies the external URL to access Coder."
23+
values={[
24+
{ label: "Value", value: "https://www.dev.coder.com" },
25+
{ label: "Flag", value: "--access-url" },
26+
{ label: "Env. Variable", value: "CODER_ACCESS_URL" },
27+
]}
28+
/>
29+
30+
<SettingsItem
31+
title="Address"
32+
description="The address to serve the API and dashboard."
33+
values={[
34+
{ label: "Value", value: "127.0.0.1:3000" },
35+
{ label: "Flag", value: "--address" },
36+
{ label: "Env. Variable", value: "CODER_ADDRESS" },
37+
]}
38+
/>
39+
</SettingsList>
40+
</DeploySettingsLayout>
41+
)
42+
}

0 commit comments

Comments
 (0)