Skip to content

Commit 8649a10

Browse files
feat(site): add template insights page (#8722)
1 parent da7e1eb commit 8649a10

File tree

20 files changed

+814
-188
lines changed

20 files changed

+814
-188
lines changed

coderd/apidoc/docs.go

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codersdk/deployment.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,9 @@ const (
18541854
// quiet hours instead of max_ttl.
18551855
ExperimentTemplateRestartRequirement Experiment = "template_restart_requirement"
18561856

1857+
// Insights page
1858+
ExperimentTemplateInsightsPage Experiment = "template_insights_page"
1859+
18571860
// Add new experiments here!
18581861
// ExperimentExample Experiment = "example"
18591862
)
@@ -1862,7 +1865,9 @@ const (
18621865
// users to opt-in to via --experimental='*'.
18631866
// Experiments that are not ready for consumption by all users should
18641867
// not be included here and will be essentially hidden.
1865-
var ExperimentsAll = Experiments{}
1868+
var ExperimentsAll = Experiments{
1869+
ExperimentTemplateInsightsPage,
1870+
}
18661871

18671872
// Experiments is a list of experiments that are enabled for the deployment.
18681873
// Multiple experiments may be enabled at the same time.

docs/api/schemas.md

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"canvas": "2.11.0",
5757
"chart.js": "3.9.1",
5858
"chartjs-adapter-date-fns": "3.0.0",
59+
"chroma-js": "2.4.2",
5960
"color-convert": "2.0.1",
6061
"cron-parser": "4.7.0",
6162
"cronstrue": "2.28.0",
@@ -116,6 +117,7 @@
116117
"@testing-library/jest-dom": "5.17.0",
117118
"@testing-library/react": "14.0.0",
118119
"@testing-library/user-event": "14.4.3",
120+
"@types/chroma-js": "2.4.0",
119121
"@types/jest": "29.5.2",
120122
"@types/node": "14.18.22",
121123
"@types/react": "18.2.6",

site/src/AppRouter.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ const AddNewLicensePage = lazy(
179179
const TemplateEmbedPage = lazy(
180180
() => import("./pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage"),
181181
)
182+
const TemplateInsightsPage = lazy(
183+
() =>
184+
import("./pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage"),
185+
)
182186

183187
export const AppRouter: FC = () => {
184188
return (
@@ -212,6 +216,7 @@ export const AppRouter: FC = () => {
212216
<Route path="files" element={<TemplateFilesPage />} />
213217
<Route path="versions" element={<TemplateVersionsPage />} />
214218
<Route path="embed" element={<TemplateEmbedPage />} />
219+
<Route path="insights" element={<TemplateInsightsPage />} />
215220
</Route>
216221

217222
<Route path="workspace" element={<CreateWorkspacePage />} />

site/src/api/api.ts

+25
Original file line numberDiff line numberDiff line change
@@ -1368,3 +1368,28 @@ export const getWorkspaceParameters = async (workspace: TypesGen.Workspace) => {
13681368
buildParameters,
13691369
}
13701370
}
1371+
1372+
type InsightsFilter = {
1373+
start_time: string
1374+
end_time: string
1375+
template_ids: string
1376+
}
1377+
1378+
export const getInsightsUserLatency = async (
1379+
filters: InsightsFilter,
1380+
): Promise<TypesGen.UserLatencyInsightsResponse> => {
1381+
const params = new URLSearchParams(filters)
1382+
const response = await axios.get(`/api/v2/insights/user-latency?${params}`)
1383+
return response.data
1384+
}
1385+
1386+
export const getInsightsTemplate = async (
1387+
filters: InsightsFilter,
1388+
): Promise<TypesGen.TemplateInsightsResponse> => {
1389+
const params = new URLSearchParams({
1390+
...filters,
1391+
interval: "day",
1392+
})
1393+
const response = await axios.get(`/api/v2/insights/templates?${params}`)
1394+
return response.data
1395+
}

site/src/api/typesGenerated.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1555,13 +1555,15 @@ export type Experiment =
15551555
| "moons"
15561556
| "single_tailnet"
15571557
| "tailnet_ha_coordinator"
1558+
| "template_insights_page"
15581559
| "template_restart_requirement"
15591560
| "workspace_actions"
15601561
export const Experiments: Experiment[] = [
15611562
"convert-to-oidc",
15621563
"moons",
15631564
"single_tailnet",
15641565
"tailnet_ha_coordinator",
1566+
"template_insights_page",
15651567
"template_restart_requirement",
15661568
"workspace_actions",
15671569
]

site/src/components/DAUChart/DAUChart.test.tsx

-37
This file was deleted.
+33-51
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Box from "@mui/material/Box"
12
import { Theme } from "@mui/material/styles"
23
import useTheme from "@mui/styles/useTheme"
34
import * as TypesGen from "api/typesGenerated"
@@ -15,13 +16,11 @@ import {
1516
Tooltip,
1617
} from "chart.js"
1718
import "chartjs-adapter-date-fns"
18-
import { Stack } from "components/Stack/Stack"
1919
import {
2020
HelpTooltip,
21-
HelpTooltipText,
2221
HelpTooltipTitle,
22+
HelpTooltipText,
2323
} from "components/Tooltips/HelpTooltip"
24-
import { WorkspaceSection } from "components/WorkspaceSection/WorkspaceSection"
2524
import dayjs from "dayjs"
2625
import { FC } from "react"
2726
import { Line } from "react-chartjs-2"
@@ -40,24 +39,10 @@ ChartJS.register(
4039
export interface DAUChartProps {
4140
daus: TypesGen.DAUsResponse
4241
}
43-
export const Language = {
44-
loadingText: "DAU stats are loading. Check back later.",
45-
chartTitle: "Daily Active Users",
46-
}
4742

4843
export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
4944
const theme: Theme = useTheme()
5045

51-
if (daus.entries.length === 0) {
52-
return (
53-
// We generate hidden element to prove this path is taken in the test
54-
// and through site inspection.
55-
<div style={{ display: "none" }}>
56-
<p>{Language.loadingText}</p>
57-
</div>
58-
)
59-
}
60-
6146
const labels = daus.entries.map((val) => {
6247
return dayjs(val.date).format("YYYY-MM-DD")
6348
})
@@ -92,42 +77,39 @@ export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
9277
},
9378
},
9479
},
95-
aspectRatio: 10 / 1,
80+
maintainAspectRatio: false,
9681
}
9782

9883
return (
99-
<>
100-
<WorkspaceSection
101-
title={
102-
<Stack direction="row" spacing={1} alignItems="center">
103-
{Language.chartTitle}
104-
<HelpTooltip size="small">
105-
<HelpTooltipTitle>How do we calculate DAUs?</HelpTooltipTitle>
106-
<HelpTooltipText>
107-
We use all workspace connection traffic to calculate DAUs.
108-
</HelpTooltipText>
109-
</HelpTooltip>
110-
</Stack>
111-
}
112-
>
113-
<Line
114-
data-chromatic="ignore"
115-
data={{
116-
labels: labels,
117-
datasets: [
118-
{
119-
label: "Daily Active Users",
120-
data: data,
121-
tension: 1 / 4,
122-
backgroundColor: theme.palette.secondary.dark,
123-
borderColor: theme.palette.secondary.dark,
124-
},
125-
],
126-
}}
127-
options={options}
128-
height={400}
129-
/>
130-
</WorkspaceSection>
131-
</>
84+
<Line
85+
data-chromatic="ignore"
86+
data={{
87+
labels: labels,
88+
datasets: [
89+
{
90+
label: "Daily Active Users",
91+
data: data,
92+
tension: 1 / 4,
93+
backgroundColor: theme.palette.secondary.dark,
94+
borderColor: theme.palette.secondary.dark,
95+
},
96+
],
97+
}}
98+
options={options}
99+
/>
100+
)
101+
}
102+
103+
export const DAUTitle = () => {
104+
return (
105+
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
106+
Daily Active Users
107+
<HelpTooltip size="small">
108+
<HelpTooltipTitle>How do we calculate DAUs?</HelpTooltipTitle>
109+
<HelpTooltipText>
110+
We use all workspace connection traffic to calculate DAUs.
111+
</HelpTooltipText>
112+
</HelpTooltip>
113+
</Box>
132114
)
133115
}

site/src/components/Loader/Loader.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import Box from "@mui/material/Box"
1+
import Box, { BoxProps } from "@mui/material/Box"
22
import CircularProgress from "@mui/material/CircularProgress"
33
import { FC } from "react"
44

5-
export const Loader: FC<React.PropsWithChildren<{ size?: number }>> = ({
5+
export const Loader: FC<{ size?: number } & BoxProps> = ({
66
size = 26,
7+
...boxProps
78
}) => {
89
return (
910
<Box
@@ -13,6 +14,7 @@ export const Loader: FC<React.PropsWithChildren<{ size?: number }>> = ({
1314
alignItems="center"
1415
justifyContent="center"
1516
data-testid="loader"
17+
{...boxProps}
1618
>
1719
<CircularProgress size={size} />
1820
</Box>

site/src/components/TemplateLayout/TemplateLayout.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { useQuery } from "@tanstack/react-query"
1616
import { AuthorizationRequest } from "api/typesGenerated"
1717
import { ErrorAlert } from "components/Alert/ErrorAlert"
18+
import { useDashboard } from "components/Dashboard/DashboardProvider"
1819

1920
const templatePermissions = (
2021
templateId: string,
@@ -71,6 +72,12 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
7172
queryKey: ["template", templateName],
7273
queryFn: () => fetchTemplate(orgId, templateName),
7374
})
75+
const dashboard = useDashboard()
76+
const hasInsightsEnabled =
77+
dashboard.experiments.includes("template_insights_page") ||
78+
process.env.NODE_ENV === "development"
79+
const shouldShowInsights =
80+
hasInsightsEnabled && data?.permissions?.canUpdateTemplate
7481

7582
if (error) {
7683
return (
@@ -157,6 +164,19 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
157164
>
158165
Embed
159166
</NavLink>
167+
{shouldShowInsights && (
168+
<NavLink
169+
to={`/templates/${templateName}/insights`}
170+
className={({ isActive }) =>
171+
combineClasses([
172+
styles.tabItem,
173+
isActive ? styles.tabItemActive : undefined,
174+
])
175+
}
176+
>
177+
Insights
178+
</NavLink>
179+
)}
160180
</Stack>
161181
</Margins>
162182
</div>

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

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import Box from "@mui/material/Box"
12
import { DeploymentOption } from "api/types"
23
import { DAUsResponse } from "api/typesGenerated"
34
import { ErrorAlert } from "components/Alert/ErrorAlert"
4-
import { DAUChart } from "components/DAUChart/DAUChart"
5+
import { DAUChart, DAUTitle } from "components/DAUChart/DAUChart"
56
import { Header } from "components/DeploySettingsLayout/Header"
67
import OptionsTable from "components/DeploySettingsLayout/OptionsTable"
78
import { Stack } from "components/Stack/Stack"
9+
import { WorkspaceSection } from "components/WorkspaceSection/WorkspaceSection"
810
import { useDeploymentOptions } from "utils/deployOptions"
911
import { docs } from "utils/docs"
1012

@@ -29,7 +31,13 @@ export const GeneralSettingsPageView = ({
2931
{Boolean(getDeploymentDAUsError) && (
3032
<ErrorAlert error={getDeploymentDAUsError} />
3133
)}
32-
{deploymentDAUs && <DAUChart daus={deploymentDAUs} />}
34+
{deploymentDAUs && (
35+
<Box height={200} sx={{ mb: 3 }}>
36+
<WorkspaceSection title={<DAUTitle />}>
37+
<DAUChart daus={deploymentDAUs} />
38+
</WorkspaceSection>
39+
</Box>
40+
)}
3341
<OptionsTable
3442
options={useDeploymentOptions(
3543
deploymentOptions,

0 commit comments

Comments
 (0)