Skip to content

feat(site): add template insights page #8722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1f7120f
Add insights page
BrunoQuaresma Jul 24, 2023
ed5784e
Add experiment
BrunoQuaresma Jul 24, 2023
c5efc1e
fix(site): fix error 'Reduce of empty array with no initial value'
BrunoQuaresma Jul 24, 2023
a1a9952
Merge branch 'bq/fix-js-error' into bq/insights-page
BrunoQuaresma Jul 24, 2023
fdcf23f
Show or not insights page
BrunoQuaresma Jul 24, 2023
dfd2f03
Set basic grid for the panels
BrunoQuaresma Jul 24, 2023
b40bdae
Add DAU panel
BrunoQuaresma Jul 24, 2023
6679ae3
Add avatar url into user latency insihgts
BrunoQuaresma Jul 24, 2023
e35a3b5
List latency by user
BrunoQuaresma Jul 24, 2023
544590d
Add user avatar to the list
BrunoQuaresma Jul 24, 2023
fb32677
Merge branch 'main' into bq/insights-page
BrunoQuaresma Jul 25, 2023
1a32402
Use avatar url
BrunoQuaresma Jul 25, 2023
5274733
Fix icon url
BrunoQuaresma Jul 25, 2023
864c6dd
Add apps and IDE usage
BrunoQuaresma Jul 25, 2023
7ea9a26
Fix title
BrunoQuaresma Jul 25, 2023
2aafcee
Add load and empty states
BrunoQuaresma Jul 25, 2023
941671b
Update date range
BrunoQuaresma Jul 25, 2023
37da9b1
Fix loading and not available data
BrunoQuaresma Jul 25, 2023
b8ce94a
Fix data empty
BrunoQuaresma Jul 25, 2023
06178c9
feat(site): add terminal icon
BrunoQuaresma Jul 25, 2023
117aa82
Merge branch 'main' into bq/insights-page
BrunoQuaresma Jul 25, 2023
e91ca19
Merge branch 'bq/add-terminal-icon' into bq/insights-page
BrunoQuaresma Jul 25, 2023
8be0e42
Consume DAUs from insights
BrunoQuaresma Jul 25, 2023
e2768e3
Apply minor improvements
BrunoQuaresma Jul 25, 2023
9236240
Minor improvements
BrunoQuaresma Jul 25, 2023
f147a4d
Fix deps
BrunoQuaresma Jul 25, 2023
03cb386
Merge branch 'main' into bq/insights-page
BrunoQuaresma Jul 25, 2023
8ed5ca3
Add storybook
BrunoQuaresma Jul 25, 2023
3bc6e6b
Fix deploy settings page
BrunoQuaresma Jul 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1854,6 +1854,9 @@ const (
// quiet hours instead of max_ttl.
ExperimentTemplateRestartRequirement Experiment = "template_restart_requirement"

// Insights page
ExperimentTemplateInsightsPage Experiment = "template_insights_page"

// Add new experiments here!
// ExperimentExample Experiment = "example"
)
Expand All @@ -1862,7 +1865,9 @@ const (
// users to opt-in to via --experimental='*'.
// Experiments that are not ready for consumption by all users should
// not be included here and will be essentially hidden.
var ExperimentsAll = Experiments{}
var ExperimentsAll = Experiments{
ExperimentTemplateInsightsPage,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intended to release as experimental?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I talked with @bpmct about this

}

// Experiments is a list of experiments that are enabled for the deployment.
// Multiple experiments may be enabled at the same time.
Expand Down
1 change: 1 addition & 0 deletions docs/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"canvas": "2.11.0",
"chart.js": "3.9.1",
"chartjs-adapter-date-fns": "3.0.0",
"chroma-js": "2.4.2",
"color-convert": "2.0.1",
"cron-parser": "4.7.0",
"cronstrue": "2.28.0",
Expand Down Expand Up @@ -116,6 +117,7 @@
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"@types/chroma-js": "2.4.0",
"@types/jest": "29.5.2",
"@types/node": "14.18.22",
"@types/react": "18.2.6",
Expand Down
5 changes: 5 additions & 0 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ const AddNewLicensePage = lazy(
const TemplateEmbedPage = lazy(
() => import("./pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage"),
)
const TemplateInsightsPage = lazy(
() =>
import("./pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage"),
)

export const AppRouter: FC = () => {
return (
Expand Down Expand Up @@ -212,6 +216,7 @@ export const AppRouter: FC = () => {
<Route path="files" element={<TemplateFilesPage />} />
<Route path="versions" element={<TemplateVersionsPage />} />
<Route path="embed" element={<TemplateEmbedPage />} />
<Route path="insights" element={<TemplateInsightsPage />} />
</Route>

<Route path="workspace" element={<CreateWorkspacePage />} />
Expand Down
25 changes: 25 additions & 0 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1368,3 +1368,28 @@ export const getWorkspaceParameters = async (workspace: TypesGen.Workspace) => {
buildParameters,
}
}

type InsightsFilter = {
start_time: string
end_time: string
template_ids: string
}

export const getInsightsUserLatency = async (
filters: InsightsFilter,
): Promise<TypesGen.UserLatencyInsightsResponse> => {
const params = new URLSearchParams(filters)
const response = await axios.get(`/api/v2/insights/user-latency?${params}`)
return response.data
}

export const getInsightsTemplate = async (
filters: InsightsFilter,
): Promise<TypesGen.TemplateInsightsResponse> => {
const params = new URLSearchParams({
...filters,
interval: "day",
})
const response = await axios.get(`/api/v2/insights/templates?${params}`)
return response.data
}
2 changes: 2 additions & 0 deletions site/src/api/typesGenerated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1555,13 +1555,15 @@ export type Experiment =
| "moons"
| "single_tailnet"
| "tailnet_ha_coordinator"
| "template_insights_page"
| "template_restart_requirement"
| "workspace_actions"
export const Experiments: Experiment[] = [
"convert-to-oidc",
"moons",
"single_tailnet",
"tailnet_ha_coordinator",
"template_insights_page",
"template_restart_requirement",
"workspace_actions",
]
Expand Down
37 changes: 0 additions & 37 deletions site/src/components/DAUChart/DAUChart.test.tsx

This file was deleted.

84 changes: 33 additions & 51 deletions site/src/components/DAUChart/DAUChart.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Box from "@mui/material/Box"
import { Theme } from "@mui/material/styles"
import useTheme from "@mui/styles/useTheme"
import * as TypesGen from "api/typesGenerated"
Expand All @@ -15,13 +16,11 @@ import {
Tooltip,
} from "chart.js"
import "chartjs-adapter-date-fns"
import { Stack } from "components/Stack/Stack"
import {
HelpTooltip,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipText,
} from "components/Tooltips/HelpTooltip"
import { WorkspaceSection } from "components/WorkspaceSection/WorkspaceSection"
import dayjs from "dayjs"
import { FC } from "react"
import { Line } from "react-chartjs-2"
Expand All @@ -40,24 +39,10 @@ ChartJS.register(
export interface DAUChartProps {
daus: TypesGen.DAUsResponse
}
export const Language = {
loadingText: "DAU stats are loading. Check back later.",
chartTitle: "Daily Active Users",
}

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

if (daus.entries.length === 0) {
return (
// We generate hidden element to prove this path is taken in the test
// and through site inspection.
<div style={{ display: "none" }}>
<p>{Language.loadingText}</p>
</div>
)
}

const labels = daus.entries.map((val) => {
return dayjs(val.date).format("YYYY-MM-DD")
})
Expand Down Expand Up @@ -92,42 +77,39 @@ export const DAUChart: FC<DAUChartProps> = ({ daus }) => {
},
},
},
aspectRatio: 10 / 1,
maintainAspectRatio: false,
}

return (
<>
<WorkspaceSection
title={
<Stack direction="row" spacing={1} alignItems="center">
{Language.chartTitle}
<HelpTooltip size="small">
<HelpTooltipTitle>How do we calculate DAUs?</HelpTooltipTitle>
<HelpTooltipText>
We use all workspace connection traffic to calculate DAUs.
</HelpTooltipText>
</HelpTooltip>
</Stack>
}
>
<Line
data-chromatic="ignore"
data={{
labels: labels,
datasets: [
{
label: "Daily Active Users",
data: data,
tension: 1 / 4,
backgroundColor: theme.palette.secondary.dark,
borderColor: theme.palette.secondary.dark,
},
],
}}
options={options}
height={400}
/>
</WorkspaceSection>
</>
<Line
data-chromatic="ignore"
data={{
labels: labels,
datasets: [
{
label: "Daily Active Users",
data: data,
tension: 1 / 4,
backgroundColor: theme.palette.secondary.dark,
borderColor: theme.palette.secondary.dark,
},
],
}}
options={options}
/>
)
}

export const DAUTitle = () => {
return (
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
Daily Active Users
<HelpTooltip size="small">
<HelpTooltipTitle>How do we calculate DAUs?</HelpTooltipTitle>
<HelpTooltipText>
We use all workspace connection traffic to calculate DAUs.
</HelpTooltipText>
</HelpTooltip>
</Box>
)
}
6 changes: 4 additions & 2 deletions site/src/components/Loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Box from "@mui/material/Box"
import Box, { BoxProps } from "@mui/material/Box"
import CircularProgress from "@mui/material/CircularProgress"
import { FC } from "react"

export const Loader: FC<React.PropsWithChildren<{ size?: number }>> = ({
export const Loader: FC<{ size?: number } & BoxProps> = ({
size = 26,
...boxProps
}) => {
return (
<Box
Expand All @@ -13,6 +14,7 @@ export const Loader: FC<React.PropsWithChildren<{ size?: number }>> = ({
alignItems="center"
justifyContent="center"
data-testid="loader"
{...boxProps}
>
<CircularProgress size={size} />
</Box>
Expand Down
20 changes: 20 additions & 0 deletions site/src/components/TemplateLayout/TemplateLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { useQuery } from "@tanstack/react-query"
import { AuthorizationRequest } from "api/typesGenerated"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { useDashboard } from "components/Dashboard/DashboardProvider"

const templatePermissions = (
templateId: string,
Expand Down Expand Up @@ -71,6 +72,12 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
queryKey: ["template", templateName],
queryFn: () => fetchTemplate(orgId, templateName),
})
const dashboard = useDashboard()
const hasInsightsEnabled =
dashboard.experiments.includes("template_insights_page") ||
process.env.NODE_ENV === "development"
const shouldShowInsights =
hasInsightsEnabled && data?.permissions?.canUpdateTemplate

if (error) {
return (
Expand Down Expand Up @@ -157,6 +164,19 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
>
Embed
</NavLink>
{shouldShowInsights && (
<NavLink
to={`/templates/${templateName}/insights`}
className={({ isActive }) =>
combineClasses([
styles.tabItem,
isActive ? styles.tabItemActive : undefined,
])
}
>
Insights
</NavLink>
)}
</Stack>
</Margins>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import Box from "@mui/material/Box"
import { DeploymentOption } from "api/types"
import { DAUsResponse } from "api/typesGenerated"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { DAUChart } from "components/DAUChart/DAUChart"
import { DAUChart, DAUTitle } from "components/DAUChart/DAUChart"
import { Header } from "components/DeploySettingsLayout/Header"
import OptionsTable from "components/DeploySettingsLayout/OptionsTable"
import { Stack } from "components/Stack/Stack"
import { WorkspaceSection } from "components/WorkspaceSection/WorkspaceSection"
import { useDeploymentOptions } from "utils/deployOptions"
import { docs } from "utils/docs"

Expand All @@ -29,7 +31,13 @@ export const GeneralSettingsPageView = ({
{Boolean(getDeploymentDAUsError) && (
<ErrorAlert error={getDeploymentDAUsError} />
)}
{deploymentDAUs && <DAUChart daus={deploymentDAUs} />}
{deploymentDAUs && (
<Box height={200} sx={{ mb: 3 }}>
<WorkspaceSection title={<DAUTitle />}>
<DAUChart daus={deploymentDAUs} />
</WorkspaceSection>
</Box>
)}
<OptionsTable
options={useDeploymentOptions(
deploymentOptions,
Expand Down
Loading