feat: metrics qb

This commit is contained in:
Yunus M 2025-05-16 02:48:03 +05:30 committed by ahrefabhi
parent 353b1efe95
commit ba73c72b76
15 changed files with 428 additions and 28 deletions

View File

@ -6,12 +6,11 @@
.label {
color: var(--bg-vanilla-400);
font-size: 11px;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 18px; /* 128.571% */
letter-spacing: 0.56px;
text-transform: uppercase;
max-width: 150px;
min-width: 120px;
@ -61,6 +60,14 @@
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
}
.label {
border-left: none;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
}
}

View File

@ -0,0 +1,100 @@
.metrics-aggregate-section {
display: flex;
flex-direction: column;
gap: 16px;
margin: 4px 0;
.metrics-time-aggregation-section {
display: flex;
flex-direction: column;
gap: 4px;
.metrics-time-aggregation-section-title {
display: flex;
align-items: center;
gap: 6px;
color: var(--Slate-50, #62687c);
font-family: 'Geist Mono';
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 18px; /* 150% */
letter-spacing: 0.48px;
}
}
.metrics-space-aggregation-section {
display: flex;
flex-direction: column;
gap: 4px;
.metrics-space-aggregation-section-title {
display: flex;
align-items: center;
gap: 6px;
color: var(--Slate-50, #62687c);
font-family: 'Geist Mono';
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 18px; /* 150% */
letter-spacing: 0.48px;
}
}
.metrics-aggregation-section-content {
display: flex;
flex-direction: row;
gap: 8px;
.metrics-aggregation-section-content-item {
display: flex;
align-items: center;
gap: 10px;
.metrics-aggregation-section-content-item-label {
color: var(--Vanilla-400, #c0c1c3);
font-family: 'Geist Mono';
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
}
.metrics-aggregation-section-content-item-value {
min-width: 320px;
.ant-select {
width: 100%;
}
.ant-select-selector {
border-radius: 2px;
border: 1.005px solid var(--Slate-400, #1d212d);
background: var(--Ink-300, #16181d);
}
}
}
.space-aggregation-select {
min-width: 480px !important;
}
}
}
.metrics-operators-select {
border-radius: 2px;
border: 1.005px solid var(--Slate-400, #1d212d);
background: var(--Ink-300, #16181d);
color: var(--Vanilla-400, #c0c1c3);
font-family: 'Geist Mono';
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
}

View File

@ -0,0 +1,133 @@
import './MetricsAggegateSection.styles.scss';
import { Tooltip } from 'antd';
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
import SpaceAggregationOptions from 'container/QueryBuilder/components/SpaceAggregationOptions/SpaceAggregationOptions';
import { GroupByFilter, OperatorsSelect } from 'container/QueryBuilder/filters';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { Info } from 'lucide-react';
import { memo, useCallback } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
const MetricsAggregateSection = memo(function MetricsAggregateSection({
query,
index,
version,
}: {
query: IBuilderQuery;
index: number;
version: string;
}): JSX.Element {
const panelType = PANEL_TYPES.LIST; // hardcoded for now
const {
operators,
spaceAggregationOptions,
handleChangeQueryData,
handleChangeOperator,
handleSpaceAggregationChange,
} = useQueryOperations({
index,
query,
entityVersion: version,
});
// console.log('operators', cloneDeep(operators));
const handleChangeGroupByKeys = useCallback(
(value: IBuilderQuery['groupBy']) => {
handleChangeQueryData('groupBy', value);
},
[handleChangeQueryData],
);
const disableOperatorSelector =
!query?.aggregateAttribute.key || query?.aggregateAttribute.key === '';
return (
<div className="metrics-aggregate-section">
<div className="metrics-time-aggregation-section">
<div className="metrics-time-aggregation-section-title">
AGGREGATE BY TIME{' '}
<Tooltip title="AGGREGATE BY TIME">
<Info size={12} />
</Tooltip>
</div>
<div className="metrics-aggregation-section-content">
<div className="metrics-aggregation-section-content-item">
<div className="metrics-aggregation-section-content-item-label">
Align with
</div>
<div className="metrics-aggregation-section-content-item-value">
<OperatorsSelect
value={query.aggregateOperator}
onChange={handleChangeOperator}
operators={operators}
className="metrics-operators-select"
/>
</div>
</div>
<div className="metrics-aggregation-section-content-item">
<div className="metrics-aggregation-section-content-item-label">
aggregated every
</div>
<div className="metrics-aggregation-section-content-item-value">
<InputWithLabel
label="Seconds"
placeholder="Enter a number"
labelAfter
/>
</div>
</div>
</div>
</div>
<div className="metrics-space-aggregation-section">
<div className="metrics-space-aggregation-section-title">
AGGREGATE LABELS
<Tooltip title="AGGREGATE LABELS">
<Info size={12} />
</Tooltip>
</div>
<div className="metrics-aggregation-section-content">
<div className="metrics-aggregation-section-content-item">
<div className="metrics-aggregation-section-content-item-value space-aggregation-select">
<SpaceAggregationOptions
panelType={panelType}
key={`${panelType}${query.spaceAggregation}${query.timeAggregation}`}
aggregatorAttributeType={
query?.aggregateAttribute.type as ATTRIBUTE_TYPES
}
selectedValue={query.spaceAggregation}
disabled={disableOperatorSelector}
onSelect={handleSpaceAggregationChange}
operators={spaceAggregationOptions}
qbVersion="v3"
/>
</div>
</div>
<div className="metrics-aggregation-section-content-item">
<div className="metrics-aggregation-section-content-item-label">by</div>
<div className="metrics-aggregation-section-content-item-value">
<GroupByFilter
disabled={!query.aggregateAttribute.key}
query={query}
onChange={handleChangeGroupByKeys}
/>
</div>
</div>
</div>
</div>
</div>
);
});
export default MetricsAggregateSection;

View File

@ -0,0 +1,42 @@
.metrics-select-container {
margin-bottom: 8px;
.ant-select-selector {
width: 100%;
border-radius: 2px;
border: 1px solid #1d212d !important;
background: #16181d;
color: #fff;
font-family: 'Geist Mono';
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
min-height: 36px;
}
.ant-select-dropdown {
border-radius: 4px;
border: 1px solid var(--Slate-400, #1d212d);
background: linear-gradient(
139deg,
rgba(18, 19, 23, 0.8) 0%,
rgba(18, 19, 23, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
.ant-select-item {
color: #fff;
font-family: 'Geist Mono';
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
&:hover {
background: rgba(171, 189, 255, 0.04) !important;
}
}
}
}

View File

@ -0,0 +1,28 @@
import './MetricsSelect.styles.scss';
import { AggregatorFilter } from 'container/QueryBuilder/filters';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { memo } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
export const MetricsSelect = memo(function MetricsSelect({
query,
index,
version,
}: {
query: IBuilderQuery;
index: number;
version: string;
}): JSX.Element {
const { handleChangeAggregatorAttribute } = useQueryOperations({
index,
query,
entityVersion: version,
});
return (
<div className="metrics-select-container">
<AggregatorFilter onChange={handleChangeAggregatorAttribute} query={query} />
</div>
);
});

View File

@ -9,7 +9,9 @@ import {
import { javascript } from '@codemirror/lang-javascript';
import { copilot } from '@uiw/codemirror-theme-copilot';
import CodeMirror, { EditorView, keymap } from '@uiw/react-codemirror';
import { Button } from 'antd';
import { useQueryBuilderV2Context } from 'components/QueryBuilderV2/QueryBuilderV2Context';
import { X } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
const havingOperators = [
@ -52,7 +54,7 @@ const conjunctions = [
{ label: 'OR', value: 'OR' },
];
function HavingFilter(): JSX.Element {
function HavingFilter({ onClose }: { onClose: () => void }): JSX.Element {
const { aggregationOptions } = useQueryBuilderV2Context();
const [input, setInput] = useState('');
@ -170,6 +172,11 @@ function HavingFilter(): JSX.Element {
}}
ref={editorRef}
/>
<Button
className="close-btn periscope-btn ghost"
icon={<X size={16} />}
onClick={onClose}
/>
</div>
</div>
);

View File

@ -30,9 +30,13 @@
.query-aggregation-select-container {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
.query-aggregation-select-editor {
border-radius: 2px;
flex: 1;
.cm-content {
padding: 0;
@ -50,6 +54,8 @@
.cm-content {
border-radius: 2px;
border: 1px solid var(--Slate-400, #1d212d);
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
padding: 0px !important;
background-color: #121317 !important;
@ -168,4 +174,16 @@
}
}
}
.close-btn {
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
height: 38px;
width: 38px;
border-left: none;
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
}

View File

@ -1,10 +1,17 @@
import './QueryAggregation.styles.scss';
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
import { DataSource } from 'types/common/queryBuilder';
import QueryAggregationSelect from './QueryAggregationSelect';
function QueryAggregationOptions(): JSX.Element {
function QueryAggregationOptions({
source,
}: {
source: DataSource;
}): JSX.Element {
console.log('source', source);
return (
<div className="query-aggregation-container">
<QueryAggregationSelect />

View File

@ -1,32 +1,51 @@
import './QueryBuilderV2.styles.scss';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import MetricsAggregateSection from './Metrics/MerticsAggregateSection/MetricsAggregateSection';
import { MetricsSelect } from './Metrics/MetricsSelect/MetricsSelect';
import QueryAddOns from './QueryAddOns/QueryAddOns';
import QueryAggregation from './QueryAggregation/QueryAggregation';
import { QueryBuilderV2Provider } from './QueryBuilderV2Context';
import QuerySearch from './QuerySearch/QuerySearch';
function QueryBuilderV2Main(): JSX.Element {
const { currentQuery } = useQueryBuilder();
type QueryBuilderV2Props = {
source: DataSource;
query: IBuilderQuery;
};
function QueryBuilderV2Main({
source,
query,
}: QueryBuilderV2Props): JSX.Element {
const isMetricsDataSource = query.dataSource === DataSource.METRICS;
return (
<div className="query-builder-v2">
{isMetricsDataSource && (
<MetricsSelect query={query} index={0} version="v4" />
)}
<QuerySearch />
<QueryAggregation />
<QueryAddOns
query={currentQuery.builder.queryData[0]}
version="v3"
isListViewPanel={false}
/>
{isMetricsDataSource ? (
<MetricsAggregateSection query={query} index={0} version="v4" />
) : (
<QueryAggregation source={source} />
)}
<QueryAddOns query={query} version="v3" isListViewPanel={false} />
</div>
);
}
function QueryBuilderV2(): JSX.Element {
function QueryBuilderV2(props: QueryBuilderV2Props): JSX.Element {
const { source, query } = props;
return (
<QueryBuilderV2Provider>
<QueryBuilderV2Main />
<QueryBuilderV2Main source={source} query={query} />
</QueryBuilderV2Provider>
);
}

View File

@ -1,6 +1,5 @@
.code-mirror-where-clause {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;

View File

@ -109,7 +109,9 @@ function LogExplorerQuerySection({
/>
)}
{selectedView === SELECTED_VIEWS.QUERY_BUILDER_V2 && <QueryBuilderV2 />}
{selectedView === SELECTED_VIEWS.QUERY_BUILDER_V2 && (
<QueryBuilderV2 source={DataSource.LOGS} query={query} />
)}
</>
);
}

View File

@ -3,6 +3,7 @@ import './Explorer.styles.scss';
import * as Sentry from '@sentry/react';
import { Switch } from 'antd';
import logEvent from 'api/common/logEvent';
import QueryBuilderV2 from 'components/QueryBuilderV2/QueryBuilderV2';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
@ -20,7 +21,7 @@ import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToD
import { v4 as uuid } from 'uuid';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import QuerySection from './QuerySection';
// import QuerySection from './QuerySection';
import TimeSeries from './TimeSeries';
import { ExplorerTabs } from './types';
import { splitQueryIntoOneChartPerQuery } from './utils';
@ -118,7 +119,11 @@ function Explorer(): JSX.Element {
<RightToolbarActions onStageRunQuery={handleRunQuery} />
</div>
</div>
<QuerySection />
{/* <QuerySection /> */}
<QueryBuilderV2
source={DataSource.METRICS}
query={currentQuery.builder.queryData[0]}
/>
{/* TODO: Enable once we have resolved all related metrics issues */}
{/* <Button.Group className="explore-tabs">
<Button

View File

@ -10,6 +10,7 @@ interface SpaceAggregationOptionsProps {
disabled: boolean;
onSelect: (value: string) => void;
operators: any[];
qbVersion?: string;
}
export default function SpaceAggregationOptions({
@ -19,8 +20,10 @@ export default function SpaceAggregationOptions({
disabled,
onSelect,
operators,
qbVersion,
}: SpaceAggregationOptionsProps): JSX.Element {
const placeHolderText = panelType === PANEL_TYPES.VALUE ? 'Sum' : 'Sum By';
const placeHolderText =
panelType === PANEL_TYPES.VALUE || qbVersion === 'v3' ? 'Sum' : 'Sum By';
const [defaultValue, setDefaultValue] = useState(
selectedValue || placeHolderText,
);
@ -58,10 +61,15 @@ export default function SpaceAggregationOptions({
>
{operators.map((operator) => (
<Select.Option key={operator.value} value={operator.value}>
{operator.label} {panelType !== PANEL_TYPES.VALUE ? ' By' : ''}
{operator.label}{' '}
{panelType !== PANEL_TYPES.VALUE && qbVersion === 'v2' ? ' By' : ''}
</Select.Option>
))}
</Select>
</div>
);
}
SpaceAggregationOptions.defaultProps = {
qbVersion: 'v2',
};

View File

@ -9,6 +9,7 @@ export const OperatorsSelect = memo(function OperatorsSelect({
operators,
value,
onChange,
className,
...props
}: OperatorsSelectProps): JSX.Element {
return (
@ -20,6 +21,7 @@ export const OperatorsSelect = memo(function OperatorsSelect({
showSearch
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
popupClassName={className}
/>
);
});

View File

@ -23,7 +23,8 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
import { isEmpty } from 'lodash-es';
import { isEmpty, isEqual } from 'lodash-es';
import cloneDeep from 'lodash-es/cloneDeep';
import { useCallback, useEffect, useState } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
@ -181,6 +182,11 @@ export const useQueryOperations: UseQueryOperations = ({
break;
}
console.log(
'newOperators handleMetricAggregateAtributeTypes',
cloneDeep(newOperators),
);
setOperators(newOperators);
},
[panelType],
@ -194,6 +200,9 @@ export const useQueryOperations: UseQueryOperations = ({
having: [],
};
console.log('newQuery', newQuery.dataSource);
console.log('entityVersion', entityVersion);
if (
newQuery.dataSource === DataSource.METRICS &&
entityVersion === ENTITY_VERSION_V4
@ -267,6 +276,8 @@ export const useQueryOperations: UseQueryOperations = ({
aggregateOperator: newOperators[0].value,
};
console.log('newOperators handleChangeDataSource', cloneDeep(newOperators));
setOperators(newOperators);
handleSetQueryData(index, newQuery);
},
@ -358,13 +369,23 @@ export const useQueryOperations: UseQueryOperations = ({
panelType: panelType || PANEL_TYPES.TIME_SERIES,
});
if (JSON.stringify(operators) === JSON.stringify(initialOperators)) return;
setOperators(initialOperators);
if (
!operators ||
operators.length === 0 ||
!isEqual(operators, initialOperators)
) {
setOperators(initialOperators);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataSource, initialDataSource, panelType, operators, entityVersion]);
}, [
dataSource,
initialDataSource,
panelType,
entityVersion,
query,
operators,
handleMetricAggregateAtributeTypes,
]);
useEffect(() => {
const additionalFilters = getNewListOfAdditionalFilters(dataSource, true);
@ -378,6 +399,8 @@ export const useQueryOperations: UseQueryOperations = ({
setListOfAdditionalFormulaFilters(additionalFilters);
}, [dataSource, aggregateOperator, getNewListOfAdditionalFilters]);
console.log('operators useQueryOperations', cloneDeep(operators));
return {
isTracePanelType,
isMetricsDataSource,