Skip to content

Commit 712662d

Browse files
authored
chore: embed audit log in deployment settings page (coder#14023)
* Move audit page to /deployment/audit The existing link remains but will redirect to the new URL. If multi-org is not enabled, nothing changes. * Redirect organization audit page to site-wide audit page * Always wrap audit log filters when in deployment settings Otherwise the input is teeny tiny and barely fits a few characters. Normally we only wrap when the screen shrinks. Again, no change if multi-org is not enabled. This also makes the filter menus take up available space when wrapping (*does* apply to non-multi-org setups as well). * Show audit log details in a tooltip If multi-org is not enabled, details continue to be shown inline.
1 parent eacdfb9 commit 712662d

File tree

11 files changed

+158
-57
lines changed

11 files changed

+158
-57
lines changed

site/src/components/Filter/SelectFilter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const SelectFilter: FC<SelectFilterProps> = ({
5252
<SelectMenuTrigger>
5353
<SelectMenuButton
5454
startIcon={selectedOption?.startIcon}
55-
css={{ width }}
55+
css={{ width, flexGrow: 1 }}
5656
aria-label={label}
5757
>
5858
{selectedOption?.label ?? placeholder}

site/src/components/Filter/filter.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ type FilterProps = {
142142
error?: unknown;
143143
options?: ReactNode;
144144
presets: PresetFilter[];
145+
/** Set to true if there is not much horizontal space. */
146+
compact?: boolean;
145147
};
146148

147149
export const Filter: FC<FilterProps> = ({
@@ -154,6 +156,7 @@ export const Filter: FC<FilterProps> = ({
154156
learnMoreLabel2,
155157
learnMoreLink2,
156158
presets,
159+
compact,
157160
}) => {
158161
const theme = useTheme();
159162
// Storing local copy of the filter query so that it can be updated more
@@ -184,7 +187,10 @@ export const Filter: FC<FilterProps> = ({
184187
display: "flex",
185188
gap: 8,
186189
marginBottom: 16,
187-
flexWrap: "nowrap",
190+
// For now compact just means immediately wrapping, but maybe we should
191+
// have a collapsible section or consolidate into one menu or something.
192+
// TODO: Remove separate compact mode once multi-org is stable.
193+
flexWrap: compact ? "wrap" : "nowrap",
188194

189195
[theme.breakpoints.down("md")]: {
190196
flexWrap: "wrap",

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
PopoverTrigger,
1111
usePopover,
1212
} from "components/Popover/Popover";
13-
import { USERS_LINK } from "modules/navigation";
13+
import { AUDIT_LINK, USERS_LINK } from "modules/navigation";
1414

1515
interface DeploymentDropdownProps {
1616
canViewDeployment: boolean;
@@ -114,7 +114,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
114114
{canViewAllUsers && (
115115
<MenuItem
116116
component={NavLink}
117-
to={USERS_LINK}
117+
to={canViewOrganizations ? `/deployment${USERS_LINK}` : USERS_LINK}
118118
css={styles.menuItem}
119119
onClick={onPopoverClose}
120120
>
@@ -124,7 +124,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
124124
{canViewAuditLog && (
125125
<MenuItem
126126
component={NavLink}
127-
to="/audit"
127+
to={canViewOrganizations ? `/deployment${AUDIT_LINK}` : AUDIT_LINK}
128128
css={styles.menuItem}
129129
onClick={onPopoverClose}
130130
>

site/src/modules/navigation.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
* @fileoverview TODO: centralize navigation code here! URL constants, URL formatting, all of it
33
*/
44

5-
export const USERS_LINK = `/users?filter=${encodeURIComponent(
6-
"status:active",
7-
)}`;
5+
export function withFilter(path: string, filter: string) {
6+
return path + (filter ? `?filter=${encodeURIComponent(filter)}` : "");
7+
}
8+
9+
export const AUDIT_LINK = "/audit";
10+
11+
export const USERS_LINK = withFilter("/users", "status:active");

site/src/pages/AuditPage/AuditFilter.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,23 @@ interface AuditFilterProps {
5151
}
5252

5353
export const AuditFilter: FC<AuditFilterProps> = ({ filter, error, menus }) => {
54-
// Use a smaller width if including the organization filter.
55-
const width = menus.organization && 175;
5654
return (
5755
<Filter
5856
learnMoreLink={docs("/admin/audit-logs#filtering-logs")}
5957
presets={PRESET_FILTERS}
6058
isLoading={menus.user.isInitializing}
6159
filter={filter}
6260
error={error}
61+
// There is not much space with the sidebar and four filters, so in this
62+
// case we will use the compact mode.
63+
compact={Boolean(menus.organization)}
6364
options={
6465
<>
65-
<ResourceTypeMenu width={width} menu={menus.resourceType} />
66-
<ActionMenu width={width} menu={menus.action} />
67-
<UserMenu width={width} menu={menus.user} />
66+
<ResourceTypeMenu menu={menus.resourceType} />
67+
<ActionMenu menu={menus.action} />
68+
<UserMenu menu={menus.user} />
6869
{menus.organization && (
69-
<OrganizationsMenu width={width} menu={menus.organization} />
70+
<OrganizationsMenu menu={menus.organization} />
7071
)}
7172
</>
7273
}

site/src/pages/AuditPage/AuditLogRow/AuditLogRow.stories.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,10 @@ export const SecretDiffValue: Story = {
113113
auditLog: MockAuditLogGitSSH,
114114
},
115115
};
116+
117+
export const WithOrganization: Story = {
118+
args: {
119+
auditLog: MockAuditLog,
120+
showOrgDetails: true,
121+
},
122+
};

site/src/pages/AuditPage/AuditLogRow/AuditLogRow.tsx

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { CSSObject, Interpolation, Theme } from "@emotion/react";
2+
import InfoOutlined from "@mui/icons-material/InfoOutlined";
23
import Collapse from "@mui/material/Collapse";
34
import Link from "@mui/material/Link";
45
import TableCell from "@mui/material/TableCell";
6+
import Tooltip from "@mui/material/Tooltip";
57
import { type FC, useState } from "react";
68
import { Link as RouterLink } from "react-router-dom";
79
import userAgentParser from "ua-parser-js";
@@ -115,42 +117,80 @@ export const AuditLogRow: FC<AuditLogRowProps> = ({
115117
</Stack>
116118

117119
<Stack direction="row" alignItems="center">
118-
<Stack direction="row" spacing={1} alignItems="baseline">
119-
{auditLog.ip && (
120-
<span css={styles.auditLogInfo}>
121-
<>IP: </>
122-
<strong>{auditLog.ip}</strong>
123-
</span>
124-
)}
125-
{os.name && (
126-
<span css={styles.auditLogInfo}>
127-
<>OS: </>
128-
<strong>{os.name}</strong>
129-
</span>
130-
)}
131-
{browser.name && (
132-
<span css={styles.auditLogInfo}>
133-
<>Browser: </>
134-
<strong>
135-
{browser.name} {browser.version}
136-
</strong>
137-
</span>
138-
)}
139-
{showOrgDetails && auditLog.organization && (
140-
<span css={styles.auditLogInfo}>
141-
<>Org: </>
142-
<Link
143-
component={RouterLink}
144-
to={`/organizations/${auditLog.organization.name}`}
145-
>
120+
{/* With multi-org, there is not enough space so show
121+
everything in a tooltip. */}
122+
{showOrgDetails ? (
123+
<Tooltip
124+
title={
125+
<div css={styles.auditLogInfoTooltip}>
126+
{auditLog.ip && (
127+
<div>
128+
<h4 css={styles.auditLogInfoHeader}>IP:</h4>
129+
<div>{auditLog.ip}</div>
130+
</div>
131+
)}
132+
{os.name && (
133+
<div>
134+
<h4 css={styles.auditLogInfoHeader}>OS:</h4>
135+
<div>{os.name}</div>
136+
</div>
137+
)}
138+
{browser.name && (
139+
<div>
140+
<h4 css={styles.auditLogInfoHeader}>Browser:</h4>
141+
<div>
142+
{browser.name} {browser.version}
143+
</div>
144+
</div>
145+
)}
146+
{auditLog.organization && (
147+
<div>
148+
<h4 css={styles.auditLogInfoHeader}>
149+
Organization:
150+
</h4>
151+
<Link
152+
component={RouterLink}
153+
to={`/organizations/${auditLog.organization.name}`}
154+
>
155+
{auditLog.organization.display_name ||
156+
auditLog.organization.name}
157+
</Link>
158+
</div>
159+
)}
160+
</div>
161+
}
162+
>
163+
<InfoOutlined
164+
css={(theme) => ({
165+
fontSize: 20,
166+
color: theme.palette.info.light,
167+
})}
168+
/>
169+
</Tooltip>
170+
) : (
171+
<Stack direction="row" spacing={1} alignItems="baseline">
172+
{auditLog.ip && (
173+
<span css={styles.auditLogInfo}>
174+
<span>IP: </span>
175+
<strong>{auditLog.ip}</strong>
176+
</span>
177+
)}
178+
{os.name && (
179+
<span css={styles.auditLogInfo}>
180+
<span>OS: </span>
181+
<strong>{os.name}</strong>
182+
</span>
183+
)}
184+
{browser.name && (
185+
<span css={styles.auditLogInfo}>
186+
<span>Browser: </span>
146187
<strong>
147-
{auditLog.organization.display_name ||
148-
auditLog.organization.name}
188+
{browser.name} {browser.version}
149189
</strong>
150-
</Link>
151-
</span>
152-
)}
153-
</Stack>
190+
</span>
191+
)}
192+
</Stack>
193+
)}
154194

155195
<Pill
156196
css={styles.httpStatusPill}
@@ -212,6 +252,20 @@ const styles = {
212252
display: "block",
213253
}),
214254

255+
auditLogInfoHeader: (theme) => ({
256+
margin: 0,
257+
color: theme.palette.text.primary,
258+
fontSize: 14,
259+
lineHeight: "150%",
260+
fontWeight: 600,
261+
}),
262+
263+
auditLogInfoTooltip: {
264+
display: "flex",
265+
flexDirection: "column",
266+
gap: 8,
267+
},
268+
215269
// offset the absence of the arrow icon on diff-less logs
216270
columnWithoutDiff: {
217271
marginLeft: "24px",

site/src/pages/AuditPage/AuditPage.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { FC } from "react";
22
import { Helmet } from "react-helmet-async";
3-
import { useSearchParams } from "react-router-dom";
3+
import { useSearchParams, Navigate, useLocation } from "react-router-dom";
44
import { paginatedAudits } from "api/queries/audits";
55
import { useFilter } from "components/Filter/filter";
66
import { useUserFilterMenu } from "components/Filter/UserFilter";
@@ -19,6 +19,8 @@ import { AuditPageView } from "./AuditPageView";
1919
const AuditPage: FC = () => {
2020
const { audit_log: isAuditLogVisible } = useFeatureVisibility();
2121
const { experiments } = useDashboard();
22+
const location = useLocation();
23+
const isMultiOrg = experiments.includes("multi-organization");
2224

2325
/**
2426
* There is an implicit link between auditsQuery and filter via the
@@ -70,6 +72,13 @@ const AuditPage: FC = () => {
7072
}),
7173
});
7274

75+
// TODO: Once multi-org is stable, we should place this redirect into the
76+
// router directly, if we still need to maintain it (for users who are
77+
// typing the old URL manually or have it bookmarked).
78+
if (isMultiOrg && location.pathname !== "/deployment/audit") {
79+
return <Navigate to={`/deployment/audit${location.search}`} replace />;
80+
}
81+
7382
return (
7483
<>
7584
<Helmet>
@@ -82,17 +91,15 @@ const AuditPage: FC = () => {
8291
isAuditLogVisible={isAuditLogVisible}
8392
auditsQuery={auditsQuery}
8493
error={auditsQuery.error}
85-
showOrgDetails={experiments.includes("multi-organization")}
94+
showOrgDetails={isMultiOrg}
8695
filterProps={{
8796
filter,
8897
error: auditsQuery.error,
8998
menus: {
9099
user: userMenu,
91100
action: actionMenu,
92101
resourceType: resourceTypeMenu,
93-
organization: experiments.includes("multi-organization")
94-
? organizationsMenu
95-
: undefined,
102+
organization: isMultiOrg ? organizationsMenu : undefined,
96103
},
97104
}}
98105
/>

site/src/pages/AuditPage/AuditPageView.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,20 @@ export const AuditPageView: FC<AuditPageViewProps> = ({
5757
const isEmpty = !isLoading && auditLogs?.length === 0;
5858

5959
return (
60-
<Margins>
61-
<PageHeader>
60+
<Margins
61+
css={{
62+
// When acting as a deployment settings page, there is already padding.
63+
// TODO: When multi-org is stable we should move this to the deployment
64+
// settings dir, use the standard header, and remove these margins.
65+
padding: showOrgDetails ? 0 : undefined,
66+
}}
67+
>
68+
<PageHeader
69+
css={{
70+
// When acting as a deployment settings page, there is already padding.
71+
paddingTop: showOrgDetails ? 0 : undefined,
72+
}}
73+
>
6274
<PageHeaderTitle>
6375
<Stack direction="row" spacing={1} alignItems="center">
6476
<span>{Language.title}</span>

site/src/pages/ManagementSettingsPage/Sidebar.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Stack } from "components/Stack/Stack";
1010
import { UserAvatar } from "components/UserAvatar/UserAvatar";
1111
import { type ClassName, useClassName } from "hooks/useClassName";
1212
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
13-
import { USERS_LINK } from "modules/navigation";
13+
import { AUDIT_LINK, USERS_LINK, withFilter } from "modules/navigation";
1414
import { useOrganizationSettings } from "./ManagementSettingsLayout";
1515

1616
export const Sidebar: FC = () => {
@@ -103,6 +103,9 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
103103
{!organizationsEnabled && (
104104
<SidebarNavSubItem href="groups">Groups</SidebarNavSubItem>
105105
)}
106+
<SidebarNavSubItem href={AUDIT_LINK.slice(1)}>
107+
Auditing
108+
</SidebarNavSubItem>
106109
</Stack>
107110
)}
108111
</div>
@@ -148,8 +151,14 @@ export const OrganizationSettingsNavigation: FC<
148151
<SidebarNavSubItem href={urlForSubpage(organization.name, "groups")}>
149152
Groups
150153
</SidebarNavSubItem>
154+
{/* For now redirect to the site-wide audit page with the organization
155+
pre-filled into the filter. Based on user feedback we might want
156+
to serve a copy of the audit page or even delete this link. */}
151157
<SidebarNavSubItem
152-
href={urlForSubpage(organization.name, "auditing")}
158+
href={`/deployment${withFilter(
159+
AUDIT_LINK,
160+
`organization:${organization.name}`,
161+
)}`}
153162
>
154163
Auditing
155164
</SidebarNavSubItem>

site/src/router.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ export const router = createBrowserRouter(
406406
<Route path="users" element={<UsersPage />} />
407407
<Route path="users/create" element={<CreateUserPage />} />
408408
{groupsRouter()}
409+
<Route path="audit" element={<AuditPage />} />
409410
</Route>
410411

411412
<Route path="/settings" element={<UserSettingsLayout />}>

0 commit comments

Comments
 (0)