import { useQuery } from '@tanstack/react-query' import { Loader2 } from 'lucide-react' import { useState } from 'react' import { Card, CardContent, cn } from 'ui' import { ReportChartUpsell } from './ReportChartUpsell' import type { ChartHighlightAction } from '@/components/ui/Charts/ChartHighlightActions' import { ComposedChart } from '@/components/ui/Charts/ComposedChart' import type { MultiAttribute } from '@/components/ui/Charts/ComposedChart.utils' import { useChartHighlight } from '@/components/ui/Charts/useChartHighlight' import type { AnalyticsInterval } from '@/data/analytics/constants' import type { ReportConfig } from '@/data/reports/v2/reports.types' import { useFillTimeseriesSorted } from '@/hooks/analytics/useFillTimeseriesSorted' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' export interface ReportChartV2Props { report: ReportConfig projectRef: string startDate: string endDate: string interval: AnalyticsInterval updateDateRange: (from: string, to: string) => void /** * Group ID used to invalidate React Query caches */ queryGroup?: string className?: string syncId?: string filters?: any highlightActions?: ChartHighlightAction[] } // Compute total across entire period over unique attribute keys. // Excludes attributes that are disabled, reference lines, max values, or marked omitFromTotal. export function computePeriodTotal( chartData: Record[], dynamicAttributes: MultiAttribute[] ): number { const attributeKeys = Array.from( new Set( dynamicAttributes .filter( (a) => a?.enabled !== false && a?.provider !== 'reference-line' && !a?.isMaxValue && !a?.omitFromTotal ) .map((a) => a.attribute) ) ) return chartData.reduce((sum: number, row: Record) => { const rowTotal = attributeKeys.reduce((acc: number, key: string) => { const value = row?.[key] return acc + (typeof value === 'number' ? value : 0) }, 0) return sum + rowTotal }, 0) } export const ReportChartV2 = ({ report, projectRef, startDate, endDate, interval, updateDateRange, className, syncId, filters, highlightActions, queryGroup, }: ReportChartV2Props) => { const { data: org } = useSelectedOrganizationQuery() const { getEntitlementSetValues, isLoading: isEntitlementLoading } = useCheckEntitlements( 'observability.dashboard_advanced_metrics', undefined, { enabled: !!report.entitlement } ) const entitledFeatures = getEntitlementSetValues() const isAvailable = !report.entitlement || entitledFeatures.includes(report.entitlement) const canFetch = isAvailable const { data: queryResult, isLoading: isLoadingChart, error, isFetching, } = useQuery({ queryKey: [ 'projects', projectRef, 'report-v2', { reportId: report.id, queryGroup, startDate, endDate, interval, filters }, ], queryFn: async () => { return await report.dataProvider(projectRef, startDate, endDate, interval, filters) }, enabled: Boolean(projectRef && canFetch && isAvailable && !report.hide), refetchOnWindowFocus: false, staleTime: 0, }) const chartData = queryResult?.data || [] const dynamicAttributes = queryResult?.attributes || [] const showSumAsDefaultHighlight = report.showSumAsDefaultHighlight ?? true const headerTotal = showSumAsDefaultHighlight ? computePeriodTotal(chartData, dynamicAttributes) : undefined /** * Depending on the source the timestamp key could be 'timestamp' or 'period_start' */ const firstItem = chartData[0] const timestampKey = firstItem?.hasOwnProperty('timestamp') ? 'timestamp' : 'period_start' const { data: filledChartData } = useFillTimeseriesSorted({ data: chartData, timestampKey, valueKey: dynamicAttributes.map((attr) => attr.attribute), defaultValue: 0, startDate, endDate, minPointsToFill: undefined, interval, }) const [chartStyle, setChartStyle] = useState(report.defaultChartStyle) const chartHighlight = useChartHighlight() if (!isAvailable && !isEntitlementLoading) { return } const isErrorState = error && !isLoadingChart if (report.hide) return null return ( {isLoadingChart ? ( ) : isErrorState ? (

Error loading chart data

) : (
)}
) }