/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import { VChart } from '@visactor/react-vchart'
import { AreaChart, BarChart3, WalletCards } from 'lucide-react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { IconBadge } from '@/components/ui/icon-badge'
import { useThemeCustomization } from '@/context/theme-customization-provider'
import { useTheme } from '@/context/theme-provider'
import {
CONSUMPTION_DISTRIBUTION_CHART_OPTIONS,
DEFAULT_TIME_GRANULARITY,
} from '@/features/dashboard/constants'
import { processChartData } from '@/features/dashboard/lib'
import type {
ConsumptionDistributionChartType,
QuotaDataItem,
} from '@/features/dashboard/types'
import { useThemeRadiusPx } from '@/lib/theme-radius'
import type { TimeGranularity } from '@/lib/time'
import { VCHART_OPTION } from '@/lib/vchart'
let themeManagerPromise: Promise<
(typeof import('@visactor/vchart'))['ThemeManager']
> | null = null
interface ConsumptionDistributionChartProps {
data: QuotaDataItem[]
loading?: boolean
timeGranularity?: TimeGranularity
defaultChartType?: ConsumptionDistributionChartType
}
const CHART_TYPE_ICONS: Record<
ConsumptionDistributionChartType,
typeof BarChart3
> = {
bar: BarChart3,
area: AreaChart,
}
export function ConsumptionDistributionChart(
props: ConsumptionDistributionChartProps
) {
const { t } = useTranslation()
const { resolvedTheme } = useTheme()
const { customization } = useThemeCustomization()
const chartRadius = useThemeRadiusPx(
'--radius-md',
`${customization.preset}:${customization.radius}`
)
const [chartType, setChartType] = useState(
props.defaultChartType ?? 'bar'
)
const [themeReady, setThemeReady] = useState(false)
const themeManagerRef = useRef<
(typeof import('@visactor/vchart'))['ThemeManager'] | null
>(null)
const timeGranularity = props.timeGranularity ?? DEFAULT_TIME_GRANULARITY
useEffect(() => {
if (props.defaultChartType) setChartType(props.defaultChartType)
}, [props.defaultChartType])
useEffect(() => {
const updateTheme = async () => {
setThemeReady(false)
if (!themeManagerPromise) {
themeManagerPromise = import('@visactor/vchart').then(
(m) => m.ThemeManager
)
}
const ThemeManager = await themeManagerPromise
themeManagerRef.current = ThemeManager
ThemeManager.setCurrentTheme(resolvedTheme === 'dark' ? 'dark' : 'light')
setThemeReady(true)
}
updateTheme()
}, [resolvedTheme])
const chartData = useMemo(
() =>
processChartData(
props.loading ? [] : props.data,
timeGranularity,
t,
chartRadius
),
[props.data, props.loading, timeGranularity, t, chartRadius]
)
const spec = chartType === 'bar' ? chartData.spec_line : chartData.spec_area
const specType = typeof spec?.type === 'string' ? spec.type : chartType
const chartKey = [
chartType,
specType,
props.loading ? 'loading' : 'ready',
props.data.length,
resolvedTheme,
customization.preset,
].join('-')
return (