import { useEffect, useState } from 'react' import { formatNumber, formatQuota } from '@/lib/format' import { computeTimeRange } from '@/lib/time' import { useAuthStore } from '@/stores/auth-store' import { Skeleton } from '@/components/ui/skeleton' import { getUserQuotaDates } from '@/features/dashboard/api' import { useModelStatCardsConfig } from '@/features/dashboard/hooks/use-dashboard-config' import { buildQueryParams, calculateDashboardStats, getDefaultDays, } from '@/features/dashboard/lib' import type { QuotaDataItem, DashboardFilters, } from '@/features/dashboard/types' interface LogStatCardsProps { filters?: DashboardFilters onDataUpdate?: (data: QuotaDataItem[], loading: boolean) => void } export function LogStatCards(props: LogStatCardsProps) { const statCardsConfig = useModelStatCardsConfig() const user = useAuthStore((state) => state.auth.user) const isAdmin = !!(user?.role && user.role >= 10) const [stats, setStats] = useState<{ totalQuota: number totalCount: number totalTokens: number } | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) const [timeRangeMinutes, setTimeRangeMinutes] = useState(0) const { filters, onDataUpdate } = props useEffect(() => { const abortController = new AbortController() // eslint-disable-next-line react-hooks/set-state-in-effect setLoading(true) setError(false) onDataUpdate?.([], true) const timeRange = computeTimeRange( getDefaultDays(filters?.time_granularity), filters?.start_timestamp, filters?.end_timestamp ) const timeDiff = (timeRange.end_timestamp - timeRange.start_timestamp) / 60 setTimeRangeMinutes(timeDiff) getUserQuotaDates(buildQueryParams(timeRange, filters), isAdmin) .then((res) => { if (abortController.signal.aborted) return const data = res?.data || [] setStats(calculateDashboardStats(data)) onDataUpdate?.(data, false) }) .catch(() => { if (abortController.signal.aborted) return setStats(null) setError(true) onDataUpdate?.([], false) }) .finally(() => { if (!abortController.signal.aborted) { setLoading(false) } }) return () => { abortController.abort() } }, [filters, isAdmin, onDataUpdate]) const adaptedStats = { rpm: stats?.totalCount ?? 0, quota: stats?.totalQuota ?? 0, tpm: stats?.totalTokens ?? 0, } const items = statCardsConfig.map((config) => ({ title: config.title, value: config.key === 'quota' ? formatQuota(config.getValue(adaptedStats, timeRangeMinutes)) : formatNumber(config.getValue(adaptedStats, timeRangeMinutes)), desc: config.description, icon: config.icon, })) return (
{items.map((it, idx) => { const Icon = it.icon return (
{it.title}
{loading ? (
) : error ? ( <>
--
{it.desc}
) : ( <>
{it.value}
{it.desc}
)}
) })}
) }