'use client'
import dayjs from 'dayjs'
import { formatBytes } from 'lib/helpers'
import { useState } from 'react'
import { cn, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui'
import { CHART_COLORS, DateTimeFormats } from './Charts.constants'
import { numberFormatter } from './Charts.utils'
export interface ReportAttributes {
id?: string
titleTooltip?: string
label: string
attributes?: (MultiAttribute | false)[]
defaultChartStyle?: 'bar' | 'line' | 'stackedAreaLine'
hide?: boolean
availableIn?: string[]
hideChartType?: boolean
format?: string
className?: string
showTooltip?: boolean
showLegend?: boolean
showTotal?: boolean
showMaxValue?: boolean
valuePrecision?: number
docsUrl?: string
syncId?: string
showGrid?: boolean
YAxisProps?: {
width?: number
tickFormatter?: (value: any) => string
}
hideHighlightedValue?: boolean
}
export type Provider = 'infra-monitoring' | 'daily-stats' | 'mock' | 'reference-line' | 'logs'
export type MultiAttribute = {
attribute: string
provider?: Provider
label?: string
color?: {
light: string
dark: string
}
statusCode?: string
grantType?: string
providerType?: string
stackId?: string
format?: string
description?: string
docsLink?: string
isMaxValue?: boolean
type?: 'line' | 'area-bar'
omitFromTotal?: boolean
tooltip?: string
customValue?: number
[key: string]: any
/**
* Manipulate the value of the attribute before it is displayed on the chart.
* @param value - The value of the attribute.
* @returns The manipulated value.
*/
manipulateValue?: (value: number) => number
/**
* Create a virtual attribute by combining values from other attributes.
* Expression should use attribute names and basic math operators (+, -, *, /).
* Example: 'disk_fs_used - pg_database_size - disk_fs_used_wal'
*/
combine?: string
id?: string
value?: number
isReferenceLine?: boolean
strokeDasharray?: string
className?: string
hide?: boolean
enabled?: boolean
}
interface CustomIconProps {
color: string
}
const CustomIcon = ({ color }: CustomIconProps) => (
)
const MaxConnectionsIcon = ({ color }: { color?: string }) => (
)
interface TooltipProps {
active?: boolean
payload?: any[]
label?: string | number
attributes?: MultiAttribute[]
isPercentage?: boolean
format?: string | ((value: unknown) => string)
valuePrecision?: number
showMaxValue?: boolean
showTotal?: boolean
isActiveHoveredChart?: boolean
}
const isMaxAttribute = (attributes?: MultiAttribute[]) => attributes?.find((a) => a.isMaxValue)
/**
* Calculate the total aggregate of the chart values
* by summing the values of the attributes
* that are not in the `ignoreAttributes` array
*/
export const calculateTotalChartAggregate = (
payload: { dataKey: string; value: number }[],
ignoreAttributes?: string[]
) =>
payload
?.filter((p) => !ignoreAttributes?.includes(p.dataKey))
.reduce((acc, curr) => acc + curr.value, 0)
export const CustomTooltip = ({
active,
payload,
label,
attributes,
isPercentage,
format,
valuePrecision,
showTotal,
isActiveHoveredChart,
}: TooltipProps) => {
if (active && payload && payload.length) {
/**
* Depending on the data source, the timestamp key could be 'timestamp' or 'period_start'
*/
const firstItem = payload[0].payload
const timestampKey = firstItem?.hasOwnProperty('timestamp') ? 'timestamp' : 'period_start'
const timestamp = payload[0].payload[timestampKey]
const maxValueAttribute = isMaxAttribute(attributes)
const maxValueData =
maxValueAttribute && payload?.find((p: any) => p.dataKey === maxValueAttribute.attribute)
const maxValue = maxValueData?.value
const isRamChart =
!payload?.some((p: any) => p.dataKey.toLowerCase() === 'ram_usage') &&
payload?.some((p: any) => p.dataKey.toLowerCase().includes('ram_'))
const isDBSizeChart =
payload?.some((p: any) => p.dataKey.toLowerCase().includes('disk_fs_')) ||
payload?.some((p: any) => p.dataKey.toLowerCase().includes('pg_database_size'))
const isNetworkChart = payload?.some((p: any) => p.dataKey.toLowerCase().includes('network_'))
const isBytesFormat = format === 'bytes' || format === 'bytes-per-second'
const shouldFormatBytes = isBytesFormat || isRamChart || isDBSizeChart || isNetworkChart
const byteUnitSuffix = format === 'bytes-per-second' ? '/s' : ''
const attributesToIgnore =
attributes?.filter((a) => a.omitFromTotal)?.map((a) => a.attribute) ?? []
const referenceLines =
attributes
?.filter((attribute: MultiAttribute) => attribute?.provider === 'reference-line')
?.map((a: MultiAttribute) => a.attribute) ?? []
const attributesToIgnoreFromTotal = [
...attributesToIgnore,
...referenceLines,
...(maxValueAttribute?.attribute ? [maxValueAttribute.attribute] : []),
]
const localTimeZone = dayjs.tz.guess()
const total = showTotal && calculateTotalChartAggregate(payload, attributesToIgnoreFromTotal)
const getIcon = (color: string, isMax: boolean) =>
isMax ?
{localTimeZone}
{dayjs(timestamp).format(DateTimeFormats.FULL_SECONDS)}