import { useState } from 'react'; import { Button, Tooltip } from 'antd'; import { Typography } from '@signozhq/ui/typography'; import cx from 'classnames'; import { cloneDeep, pullAt } from 'lodash-es'; import { Plus } from '@signozhq/icons'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { QueryFunction } from 'types/api/v5/queryRange'; import { DataSource, QueryFunctionsTypes } from 'types/common/queryBuilder'; import { normalizeFunctionName } from 'utils/functionNameNormalizer'; import Function from './Function'; import { toFloat64 } from './utils'; import './QueryFunctions.styles.scss'; const defaultMetricFunctionStruct: QueryFunction = { name: QueryFunctionsTypes.CUTOFF_MIN as any, args: [], }; const defaultLogFunctionStruct: QueryFunction = { name: QueryFunctionsTypes.TIME_SHIFT as any, args: [], }; interface QueryFunctionsProps { query: IBuilderQuery; queryFunctions: QueryFunction[]; onChange: (functions: QueryFunction[]) => void; maxFunctions: number; } // SVG component function FunctionIcon({ fillColor = 'white', className, }: { fillColor: string; className: string; }): JSX.Element { return ( ); } export default function QueryFunctions({ query, queryFunctions, onChange, maxFunctions = 3, }: QueryFunctionsProps): JSX.Element { const [functions, setFunctions] = useState( queryFunctions.map((func) => ({ ...func, name: normalizeFunctionName(func.name) as any, })), ); const hasAnomalyFunction = functions.some((func) => func.name === 'anomaly'); const hasFunctions = functions.length > 0; const handleAddNewFunction = (): void => { const defaultFunctionStruct = query.dataSource === DataSource.LOGS ? defaultLogFunctionStruct : defaultMetricFunctionStruct; const updatedFunctionsArr = [ ...functions, { ...defaultFunctionStruct, }, ]; const functionsCopy = cloneDeep(updatedFunctionsArr); const anomalyFuncIndex = functionsCopy.findIndex( (func) => func.name === 'anomaly', ); if (anomalyFuncIndex !== -1) { const anomalyFunc = functionsCopy[anomalyFuncIndex]; functionsCopy.splice(anomalyFuncIndex, 1); functionsCopy.push(anomalyFunc); } setFunctions(functionsCopy); onChange(functionsCopy); }; const handleDeleteFunction = ( queryFunction: QueryFunction, index: number, ): void => { const clonedFunctions = cloneDeep(functions); pullAt(clonedFunctions, index); setFunctions(clonedFunctions); onChange(clonedFunctions); }; const handleUpdateFunctionName = ( func: QueryFunction, index: number, value: string, ): void => { const updateFunctions = cloneDeep(functions); if (updateFunctions && updateFunctions.length > 0 && updateFunctions[index]) { // Normalize function name from backend response to match frontend expectations const normalizedValue = normalizeFunctionName(value); updateFunctions[index].name = normalizedValue as any; setFunctions(updateFunctions); onChange(updateFunctions); } }; const handleUpdateFunctionArgs = ( func: QueryFunction, index: number, value: string, ): void => { const updateFunctions = cloneDeep(functions); if (updateFunctions && updateFunctions.length > 0 && updateFunctions[index]) { updateFunctions[index].args = [ { value: updateFunctions[index].name === QueryFunctionsTypes.TIME_SHIFT ? toFloat64(value) : value, }, ]; setFunctions(updateFunctions); onChange(updateFunctions); } }; return (
{functions.map((func, index) => ( ))}
= 3 ? ( `Functions are in early access. You can add a maximum of ${ hasAnomalyFunction ? 2 : 3 } function as of now.` ) : (
Add new function {' '}
Learn more
) } placement="right" >
); }