mirror of https://github.com/coder/coder.git
170 lines
3.9 KiB
TypeScript
170 lines
3.9 KiB
TypeScript
import "chartjs-adapter-date-fns";
|
|
import { useTheme } from "@emotion/react";
|
|
import {
|
|
CategoryScale,
|
|
Chart as ChartJS,
|
|
type ChartOptions,
|
|
defaults,
|
|
Filler,
|
|
Legend,
|
|
LinearScale,
|
|
LineElement,
|
|
TimeScale,
|
|
Title,
|
|
Tooltip,
|
|
PointElement,
|
|
} from "chart.js";
|
|
import annotationPlugin from "chartjs-plugin-annotation";
|
|
import dayjs from "dayjs";
|
|
import type { FC } from "react";
|
|
import { Line } from "react-chartjs-2";
|
|
import {
|
|
HelpTooltip,
|
|
HelpTooltipTitle,
|
|
HelpTooltipText,
|
|
HelpTooltipContent,
|
|
HelpTooltipTrigger,
|
|
} from "components/HelpTooltip/HelpTooltip";
|
|
|
|
ChartJS.register(
|
|
CategoryScale,
|
|
LinearScale,
|
|
TimeScale,
|
|
LineElement,
|
|
PointElement,
|
|
Filler,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
annotationPlugin,
|
|
);
|
|
|
|
const USER_LIMIT_DISPLAY_THRESHOLD = 60;
|
|
|
|
export interface ActiveUserChartProps {
|
|
data: Array<{ date: string; amount: number }>;
|
|
interval: "day" | "week";
|
|
userLimit: number | undefined;
|
|
}
|
|
|
|
export const ActiveUserChart: FC<ActiveUserChartProps> = ({
|
|
data,
|
|
interval,
|
|
userLimit,
|
|
}) => {
|
|
const theme = useTheme();
|
|
|
|
const labels = data.map((val) => dayjs(val.date).format("YYYY-MM-DD"));
|
|
const chartData = data.map((val) => val.amount);
|
|
|
|
defaults.font.family = theme.typography.fontFamily as string;
|
|
defaults.color = theme.palette.text.secondary;
|
|
|
|
const options: ChartOptions<"line"> = {
|
|
responsive: true,
|
|
animation: false,
|
|
plugins: {
|
|
annotation: {
|
|
annotations: [
|
|
{
|
|
type: "line",
|
|
scaleID: "y",
|
|
display: shouldDisplayUserLimit(userLimit, chartData),
|
|
value: userLimit,
|
|
borderColor: theme.palette.secondary.contrastText,
|
|
borderWidth: 5,
|
|
label: {
|
|
content: "User limit",
|
|
color: theme.palette.primary.contrastText,
|
|
display: true,
|
|
font: { weight: "normal" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
displayColors: false,
|
|
callbacks: {
|
|
title: (context) => {
|
|
const date = new Date(context[0].parsed.x);
|
|
return date.toLocaleDateString();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
scales: {
|
|
y: {
|
|
grid: { color: theme.palette.divider },
|
|
suggestedMin: 0,
|
|
ticks: {
|
|
precision: 0,
|
|
},
|
|
},
|
|
|
|
x: {
|
|
grid: { color: theme.palette.divider },
|
|
ticks: {
|
|
stepSize: data.length > 10 ? 2 : undefined,
|
|
},
|
|
type: "time",
|
|
time: {
|
|
unit: interval,
|
|
},
|
|
},
|
|
},
|
|
maintainAspectRatio: false,
|
|
};
|
|
|
|
return (
|
|
<Line
|
|
data-chromatic="ignore"
|
|
data={{
|
|
labels: labels,
|
|
datasets: [
|
|
{
|
|
label: `${interval === "day" ? "Daily" : "Weekly"} Active Users`,
|
|
data: chartData,
|
|
pointBackgroundColor: theme.roles.active.outline,
|
|
pointBorderColor: theme.roles.active.outline,
|
|
borderColor: theme.roles.active.outline,
|
|
},
|
|
],
|
|
}}
|
|
options={options}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export const ActiveUsersTitle: FC = () => {
|
|
return (
|
|
<div css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
Active Users
|
|
<HelpTooltip>
|
|
<HelpTooltipTrigger size="small" />
|
|
<HelpTooltipContent>
|
|
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
|
|
<HelpTooltipText>
|
|
When a connection is initiated to a user's workspace they are
|
|
considered an active user. e.g. apps, web terminal, SSH
|
|
</HelpTooltipText>
|
|
</HelpTooltipContent>
|
|
</HelpTooltip>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function shouldDisplayUserLimit(
|
|
userLimit: number | undefined,
|
|
activeUsers: number[],
|
|
): boolean {
|
|
if (!userLimit || activeUsers.length === 0) {
|
|
return false;
|
|
}
|
|
return (
|
|
Math.max(...activeUsers) >= (userLimit * USER_LIMIT_DISPLAY_THRESHOLD) / 100
|
|
);
|
|
}
|