Skip to content

Commit 7c5a750

Browse files
committed
feat(site): add deployment menu to navbar
1 parent 9eb797e commit 7c5a750

File tree

5 files changed

+215
-107
lines changed

5 files changed

+215
-107
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
2+
import Button from "@mui/material/Button";
3+
import MenuItem from "@mui/material/MenuItem";
4+
import { type FC } from "react";
5+
import { NavLink } from "react-router-dom";
6+
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
7+
import {
8+
Popover,
9+
PopoverContent,
10+
PopoverTrigger,
11+
usePopover,
12+
} from "components/Popover/Popover";
13+
14+
import { USERS_LINK } from "modules/navigation";
15+
16+
interface DeploymentDropdownProps {
17+
canViewAuditLog: boolean;
18+
canViewDeployment: boolean;
19+
canViewAllUsers: boolean;
20+
canViewHealth: boolean;
21+
}
22+
23+
export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
24+
canViewAuditLog,
25+
canViewDeployment,
26+
canViewAllUsers,
27+
canViewHealth,
28+
}) => {
29+
const theme = useTheme();
30+
31+
if (
32+
!canViewAuditLog &&
33+
!canViewDeployment &&
34+
!canViewAllUsers &&
35+
!canViewHealth
36+
) {
37+
return null;
38+
}
39+
40+
return (
41+
<Popover>
42+
<PopoverTrigger>
43+
<Button>
44+
Deployment
45+
<DropdownArrow
46+
color={theme.experimental.l2.fill.solid}
47+
close={false}
48+
/>
49+
</Button>
50+
</PopoverTrigger>
51+
52+
<PopoverContent
53+
horizontal="right"
54+
css={{
55+
".MuiPaper-root": {
56+
minWidth: "auto",
57+
width: 180,
58+
boxShadow: theme.shadows[6],
59+
},
60+
}}
61+
>
62+
<DeploymentDropdownContent
63+
canViewAuditLog={canViewAuditLog}
64+
canViewDeployment={canViewDeployment}
65+
canViewAllUsers={canViewAllUsers}
66+
canViewHealth={canViewHealth}
67+
/>
68+
</PopoverContent>
69+
</Popover>
70+
);
71+
};
72+
73+
const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
74+
canViewAuditLog,
75+
canViewDeployment,
76+
canViewAllUsers,
77+
canViewHealth,
78+
}) => {
79+
const popover = usePopover();
80+
81+
const onPopoverClose = () => popover.setIsOpen(false);
82+
83+
return (
84+
<nav>
85+
{canViewDeployment && (
86+
<NavLink css={styles.link} to="/deployment/general">
87+
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
88+
Settings
89+
</MenuItem>
90+
</NavLink>
91+
)}
92+
{canViewAllUsers && (
93+
<NavLink css={styles.link} to={USERS_LINK}>
94+
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
95+
Users
96+
</MenuItem>
97+
</NavLink>
98+
)}
99+
{canViewAuditLog && (
100+
<NavLink css={styles.link} to="/audit">
101+
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
102+
Auditing
103+
</MenuItem>
104+
</NavLink>
105+
)}
106+
{canViewHealth && (
107+
<NavLink css={styles.link} to="/health">
108+
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
109+
Healthcheck
110+
</MenuItem>
111+
</NavLink>
112+
)}
113+
</nav>
114+
);
115+
};
116+
117+
const styles = {
118+
link: {
119+
textDecoration: "none",
120+
color: "inherit",
121+
},
122+
menuItem: (theme) => css`
123+
gap: 20px;
124+
padding: 8px 20px;
125+
font-size: 14px;
126+
127+
&:hover {
128+
background-color: ${theme.palette.action.hover};
129+
transition: background-color 0.3s ease;
130+
}
131+
`,
132+
menuItemIcon: (theme) => ({
133+
color: theme.palette.text.secondary,
134+
width: 20,
135+
height: 20,
136+
}),
137+
} satisfies Record<string, Interpolation<Theme>>;

site/src/modules/dashboard/Navbar/NavbarView.tsx

+15-51
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import Menu from "@mui/material/Menu";
99
import MenuItem from "@mui/material/MenuItem";
1010
import Skeleton from "@mui/material/Skeleton";
1111
import { visuallyHidden } from "@mui/utils";
12-
import { type FC, type ReactNode, useRef, useState } from "react";
12+
import { type FC, useRef, useState } from "react";
1313
import { NavLink, useLocation, useNavigate } from "react-router-dom";
1414
import type * as TypesGen from "api/typesGenerated";
1515
import { Abbr } from "components/Abbr/Abbr";
16+
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
1617
import { ExternalImage } from "components/ExternalImage/ExternalImage";
1718
import { displayError } from "components/GlobalSnackbar/utils";
1819
import { CoderIcon } from "components/Icons/CoderIcon";
@@ -21,10 +22,7 @@ import { useAuthenticated } from "contexts/auth/RequireAuth";
2122
import type { ProxyContextValue } from "contexts/ProxyContext";
2223
import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants";
2324
import { UserDropdown } from "./UserDropdown/UserDropdown";
24-
25-
export const USERS_LINK = `/users?filter=${encodeURIComponent(
26-
"status:active",
27-
)}`;
25+
import { DeploymentDropdown } from "./DeploymentDropdown";
2826

2927
export interface NavbarViewProps {
3028
logo_url?: string;
@@ -44,25 +42,14 @@ export const Language = {
4442
templates: "Templates",
4543
users: "Users",
4644
audit: "Audit",
47-
deployment: "Deployment",
45+
deployment: "Settings",
4846
};
4947

5048
interface NavItemsProps {
51-
children?: ReactNode;
5249
className?: string;
53-
canViewAuditLog: boolean;
54-
canViewDeployment: boolean;
55-
canViewAllUsers: boolean;
56-
canViewHealth: boolean;
5750
}
5851

59-
const NavItems: FC<NavItemsProps> = ({
60-
className,
61-
canViewAuditLog,
62-
canViewDeployment,
63-
canViewAllUsers,
64-
canViewHealth,
65-
}) => {
52+
const NavItems: FC<NavItemsProps> = ({ className }) => {
6653
const location = useLocation();
6754
const theme = useTheme();
6855

@@ -83,26 +70,6 @@ const NavItems: FC<NavItemsProps> = ({
8370
<NavLink css={styles.link} to="/templates">
8471
{Language.templates}
8572
</NavLink>
86-
{canViewAllUsers && (
87-
<NavLink css={styles.link} to={USERS_LINK}>
88-
{Language.users}
89-
</NavLink>
90-
)}
91-
{canViewAuditLog && (
92-
<NavLink css={styles.link} to="/audit">
93-
{Language.audit}
94-
</NavLink>
95-
)}
96-
{canViewDeployment && (
97-
<NavLink css={styles.link} to="/deployment/general">
98-
{Language.deployment}
99-
</NavLink>
100-
)}
101-
{canViewHealth && (
102-
<NavLink css={styles.link} to="/health">
103-
Health
104-
</NavLink>
105-
)}
10673
</nav>
10774
);
10875
};
@@ -157,12 +124,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
157124
)}
158125
</div>
159126
</div>
160-
<NavItems
161-
canViewAuditLog={canViewAuditLog}
162-
canViewDeployment={canViewDeployment}
163-
canViewAllUsers={canViewAllUsers}
164-
canViewHealth={canViewHealth}
165-
/>
127+
<NavItems />
166128
</div>
167129
</Drawer>
168130

@@ -174,18 +136,20 @@ export const NavbarView: FC<NavbarViewProps> = ({
174136
)}
175137
</NavLink>
176138

177-
<NavItems
178-
css={styles.desktopNavItems}
179-
canViewAuditLog={canViewAuditLog}
180-
canViewDeployment={canViewDeployment}
181-
canViewAllUsers={canViewAllUsers}
182-
canViewHealth={canViewHealth}
183-
/>
139+
<NavItems css={styles.desktopNavItems} />
184140

185141
<div css={styles.navMenus}>
186142
{proxyContextValue && (
187143
<ProxyMenu proxyContextValue={proxyContextValue} />
188144
)}
145+
146+
<DeploymentDropdown
147+
canViewAuditLog={canViewAuditLog}
148+
canViewDeployment={canViewDeployment}
149+
canViewAllUsers={canViewAllUsers}
150+
canViewHealth={canViewHealth}
151+
/>
152+
189153
{user && (
190154
<UserDropdown
191155
user={user}

site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx

+55-55
Original file line numberDiff line numberDiff line change
@@ -27,61 +27,6 @@ export const Language = {
2727
copyrightText: `\u00a9 ${new Date().getFullYear()} Coder Technologies, Inc.`,
2828
};
2929

30-
const styles = {
31-
info: (theme) => [
32-
theme.typography.body2 as CSSObject,
33-
{
34-
padding: 20,
35-
},
36-
],
37-
userName: {
38-
fontWeight: 600,
39-
},
40-
userEmail: (theme) => ({
41-
color: theme.palette.text.secondary,
42-
width: "100%",
43-
textOverflow: "ellipsis",
44-
overflow: "hidden",
45-
}),
46-
link: {
47-
textDecoration: "none",
48-
color: "inherit",
49-
},
50-
menuItem: (theme) => css`
51-
gap: 20px;
52-
padding: 8px 20px;
53-
54-
&:hover {
55-
background-color: ${theme.palette.action.hover};
56-
transition: background-color 0.3s ease;
57-
}
58-
`,
59-
menuItemIcon: (theme) => ({
60-
color: theme.palette.text.secondary,
61-
width: 20,
62-
height: 20,
63-
}),
64-
menuItemText: {
65-
fontSize: 14,
66-
},
67-
footerText: (theme) => css`
68-
font-size: 12px;
69-
text-decoration: none;
70-
color: ${theme.palette.text.secondary};
71-
display: flex;
72-
align-items: center;
73-
gap: 4px;
74-
75-
& svg {
76-
width: 12px;
77-
height: 12px;
78-
}
79-
`,
80-
buildInfo: (theme) => ({
81-
color: theme.palette.text.primary,
82-
}),
83-
} satisfies Record<string, Interpolation<Theme>>;
84-
8530
export interface UserDropdownContentProps {
8631
user: TypesGen.User;
8732
organizations?: TypesGen.Organization[];
@@ -269,3 +214,58 @@ const includeBuildInfo = (
269214
)}`,
270215
);
271216
};
217+
218+
const styles = {
219+
info: (theme) => [
220+
theme.typography.body2 as CSSObject,
221+
{
222+
padding: 20,
223+
},
224+
],
225+
userName: {
226+
fontWeight: 600,
227+
},
228+
userEmail: (theme) => ({
229+
color: theme.palette.text.secondary,
230+
width: "100%",
231+
textOverflow: "ellipsis",
232+
overflow: "hidden",
233+
}),
234+
link: {
235+
textDecoration: "none",
236+
color: "inherit",
237+
},
238+
menuItem: (theme) => css`
239+
gap: 20px;
240+
padding: 8px 20px;
241+
242+
&:hover {
243+
background-color: ${theme.palette.action.hover};
244+
transition: background-color 0.3s ease;
245+
}
246+
`,
247+
menuItemIcon: (theme) => ({
248+
color: theme.palette.text.secondary,
249+
width: 20,
250+
height: 20,
251+
}),
252+
menuItemText: {
253+
fontSize: 14,
254+
},
255+
footerText: (theme) => css`
256+
font-size: 12px;
257+
text-decoration: none;
258+
color: ${theme.palette.text.secondary};
259+
display: flex;
260+
align-items: center;
261+
gap: 4px;
262+
263+
& svg {
264+
width: 12px;
265+
height: 12px;
266+
}
267+
`,
268+
buildInfo: (theme) => ({
269+
color: theme.palette.text.primary,
270+
}),
271+
} satisfies Record<string, Interpolation<Theme>>;

site/src/modules/navigation.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* @fileoverview TODO: centralize navigation code here! URL constants, URL formatting, all of it
3+
*/
4+
5+
export const USERS_LINK = `/users?filter=${encodeURIComponent(
6+
"status:active",
7+
)}`;

site/src/pages/UsersPage/UsersLayout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { Margins } from "components/Margins/Margins";
1313
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
1414
import { TAB_PADDING_Y, TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
1515
import { useAuthenticated } from "contexts/auth/RequireAuth";
16-
import { USERS_LINK } from "modules/dashboard/Navbar/NavbarView";
16+
import { USERS_LINK } from "modules/navigation";
1717
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
1818

1919
export const UsersLayout: FC = () => {

0 commit comments

Comments
 (0)