import { useEffect, useMemo, useState, useRef, useCallback } from 'react' import { useQuery } from '@tanstack/react-query' import { VChart } from '@visactor/react-vchart' import { Users, Loader2 } from 'lucide-react' import { useTranslation } from 'react-i18next' import { getRollingDateRange, type TimeGranularity } from '@/lib/time' import { VCHART_OPTION } from '@/lib/vchart' import { useTheme } from '@/context/theme-provider' import { Skeleton } from '@/components/ui/skeleton' import { getUserQuotaDataByUsers } from '@/features/dashboard/api' import { TIME_GRANULARITY_OPTIONS, TIME_RANGE_PRESETS, } from '@/features/dashboard/constants' import { getDefaultDays, getSavedGranularity, saveGranularity, processUserChartData, } from '@/features/dashboard/lib' import type { ProcessedUserChartData } from '@/features/dashboard/types' let themeManagerPromise: Promise< (typeof import('@visactor/vchart'))['ThemeManager'] > | null = null const USER_CHARTS: { value: string labelKey: string specKey: keyof ProcessedUserChartData }[] = [ { value: 'rank', labelKey: 'User Consumption Ranking', specKey: 'spec_user_rank', }, { value: 'trend', labelKey: 'User Consumption Trend', specKey: 'spec_user_trend', }, ] const TOP_USER_LIMIT_OPTIONS = [5, 10, 20, 50] export function UserCharts() { const { t } = useTranslation() const { resolvedTheme } = useTheme() const [themeReady, setThemeReady] = useState(false) const themeManagerRef = useRef< (typeof import('@visactor/vchart'))['ThemeManager'] | null >(null) const [timeGranularity, setTimeGranularity] = useState(() => getSavedGranularity() ) const [selectedRange, setSelectedRange] = useState(() => getDefaultDays(timeGranularity) ) const [topUserLimit, setTopUserLimit] = useState(10) const [timeRange, setTimeRange] = useState(() => { const days = getDefaultDays(timeGranularity) const { start, end } = getRollingDateRange(days) return { start_timestamp: Math.floor(start.getTime() / 1000), end_timestamp: Math.floor(end.getTime() / 1000), } }) const handleRangeChange = useCallback((days: number) => { setSelectedRange(days) const { start, end } = getRollingDateRange(days) setTimeRange({ start_timestamp: Math.floor(start.getTime() / 1000), end_timestamp: Math.floor(end.getTime() / 1000), }) }, []) const handleGranularityChange = useCallback( (g: TimeGranularity) => { setTimeGranularity(g) saveGranularity(g) const days = getDefaultDays(g) if (days !== selectedRange) { handleRangeChange(days) } }, [selectedRange, handleRangeChange] ) 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 { data: userData, isLoading } = useQuery({ queryKey: ['dashboard', 'user-quota', timeRange], queryFn: () => getUserQuotaDataByUsers(timeRange), select: (res) => (res.success ? res.data : []), staleTime: 60_000, }) const chartData = useMemo( () => processUserChartData( isLoading ? [] : (userData ?? []), timeGranularity, t, topUserLimit ), [userData, isLoading, timeGranularity, t, topUserLimit] ) return (
{TIME_RANGE_PRESETS.map((preset) => ( ))}
{TIME_GRANULARITY_OPTIONS.map((opt) => ( ))}
{t('Top Users')} {TOP_USER_LIMIT_OPTIONS.map((limit) => ( ))}
{isLoading && ( )}
{USER_CHARTS.map((chart) => { const spec = chartData[chart.specKey] return (
{t(chart.labelKey)}
{isLoading ? ( ) : ( themeReady && spec && ( ) )}
) })}
) }