@@ -26,6 +26,7 @@ import {
26
26
TemplateInsightsResponse ,
27
27
TemplateParameterUsage ,
28
28
TemplateParameterValue ,
29
+ UserActivityInsightsResponse ,
29
30
UserLatencyInsightsResponse ,
30
31
} from "api/typesGenerated" ;
31
32
import { ComponentProps , ReactNode } from "react" ;
@@ -41,7 +42,11 @@ import Tooltip from "@mui/material/Tooltip";
41
42
import LinkOutlined from "@mui/icons-material/LinkOutlined" ;
42
43
import { InsightsInterval , IntervalMenu } from "./IntervalMenu" ;
43
44
import { WeekPicker , numberOfWeeksOptions } from "./WeekPicker" ;
44
- import { insightsTemplate , insightsUserLatency } from "api/queries/insights" ;
45
+ import {
46
+ insightsTemplate ,
47
+ insightsUserActivity ,
48
+ insightsUserLatency ,
49
+ } from "api/queries/insights" ;
45
50
import { useSearchParams } from "react-router-dom" ;
46
51
47
52
const DEFAULT_NUMBER_OF_WEEKS = numberOfWeeksOptions [ 0 ] ;
@@ -65,9 +70,11 @@ export default function TemplateInsightsPage() {
65
70
template_ids : template . id ,
66
71
...getDateRangeFilter ( dateRange ) ,
67
72
} ;
73
+
68
74
const insightsFilter = { ...commonFilters , interval } ;
69
75
const { data : templateInsights } = useQuery ( insightsTemplate ( insightsFilter ) ) ;
70
76
const { data : userLatency } = useQuery ( insightsUserLatency ( commonFilters ) ) ;
77
+ const { data : userActivity } = useQuery ( insightsUserActivity ( commonFilters ) ) ;
71
78
72
79
return (
73
80
< >
@@ -97,6 +104,7 @@ export default function TemplateInsightsPage() {
97
104
}
98
105
templateInsights = { templateInsights }
99
106
userLatency = { userLatency }
107
+ userActivity = { userActivity }
100
108
interval = { interval }
101
109
/>
102
110
</ >
@@ -137,11 +145,13 @@ const getDateRange = (
137
145
export const TemplateInsightsPageView = ( {
138
146
templateInsights,
139
147
userLatency,
148
+ userActivity,
140
149
controls,
141
150
interval,
142
151
} : {
143
152
templateInsights : TemplateInsightsResponse | undefined ;
144
153
userLatency : UserLatencyInsightsResponse | undefined ;
154
+ userActivity : UserActivityInsightsResponse | undefined ;
145
155
controls : ReactNode ;
146
156
interval : InsightsInterval ;
147
157
} ) => {
@@ -161,7 +171,7 @@ export const TemplateInsightsPageView = ({
161
171
sx = { {
162
172
display : "grid" ,
163
173
gridTemplateColumns : "repeat(3, minmax(0, 1fr))" ,
164
- gridTemplateRows : "440px auto" ,
174
+ gridTemplateRows : "440px 440px auto" ,
165
175
gap : ( theme ) => theme . spacing ( 3 ) ,
166
176
} }
167
177
>
@@ -170,11 +180,12 @@ export const TemplateInsightsPageView = ({
170
180
interval = { interval }
171
181
data = { templateInsights ?. interval_reports }
172
182
/>
173
- < UserLatencyPanel data = { userLatency } />
183
+ < UsersLatencyPanel data = { userLatency } />
174
184
< TemplateUsagePanel
175
- sx = { { gridColumn : "span 3 " } }
185
+ sx = { { gridColumn : "span 2 " } }
176
186
data = { templateInsights ?. report ?. apps_usage }
177
187
/>
188
+ < UsersActivityPanel data = { userActivity } />
178
189
< TemplateParametersUsagePanel
179
190
sx = { { gridColumn : "span 3" } }
180
191
data = { templateInsights ?. report ?. parameters_usage }
@@ -216,7 +227,7 @@ const ActiveUsersPanel = ({
216
227
) ;
217
228
} ;
218
229
219
- const UserLatencyPanel = ( {
230
+ const UsersLatencyPanel = ( {
220
231
data,
221
232
...panelProps
222
233
} : PanelProps & { data : UserLatencyInsightsResponse | undefined } ) => {
@@ -276,6 +287,65 @@ const UserLatencyPanel = ({
276
287
) ;
277
288
} ;
278
289
290
+ const UsersActivityPanel = ( {
291
+ data,
292
+ ...panelProps
293
+ } : PanelProps & { data : UserActivityInsightsResponse | undefined } ) => {
294
+ const users = data ?. report . users ;
295
+
296
+ return (
297
+ < Panel { ...panelProps } sx = { { overflowY : "auto" , ...panelProps . sx } } >
298
+ < PanelHeader >
299
+ < PanelTitle sx = { { display : "flex" , alignItems : "center" , gap : 1 } } >
300
+ Activity by user
301
+ < HelpTooltip size = "small" >
302
+ < HelpTooltipTitle > How is activity calculated?</ HelpTooltipTitle >
303
+ < HelpTooltipText >
304
+ When a connection is initiated to a user's workspace they are
305
+ considered an active user. e.g. apps, web terminal, SSH
306
+ </ HelpTooltipText >
307
+ </ HelpTooltip >
308
+ </ PanelTitle >
309
+ </ PanelHeader >
310
+ < PanelContent >
311
+ { ! data && < Loader sx = { { height : "100%" } } /> }
312
+ { users && users . length === 0 && < NoDataAvailable /> }
313
+ { users &&
314
+ users
315
+ . sort ( ( a , b ) => b . seconds - a . seconds )
316
+ . map ( ( row ) => (
317
+ < Box
318
+ key = { row . user_id }
319
+ sx = { {
320
+ display : "flex" ,
321
+ justifyContent : "space-between" ,
322
+ fontSize : 14 ,
323
+ py : 1 ,
324
+ } }
325
+ >
326
+ < Box sx = { { display : "flex" , alignItems : "center" , gap : 1.5 } } >
327
+ < UserAvatar
328
+ username = { row . username }
329
+ avatarURL = { row . avatar_url }
330
+ />
331
+ < Box sx = { { fontWeight : 500 } } > { row . username } </ Box >
332
+ </ Box >
333
+ < Box
334
+ css = { ( theme ) => ( {
335
+ color : theme . palette . text . secondary ,
336
+ fontSize : 13 ,
337
+ textAlign : "right" ,
338
+ } ) }
339
+ >
340
+ { formatTime ( row . seconds ) }
341
+ </ Box >
342
+ </ Box >
343
+ ) ) }
344
+ </ PanelContent >
345
+ </ Panel >
346
+ ) ;
347
+ } ;
348
+
279
349
const TemplateUsagePanel = ( {
280
350
data,
281
351
...panelProps
@@ -292,15 +362,21 @@ const TemplateUsagePanel = ({
292
362
// The API returns a row for each app, even if the user didn't use it.
293
363
const hasDataAvailable = validUsage && validUsage . length > 0 ;
294
364
return (
295
- < Panel { ...panelProps } >
365
+ < Panel { ...panelProps } css = { { overflowY : "auto" } } >
296
366
< PanelHeader >
297
367
< PanelTitle > App & IDE Usage </ PanelTitle >
298
368
</ PanelHeader >
299
369
< PanelContent >
300
- { ! data && < Loader sx = { { height : 200 } } /> }
370
+ { ! data && < Loader sx = { { height : "100%" } } /> }
301
371
{ data && ! hasDataAvailable && < NoDataAvailable sx = { { height : 200 } } /> }
302
372
{ data && hasDataAvailable && (
303
- < Box sx = { { display : "flex" , flexDirection : "column" , gap : 3 } } >
373
+ < Box
374
+ sx = { {
375
+ display : "flex" ,
376
+ flexDirection : "column" ,
377
+ gap : 3 ,
378
+ } }
379
+ >
304
380
{ validUsage
305
381
. sort ( ( a , b ) => b . seconds - a . seconds )
306
382
. map ( ( usage , i ) => {
0 commit comments