import { useParams } from 'common' import { Activity, BarChartIcon, GitCommitHorizontalIcon, InfoIcon, SquareTerminal, } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { Badge, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { formatPercentage, numberFormatter } from './Charts.utils' import { useChartHoverState } from './useChartHoverState' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { formatDateTime, useFormatDateTime } from '@/lib/datetime' import { formatBytes, formatBytesMinMB } from '@/lib/helpers' export interface ChartHeaderProps { title?: string format?: string | ((value: unknown) => string) customDateFormat?: string minimalHeader?: boolean displayDateInUtc?: boolean highlightedLabel?: number | string | any | null highlightedValue?: number | string | any | null hideHighlightedValue?: boolean hideHighlightedLabel?: boolean hideHighlightArea?: boolean hideChartType?: boolean chartStyle?: string onChartStyleChange?: (style: string) => void showMaxValue?: boolean setShowMaxValue?: (value: boolean) => void docsUrl?: string syncId?: string data?: any[] xAxisKey?: string yAxisKey?: string xAxisIsDate?: boolean valuePrecision?: number shouldFormatBytes?: boolean isNetworkChart?: boolean isMemoryChart?: boolean attributes?: any[] sql?: string titleTooltip?: string showNewBadge?: boolean } export const ChartHeader = ({ format, highlightedValue, highlightedLabel, hideHighlightedValue = false, hideHighlightedLabel = false, hideHighlightArea = false, title, minimalHeader = false, hideChartType = false, chartStyle = 'bar', onChartStyleChange, showMaxValue = false, setShowMaxValue, docsUrl, syncId, data, xAxisKey, yAxisKey, xAxisIsDate = true, displayDateInUtc, customDateFormat, valuePrecision = 2, shouldFormatBytes = false, isNetworkChart = false, attributes, sql, titleTooltip, showNewBadge, isMemoryChart, }: ChartHeaderProps) => { const { ref } = useParams() const { hoveredIndex, isHovered } = useChartHoverState(syncId || 'default') const [localHighlightedValue, setLocalHighlightedValue] = useState(highlightedValue) const [localHighlightedLabel, setLocalHighlightedLabel] = useState(highlightedLabel) // When `displayDateInUtc` is set the chart explicitly wants UTC labels. // Otherwise honour the user's selected timezone via the picker. const formatPickerDate = useFormatDateTime() const formatHighlightedValue = (value: any) => { if (typeof value !== 'number') { return value } if (typeof format === 'function') { return format(value) } if (shouldFormatBytes) { const bytesValue = isNetworkChart ? Math.abs(value) : value return isMemoryChart ? formatBytesMinMB(bytesValue, valuePrecision) : formatBytes(bytesValue, valuePrecision) } if (format === '%') { return formatPercentage(value, valuePrecision) } const formattedValue = numberFormatter(value, valuePrecision) if (typeof format === 'string' && format) { return `${formattedValue} ${format}` } return formattedValue } useEffect(() => { if (syncId && hoveredIndex !== null && isHovered && data && xAxisKey && yAxisKey) { const activeDataPoint = data[hoveredIndex] if (activeDataPoint) { // For stacked charts, we need to calculate the total of all attributes // that should be included in the total (excluding reference lines, max values, etc.) let newValue = activeDataPoint[yAxisKey] // If this is a stacked chart with multiple attributes, calculate the total if (attributes && attributes.length > 1) { const attributesToIgnore = attributes ?.filter((a) => a.omitFromTotal || a.isMaxValue || a.provider === 'reference-line') ?.map((a) => a.attribute) ?? [] const totalValue = Object.entries(activeDataPoint) .filter(([key, value]) => { // Include only numeric values that are not in the ignore list return ( typeof value === 'number' && key !== 'timestamp' && key !== 'period_start' && !attributesToIgnore.includes(key) && attributes.some((attr) => attr.attribute === key && attr.enabled !== false) ) }) .reduce((sum, [_, value]) => sum + (value as number), 0) newValue = totalValue } setLocalHighlightedValue(newValue) // Update highlighted label based on sync state let newLabel = highlightedLabel if (xAxisIsDate && activeDataPoint[xAxisKey]) { const value = activeDataPoint[xAxisKey] as number | string const fmt = customDateFormat || 'YYYY-MM-DD HH:mm:ss' newLabel = displayDateInUtc ? formatDateTime(value, { tz: 'UTC', format: fmt }) : formatPickerDate(value, fmt) } else if (activeDataPoint[xAxisKey]) { newLabel = activeDataPoint[xAxisKey] } setLocalHighlightedLabel(newLabel) } } else { // Reset to original values when not syncing setLocalHighlightedValue(highlightedValue) setLocalHighlightedLabel(highlightedLabel) } }, [ hoveredIndex, isHovered, syncId, data, xAxisKey, yAxisKey, xAxisIsDate, displayDateInUtc, customDateFormat, highlightedValue, highlightedLabel, attributes, formatPickerDate, ]) const chartTitle = (