mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 04:29:04 +08:00
chore: metris explorer fixes (#7377)
This commit is contained in:
parent
efd4e30edf
commit
ad2b75e3f0
@ -20,7 +20,7 @@ export interface MetricDetails {
|
||||
metric_type: MetricType;
|
||||
description: string;
|
||||
unit: string;
|
||||
temporality: Temporality;
|
||||
temporality?: Temporality;
|
||||
};
|
||||
alerts: MetricDetailsAlert[] | null;
|
||||
dashboards: MetricDetailsDashboard[] | null;
|
||||
|
@ -7,7 +7,7 @@ import { MetricType } from './getMetricsList';
|
||||
export interface UpdateMetricMetadataProps {
|
||||
description: string;
|
||||
metricType: MetricType;
|
||||
temporality: Temporality;
|
||||
temporality?: Temporality;
|
||||
isMonotonic?: boolean;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -7,7 +7,7 @@ export default function EmptyMetricsSearch(): JSX.Element {
|
||||
<Empty
|
||||
description={
|
||||
<Typography.Title level={5}>
|
||||
Please build and run query to see the result
|
||||
Please build and run a valid query to see the result
|
||||
</Typography.Title>
|
||||
}
|
||||
/>
|
||||
|
@ -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 {
|
||||
|
@ -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>(
|
||||
ExplorerTabs.TIME_SERIES,
|
||||
);
|
||||
const [selectedTab] = useState<ExplorerTabs>(ExplorerTabs.TIME_SERIES);
|
||||
|
||||
const handleToggleShowOneChartPerQuery = (): void =>
|
||||
toggleShowOneChartPerQuery(!showOneChartPerQuery);
|
||||
@ -139,7 +136,8 @@ function Explorer(): JSX.Element {
|
||||
</div>
|
||||
</div>
|
||||
<QuerySection />
|
||||
<Button.Group className="explore-tabs">
|
||||
{/* TODO: Enable once we have resolved all related metrics issues */}
|
||||
{/* <Button.Group className="explore-tabs">
|
||||
<Button
|
||||
value={ExplorerTabs.TIME_SERIES}
|
||||
className={classNames('tab', {
|
||||
@ -149,8 +147,7 @@ function Explorer(): JSX.Element {
|
||||
>
|
||||
<Typography.Text>Time series</Typography.Text>
|
||||
</Button>
|
||||
{/* TODO: Enable once we have resolved all related metrics issues */}
|
||||
{/* <Button
|
||||
<Button
|
||||
value={ExplorerTabs.RELATED_METRICS}
|
||||
className={classNames('tab', {
|
||||
'selected-view': selectedTab === ExplorerTabs.RELATED_METRICS,
|
||||
@ -158,8 +155,8 @@ function Explorer(): JSX.Element {
|
||||
onClick={(): void => setSelectedTab(ExplorerTabs.RELATED_METRICS)}
|
||||
>
|
||||
<Typography.Text>Related</Typography.Text>
|
||||
</Button> */}
|
||||
</Button.Group>
|
||||
</Button>
|
||||
</Button.Group> */}
|
||||
<div className="explore-content">
|
||||
{selectedTab === ExplorerTabs.TIME_SERIES && (
|
||||
<TimeSeries showOneChartPerQuery={showOneChartPerQuery} />
|
||||
|
@ -106,29 +106,31 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
'time-series-container': changeLayoutForOneChartPerQuery,
|
||||
})}
|
||||
>
|
||||
<>
|
||||
<BuilderUnitsFilter onChange={onUnitChangeHandler} yAxisUnit={yAxisUnit} />
|
||||
{responseData.map((datapoint, index) => (
|
||||
<div
|
||||
className="time-series-view"
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
>
|
||||
<TimeSeriesView
|
||||
isFilterApplied={false}
|
||||
isError={queries[index].isError}
|
||||
isLoading={queries[index].isLoading}
|
||||
data={datapoint}
|
||||
yAxisUnit={yAxisUnit}
|
||||
dataSource={DataSource.METRICS}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className={classNames({
|
||||
'time-series-container': changeLayoutForOneChartPerQuery,
|
||||
})}
|
||||
>
|
||||
{responseData.map((datapoint, index) => (
|
||||
<div
|
||||
className="time-series-view"
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
>
|
||||
<TimeSeriesView
|
||||
isFilterApplied={false}
|
||||
isError={queries[index].isError}
|
||||
isLoading={queries[index].isLoading}
|
||||
data={datapoint}
|
||||
yAxisUnit={yAxisUnit}
|
||||
dataSource={DataSource.METRICS}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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<UpdateMetricMetadataProps>({
|
||||
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 (
|
||||
<Input
|
||||
name={field.key}
|
||||
value={
|
||||
defaultValue={
|
||||
metricMetadata[
|
||||
field.key as Exclude<keyof UpdateMetricMetadataProps, 'isMonotonic'>
|
||||
]
|
||||
}
|
||||
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 (
|
||||
<div className="action-menu">
|
||||
<Button
|
||||
className="action-button"
|
||||
type="text"
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
setIsEditing(false);
|
||||
}}
|
||||
disabled={isUpdatingMetricsMetadata}
|
||||
>
|
||||
<X size={14} />
|
||||
<Typography.Text>Cancel</Typography.Text>
|
||||
</Button>
|
||||
<Button
|
||||
className="action-button"
|
||||
type="text"
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
handleSave();
|
||||
}}
|
||||
disabled={isUpdatingMetricsMetadata}
|
||||
>
|
||||
<Save size={14} />
|
||||
<Typography.Text>Save</Typography.Text>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="action-menu">
|
||||
<Button
|
||||
className="action-button"
|
||||
type="text"
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
handleSave();
|
||||
setIsEditing(true);
|
||||
}}
|
||||
disabled={isUpdatingMetricsMetadata}
|
||||
>
|
||||
<Save size={14} />
|
||||
<Typography.Text>Save</Typography.Text>
|
||||
<Edit2 size={14} />
|
||||
<Typography.Text>Edit</Typography.Text>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
className="action-button"
|
||||
type="text"
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
setIsEditing(true);
|
||||
}}
|
||||
disabled={isUpdatingMetricsMetadata}
|
||||
>
|
||||
<Edit2 size={14} />
|
||||
<Typography.Text>Edit</Typography.Text>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}, [handleSave, isEditing, isUpdatingMetricsMetadata]);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -41,7 +41,7 @@ export function formatNumberToCompactFormat(num: number): string {
|
||||
|
||||
export function determineIsMonotonic(
|
||||
metricType: MetricType,
|
||||
temporality: Temporality,
|
||||
temporality?: Temporality,
|
||||
): boolean {
|
||||
if (
|
||||
metricType === MetricType.HISTOGRAM ||
|
||||
|
@ -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(
|
||||
|
@ -36,6 +36,7 @@ function MetricTypeSearch(): JSX.Element {
|
||||
if (selectedMetricType !== 'all') {
|
||||
handleChangeQueryData('filters', {
|
||||
items: [
|
||||
...currentQuery.builder.queryData[0].filters.items,
|
||||
{
|
||||
id: 'metric_type',
|
||||
op: '=',
|
||||
|
@ -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 || {
|
||||
|
@ -161,6 +161,7 @@ export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record<string, string> = {
|
||||
[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;
|
||||
|
@ -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 (
|
||||
<div className="metrics-explorer-page">
|
||||
<RouteTab routes={routes} activeKey={pathname} history={history} />
|
||||
|
@ -116,4 +116,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
METRICS_EXPLORER_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
METRICS_EXPLORER_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
WORKSPACE_ACCESS_RESTRICTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
METRICS_EXPLORER_BASE: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user