124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
"use client"
|
|
|
|
import React, { useMemo } from "react"
|
|
import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts"
|
|
import { formatCurrency } from "@/lib/formatters"
|
|
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card"
|
|
import {
|
|
ChartContainer,
|
|
ChartTooltip,
|
|
ChartTooltipContent
|
|
|
|
} from "@/components/ui/chart"
|
|
import type {ChartConfig} from "@/components/ui/chart";
|
|
|
|
export const CustomBarChart = React.memo(function CustomBarChart({
|
|
title,
|
|
description,
|
|
data,
|
|
colorOffset = 0,
|
|
isCurrency = false
|
|
}: {
|
|
title: string
|
|
description: string
|
|
data: any[]
|
|
colorOffset?: number
|
|
isCurrency?: boolean
|
|
}) {
|
|
const { config, chartData } = useMemo(() => {
|
|
const cfg: ChartConfig = {
|
|
total: { label: "Total" },
|
|
}
|
|
|
|
const formattedData = data.map((item, index) => {
|
|
const key = String(item.name).toLowerCase().replace(/[^a-z0-9]/g, '_')
|
|
|
|
const hue = Math.floor((index * 137.5 + colorOffset) % 360)
|
|
|
|
cfg[key] = {
|
|
label: String(item.name),
|
|
color: `hsl(${hue}, 60%, 65%)`,
|
|
}
|
|
|
|
return {
|
|
...item,
|
|
fill: `var(--color-${key})`,
|
|
name: String(item.name),
|
|
total: Number(item.total)
|
|
}
|
|
})
|
|
|
|
return { config: cfg, chartData: formattedData }
|
|
}, [data, colorOffset])
|
|
|
|
const formatValue = (val: any) => {
|
|
if (!isCurrency) {
|
|
return val;
|
|
}
|
|
|
|
return formatCurrency(val);
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{title}</CardTitle>
|
|
<CardDescription>{description}</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ChartContainer config={config} className="[&_.recharts-wrapper]:outline-none [&_.recharts-surface]:outline-none">
|
|
<BarChart
|
|
accessibilityLayer
|
|
data={chartData}
|
|
layout="vertical"
|
|
margin={{
|
|
right: isCurrency ? 90 : 32, // More space if currency
|
|
left: 0
|
|
}}
|
|
style={{ outline: "none" }}
|
|
>
|
|
<CartesianGrid horizontal={false} vertical={false} />
|
|
<YAxis
|
|
dataKey="name"
|
|
type="category"
|
|
tickLine={false}
|
|
tickMargin={10}
|
|
axisLine={false}
|
|
hide
|
|
/>
|
|
<XAxis dataKey="total" type="number" hide />
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent indicator="line" valueFormatter={formatValue} />}
|
|
/>
|
|
<Bar dataKey="total" radius={4}>
|
|
<LabelList
|
|
dataKey="name"
|
|
position="insideLeft"
|
|
offset={8}
|
|
className="fill-white drop-shadow-md"
|
|
fontSize={12}
|
|
/>
|
|
<LabelList
|
|
dataKey="total"
|
|
position="right"
|
|
offset={8}
|
|
className="fill-foreground"
|
|
fontSize={12}
|
|
formatter={formatValue}
|
|
/>
|
|
</Bar>
|
|
</BarChart>
|
|
</ChartContainer>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
})
|