Skip to content

Commit bdb9954

Browse files
feat(site): add support for weekly data on template insights (#9997)
Close #9495
1 parent e7042e6 commit bdb9954

File tree

8 files changed

+365
-93
lines changed

8 files changed

+365
-93
lines changed

site/src/api/api.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -1474,28 +1474,31 @@ export const getWorkspaceParameters = async (workspace: TypesGen.Workspace) => {
14741474
};
14751475
};
14761476

1477-
type InsightsFilter = {
1477+
export type InsightsParams = {
14781478
start_time: string;
14791479
end_time: string;
14801480
template_ids: string;
14811481
};
14821482

14831483
export const getInsightsUserLatency = async (
1484-
filters: InsightsFilter,
1484+
filters: InsightsParams,
14851485
): Promise<TypesGen.UserLatencyInsightsResponse> => {
14861486
const params = new URLSearchParams(filters);
14871487
const response = await axios.get(`/api/v2/insights/user-latency?${params}`);
14881488
return response.data;
14891489
};
14901490

1491+
export type InsightsTemplateParams = InsightsParams & {
1492+
interval: "day" | "week";
1493+
};
1494+
14911495
export const getInsightsTemplate = async (
1492-
filters: InsightsFilter,
1496+
params: InsightsTemplateParams,
14931497
): Promise<TypesGen.TemplateInsightsResponse> => {
1494-
const params = new URLSearchParams({
1495-
...filters,
1496-
interval: "day",
1497-
});
1498-
const response = await axios.get(`/api/v2/insights/templates?${params}`);
1498+
const searchParams = new URLSearchParams(params);
1499+
const response = await axios.get(
1500+
`/api/v2/insights/templates?${searchParams}`,
1501+
);
14991502
return response.data;
15001503
};
15011504

site/src/api/queries/insights.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as API from "api/api";
2+
3+
export const insightsTemplate = (params: API.InsightsTemplateParams) => {
4+
return {
5+
queryKey: ["insights", "templates", params.template_ids, params],
6+
queryFn: () => API.getInsightsTemplate(params),
7+
};
8+
};
9+
10+
export const insightsUserLatency = (params: API.InsightsParams) => {
11+
return {
12+
queryKey: ["insights", "userLatency", params.template_ids, params],
13+
queryFn: () => API.getInsightsUserLatency(params),
14+
};
15+
};

site/src/components/DAUChart/DAUChart.tsx renamed to site/src/components/ActiveUserChart/ActiveUserChart.tsx

+16-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import Box from "@mui/material/Box";
22
import { Theme } from "@mui/material/styles";
33
import useTheme from "@mui/styles/useTheme";
4-
import * as TypesGen from "api/typesGenerated";
54
import {
65
CategoryScale,
76
Chart as ChartJS,
@@ -38,20 +37,19 @@ ChartJS.register(
3837
Legend,
3938
);
4039

41-
export interface DAUChartProps {
42-
daus: TypesGen.DAUsResponse;
40+
export interface ActiveUserChartProps {
41+
data: { date: string; amount: number }[];
42+
interval: "day" | "week";
4343
}
4444

45-
export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
45+
export const ActiveUserChart: FC<ActiveUserChartProps> = ({
46+
data,
47+
interval,
48+
}) => {
4649
const theme: Theme = useTheme();
4750

48-
const labels = daus.entries.map((val) => {
49-
return dayjs(val.date).format("YYYY-MM-DD");
50-
});
51-
52-
const data = daus.entries.map((val) => {
53-
return val.amount;
54-
});
51+
const labels = data.map((val) => dayjs(val.date).format("YYYY-MM-DD"));
52+
const chartData = data.map((val) => val.amount);
5553

5654
defaults.font.family = theme.typography.fontFamily as string;
5755
defaults.color = theme.palette.text.secondary;
@@ -82,11 +80,11 @@ export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
8280

8381
x: {
8482
ticks: {
85-
stepSize: daus.entries.length > 10 ? 2 : undefined,
83+
stepSize: data.length > 10 ? 2 : undefined,
8684
},
8785
type: "time",
8886
time: {
89-
unit: "day",
87+
unit: interval,
9088
},
9189
},
9290
},
@@ -101,7 +99,7 @@ export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
10199
datasets: [
102100
{
103101
label: "Daily Active Users",
104-
data: data,
102+
data: chartData,
105103
pointBackgroundColor: theme.palette.info.light,
106104
pointBorderColor: theme.palette.info.light,
107105
borderColor: theme.palette.info.light,
@@ -115,17 +113,15 @@ export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
115113
);
116114
};
117115

118-
export const DAUTitle = () => {
116+
export const ActiveUsersTitle = () => {
119117
return (
120118
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
121-
Daily Active Users
119+
Active Users
122120
<HelpTooltip size="small">
123-
<HelpTooltipTitle>
124-
How do we calculate daily active users?
125-
</HelpTooltipTitle>
121+
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
126122
<HelpTooltipText>
127123
When a connection is initiated to a user&apos;s workspace they are
128-
considered a daily active user. e.g. apps, web terminal, SSH
124+
considered an active user. e.g. apps, web terminal, SSH
129125
</HelpTooltipText>
130126
</HelpTooltip>
131127
</Box>

site/src/pages/DeploySettingsPage/GeneralSettingsPage/GeneralSettingsPageView.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import Box from "@mui/material/Box";
22
import { ClibaseOption, DAUsResponse } from "api/typesGenerated";
33
import { ErrorAlert } from "components/Alert/ErrorAlert";
4-
import { DAUChart, DAUTitle } from "components/DAUChart/DAUChart";
4+
import {
5+
ActiveUserChart,
6+
ActiveUsersTitle,
7+
} from "components/ActiveUserChart/ActiveUserChart";
58
import { Header } from "components/DeploySettingsLayout/Header";
69
import OptionsTable from "components/DeploySettingsLayout/OptionsTable";
710
import { Stack } from "components/Stack/Stack";
@@ -32,8 +35,8 @@ export const GeneralSettingsPageView = ({
3235
)}
3336
{deploymentDAUs && (
3437
<Box height={200} sx={{ mb: 3 }}>
35-
<ChartSection title={<DAUTitle />}>
36-
<DAUChart daus={deploymentDAUs} />
38+
<ChartSection title={<ActiveUsersTitle />}>
39+
<ActiveUserChart data={deploymentDAUs.entries} interval="day" />
3740
</ChartSection>
3841
</Box>
3942
)}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import CheckOutlined from "@mui/icons-material/CheckOutlined";
2+
import ExpandMoreOutlined from "@mui/icons-material/ExpandMoreOutlined";
3+
import Box from "@mui/material/Box";
4+
import Button from "@mui/material/Button";
5+
import Menu from "@mui/material/Menu";
6+
import MenuItem from "@mui/material/MenuItem";
7+
import { useState, useRef } from "react";
8+
9+
export const insightsIntervals = {
10+
day: {
11+
label: "Daily",
12+
},
13+
week: {
14+
label: "Weekly",
15+
},
16+
} as const;
17+
18+
export type InsightsInterval = keyof typeof insightsIntervals;
19+
20+
export const IntervalMenu = ({
21+
value,
22+
onChange,
23+
}: {
24+
value: InsightsInterval;
25+
onChange: (value: InsightsInterval) => void;
26+
}) => {
27+
const anchorRef = useRef<HTMLButtonElement>(null);
28+
const [open, setOpen] = useState(false);
29+
30+
const handleClose = () => {
31+
setOpen(false);
32+
};
33+
34+
return (
35+
<div>
36+
<Button
37+
ref={anchorRef}
38+
id="interval-button"
39+
aria-controls={open ? "interval-menu" : undefined}
40+
aria-haspopup="true"
41+
aria-expanded={open ? "true" : undefined}
42+
onClick={() => setOpen(true)}
43+
endIcon={<ExpandMoreOutlined />}
44+
>
45+
{insightsIntervals[value].label}
46+
</Button>
47+
<Menu
48+
id="interval-menu"
49+
anchorEl={anchorRef.current}
50+
open={open}
51+
onClose={handleClose}
52+
MenuListProps={{
53+
"aria-labelledby": "interval-button",
54+
}}
55+
anchorOrigin={{
56+
vertical: "bottom",
57+
horizontal: "left",
58+
}}
59+
transformOrigin={{
60+
vertical: "top",
61+
horizontal: "left",
62+
}}
63+
>
64+
{Object.entries(insightsIntervals).map(([interval, { label }]) => {
65+
return (
66+
<MenuItem
67+
css={{ fontSize: 14, justifyContent: "space-between" }}
68+
key={interval}
69+
onClick={() => {
70+
onChange(interval as InsightsInterval);
71+
handleClose();
72+
}}
73+
>
74+
{label}
75+
<Box css={{ width: 16, height: 16 }}>
76+
{value === interval && (
77+
<CheckOutlined css={{ width: 16, height: 16 }} />
78+
)}
79+
</Box>
80+
</MenuItem>
81+
);
82+
})}
83+
</Menu>
84+
</div>
85+
);
86+
};

0 commit comments

Comments
 (0)