diff --git a/frontend/src/api/metricsExplorer/getMetricDetails.ts b/frontend/src/api/metricsExplorer/getMetricDetails.ts index 2cea70a2af..06ab7e62bb 100644 --- a/frontend/src/api/metricsExplorer/getMetricDetails.ts +++ b/frontend/src/api/metricsExplorer/getMetricDetails.ts @@ -20,7 +20,7 @@ export interface MetricDetails { metric_type: MetricType; description: string; unit: string; - temporality: Temporality; + temporality?: Temporality; }; alerts: MetricDetailsAlert[] | null; dashboards: MetricDetailsDashboard[] | null; diff --git a/frontend/src/api/metricsExplorer/updateMetricMetadata.ts b/frontend/src/api/metricsExplorer/updateMetricMetadata.ts index a739646690..f641772c3e 100644 --- a/frontend/src/api/metricsExplorer/updateMetricMetadata.ts +++ b/frontend/src/api/metricsExplorer/updateMetricMetadata.ts @@ -7,7 +7,7 @@ import { MetricType } from './getMetricsList'; export interface UpdateMetricMetadataProps { description: string; metricType: MetricType; - temporality: Temporality; + temporality?: Temporality; isMonotonic?: boolean; } diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index fd7b2a5aac..f786d5fb24 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -69,6 +69,7 @@ const ROUTES = { METRICS_EXPLORER: '/metrics-explorer/summary', METRICS_EXPLORER_EXPLORER: '/metrics-explorer/explorer', METRICS_EXPLORER_VIEWS: '/metrics-explorer/views', + METRICS_EXPLORER_BASE: '/metrics-explorer', WORKSPACE_ACCESS_RESTRICTED: '/workspace-access-restricted', HOME_PAGE: '/', } as const; diff --git a/frontend/src/container/MetricsExplorer/Explorer/EmptyMetricsSearch.tsx b/frontend/src/container/MetricsExplorer/Explorer/EmptyMetricsSearch.tsx index 66962c7e97..a343342996 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/EmptyMetricsSearch.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/EmptyMetricsSearch.tsx @@ -7,7 +7,7 @@ export default function EmptyMetricsSearch(): JSX.Element { - Please build and run query to see the result + Please build and run a valid query to see the result } /> diff --git a/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss b/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss index b2b70ee034..691cf6eaef 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss +++ b/frontend/src/container/MetricsExplorer/Explorer/Explorer.styles.scss @@ -69,17 +69,22 @@ height: 100%; } + .time-series-view { + min-width: 100%; + width: 100%; + } + .time-series-container { display: flex; gap: 10px; width: 100%; height: fit-content; overflow-y: scroll; - } - .time-series-view { - min-width: 100%; - width: 100%; + .time-series-view { + min-width: 80%; + width: 80%; + } } .related-metrics-container { diff --git a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx index 3bfc8fb37e..3b84ad1bbe 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx @@ -1,9 +1,8 @@ import './Explorer.styles.scss'; import * as Sentry from '@sentry/react'; -import { Button, Switch, Typography } from 'antd'; +import { Switch } from 'antd'; import axios from 'axios'; -import classNames from 'classnames'; import { LOCALSTORAGE } from 'constants/localStorage'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper'; @@ -43,9 +42,7 @@ function Explorer(): JSX.Element { }); const [showOneChartPerQuery, toggleShowOneChartPerQuery] = useState(false); - const [selectedTab, setSelectedTab] = useState( - ExplorerTabs.TIME_SERIES, - ); + const [selectedTab] = useState(ExplorerTabs.TIME_SERIES); const handleToggleShowOneChartPerQuery = (): void => toggleShowOneChartPerQuery(!showOneChartPerQuery); @@ -139,7 +136,8 @@ function Explorer(): JSX.Element { - + {/* TODO: Enable once we have resolved all related metrics issues */} + {/* - {/* TODO: Enable once we have resolved all related metrics issues */} - {/* */} - + + */}
{selectedTab === ExplorerTabs.TIME_SERIES && ( diff --git a/frontend/src/container/MetricsExplorer/Explorer/TimeSeries.tsx b/frontend/src/container/MetricsExplorer/Explorer/TimeSeries.tsx index 8e4ae7d144..11363610b9 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/TimeSeries.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/TimeSeries.tsx @@ -106,29 +106,31 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element { }; return ( -
+ <> - {responseData.map((datapoint, index) => ( -
- -
- ))} -
+
+ {responseData.map((datapoint, index) => ( +
+ +
+ ))} +
+ ); } diff --git a/frontend/src/container/MetricsExplorer/Explorer/utils.tsx b/frontend/src/container/MetricsExplorer/Explorer/utils.tsx index 82f1d885e0..78af296684 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/utils.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/utils.tsx @@ -1,12 +1,37 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { v4 as uuid } from 'uuid'; -export const splitQueryIntoOneChartPerQuery = (query: Query): Query[] => - query.builder.queryData.map((currentQuery) => ({ - ...query, - id: uuid(), - builder: { - ...query.builder, - queryData: [currentQuery], - }, - })); +export const splitQueryIntoOneChartPerQuery = (query: Query): Query[] => { + const queries: Query[] = []; + + query.builder.queryData.forEach((currentQuery) => { + const newQuery = { + ...query, + id: uuid(), + builder: { + ...query.builder, + queryData: [currentQuery], + queryFormulas: [], + }, + }; + queries.push(newQuery); + }); + + query.builder.queryFormulas.forEach((currentFormula) => { + const newQuery = { + ...query, + id: uuid(), + builder: { + ...query.builder, + queryFormulas: [currentFormula], + queryData: query.builder.queryData.map((currentQuery) => ({ + ...currentQuery, + disabled: true, + })), + }, + }; + queries.push(newQuery); + }); + + return queries; +}; diff --git a/frontend/src/container/MetricsExplorer/MetricDetails/Metadata.tsx b/frontend/src/container/MetricsExplorer/MetricDetails/Metadata.tsx index 9d087d1ce1..a8680b6c5d 100644 --- a/frontend/src/container/MetricsExplorer/MetricDetails/Metadata.tsx +++ b/frontend/src/container/MetricsExplorer/MetricDetails/Metadata.tsx @@ -8,7 +8,7 @@ import FieldRenderer from 'container/LogDetailedView/FieldRenderer'; import { DataType } from 'container/LogDetailedView/TableView'; import { useUpdateMetricMetadata } from 'hooks/metricsExplorer/useUpdateMetricMetadata'; import { useNotifications } from 'hooks/useNotifications'; -import { Edit2, Save } from 'lucide-react'; +import { Edit2, Save, X } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; import { @@ -32,7 +32,7 @@ function Metadata({ ] = useState({ metricType: metadata?.metric_type || MetricType.SUM, description: metadata?.description || '', - temporality: metadata?.temporality || Temporality.CUMULATIVE, + temporality: metadata?.temporality, }); const { notifications } = useNotifications(); const { @@ -46,7 +46,10 @@ function Metadata({ const tableData = useMemo( () => metadata - ? Object.keys(metadata) + ? Object.keys({ + ...metadata, + temporality: metadata?.temporality, + }) // Filter out isMonotonic as user input is not required .filter((key) => key !== 'isMonotonic') .map((key) => ({ @@ -101,12 +104,12 @@ function Metadata({ value: key, label: METRIC_TYPE_LABEL_MAP[key as MetricType], }))} - value={metricMetadata.metricType} + defaultValue={metricMetadata.metricType} onChange={(value): void => { - setMetricMetadata({ - ...metricMetadata, + setMetricMetadata((prev) => ({ + ...prev, metricType: value as MetricType, - }); + })); }} /> ); @@ -118,12 +121,12 @@ function Metadata({ value: key, label: key, }))} - value={metricMetadata.temporality} + defaultValue={metricMetadata.temporality} onChange={(value): void => { - setMetricMetadata({ - ...metricMetadata, + setMetricMetadata((prev) => ({ + ...prev, temporality: value as Temporality, - }); + })); }} /> ); @@ -131,13 +134,16 @@ function Metadata({ return ( ] } onChange={(e): void => { - setMetricMetadata({ ...metricMetadata, [field.key]: e.target.value }); + setMetricMetadata((prev) => ({ + ...prev, + [field.key]: e.target.value, + })); }} /> ); @@ -161,7 +167,7 @@ function Metadata({ }, { onSuccess: (response): void => { - if (response?.payload?.success) { + if (response?.statusCode === 200) { notifications.success({ message: 'Metadata updated successfully', }); @@ -192,33 +198,49 @@ function Metadata({ const actionButton = useMemo(() => { if (isEditing) { return ( +
+ + +
+ ); + } + return ( +
- ); - } - return ( - +
); }, [handleSave, isEditing, isUpdatingMetricsMetadata]); diff --git a/frontend/src/container/MetricsExplorer/MetricDetails/MetricDetails.styles.scss b/frontend/src/container/MetricsExplorer/MetricDetails/MetricDetails.styles.scss index 4bc76b05d3..31d72ff197 100644 --- a/frontend/src/container/MetricsExplorer/MetricDetails/MetricDetails.styles.scss +++ b/frontend/src/container/MetricsExplorer/MetricDetails/MetricDetails.styles.scss @@ -102,14 +102,21 @@ color: var(--bg-robin-400); } - .action-button { + .action-menu { display: flex; gap: 4px; align-items: center; align-self: flex-start; - .ant-typography { - font-family: 'Inter'; - color: var(--bg-vanilla-400); + + .action-button { + display: flex; + gap: 4px; + align-items: center; + align-self: flex-start; + .ant-typography { + font-family: 'Inter'; + color: var(--bg-vanilla-400); + } } } @@ -133,7 +140,7 @@ color: var(--bg-vanilla-400); background-color: rgba(171, 189, 255, 0.1); height: 24px; - width: 24px; + min-width: 24px; border-radius: 50%; text-align: center; display: flex; diff --git a/frontend/src/container/MetricsExplorer/MetricDetails/utils.tsx b/frontend/src/container/MetricsExplorer/MetricDetails/utils.tsx index 96c54551be..ec30207db2 100644 --- a/frontend/src/container/MetricsExplorer/MetricDetails/utils.tsx +++ b/frontend/src/container/MetricsExplorer/MetricDetails/utils.tsx @@ -41,7 +41,7 @@ export function formatNumberToCompactFormat(num: number): string { export function determineIsMonotonic( metricType: MetricType, - temporality: Temporality, + temporality?: Temporality, ): boolean { if ( metricType === MetricType.HISTOGRAM || diff --git a/frontend/src/container/MetricsExplorer/Summary/MetricNameSearch.tsx b/frontend/src/container/MetricsExplorer/Summary/MetricNameSearch.tsx index 4de3cc067b..b28db69413 100644 --- a/frontend/src/container/MetricsExplorer/Summary/MetricNameSearch.tsx +++ b/frontend/src/container/MetricsExplorer/Summary/MetricNameSearch.tsx @@ -68,6 +68,7 @@ function MetricNameSearch(): JSX.Element { (selectedMetricName: string): void => { handleChangeQueryData('filters', { items: [ + ...currentQuery.builder.queryData[0].filters.items, { id: 'metric_name', op: 'CONTAINS', @@ -83,7 +84,7 @@ function MetricNameSearch(): JSX.Element { }); setIsPopoverOpen(false); }, - [handleChangeQueryData], + [currentQuery.builder.queryData, handleChangeQueryData], ); const metricNameFilterValues = useMemo( diff --git a/frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx b/frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx index 4009e1edc4..24451c9ecf 100644 --- a/frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx +++ b/frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx @@ -36,6 +36,7 @@ function MetricTypeSearch(): JSX.Element { if (selectedMetricType !== 'all') { handleChangeQueryData('filters', { items: [ + ...currentQuery.builder.queryData[0].filters.items, { id: 'metric_type', op: '=', diff --git a/frontend/src/container/MetricsExplorer/Summary/Summary.tsx b/frontend/src/container/MetricsExplorer/Summary/Summary.tsx index fd09badf47..30a8fdc1d8 100644 --- a/frontend/src/container/MetricsExplorer/Summary/Summary.tsx +++ b/frontend/src/container/MetricsExplorer/Summary/Summary.tsx @@ -1,17 +1,16 @@ import './Summary.styles.scss'; import * as Sentry from '@sentry/react'; -import { initialQueriesMap } from 'constants/queryBuilder'; import { usePageSize } from 'container/InfraMonitoringK8s/utils'; import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList'; import { useGetMetricsTreeMap } from 'hooks/metricsExplorer/useGetMetricsTreeMap'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; -import { DataSource } from 'types/common/queryBuilder'; import { GlobalReducer } from 'types/reducer/globalTime'; import MetricDetails from '../MetricDetails'; @@ -44,7 +43,7 @@ function Summary(): JSX.Element { (state) => state.globalTime, ); - const currentQuery = initialQueriesMap[DataSource.METRICS]; + const { currentQuery } = useQueryBuilder(); const queryFilters = useMemo( () => currentQuery?.builder?.queryData[0]?.filters || { diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx index 47800d9d3b..2c0d3bccba 100644 --- a/frontend/src/container/SideNav/menuItems.tsx +++ b/frontend/src/container/SideNav/menuItems.tsx @@ -161,6 +161,7 @@ export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record = { [ROUTES.TRACE]: ROUTES.TRACES_EXPLORER, [ROUTES.TRACE_EXPLORER]: ROUTES.TRACES_EXPLORER, [ROUTES.LOGS_BASE]: ROUTES.LOGS_EXPLORER, + [ROUTES.METRICS_EXPLORER_BASE]: ROUTES.METRICS_EXPLORER, }; export default menuItems; diff --git a/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx b/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx index 1b416d4f64..90fe868f6f 100644 --- a/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx +++ b/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx @@ -2,8 +2,12 @@ import './MetricsExplorerPage.styles.scss'; import RouteTab from 'components/RouteTab'; import { TabRoutes } from 'components/RouteTab/types'; +import { initialQueriesMap } from 'constants/queryBuilder'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import history from 'lib/history'; +import { useLayoutEffect, useMemo } from 'react'; import { useLocation } from 'react-use'; +import { DataSource } from 'types/common/queryBuilder'; import { Explorer, Summary } from './constants'; @@ -12,6 +16,14 @@ function MetricsExplorerPage(): JSX.Element { const routes: TabRoutes[] = [Summary, Explorer]; + const initialQuery = useMemo(() => initialQueriesMap[DataSource.METRICS], []); + const { resetQuery } = useQueryBuilder(); + + useLayoutEffect(() => { + resetQuery(initialQuery); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (
diff --git a/frontend/src/utils/permission/index.ts b/frontend/src/utils/permission/index.ts index 1f5b8f6a33..f9db6b6100 100644 --- a/frontend/src/utils/permission/index.ts +++ b/frontend/src/utils/permission/index.ts @@ -116,4 +116,5 @@ export const routePermission: Record = { METRICS_EXPLORER_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'], METRICS_EXPLORER_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'], WORKSPACE_ACCESS_RESTRICTED: ['ADMIN', 'EDITOR', 'VIEWER'], + METRICS_EXPLORER_BASE: ['ADMIN', 'EDITOR', 'VIEWER'], };