UsageBarChart.tsx114 lines · main
1import dayjs from 'dayjs'
2import {
3 Bar,
4 CartesianGrid,
5 Cell,
6 ComposedChart,
7 ResponsiveContainer,
8 Tooltip,
9 XAxis,
10 YAxis,
11} from 'recharts'
12import { cn } from 'ui'
13
14import { Attribute, COLOR_MAP } from './Usage.constants'
15import { MultiAttributeTooltipContent, SingleAttributeTooltipContent } from './UsageChartTooltips'
16import { DataPoint } from '@/data/analytics/constants'
17
18// [Joshen] This BarChart is specifically for usage, hence not a reusable component, and not
19// replacing the existing BarChart in ui/Charts
20
21export interface UsageBarChartProps {
22 data: DataPoint[]
23 name: string // Used within the tooltip
24 attributes: Attribute[]
25 unit: 'bytes' | 'absolute' | 'percentage' | 'hours' | 'gigabytes'
26 yLimit?: number
27 yMin?: number
28 yLeftMargin?: number
29 yFormatter?: (value: number | string) => string
30 tooltipFormatter?: (value: number | string) => string
31}
32
33const UsageBarChart = ({
34 data,
35 name,
36 attributes,
37 unit,
38 yLimit,
39 yLeftMargin = 10,
40 yFormatter,
41 yMin,
42 tooltipFormatter,
43}: UsageBarChartProps) => {
44 const yDomain = [yMin ?? 0, Math.max(yMin ?? 0, yLimit ?? 0)]
45
46 return (
47 <div className="w-full h-[200px]">
48 <ResponsiveContainer width="100%" height={200}>
49 <ComposedChart data={data} margin={{ top: 0, right: 0, left: yLeftMargin, bottom: 0 }}>
50 <CartesianGrid
51 vertical={false}
52 strokeDasharray="3 3"
53 className="stroke-border-stronger"
54 />
55 <XAxis dataKey="periodStartFormatted" />
56 <YAxis
57 width={40}
58 axisLine={false}
59 tickLine={{ stroke: 'none' }}
60 domain={yDomain}
61 tickFormatter={yFormatter}
62 />
63 <Tooltip
64 content={(props) => {
65 const { active, payload } = props
66 if (active && payload && payload.length) {
67 const dataPeriod = dayjs(payload[0].payload.period_start)
68 const isAfterToday = dataPeriod.startOf('day').isAfter(dayjs().startOf('day'))
69 return (
70 <div
71 className={cn(
72 'border bg-surface-100 rounded-md px-2 py-2',
73 attributes.length > 1 && !isAfterToday ? 'w-[250px]' : 'w-[170px]'
74 )}
75 >
76 {attributes.length > 1 ? (
77 <MultiAttributeTooltipContent
78 attributes={attributes}
79 values={payload}
80 isAfterToday={isAfterToday}
81 tooltipFormatter={tooltipFormatter}
82 unit={unit}
83 />
84 ) : (
85 <SingleAttributeTooltipContent
86 name={name}
87 unit={unit}
88 value={payload[0].value}
89 tooltipFormatter={tooltipFormatter}
90 isAfterToday={isAfterToday}
91 />
92 )}
93 <p className="text-xs text-foreground-light mt-1">
94 {dataPeriod.format('DD MMM YYYY')}
95 </p>
96 </div>
97 )
98 } else return null
99 }}
100 />
101 {attributes?.map((attr) => (
102 <Bar key={attr.key} dataKey={attr.key} stackId="a">
103 {data.map((entry) => {
104 return <Cell key={`${entry.period_start}`} className={COLOR_MAP[attr.color].bar} />
105 })}
106 </Bar>
107 ))}
108 </ComposedChart>
109 </ResponsiveContainer>
110 </div>
111 )
112}
113
114export default UsageBarChart