Files
claudemesh/apps/web/src/modules/organization/home/charts/pie.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

189 lines
5.7 KiB
TypeScript

"use client";
import dayjs from "dayjs";
import * as React from "react";
import { Label, Pie, PieChart as RechartsPieChart, Sector } from "recharts";
import { useTranslation } from "@turbostarter/i18n";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@turbostarter/ui-web/card";
import {
ChartContainer,
ChartStyle,
ChartTooltip,
ChartTooltipContent,
} from "@turbostarter/ui-web/chart";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@turbostarter/ui-web/select";
import type { ChartConfig } from "@turbostarter/ui-web/chart";
import type { PieSectorDataItem } from "recharts/types/polar/Pie";
const desktopData = [
{ month: "january", desktop: 186, fill: "var(--chart-1)" },
{ month: "february", desktop: 305, fill: "var(--chart-2)" },
{ month: "march", desktop: 237, fill: "var(--chart-3)" },
{ month: "april", desktop: 173, fill: "var(--chart-4)" },
{ month: "may", desktop: 209, fill: "var(--chart-5)" },
];
const chartConfig = {
january: {
label: dayjs().month(0).format("MMMM"),
color: "var(--chart-1)",
},
february: {
label: dayjs().month(1).format("MMMM"),
color: "var(--chart-2)",
},
march: {
label: dayjs().month(2).format("MMMM"),
color: "var(--chart-3)",
},
april: {
label: dayjs().month(3).format("MMMM"),
color: "var(--chart-4)",
},
may: {
label: dayjs().month(4).format("MMMM"),
color: "var(--chart-5)",
},
} satisfies ChartConfig;
export function PieChart() {
const { t, i18n } = useTranslation(["common", "dashboard"]);
const id = "pie-interactive";
const [activeMonth, setActiveMonth] = React.useState(
desktopData[0]?.month ?? "january",
);
const activeIndex = React.useMemo(
() => desktopData.findIndex((item) => item.month === activeMonth),
[activeMonth],
);
const months = desktopData.map((item) => item.month);
return (
<Card data-chart={id} className="flex flex-col">
<ChartStyle id={id} config={chartConfig} />
<CardHeader className="flex-row items-start space-y-0 pb-0">
<div className="grid gap-0.5">
<CardTitle className="text-xl">{t("chart.pie")}</CardTitle>
<CardDescription>{t("chart.period")}</CardDescription>
</div>
<Select value={activeMonth} onValueChange={setActiveMonth}>
<SelectTrigger
className="ml-auto w-[130px] rounded-lg pl-3"
aria-label={t("selectMonth")}
size="sm"
>
<SelectValue placeholder={t("selectMonth")} />
</SelectTrigger>
<SelectContent align="end" className="rounded-xl">
{months.map((key) => {
const config = chartConfig[key as keyof typeof chartConfig];
return (
<SelectItem
key={key}
value={key}
className="rounded-lg [&_span]:flex"
>
<div className="flex items-center gap-2 text-xs">
<span
className="flex h-3 w-3 shrink-0 rounded-sm"
style={{
backgroundColor:
"color" in config ? config.color : undefined,
}}
/>
{config.label}
</div>
</SelectItem>
);
})}
</SelectContent>
</Select>
</CardHeader>
<CardContent className="flex flex-1 justify-center pb-0">
<ChartContainer
id={id}
config={chartConfig}
className="mx-auto aspect-square w-full max-w-[300px]"
>
<RechartsPieChart>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
/>
<Pie
data={desktopData}
dataKey="desktop"
nameKey="month"
innerRadius={60}
strokeWidth={5}
activeIndex={activeIndex}
activeShape={({
outerRadius = 0,
...props
}: PieSectorDataItem) => (
<g>
<Sector {...props} outerRadius={outerRadius + 10} />
<Sector
{...props}
outerRadius={outerRadius + 25}
innerRadius={outerRadius + 12}
/>
</g>
)}
>
<Label
content={({ viewBox }) => {
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
const data = desktopData[activeIndex];
if (!data) return null;
return (
<text
x={viewBox.cx}
y={viewBox.cy}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan
x={viewBox.cx}
y={viewBox.cy}
className="fill-foreground text-3xl font-bold"
>
{data.desktop.toLocaleString(i18n.language)}
</tspan>
<tspan
x={viewBox.cx}
y={(viewBox.cy ?? 0) + 24}
className="fill-muted-foreground"
>
{t("visitors")}
</tspan>
</text>
);
}
}}
/>
</Pie>
</RechartsPieChart>
</ChartContainer>
</CardContent>
</Card>
);
}