Skip to content

Commit 0049556

Browse files
refactor: Make the audit log looks like a timeline (#4765)
1 parent 9d8c3ca commit 0049556

File tree

4 files changed

+195
-78
lines changed

4 files changed

+195
-78
lines changed

site/src/components/AuditLogRow/AuditLogRow.tsx

Lines changed: 111 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ import {
1010
import { Pill } from "components/Pill/Pill"
1111
import { Stack } from "components/Stack/Stack"
1212
import { UserAvatar } from "components/UserAvatar/UserAvatar"
13-
import { ComponentProps, useState } from "react"
14-
import { MONOSPACE_FONT_FAMILY } from "theme/constants"
13+
import { useState } from "react"
14+
import { PaletteIndex } from "theme/palettes"
1515
import userAgentParser from "ua-parser-js"
16-
import { createDayString } from "util/createDayString"
16+
import { combineClasses } from "util/combineClasses"
1717
import { AuditLogDiff } from "./AuditLogDiff"
1818

19-
const pillTypeByHttpStatus = (
20-
httpStatus: number,
21-
): ComponentProps<typeof Pill>["type"] => {
19+
const readableActionMessage = (auditLog: AuditLog) => {
20+
return auditLog.description
21+
.replace("{user}", `<strong>${auditLog.user?.username.trim()}</strong>`)
22+
.replace("{target}", `<strong>${auditLog.resource_target.trim()}</strong>`)
23+
}
24+
25+
const httpStatusColor = (httpStatus: number): PaletteIndex => {
2226
if (httpStatus >= 300 && httpStatus < 500) {
2327
return "warning"
2428
}
@@ -30,12 +34,6 @@ const pillTypeByHttpStatus = (
3034
return "success"
3135
}
3236

33-
const readableActionMessage = (auditLog: AuditLog) => {
34-
return auditLog.description
35-
.replace("{user}", `<strong>${auditLog.user?.username}</strong>`)
36-
.replace("{target}", `<strong>${auditLog.resource_target}</strong>`)
37-
}
38-
3937
export interface AuditLogRowProps {
4038
auditLog: AuditLog
4139
// Useful for Storybook
@@ -63,13 +61,19 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
6361
}
6462

6563
return (
66-
<TableRow key={auditLog.id} data-testid={`audit-log-row-${auditLog.id}`}>
64+
<TableRow
65+
key={auditLog.id}
66+
data-testid={`audit-log-row-${auditLog.id}`}
67+
className={styles.auditLogRow}
68+
>
6769
<TableCell className={styles.auditLogCell}>
6870
<Stack
69-
style={{ cursor: shouldDisplayDiff ? "pointer" : undefined }}
7071
direction="row"
7172
alignItems="center"
72-
className={styles.auditLogRow}
73+
className={combineClasses({
74+
[styles.auditLogHeader]: true,
75+
[styles.clickable]: shouldDisplayDiff,
76+
})}
7377
tabIndex={0}
7478
onClick={toggle}
7579
onKeyDown={(event) => {
@@ -81,51 +85,61 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
8185
<Stack
8286
direction="row"
8387
alignItems="center"
84-
justifyContent="space-between"
85-
className={styles.auditLogRowInfo}
88+
className={styles.auditLogHeaderInfo}
8689
>
87-
<Stack direction="row" alignItems="center">
90+
<Stack
91+
direction="row"
92+
alignItems="center"
93+
className={styles.fullWidth}
94+
>
8895
<UserAvatar
8996
username={auditLog.user?.username ?? ""}
9097
avatarURL={auditLog.user?.avatar_url}
9198
/>
92-
<div>
93-
<span
94-
className={styles.auditLogResume}
95-
dangerouslySetInnerHTML={{
96-
__html: readableActionMessage(auditLog),
97-
}}
98-
/>
99-
<span className={styles.auditLogTime}>
100-
{createDayString(auditLog.time)}
101-
</span>
102-
</div>
103-
</Stack>
10499

105-
<Stack
106-
direction="column"
107-
alignItems="flex-end"
108-
spacing={1}
109-
className={styles.auditLogRight}
110-
>
111-
<Pill
112-
type={pillTypeByHttpStatus(auditLog.status_code)}
113-
text={auditLog.status_code.toString()}
114-
/>
115100
<Stack
101+
alignItems="baseline"
102+
className={styles.fullWidth}
103+
justifyContent="space-between"
116104
direction="row"
117-
alignItems="center"
118-
className={styles.auditLogExtraInfo}
119105
>
120-
<div>
121-
<strong>IP</strong> {auditLog.ip ?? notAvailableLabel}
122-
</div>
123-
<div>
124-
<strong>OS</strong> {os.name ?? notAvailableLabel}
125-
</div>
126-
<div>
127-
<strong>Browser</strong> {displayBrowserInfo}
128-
</div>
106+
<Stack
107+
className={styles.auditLogSummary}
108+
direction="row"
109+
alignItems="baseline"
110+
spacing={1}
111+
>
112+
<span
113+
dangerouslySetInnerHTML={{
114+
__html: readableActionMessage(auditLog),
115+
}}
116+
/>
117+
<span className={styles.auditLogTime}>
118+
{new Date(auditLog.time).toLocaleTimeString()}
119+
</span>
120+
</Stack>
121+
122+
<Stack direction="row" alignItems="center">
123+
<Stack direction="row" spacing={1} alignItems="baseline">
124+
<span className={styles.auditLogInfo}>
125+
IP: <strong>{auditLog.ip ?? notAvailableLabel}</strong>
126+
</span>
127+
128+
<span className={styles.auditLogInfo}>
129+
OS: <strong>{os.name ?? notAvailableLabel}</strong>
130+
</span>
131+
132+
<span className={styles.auditLogInfo}>
133+
Browser: <strong>{displayBrowserInfo}</strong>
134+
</span>
135+
</Stack>
136+
137+
<Pill
138+
className={styles.httpStatusPill}
139+
type={httpStatusColor(auditLog.status_code)}
140+
text={auditLog.status_code.toString()}
141+
/>
142+
</Stack>
129143
</Stack>
130144
</Stack>
131145
</Stack>
@@ -150,46 +164,79 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
150164
const useStyles = makeStyles((theme) => ({
151165
auditLogCell: {
152166
padding: "0 !important",
167+
border: 0,
153168
},
154169

155170
auditLogRow: {
171+
position: "relative",
172+
173+
"&:focus": {
174+
outlineStyle: "solid",
175+
outlineOffset: -1,
176+
outlineWidth: 2,
177+
outlineColor: theme.palette.secondary.dark,
178+
},
179+
180+
"&:not(:last-child) td:before": {
181+
position: "absolute",
182+
top: 20,
183+
left: 50,
184+
display: "block",
185+
content: "''",
186+
height: "100%",
187+
width: 2,
188+
background: theme.palette.divider,
189+
},
190+
},
191+
192+
auditLogHeader: {
156193
padding: theme.spacing(2, 4),
194+
},
195+
196+
clickable: {
197+
cursor: "pointer",
157198

158199
"&:hover": {
159200
backgroundColor: theme.palette.action.hover,
160201
},
161202
},
162203

163-
auditLogRowInfo: {
204+
auditLogHeaderInfo: {
164205
flex: 1,
165206
},
166207

167-
auditLogResume: {
208+
auditLogSummary: {
168209
...theme.typography.body1,
169210
fontFamily: "inherit",
170-
display: "block",
171211
},
172212

173213
auditLogTime: {
214+
color: theme.palette.text.secondary,
215+
fontSize: 12,
216+
},
217+
218+
auditLogInfo: {
174219
...theme.typography.body2,
175220
fontSize: 12,
176221
fontFamily: "inherit",
177222
color: theme.palette.text.secondary,
178223
display: "block",
179224
},
180225

181-
auditLogRight: {
182-
width: "auto",
183-
},
184-
185-
auditLogExtraInfo: {
186-
...theme.typography.body2,
187-
fontFamily: MONOSPACE_FONT_FAMILY,
188-
color: theme.palette.text.secondary,
189-
whiteSpace: "nowrap",
190-
},
191226
// offset the absence of the arrow icon on diff-less logs
192227
columnWithoutDiff: {
193228
marginLeft: "24px",
194229
},
230+
231+
fullWidth: {
232+
width: "100%",
233+
},
234+
235+
httpStatusPill: {
236+
fontSize: 10,
237+
height: 20,
238+
paddingLeft: 10,
239+
paddingRight: 10,
240+
fontWeight: 600,
241+
},
195242
}))

site/src/components/BuildsTable/BuildRow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const BuildRow: React.FC<BuildRowProps> = ({ build }) => {
4343
<BuildAvatar build={build} />
4444
<div>
4545
<Stack
46-
className={styles.buildResume}
46+
className={styles.buildSummary}
4747
direction="row"
4848
alignItems="center"
4949
spacing={1}
@@ -113,7 +113,7 @@ const useStyles = makeStyles((theme) => ({
113113
borderBottom: 0,
114114
},
115115

116-
buildResume: {
116+
buildSummary: {
117117
...theme.typography.body1,
118118
fontFamily: "inherit",
119119
},
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import TableCell from "@material-ui/core/TableCell"
3+
import TableRow from "@material-ui/core/TableRow"
4+
import formatRelative from "date-fns/formatRelative"
5+
import { FC } from "react"
6+
7+
export interface TableDateRow {
8+
date: Date
9+
}
10+
11+
export const TableDateRow: FC<TableDateRow> = ({ date }) => {
12+
const styles = useStyles()
13+
// We only want the message related to the date since the time is displayed
14+
// inside of the build row
15+
const displayDate = formatRelative(date, new Date()).split("at")[0]
16+
17+
return (
18+
<TableRow className={styles.dateRow}>
19+
<TableCell className={styles.dateCell} title={date.toLocaleDateString()}>
20+
{displayDate}
21+
</TableCell>
22+
</TableRow>
23+
)
24+
}
25+
26+
const useStyles = makeStyles((theme) => ({
27+
dateRow: {
28+
background: theme.palette.background.paper,
29+
30+
"&:not(:first-child) td": {
31+
borderTop: `1px solid ${theme.palette.divider}`,
32+
},
33+
},
34+
35+
dateCell: {
36+
padding: `${theme.spacing(1, 4)} !important`,
37+
background: `${theme.palette.background.paperLight} !important`,
38+
fontSize: 12,
39+
position: "relative",
40+
color: theme.palette.text.secondary,
41+
textTransform: "capitalize",
42+
},
43+
}))

0 commit comments

Comments
 (0)