From 786f43753c9307ba7605b5b46b129763fdaacd81 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 3 Oct 2023 13:11:37 +0000 Subject: [PATCH 1/2] feat(site): add user activity on template insights --- site/src/api/api.ts | 8 ++ site/src/api/queries/insights.ts | 7 ++ .../TemplateInsightsPage.tsx | 92 +++++++++++++++++-- 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 09a9eb760bf3c..a7f6a9688aebf 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1488,6 +1488,14 @@ export const getInsightsUserLatency = async ( return response.data; }; +export const getInsightsUserActivity = async ( + filters: InsightsParams, +): Promise => { + const params = new URLSearchParams(filters); + const response = await axios.get(`/api/v2/insights/user-activity?${params}`); + return response.data; +}; + export type InsightsTemplateParams = InsightsParams & { interval: "day" | "week"; }; diff --git a/site/src/api/queries/insights.ts b/site/src/api/queries/insights.ts index f3aef1ba58ca0..7d60565e83bb0 100644 --- a/site/src/api/queries/insights.ts +++ b/site/src/api/queries/insights.ts @@ -13,3 +13,10 @@ export const insightsUserLatency = (params: API.InsightsParams) => { queryFn: () => API.getInsightsUserLatency(params), }; }; + +export const insightsUserActivity = (params: API.InsightsParams) => { + return { + queryKey: ["insights", "userActivity", params.template_ids, params], + queryFn: () => API.getInsightsUserActivity(params), + }; +}; diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx index 25226c9bc89fd..3bff3a456a4f2 100644 --- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx @@ -26,6 +26,7 @@ import { TemplateInsightsResponse, TemplateParameterUsage, TemplateParameterValue, + UserActivityInsightsResponse, UserLatencyInsightsResponse, } from "api/typesGenerated"; import { ComponentProps, ReactNode } from "react"; @@ -41,7 +42,11 @@ import Tooltip from "@mui/material/Tooltip"; import LinkOutlined from "@mui/icons-material/LinkOutlined"; import { InsightsInterval, IntervalMenu } from "./IntervalMenu"; import { WeekPicker, numberOfWeeksOptions } from "./WeekPicker"; -import { insightsTemplate, insightsUserLatency } from "api/queries/insights"; +import { + insightsTemplate, + insightsUserActivity, + insightsUserLatency, +} from "api/queries/insights"; import { useSearchParams } from "react-router-dom"; const DEFAULT_NUMBER_OF_WEEKS = numberOfWeeksOptions[0]; @@ -65,9 +70,11 @@ export default function TemplateInsightsPage() { template_ids: template.id, ...getDateRangeFilter(dateRange), }; + const insightsFilter = { ...commonFilters, interval }; const { data: templateInsights } = useQuery(insightsTemplate(insightsFilter)); const { data: userLatency } = useQuery(insightsUserLatency(commonFilters)); + const { data: userActivity } = useQuery(insightsUserActivity(commonFilters)); return ( <> @@ -97,6 +104,7 @@ export default function TemplateInsightsPage() { } templateInsights={templateInsights} userLatency={userLatency} + userActivity={userActivity} interval={interval} /> @@ -137,11 +145,13 @@ const getDateRange = ( export const TemplateInsightsPageView = ({ templateInsights, userLatency, + userActivity, controls, interval, }: { templateInsights: TemplateInsightsResponse | undefined; userLatency: UserLatencyInsightsResponse | undefined; + userActivity: UserActivityInsightsResponse | undefined; controls: ReactNode; interval: InsightsInterval; }) => { @@ -161,7 +171,7 @@ export const TemplateInsightsPageView = ({ sx={{ display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", - gridTemplateRows: "440px auto", + gridTemplateRows: "440px 440px auto", gap: (theme) => theme.spacing(3), }} > @@ -170,11 +180,12 @@ export const TemplateInsightsPageView = ({ interval={interval} data={templateInsights?.interval_reports} /> - + + { @@ -276,6 +287,65 @@ const UserLatencyPanel = ({ ); }; +const UsersActivityPanel = ({ + data, + ...panelProps +}: PanelProps & { data: UserActivityInsightsResponse | undefined }) => { + const users = data?.report.users; + + return ( + + + + Activity by user + + How is activity calculated? + + When a connection is initiated to a user's workspace they are + considered an active user. e.g. apps, web terminal, SSH + + + + + + {!data && } + {users && users.length === 0 && } + {users && + users + .sort((a, b) => b.seconds - a.seconds) + .map((row) => ( + + + + {row.username} + + ({ + color: theme.palette.text.secondary, + fontSize: 13, + textAlign: "right", + })} + > + {formatTime(row.seconds)} + + + ))} + + + ); +}; + const TemplateUsagePanel = ({ data, ...panelProps @@ -292,15 +362,21 @@ const TemplateUsagePanel = ({ // The API returns a row for each app, even if the user didn't use it. const hasDataAvailable = validUsage && validUsage.length > 0; return ( - + App & IDE Usage - {!data && } + {!data && } {data && !hasDataAvailable && } {data && hasDataAvailable && ( - + {validUsage .sort((a, b) => b.seconds - a.seconds) .map((usage, i) => { From 7518534be38f03f49e35cab9e3545eae8e9ee5fa Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 3 Oct 2023 18:28:09 +0000 Subject: [PATCH 2/2] Fix storybook --- .../TemplateInsightsPage.stories.tsx | 197 ++++++++++++++++++ .../TemplateInsightsPage.tsx | 4 +- 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.stories.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.stories.tsx index 889db073a6c1b..91bbc7f23be1e 100644 --- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.stories.tsx +++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.stories.tsx @@ -37,6 +37,14 @@ export const Empty: Story = { users: [], }, }, + userActivity: { + report: { + end_time: "", + start_time: "", + template_ids: [], + users: [], + }, + }, }, }; @@ -657,5 +665,194 @@ export const Loaded: Story = { ], }, }, + userActivity: { + report: { + start_time: "2023-09-03T00:00:00-03:00", + end_time: "2023-10-01T00:00:00-03:00", + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + users: [ + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "0bac0dfd-b086-4b6d-b8ba-789e0eca7451", + username: "kylecarbs", + avatar_url: "https://avatars.githubusercontent.com/u/7122116?v=4", + seconds: 671040, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "12b03f43-1bb7-4fca-967a-585c97f31682", + username: "coadler", + avatar_url: "https://avatars.githubusercontent.com/u/6332295?v=4", + seconds: 1487460, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "15890ddb-142c-443d-8fd5-cd8307256ab1", + username: "jsjoeio", + avatar_url: "https://avatars.githubusercontent.com/u/3806031?v=4", + seconds: 6600, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "1c3e3fff-6a0e-4179-9ba3-27f5443e6fce", + username: "Kira-Pilot", + avatar_url: "https://avatars.githubusercontent.com/u/19142439?v=4", + seconds: 195240, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "2e1e7f76-ae77-424a-a209-f35a99731ec9", + username: "phorcys420", + avatar_url: "https://avatars.githubusercontent.com/u/57866459?v=4", + seconds: 16320, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "3f8c0eef-6a45-4759-a4d6-d00bbffb1369", + username: "dean", + avatar_url: "https://avatars.githubusercontent.com/u/11241812?v=4", + seconds: 533520, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "59da0bfe-9c99-47fa-a563-f9fdb18449d0", + username: "cian", + avatar_url: + "https://lh3.googleusercontent.com/a/ACg8ocKKaBWosY_nuQvecIaUPh5RYjxkEN-C8FNGVPlC0Ch2fx0=s96-c", + seconds: 607080, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "5ccd3128-cbbb-4cfb-8139-5a1edbb60c71", + username: "bpmct", + avatar_url: "https://avatars.githubusercontent.com/u/22407953?v=4", + seconds: 161340, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "631f78f6-098e-4cb0-ae4f-418fafb0a406", + username: "matifali", + avatar_url: "https://avatars.githubusercontent.com/u/10648092?v=4", + seconds: 202500, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "740bba7f-356d-4203-8f15-03ddee381998", + username: "eric", + avatar_url: "https://avatars.githubusercontent.com/u/9683576?v=4", + seconds: 352680, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "78dd2361-4a5a-42b0-9ec3-3eea23af1094", + username: "code-asher", + avatar_url: "https://avatars.githubusercontent.com/u/45609798?v=4", + seconds: 518640, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "7f5cc5e9-20ee-48ce-959d-081b3f52273e", + username: "mafredri", + avatar_url: "https://avatars.githubusercontent.com/u/147409?v=4", + seconds: 218100, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "8b474a55-d414-4b53-a6ba-760f3d4eed7b", + username: "kirby", + avatar_url: + "https://lh3.googleusercontent.com/a-/ALV-UjUHd9l3CaO99BfVlP8L9D9HqKFOUac7zVCA_Bb_2lj0hcPkQvHkMk4HRaMw4b1YF7E-uHnJO-w8sXf3pqRA2EUP9sDvX6ITd2S2YN23kttVCJKTiI-YEIS8eVDfrF8YLqjfKL3PWsxyiPcgtcdfmPiEnlh4mpUMRXZudwtINfk0W3B9KEpwJTpipdlb57HdYO-mD3DEfmwnpZIO_iVjwnpWZZimXH5g15NVregb8VH_vlsW-vHrMsZ1fRGpm6GWnTcWx2rTImz5Qq5dd15MPKYUxc4wpyYImg07eD41ShzHDJhmDaj_n3hjOwFLuyloLBck-t9skQLWf2r7Voq42jVhzJ2-GAv9atC41_ohG1kq8TpCf9ak6S4hE3xMIB4yzDC0VZxl-BlsBHCuKBRTwC-58yTL2GZI31a0Q9PpR720AyiZaOWhX1QOVZmPZey8b8SG7jWTOfzNa9Shf9E0pz3yyIxFx7KSY5Qeye5AmO1au-rXuWr4whXXY6fsn0tnG4nxdyetCiXd0mOmvYHoJuuQFfqYNjdObduRD0yaVZGL-hPFDYH6K-wiedT1y-66jKXcqjVqe0Rwo7YzcVcP-IeV5RGuJ36TEpC1lhi2V-AnG7pmvIn_4AmXfycclrISO10LgQsrx8bxeBW61t9oTFTZCXXBDAd9bLRxndLi_mWYEfOSnWODgfCrapL_GNZsV0tkQ9x-zvlSXQXtze5bg__uAo7CEnZ20yWT5Gr25_NPsH6vyR3hplKn67qBti5_rKzFQ1sVbcuab2BRmF_Al9MTQw-R2gmd0mle9JRr8tyuwCYh82mBrM-dGebXSdqvabws7_WmF5TNwDHHzeeiHq1_6FYB0tBldx3yWk3U8olZ3SiPAe_NRnY0vUKI3ZANOA-IRYxyTAfjShJE0fRMCe70BsqzJj3RDAciqt5IaP2vZQeImjPZLd2NGo-Bbw=s96-c", + seconds: 543960, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "9ed91bb9-db45-4cef-b39c-819856e98c30", + username: "jon", + avatar_url: + "https://lh3.googleusercontent.com/a/ACg8ocJEE9R4__Pdh40DHGD-3noKezyw-1qo2auV_cb2gxBg=s96-c", + seconds: 464100, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "a73425d1-53a7-43d3-b6ae-cae9ba59b92b", + username: "ammar", + avatar_url: "https://avatars.githubusercontent.com/u/7416144?v=4", + seconds: 316200, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "af657bc3-6949-4b1b-bc2d-d41a40b546a4", + username: "BrunoQuaresma", + avatar_url: "https://avatars.githubusercontent.com/u/3165839?v=4", + seconds: 329100, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "b006209d-fdd2-4716-afb2-104dafb32dfb", + username: "mtojek", + avatar_url: "https://avatars.githubusercontent.com/u/14044910?v=4", + seconds: 11520, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "b3e1b884-1a5b-44eb-b8b3-423f8eddc503", + username: "spikecurtis", + avatar_url: "https://avatars.githubusercontent.com/u/5375600?v=4", + seconds: 523140, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "baf63990-16a9-472f-b715-64e24c6bcefb", + username: "atif", + avatar_url: + "https://lh3.googleusercontent.com/a-/ALV-UjVWiI2I5XOkxxi5KwAyfzZlfcOSYlMw8dIwJVwg2satTlOaLUy2PXcFcHCYtMg41DImXlB4F7YFIEW-CR_ANiCol7LnHTFomTyeh5N4ZvVQ4rx_sCl3PARywl0-UBW6usVGRVB8CnHve95q4ZDzJA6wJGVvr7gceCpgGe2A2597_KM1L5KIWKr5SAn41AZgQHZc7pgYJtiyKNleDN8LYzmceOtR3GJgFKjMrSOczNLNI3S2TrRPmIBIr_pZFDI3_npKDmQu9fPiVip5RDTAsuP9PdqruNJ4rB0rBae4Gog-RhqUV4L_i01-bJ6aepjH9gqxEkHHkXi7W0ldH8uV2fsQ4Eul78OQp0NrWxx9xZmFseTPK0toiop3EAWnuyp5ikaAnLodtvJ8L3iZXh45LvDv1ADESYPVAeuyHY5eee54O5xy72HABVB_UTE45Zhq086i4zaTNZoObXPrgiU3uNo0EhDQKa2jPNY2oQO0oZa991Oo9zCT9AULz5RP_3GTnfRMgD8ofCKr8Y3dVmSGI0RYOMI5Yqi76sEROCT5LqwAqRTFeGSMIF7-VI9qCctCtZ50n0OVtbFjPCgUGFVN1gZxe2qb66XCQnZOklTaMadj7KvtgIIJFlBSZJLkoPhSyIdiUAOp3VpDn8jOuEI0109YHzEM7l5KFNL-cHxQQyYB9hquld6y6EVRJdro8uVQdwkZ-_Yu4oD70A-WLb-Gi5RLdbB1iFwr99Lg-l4HNDWhh0h1wT5yhn4kgjPMgeTNT7F6fkiteAIvK_jJjVVh-PtKTt48kPv9c7rbc_jCBP70zUQ9X4Xxf9917BPUfvMgLk0gShSaFXxAGTgA7TzRaEsWSi9_DuJ0Q-yQZXwCJ1Y_1VrSF9B2FKsrugotVoC5BORu9tiaWi9jRP6RymM2X0HxsLv0lUFFVjgV0SZnynBNCgqyS02xAs8vEYpw-T7RJg=s96-c", + seconds: 2040, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "c0240345-f14a-4632-b713-a0f09c2ed927", + username: "asher-user", + avatar_url: "", + seconds: 0, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "c5eb8310-cf4f-444c-b223-0e991f828b40", + username: "Emyrk", + avatar_url: "https://avatars.githubusercontent.com/u/5446298?v=4", + seconds: 24540, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "d96bf761-3f94-46b3-a1da-6316e2e4735d", + username: "aslilac", + avatar_url: "https://avatars.githubusercontent.com/u/418348?v=4", + seconds: 824820, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "db38462b-e63d-4304-87e9-2640eea4a3b8", + username: "marc", + avatar_url: "", + seconds: 120, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "e9b24091-a633-4ba0-9746-ca325a86f0f5", + username: "michaelsmith", + avatar_url: + "https://lh3.googleusercontent.com/a-/ALV-UjXDMd9gEl4aMyM5ENfj4Ruzzn57AETWW6UuNC3Od03Y3AjvrCDhp8iE4I8L0393C_peQF9PZQyVklGCW-FCzODkvyVojUFqafbFi6AtvxjKn59ZyUVtG0ELoDNZOtRQaqUuMNIjtafNQ19LgwYm7LSB47My__oafDZ6jw6Kd_H-qtx19Vh62t3ACoJBHpDrF0BdDxWGBCkUAlC8aJcnqdRqPbKB5WGGcEfwLzrhLc5REN4CuXzm09_ZpU2jdvMUKCBX9H_8j2wcPwtgY0JG0DfIOX_VgTdM6Zy7BLiVQHSjD-uSkwqOEoXvsuKWlEBt74rqjyNDjjM1NyHiUdKpUd26hI2jcro_yrf4Jli7MCf5SjnkGMxQCgrD6-D9bcyBNzXpc1_5mDWrGpSh0X6pVK6GsmuYAc68hfTIHYVs-jB97mls9ClOJ2m51AdOAlizT80Ram2yJ09l-YbTVd4fG3L9FajMsvRhcvwwvN5tGcOk36KcIm0wFy9NQyH09QP3M1Rr2kDn9MzYYuyAZ9Um0tZydrPN9FA59JUytq8GtwnZZVmlZk2X2fXsCgJBv3dCwuF3THqSvL0M3lQa89-slrp2qgSRekiCmbb0-b62T413mOA9KNXcCvct_NN-JAE0b6o7To8B1WW8-AZiFQ2DesSEXL-CWYfqfecs4hoIrSBnQLa3Pm2Q5O-R7R99eRD7H3EqPihl_TiG2s_8gvLUF7ft55hYkV0j-YzTS4nOnUtEAXSqN-JYAd_BTJPJ0kyJLGIScwUQGoNFUQYs5nmlKPepeNpoQYYpQe0zK4ZVYm6fnRXUgv1cWvkD5RuxbBs1kgoVyZrZSNco8apuIjg6sBejRJFre_m0N6emp-Jn5wIkFB1f6IRb7S1aPvCqrqgqI8mTcI6Z-4Z3E3YwiYsn8_zVF9EPa1f1zpzeoppGd_YKaAxLjyOv_nC15bN3eio43A=s96-c", + seconds: 449820, + }, + { + template_ids: ["0d286645-29aa-4eaf-9b52-cc5d2740c90b"], + user_id: "fdc2dab9-dabd-4980-843f-2e93042db566", + username: "sharkymark", + avatar_url: "https://avatars.githubusercontent.com/u/2022166?v=4", + seconds: 124440, + }, + ], + }, + }, }, }; diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx index 3bff3a456a4f2..2519c2f015264 100644 --- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx @@ -259,6 +259,7 @@ const UsersLatencyPanel = ({ sx={{ display: "flex", justifyContent: "space-between", + alignItems: "center", fontSize: 14, py: 1, }} @@ -319,6 +320,7 @@ const UsersActivityPanel = ({ sx={{ display: "flex", justifyContent: "space-between", + alignItems: "center", fontSize: 14, py: 1, }} @@ -368,7 +370,7 @@ const TemplateUsagePanel = ({ {!data && } - {data && !hasDataAvailable && } + {data && !hasDataAvailable && } {data && hasDataAvailable && (