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

Skip to content

Commit f5d623f

Browse files
refactor: User settings page (#5661)
1 parent d5ab06e commit f5d623f

File tree

11 files changed

+324
-57
lines changed

11 files changed

+324
-57
lines changed

site/src/AppRouter.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,37 @@ export const AppRouter: FC = () => {
420420
/>
421421
</Route>
422422

423-
<Route path="settings" element={<SettingsLayout />}>
424-
<Route path="account" element={<AccountPage />} />
425-
<Route path="security" element={<SecurityPage />} />
426-
<Route path="ssh-keys" element={<SSHKeysPage />} />
423+
<Route path="settings">
424+
<Route
425+
path="account"
426+
element={
427+
<AuthAndFrame>
428+
<SettingsLayout>
429+
<AccountPage />
430+
</SettingsLayout>
431+
</AuthAndFrame>
432+
}
433+
/>
434+
<Route
435+
path="security"
436+
element={
437+
<AuthAndFrame>
438+
<SettingsLayout>
439+
<SecurityPage />
440+
</SettingsLayout>
441+
</AuthAndFrame>
442+
}
443+
/>
444+
<Route
445+
path="ssh-keys"
446+
element={
447+
<AuthAndFrame>
448+
<SettingsLayout>
449+
<SSHKeysPage />
450+
</SettingsLayout>
451+
</AuthAndFrame>
452+
}
453+
/>
427454
</Route>
428455

429456
<Route path="/@:username">

site/src/components/DeploySettingsLayout/DeploySettingsLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const DeploySettingsLayout: FC<PropsWithChildren> = ({ children }) => {
4545

4646
return (
4747
<Margins>
48-
<Stack className={styles.wrapper} direction="row" spacing={5}>
48+
<Stack className={styles.wrapper} direction="row" spacing={6}>
4949
<Sidebar />
5050
<main className={styles.content}>
5151
{deploymentConfig ? (

site/src/components/DeploySettingsLayout/Sidebar.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import Brush from "@material-ui/icons/Brush"
33
import LaunchOutlined from "@material-ui/icons/LaunchOutlined"
4-
import LockRounded from "@material-ui/icons/LockRounded"
5-
import Globe from "@material-ui/icons/Public"
4+
import LockRounded from "@material-ui/icons/LockOutlined"
5+
import Globe from "@material-ui/icons/PublicOutlined"
66
import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined"
77
import { GitIcon } from "components/Icons/GitIcon"
88
import { Stack } from "components/Stack/Stack"
@@ -90,9 +90,9 @@ const useStyles = makeStyles((theme) => ({
9090
sidebarNavItem: {
9191
color: "inherit",
9292
display: "block",
93-
fontSize: 16,
93+
fontSize: 14,
9494
textDecoration: "none",
95-
padding: theme.spacing(1.5, 1.5, 1.5, 3),
95+
padding: theme.spacing(1.5, 1.5, 1.5, 2),
9696
borderRadius: theme.shape.borderRadius / 2,
9797
transition: "background-color 0.15s ease-in-out",
9898
marginBottom: 1,
@@ -115,7 +115,8 @@ const useStyles = makeStyles((theme) => ({
115115
left: 0,
116116
top: 0,
117117
backgroundColor: theme.palette.secondary.dark,
118-
borderRadius: theme.shape.borderRadius,
118+
borderTopLeftRadius: theme.shape.borderRadius,
119+
borderBottomLeftRadius: theme.shape.borderRadius,
119120
},
120121
},
121122

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import Typography from "@material-ui/core/Typography"
3+
import { FC } from "react"
4+
import { SectionAction } from "../SectionAction/SectionAction"
5+
6+
type SectionLayout = "fixed" | "fluid"
7+
8+
export interface SectionProps {
9+
title?: React.ReactNode | string
10+
description?: React.ReactNode
11+
toolbar?: React.ReactNode
12+
alert?: React.ReactNode
13+
layout?: SectionLayout
14+
className?: string
15+
children?: React.ReactNode
16+
}
17+
18+
type SectionFC = FC<React.PropsWithChildren<SectionProps>> & {
19+
Action: typeof SectionAction
20+
}
21+
22+
export const Section: SectionFC = ({
23+
title,
24+
description,
25+
toolbar,
26+
alert,
27+
className = "",
28+
children,
29+
layout = "fixed",
30+
}) => {
31+
const styles = useStyles({ layout })
32+
return (
33+
<section className={className}>
34+
<div className={styles.inner}>
35+
{(title || description) && (
36+
<div className={styles.header}>
37+
<div>
38+
{title && <Typography variant="h4">{title}</Typography>}
39+
{description && typeof description === "string" && (
40+
<Typography className={styles.description}>
41+
{description}
42+
</Typography>
43+
)}
44+
{description && typeof description !== "string" && (
45+
<div className={styles.description}>{description}</div>
46+
)}
47+
</div>
48+
{toolbar && <div>{toolbar}</div>}
49+
</div>
50+
)}
51+
{alert && <div className={styles.alert}>{alert}</div>}
52+
{children}
53+
</div>
54+
</section>
55+
)
56+
}
57+
58+
// Sub-components
59+
Section.Action = SectionAction
60+
61+
const useStyles = makeStyles((theme) => ({
62+
inner: ({ layout }: { layout: SectionLayout }) => ({
63+
maxWidth: layout === "fluid" ? "100%" : 500,
64+
}),
65+
alert: {
66+
marginBottom: theme.spacing(1),
67+
},
68+
header: {
69+
marginBottom: theme.spacing(3),
70+
display: "flex",
71+
flexDirection: "row",
72+
justifyContent: "space-between",
73+
},
74+
description: {
75+
color: theme.palette.text.secondary,
76+
fontSize: 16,
77+
marginTop: theme.spacing(0.5),
78+
},
79+
}))
Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,42 @@
1-
import Box from "@material-ui/core/Box"
2-
import { FC } from "react"
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import { Sidebar } from "./Sidebar"
3+
import { Stack } from "components/Stack/Stack"
4+
import { FC, PropsWithChildren, Suspense } from "react"
35
import { Helmet } from "react-helmet-async"
4-
import { Outlet } from "react-router-dom"
56
import { pageTitle } from "../../util/page"
6-
import { AuthAndFrame } from "../AuthAndFrame/AuthAndFrame"
77
import { Margins } from "../Margins/Margins"
8-
import { TabPanel } from "../TabPanel/TabPanel"
8+
import { useMe } from "hooks/useMe"
9+
import { Loader } from "components/Loader/Loader"
910

10-
export const Language = {
11-
accountLabel: "Account",
12-
securityLabel: "Security",
13-
sshKeysLabel: "SSH keys",
14-
settingsLabel: "Settings",
15-
}
16-
17-
const menuItems = [
18-
{ label: Language.accountLabel, path: "/settings/account" },
19-
{ label: Language.securityLabel, path: "/settings/security" },
20-
{ label: Language.sshKeysLabel, path: "/settings/ssh-keys" },
21-
]
11+
export const SettingsLayout: FC<PropsWithChildren> = ({ children }) => {
12+
const styles = useStyles()
13+
const me = useMe()
2214

23-
export const SettingsLayout: FC = () => {
2415
return (
25-
<AuthAndFrame>
26-
<Box display="flex" flexDirection="column">
27-
<Helmet>
28-
<title>{pageTitle("Settings")}</title>
29-
</Helmet>
30-
<Margins>
31-
<TabPanel title={Language.settingsLabel} menuItems={menuItems}>
32-
<Outlet />
33-
</TabPanel>
34-
</Margins>
35-
</Box>
36-
</AuthAndFrame>
16+
<>
17+
<Helmet>
18+
<title>{pageTitle("Settings")}</title>
19+
</Helmet>
20+
21+
<Margins>
22+
<Stack className={styles.wrapper} direction="row" spacing={6}>
23+
<Sidebar user={me} />
24+
<Suspense fallback={<Loader />}>
25+
<main className={styles.content}>{children}</main>
26+
</Suspense>
27+
</Stack>
28+
</Margins>
29+
</>
3730
)
3831
}
32+
33+
const useStyles = makeStyles((theme) => ({
34+
wrapper: {
35+
padding: theme.spacing(6, 0),
36+
},
37+
38+
content: {
39+
maxWidth: 800,
40+
width: "100%",
41+
},
42+
}))
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined"
3+
import { User } from "api/typesGenerated"
4+
import { Stack } from "components/Stack/Stack"
5+
import { UserAvatar } from "components/UserAvatar/UserAvatar"
6+
import { FC, ElementType, PropsWithChildren, ReactNode } from "react"
7+
import { NavLink } from "react-router-dom"
8+
import { combineClasses } from "util/combineClasses"
9+
import AccountIcon from "@material-ui/icons/Person"
10+
import SecurityIcon from "@material-ui/icons/LockOutlined"
11+
12+
const SidebarNavItem: FC<
13+
PropsWithChildren<{ href: string; icon: ReactNode }>
14+
> = ({ children, href, icon }) => {
15+
const styles = useStyles()
16+
return (
17+
<NavLink
18+
to={href}
19+
className={({ isActive }) =>
20+
combineClasses([
21+
styles.sidebarNavItem,
22+
isActive ? styles.sidebarNavItemActive : undefined,
23+
])
24+
}
25+
>
26+
<Stack alignItems="center" spacing={1.5} direction="row">
27+
{icon}
28+
{children}
29+
</Stack>
30+
</NavLink>
31+
)
32+
}
33+
34+
const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({
35+
icon: Icon,
36+
}) => {
37+
const styles = useStyles()
38+
return <Icon className={styles.sidebarNavItemIcon} />
39+
}
40+
41+
export const Sidebar: React.FC<{ user: User }> = ({ user }) => {
42+
const styles = useStyles()
43+
44+
return (
45+
<nav className={styles.sidebar}>
46+
<Stack direction="row" alignItems="center" className={styles.userInfo}>
47+
<UserAvatar username={user.username} avatarURL={user.avatar_url} />
48+
<Stack spacing={0} className={styles.userData}>
49+
<span className={styles.username}>{user.username}</span>
50+
<span className={styles.email}>{user.email}</span>
51+
</Stack>
52+
</Stack>
53+
54+
<SidebarNavItem
55+
href="../account"
56+
icon={<SidebarNavItemIcon icon={AccountIcon} />}
57+
>
58+
Account
59+
</SidebarNavItem>
60+
<SidebarNavItem
61+
href="../security"
62+
icon={<SidebarNavItemIcon icon={SecurityIcon} />}
63+
>
64+
Security
65+
</SidebarNavItem>
66+
<SidebarNavItem
67+
href="../ssh-keys"
68+
icon={<SidebarNavItemIcon icon={VpnKeyOutlined} />}
69+
>
70+
SSH Keys
71+
</SidebarNavItem>
72+
</nav>
73+
)
74+
}
75+
76+
const useStyles = makeStyles((theme) => ({
77+
sidebar: {
78+
width: 245,
79+
flexShrink: 0,
80+
},
81+
sidebarNavItem: {
82+
color: "inherit",
83+
display: "block",
84+
fontSize: 14,
85+
textDecoration: "none",
86+
padding: theme.spacing(1.5, 1.5, 1.5, 2),
87+
borderRadius: theme.shape.borderRadius / 2,
88+
transition: "background-color 0.15s ease-in-out",
89+
marginBottom: 1,
90+
position: "relative",
91+
92+
"&:hover": {
93+
backgroundColor: theme.palette.action.hover,
94+
},
95+
},
96+
sidebarNavItemActive: {
97+
backgroundColor: theme.palette.action.hover,
98+
99+
"&:before": {
100+
content: '""',
101+
display: "block",
102+
width: 3,
103+
height: "100%",
104+
position: "absolute",
105+
left: 0,
106+
top: 0,
107+
backgroundColor: theme.palette.secondary.dark,
108+
borderTopLeftRadius: theme.shape.borderRadius,
109+
borderBottomLeftRadius: theme.shape.borderRadius,
110+
},
111+
},
112+
sidebarNavItemIcon: {
113+
width: theme.spacing(2),
114+
height: theme.spacing(2),
115+
},
116+
userInfo: {
117+
marginBottom: theme.spacing(2),
118+
},
119+
userData: {
120+
overflow: "hidden",
121+
},
122+
username: {
123+
fontWeight: 600,
124+
overflow: "hidden",
125+
textOverflow: "ellipsis",
126+
},
127+
email: {
128+
color: theme.palette.text.secondary,
129+
fontSize: 12,
130+
overflow: "hidden",
131+
textOverflow: "ellipsis",
132+
},
133+
}))

0 commit comments

Comments
 (0)