Skip to content

Commit 3217cb8

Browse files
feat: improve usage visibility (#16134)
- Refactor the DAUs chart for clarity by improving the description and updating its title to better reflect the data. - Add a license consumption chart to the licenses page.
1 parent 08ffcb7 commit 3217cb8

16 files changed

+629
-216
lines changed

site/src/api/api.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,19 @@ class ApiMethods {
20892089
return response.data;
20902090
};
20912091

2092+
getInsightsUserStatusCounts = async (
2093+
offset = Math.trunc(new Date().getTimezoneOffset() / 60),
2094+
): Promise<TypesGen.GetUserStatusCountsResponse> => {
2095+
const searchParams = new URLSearchParams({
2096+
tz_offset: offset.toString(),
2097+
});
2098+
const response = await this.axios.get(
2099+
`/api/v2/insights/user-status-counts?${searchParams}`,
2100+
);
2101+
2102+
return response.data;
2103+
};
2104+
20922105
getInsightsTemplate = async (
20932106
params: InsightsTemplateParams,
20942107
): Promise<TypesGen.TemplateInsightsResponse> => {

site/src/api/queries/insights.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { API, type InsightsParams, type InsightsTemplateParams } from "api/api";
2+
import type { GetUserStatusCountsResponse } from "api/typesGenerated";
3+
import { type UseQueryOptions, UseQueryResult } from "react-query";
24

35
export const insightsTemplate = (params: InsightsTemplateParams) => {
46
return {
@@ -20,3 +22,15 @@ export const insightsUserActivity = (params: InsightsParams) => {
2022
queryFn: () => API.getInsightsUserActivity(params),
2123
};
2224
};
25+
26+
export const insightsUserStatusCounts = () => {
27+
return {
28+
queryKey: ["insights", "userStatusCounts"],
29+
queryFn: () => API.getInsightsUserStatusCounts(),
30+
select: (data) => data.status_counts,
31+
} satisfies UseQueryOptions<
32+
GetUserStatusCountsResponse,
33+
unknown,
34+
GetUserStatusCountsResponse["status_counts"]
35+
>;
36+
};

site/src/components/Chart/Chart.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const chartData = [
1919
const chartConfig = {
2020
users: {
2121
label: "Users",
22-
color: "hsl(var(--chart-1))",
22+
color: "hsl(var(--highlight-purple))",
2323
},
2424
} satisfies ChartConfig;
2525

site/src/components/Chart/Chart.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export const ChartContainer = React.forwardRef<
6666
"[&_.recharts-sector[stroke='#fff']]:stroke-transparent",
6767
"[&_.recharts-sector]:outline-none",
6868
"[&_.recharts-surface]:outline-none",
69+
"[&_.recharts-text]:fill-content-secondary [&_.recharts-text]:font-medium",
70+
"[&_.recharts-cartesian-axis-line]:stroke-[hsl(var(--border-default))]",
6971
className,
7072
)}
7173
{...props}
@@ -195,7 +197,7 @@ export const ChartTooltipContent = React.forwardRef<
195197
<div
196198
ref={ref}
197199
className={cn(
198-
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
200+
"grid min-w-[8rem] items-start gap-1 rounded-lg border border-solid border-border bg-surface-primary px-3 py-2 text-xs shadow-xl",
199201
className,
200202
)}
201203
>

site/src/components/Link/Link.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Slot } from "@radix-ui/react-slot";
1+
import { Slot, Slottable } from "@radix-ui/react-slot";
22
import { type VariantProps, cva } from "class-variance-authority";
33
import { SquareArrowOutUpRightIcon } from "lucide-react";
44
import { forwardRef } from "react";
@@ -38,7 +38,7 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
3838
ref={ref}
3939
{...props}
4040
>
41-
{children}
41+
<Slottable>{children}</Slottable>
4242
<SquareArrowOutUpRightIcon aria-hidden="true" />
4343
</Comp>
4444
);

site/src/index.css

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@
2828
--border-success: 142 76% 36%;
2929
--border-destructive: 0 84% 60%;
3030
--radius: 0.5rem;
31-
--chart-1: 12 76% 61%;
32-
--chart-2: 173 58% 39%;
33-
--chart-3: 197 37% 24%;
34-
--chart-4: 43 74% 66%;
35-
--chart-5: 27 87% 67%;
31+
--highlight-purple: 262 83% 58%;
32+
--highlight-green: 143 64% 24%;
3633
--border: 240 5.9% 90%;
3734
--input: 240 5.9% 90%;
3835
--ring: 240 10% 3.9%;
@@ -59,11 +56,8 @@
5956
--border-default: 240 4% 16%;
6057
--border-success: 142 76% 36%;
6158
--border-destructive: 0 91% 71%;
62-
--chart-1: 220 70% 50%;
63-
--chart-2: 160 60% 45%;
64-
--chart-3: 30 80% 55%;
65-
--chart-4: 280 65% 60%;
66-
--chart-5: 340 75% 55%;
59+
--highlight-purple: 252 95% 85%;
60+
--highlight-green: 141 79% 85%;
6761
--border: 240 3.7% 15.9%;
6862
--input: 240 3.7% 15.9%;
6963
--ring: 240 4.9% 83.9%;

site/src/pages/DeploymentSettingsPage/GeneralSettingsPage/GeneralSettingsPage.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
1111

1212
const GeneralSettingsPage: FC = () => {
1313
const { deploymentConfig } = useDeploymentSettings();
14-
const deploymentDAUsQuery = useQuery(deploymentDAUs());
1514
const safeExperimentsQuery = useQuery(availableExperiments());
1615

1716
const { metadata } = useEmbeddedMetadata();
18-
const entitlementsQuery = useQuery(entitlements(metadata.entitlements));
1917
const enabledExperimentsQuery = useQuery(experiments(metadata.experiments));
2018

2119
const safeExperiments = safeExperimentsQuery.data?.safe ?? [];
@@ -24,16 +22,16 @@ const GeneralSettingsPage: FC = () => {
2422
return !safeExperiments.includes(exp);
2523
}) ?? [];
2624

25+
const { data: dailyActiveUsers } = useQuery(deploymentDAUs());
26+
2727
return (
2828
<>
2929
<Helmet>
3030
<title>{pageTitle("General Settings")}</title>
3131
</Helmet>
3232
<GeneralSettingsPageView
3333
deploymentOptions={deploymentConfig.options}
34-
deploymentDAUs={deploymentDAUsQuery.data}
35-
deploymentDAUsError={deploymentDAUsQuery.error}
36-
entitlements={entitlementsQuery.data}
34+
dailyActiveUsers={dailyActiveUsers}
3735
invalidExperiments={invalidExperiments}
3836
safeExperiments={safeExperiments}
3937
/>
Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import {
3-
MockDeploymentDAUResponse,
4-
MockEntitlementsWithUserLimit,
5-
mockApiError,
6-
} from "testHelpers/entities";
2+
import { MockDeploymentDAUResponse } from "testHelpers/entities";
73
import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
84

95
const meta: Meta<typeof GeneralSettingsPageView> = {
@@ -39,10 +35,9 @@ const meta: Meta<typeof GeneralSettingsPageView> = {
3935
hidden: false,
4036
},
4137
],
42-
deploymentDAUs: MockDeploymentDAUResponse,
38+
dailyActiveUsers: MockDeploymentDAUResponse,
4339
invalidExperiments: [],
4440
safeExperiments: [],
45-
entitlements: undefined,
4641
},
4742
};
4843

@@ -51,21 +46,6 @@ type Story = StoryObj<typeof GeneralSettingsPageView>;
5146

5247
export const Page: Story = {};
5348

54-
export const NoDAUs: Story = {
55-
args: {
56-
deploymentDAUs: undefined,
57-
},
58-
};
59-
60-
export const DAUError: Story = {
61-
args: {
62-
deploymentDAUs: undefined,
63-
deploymentDAUsError: mockApiError({
64-
message: "Error fetching DAUs.",
65-
}),
66-
},
67-
};
68-
6949
export const allExperimentsEnabled: Story = {
7050
args: {
7151
deploymentOptions: [
@@ -137,74 +117,3 @@ export const invalidExperimentsEnabled: Story = {
137117
invalidExperiments: ["invalid"],
138118
},
139119
};
140-
141-
export const WithLicenseUtilization: Story = {
142-
args: {
143-
entitlements: {
144-
...MockEntitlementsWithUserLimit,
145-
features: {
146-
...MockEntitlementsWithUserLimit.features,
147-
user_limit: {
148-
...MockEntitlementsWithUserLimit.features.user_limit,
149-
enabled: true,
150-
actual: 75,
151-
limit: 100,
152-
entitlement: "entitled",
153-
},
154-
},
155-
},
156-
},
157-
};
158-
159-
export const HighLicenseUtilization: Story = {
160-
args: {
161-
entitlements: {
162-
...MockEntitlementsWithUserLimit,
163-
features: {
164-
...MockEntitlementsWithUserLimit.features,
165-
user_limit: {
166-
...MockEntitlementsWithUserLimit.features.user_limit,
167-
enabled: true,
168-
actual: 95,
169-
limit: 100,
170-
entitlement: "entitled",
171-
},
172-
},
173-
},
174-
},
175-
};
176-
177-
export const ExceedsLicenseUtilization: Story = {
178-
args: {
179-
entitlements: {
180-
...MockEntitlementsWithUserLimit,
181-
features: {
182-
...MockEntitlementsWithUserLimit.features,
183-
user_limit: {
184-
...MockEntitlementsWithUserLimit.features.user_limit,
185-
enabled: true,
186-
actual: 100,
187-
limit: 95,
188-
entitlement: "entitled",
189-
},
190-
},
191-
},
192-
},
193-
};
194-
export const NoLicenseLimit: Story = {
195-
args: {
196-
entitlements: {
197-
...MockEntitlementsWithUserLimit,
198-
features: {
199-
...MockEntitlementsWithUserLimit.features,
200-
user_limit: {
201-
...MockEntitlementsWithUserLimit.features.user_limit,
202-
enabled: false,
203-
actual: 0,
204-
limit: 0,
205-
entitlement: "entitled",
206-
},
207-
},
208-
},
209-
},
210-
};

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

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,32 @@
11
import AlertTitle from "@mui/material/AlertTitle";
2-
import LinearProgress from "@mui/material/LinearProgress";
32
import type {
43
DAUsResponse,
54
Entitlements,
65
Experiments,
76
SerpentOption,
87
} from "api/typesGenerated";
9-
import {
10-
ActiveUserChart,
11-
ActiveUsersTitle,
12-
} from "components/ActiveUserChart/ActiveUserChart";
13-
import { ErrorAlert } from "components/Alert/ErrorAlert";
148
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
159
import { Stack } from "components/Stack/Stack";
1610
import type { FC } from "react";
1711
import { useDeploymentOptions } from "utils/deployOptions";
1812
import { docs } from "utils/docs";
1913
import { Alert } from "../../../components/Alert/Alert";
2014
import OptionsTable from "../OptionsTable";
21-
import { ChartSection } from "./ChartSection";
15+
import { UserEngagementChart } from "./UserEngagementChart";
2216

2317
export type GeneralSettingsPageViewProps = {
2418
deploymentOptions: SerpentOption[];
25-
deploymentDAUs?: DAUsResponse;
26-
deploymentDAUsError: unknown;
27-
entitlements: Entitlements | undefined;
19+
dailyActiveUsers: DAUsResponse | undefined;
2820
readonly invalidExperiments: Experiments | string[];
2921
readonly safeExperiments: Experiments | string[];
3022
};
3123

3224
export const GeneralSettingsPageView: FC<GeneralSettingsPageViewProps> = ({
3325
deploymentOptions,
34-
deploymentDAUs,
35-
deploymentDAUsError,
36-
entitlements,
26+
dailyActiveUsers,
3727
safeExperiments,
3828
invalidExperiments,
3929
}) => {
40-
const licenseUtilizationPercentage =
41-
entitlements?.features?.user_limit?.actual &&
42-
entitlements?.features?.user_limit?.limit
43-
? entitlements.features.user_limit.actual /
44-
entitlements.features.user_limit.limit
45-
: undefined;
4630
return (
4731
<>
4832
<SettingsHeader
@@ -51,47 +35,12 @@ export const GeneralSettingsPageView: FC<GeneralSettingsPageViewProps> = ({
5135
docsHref={docs("/admin/setup")}
5236
/>
5337
<Stack spacing={4}>
54-
{Boolean(deploymentDAUsError) && (
55-
<ErrorAlert error={deploymentDAUsError} />
56-
)}
57-
{deploymentDAUs && (
58-
<div css={{ marginBottom: 24, height: 200 }}>
59-
<ChartSection title={<ActiveUsersTitle interval="day" />}>
60-
<ActiveUserChart data={deploymentDAUs.entries} interval="day" />
61-
</ChartSection>
62-
</div>
63-
)}
64-
{licenseUtilizationPercentage && (
65-
<ChartSection title="License Utilization">
66-
<LinearProgress
67-
variant="determinate"
68-
value={Math.min(licenseUtilizationPercentage * 100, 100)}
69-
color={
70-
licenseUtilizationPercentage < 0.9
71-
? "primary"
72-
: licenseUtilizationPercentage < 1
73-
? "warning"
74-
: "error"
75-
}
76-
css={{
77-
height: 24,
78-
borderRadius: 4,
79-
marginBottom: 8,
80-
}}
81-
/>
82-
<span
83-
css={{
84-
fontSize: "0.75rem",
85-
display: "block",
86-
textAlign: "right",
87-
}}
88-
>
89-
{Math.round(licenseUtilizationPercentage * 100)}% used (
90-
{entitlements!.features.user_limit.actual}/
91-
{entitlements!.features.user_limit.limit} users)
92-
</span>
93-
</ChartSection>
94-
)}
38+
<UserEngagementChart
39+
data={dailyActiveUsers?.entries.map((i) => ({
40+
date: i.date,
41+
users: i.amount,
42+
}))}
43+
/>
9544
{invalidExperiments.length > 0 && (
9645
<Alert severity="warning">
9746
<AlertTitle>Invalid experiments in use:</AlertTitle>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy