Much nicer chart

This commit is contained in:
Matt Aitken
2024-07-06 17:00:40 +01:00
parent 1c9f7bed08
commit 78538bbbad
2 changed files with 62 additions and 29 deletions
@@ -49,7 +49,7 @@ const ChartContainer = React.forwardRef<
data-chart={chartId}
ref={ref}
className={cn(
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line-line]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-text-dimmed/50 [&_.recharts-cartesian-grid_line]:stroke-grid-bright/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-grid-dimmed [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-grid-dimmed [&_.recharts-radial-bar-background-sector]:fill-grid-dimmed [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-grid-dimmed [&_.recharts-reference-line-line]:stroke-grid-dimmed [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
className
)}
{...props}
@@ -158,7 +158,7 @@ const ChartTooltipContent = React.forwardRef<
<div
ref={ref}
className={cn(
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-grid-bright/50 bg-background-dimmed px-2.5 py-1.5 text-xs shadow-xl",
className
)}
>
@@ -173,7 +173,7 @@ const ChartTooltipContent = React.forwardRef<
<div
key={item.dataKey}
className={cn(
"[&>svg]:text-muted-foreground flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
"flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-text-dimmed",
indicator === "dot" && "items-center"
)}
>
@@ -207,18 +207,16 @@ const ChartTooltipContent = React.forwardRef<
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
"flex flex-1 justify-between gap-1.5 leading-none",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
<span className="text-text-dimmed">{itemConfig?.label || item.name}</span>
</div>
{item.value && (
<span className="text-foreground font-mono font-medium tabular-nums">
<span className="font-mono font-medium tabular-nums text-text-bright">
{item.value.toLocaleString()}
</span>
)}
@@ -268,7 +266,7 @@ const ChartLegendContent = React.forwardRef<
<div
key={item.value}
className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-text-dimmed"
)}
>
{itemConfig?.icon && !hideIcon ? (
@@ -3,7 +3,16 @@ import { Await } from "@remix-run/react";
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { formatDurationMilliseconds } from "@trigger.dev/core/v3";
import { Suspense } from "react";
import { Bar, BarChart, Rectangle, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import {
Bar,
BarChart,
CartesianGrid,
Rectangle,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { redirect, typeddefer, useTypedLoaderData } from "remix-typedjson";
import { UsageBar } from "~/components/billing/v3/UsageBar";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
@@ -26,7 +35,12 @@ import { requireUserId } from "~/services/session.server";
import { formatCurrency, formatCurrencyAccurate, formatNumber } from "~/utils/numberFormatter";
import { OrganizationParamsSchema, organizationPath } from "~/utils/pathBuilder";
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
import { ChartConfig } from "~/components/primitives/Chart";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "~/components/primitives/Chart";
export async function loader({ params, request }: LoaderFunctionArgs) {
await requireUserId(request);
@@ -227,25 +241,31 @@ const chartConfig = {
} satisfies ChartConfig;
function UsageChart({ data }: { data: UsageSeriesData }) {
const maxDollar = Math.max(...data.map((d) => d.dollars));
const decimalPlaces = maxDollar < 1 ? 4 : 2;
return (
<ResponsiveContainer width="100%" height="100%" className="min-h-96">
<ChartContainer config={chartConfig} className="max-h-96 min-h-40 w-full">
<BarChart
accessibilityLayer
data={data}
margin={{
top: 20,
right: 0,
left: -10,
bottom: 10,
}}
// margin={{
// top: 20,
// right: 0,
// left: -10,
// bottom: 10,
// }}
>
<CartesianGrid vertical={false} stroke="#212327" />
<XAxis
stroke="#5F6570"
fontSize={12}
tickLine={false}
axisLine={true}
dataKey={(item: { date: string }) => {
if (!item.date) return "";
const date = new Date(item.date);
tickMargin={10}
axisLine={false}
dataKey="date"
tickFormatter={(value) => {
if (!value) return "";
const date = new Date(value);
if (date.getDate() === 1) {
return dateFormatter.format(date);
}
@@ -254,14 +274,14 @@ function UsageChart({ data }: { data: UsageSeriesData }) {
className="text-xs"
/>
<YAxis
stroke="#5F6570"
fontSize={12}
tickLine={false}
axisLine={true}
tickMargin={10}
axisLine={false}
allowDecimals={true}
tickFormatter={(value) => `$${value.toFixed(2)}`}
tickFormatter={(value) => `$${value.toFixed(decimalPlaces)}`}
/>
<Tooltip
{/* <Tooltip
cursor={{ fill: "rgba(255,255,255,0.05)" }}
contentStyle={tooltipStyle}
labelFormatter={(value, data) => {
@@ -276,9 +296,24 @@ function UsageChart({ data }: { data: UsageSeriesData }) {
const numValue = Number(value);
return [` ${formatCurrencyAccurate(numValue)}`];
}}
/> */}
<ChartTooltip
content={<ChartTooltipContent />}
labelFormatter={(value, data) => {
const dateString = data.at(0)?.payload.date;
if (!dateString) {
return "";
}
return dateFormatter.format(new Date(dateString));
}}
// formatter={(value) => {
// const numValue = Number(value);
// return [` ${formatCurrencyAccurate(numValue)}`];
// }}
/>
<Bar dataKey="dollars" fill="#28BF5C" activeBar={<Rectangle fill="#5ADE87" />} />
<Bar dataKey="dollars" fill="var(--color-dollars)" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</ChartContainer>
);
}