@@ -10,15 +10,19 @@ import {
10
10
import { Pill } from "components/Pill/Pill"
11
11
import { Stack } from "components/Stack/Stack"
12
12
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 "
15
15
import userAgentParser from "ua-parser-js"
16
- import { createDayString } from "util/createDayString "
16
+ import { combineClasses } from "util/combineClasses "
17
17
import { AuditLogDiff } from "./AuditLogDiff"
18
18
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 => {
22
26
if ( httpStatus >= 300 && httpStatus < 500 ) {
23
27
return "warning"
24
28
}
@@ -30,12 +34,6 @@ const pillTypeByHttpStatus = (
30
34
return "success"
31
35
}
32
36
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
-
39
37
export interface AuditLogRowProps {
40
38
auditLog : AuditLog
41
39
// Useful for Storybook
@@ -63,13 +61,19 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
63
61
}
64
62
65
63
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
+ >
67
69
< TableCell className = { styles . auditLogCell } >
68
70
< Stack
69
- style = { { cursor : shouldDisplayDiff ? "pointer" : undefined } }
70
71
direction = "row"
71
72
alignItems = "center"
72
- className = { styles . auditLogRow }
73
+ className = { combineClasses ( {
74
+ [ styles . auditLogHeader ] : true ,
75
+ [ styles . clickable ] : shouldDisplayDiff ,
76
+ } ) }
73
77
tabIndex = { 0 }
74
78
onClick = { toggle }
75
79
onKeyDown = { ( event ) => {
@@ -81,51 +85,61 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
81
85
< Stack
82
86
direction = "row"
83
87
alignItems = "center"
84
- justifyContent = "space-between"
85
- className = { styles . auditLogRowInfo }
88
+ className = { styles . auditLogHeaderInfo }
86
89
>
87
- < Stack direction = "row" alignItems = "center" >
90
+ < Stack
91
+ direction = "row"
92
+ alignItems = "center"
93
+ className = { styles . fullWidth }
94
+ >
88
95
< UserAvatar
89
96
username = { auditLog . user ?. username ?? "" }
90
97
avatarURL = { auditLog . user ?. avatar_url }
91
98
/>
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 >
104
99
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
- />
115
100
< Stack
101
+ alignItems = "baseline"
102
+ className = { styles . fullWidth }
103
+ justifyContent = "space-between"
116
104
direction = "row"
117
- alignItems = "center"
118
- className = { styles . auditLogExtraInfo }
119
105
>
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 >
129
143
</ Stack >
130
144
</ Stack >
131
145
</ Stack >
@@ -150,46 +164,79 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
150
164
const useStyles = makeStyles ( ( theme ) => ( {
151
165
auditLogCell : {
152
166
padding : "0 !important" ,
167
+ border : 0 ,
153
168
} ,
154
169
155
170
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 : {
156
193
padding : theme . spacing ( 2 , 4 ) ,
194
+ } ,
195
+
196
+ clickable : {
197
+ cursor : "pointer" ,
157
198
158
199
"&:hover" : {
159
200
backgroundColor : theme . palette . action . hover ,
160
201
} ,
161
202
} ,
162
203
163
- auditLogRowInfo : {
204
+ auditLogHeaderInfo : {
164
205
flex : 1 ,
165
206
} ,
166
207
167
- auditLogResume : {
208
+ auditLogSummary : {
168
209
...theme . typography . body1 ,
169
210
fontFamily : "inherit" ,
170
- display : "block" ,
171
211
} ,
172
212
173
213
auditLogTime : {
214
+ color : theme . palette . text . secondary ,
215
+ fontSize : 12 ,
216
+ } ,
217
+
218
+ auditLogInfo : {
174
219
...theme . typography . body2 ,
175
220
fontSize : 12 ,
176
221
fontFamily : "inherit" ,
177
222
color : theme . palette . text . secondary ,
178
223
display : "block" ,
179
224
} ,
180
225
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
- } ,
191
226
// offset the absence of the arrow icon on diff-less logs
192
227
columnWithoutDiff : {
193
228
marginLeft : "24px" ,
194
229
} ,
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
+ } ,
195
242
} ) )
0 commit comments