mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 10:29:11 +08:00
chore: add activation events (#5474)
This commit is contained in:
parent
cd07c743b6
commit
df751c7f38
@ -12,6 +12,7 @@ import { ColumnType, TablePaginationConfig } from 'antd/es/table';
|
|||||||
import { FilterValue, SorterResult } from 'antd/es/table/interface';
|
import { FilterValue, SorterResult } from 'antd/es/table/interface';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { FilterConfirmProps } from 'antd/lib/table/interface';
|
import { FilterConfirmProps } from 'antd/lib/table/interface';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import getAll from 'api/errors/getAll';
|
import getAll from 'api/errors/getAll';
|
||||||
import getErrorCounts from 'api/errors/getErrorCounts';
|
import getErrorCounts from 'api/errors/getErrorCounts';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
@ -23,7 +24,8 @@ import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute
|
|||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { isUndefined } from 'lodash-es';
|
||||||
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQueries } from 'react-query';
|
import { useQueries } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
@ -410,6 +412,26 @@ function AllErrors(): JSX.Element {
|
|||||||
[pathname],
|
[pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!logEventCalledRef.current &&
|
||||||
|
!isUndefined(errorCountResponse.data?.payload)
|
||||||
|
) {
|
||||||
|
const selectedEnvironments = queries.find(
|
||||||
|
(val) => val.tagKey === 'resource_deployment_environment',
|
||||||
|
)?.tagValue;
|
||||||
|
|
||||||
|
logEvent('Exception: List page visited', {
|
||||||
|
numberOfExceptions: errorCountResponse.data?.payload,
|
||||||
|
selectedEnvironments,
|
||||||
|
resourceAttributeUsed: !!queries.length,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [errorCountResponse.data?.payload]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<ResizeTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -1,8 +1,34 @@
|
|||||||
import './EmptyLogsSearch.styles.scss';
|
import './EmptyLogsSearch.styles.scss';
|
||||||
|
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { DataSource, PanelTypeKeys } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
export default function EmptyLogsSearch({
|
||||||
|
dataSource,
|
||||||
|
panelType,
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
panelType: PanelTypeKeys;
|
||||||
|
}): JSX.Element {
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
if (dataSource === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: No results', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
} else if (dataSource === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: No results', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
export default function EmptyLogsSearch(): JSX.Element {
|
|
||||||
return (
|
return (
|
||||||
<div className="empty-logs-search-container">
|
<div className="empty-logs-search-container">
|
||||||
<div className="empty-logs-search-container-content">
|
<div className="empty-logs-search-container-content">
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './styles.scss';
|
import './styles.scss';
|
||||||
|
|
||||||
import { Button, Divider, Space, Typography } from 'antd';
|
import { Button, Divider, Space, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import getNextPrevId from 'api/errors/getNextPrevId';
|
import getNextPrevId from 'api/errors/getNextPrevId';
|
||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
@ -9,8 +10,9 @@ import dayjs from 'dayjs';
|
|||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { isUndefined } from 'lodash-es';
|
||||||
import { urlKey } from 'pages/ErrorDetails/utils';
|
import { urlKey } from 'pages/ErrorDetails/utils';
|
||||||
import { useMemo, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
@ -111,9 +113,29 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const onClickTraceHandler = (): void => {
|
const onClickTraceHandler = (): void => {
|
||||||
|
logEvent('Exception: Navigate to trace detail page', {
|
||||||
|
groupId: errorDetail.groupID,
|
||||||
|
spanId: errorDetail.spanID,
|
||||||
|
traceId: errorDetail.traceID,
|
||||||
|
exceptionId: errorDetail.errorId,
|
||||||
|
});
|
||||||
history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`);
|
history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||||
|
logEvent('Exception: Detail page visited', {
|
||||||
|
groupId: errorDetail.groupID,
|
||||||
|
spanId: errorDetail.spanID,
|
||||||
|
traceId: errorDetail.traceID,
|
||||||
|
exceptionId: errorDetail.errorId,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography>{errorDetail.exceptionType}</Typography>
|
<Typography>{errorDetail.exceptionType}</Typography>
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils';
|
import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils';
|
||||||
@ -93,7 +94,23 @@ function ExplorerOptions({
|
|||||||
setIsExport(value);
|
setIsExport(value);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const {
|
||||||
|
currentQuery,
|
||||||
|
panelType,
|
||||||
|
isStagedQueryUpdated,
|
||||||
|
redirectWithQueryBuilderData,
|
||||||
|
} = useQueryBuilder();
|
||||||
|
|
||||||
const handleSaveViewModalToggle = (): void => {
|
const handleSaveViewModalToggle = (): void => {
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Save view clicked', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Save view clicked', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
}
|
||||||
setIsSaveModalOpen(!isSaveModalOpen);
|
setIsSaveModalOpen(!isSaveModalOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,11 +121,21 @@ function ExplorerOptions({
|
|||||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const onCreateAlertsHandler = useCallback(() => {
|
const onCreateAlertsHandler = useCallback(() => {
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Create alert', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Create alert', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
}
|
||||||
history.push(
|
history.push(
|
||||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||||
JSON.stringify(query),
|
JSON.stringify(query),
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [history, query]);
|
}, [history, query]);
|
||||||
|
|
||||||
const onCancel = (value: boolean) => (): void => {
|
const onCancel = (value: boolean) => (): void => {
|
||||||
@ -116,6 +143,15 @@ function ExplorerOptions({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onAddToDashboard = (): void => {
|
const onAddToDashboard = (): void => {
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Add to dashboard clicked', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Add to dashboard clicked', {
|
||||||
|
panelType,
|
||||||
|
});
|
||||||
|
}
|
||||||
setIsExport(true);
|
setIsExport(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -127,13 +163,6 @@ function ExplorerOptions({
|
|||||||
refetch: refetchAllView,
|
refetch: refetchAllView,
|
||||||
} = useGetAllViews(sourcepage);
|
} = useGetAllViews(sourcepage);
|
||||||
|
|
||||||
const {
|
|
||||||
currentQuery,
|
|
||||||
panelType,
|
|
||||||
isStagedQueryUpdated,
|
|
||||||
redirectWithQueryBuilderData,
|
|
||||||
} = useQueryBuilder();
|
|
||||||
|
|
||||||
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
||||||
|
|
||||||
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
||||||
@ -224,6 +253,17 @@ function ExplorerOptions({
|
|||||||
onMenuItemSelectHandler({
|
onMenuItemSelectHandler({
|
||||||
key: option.key,
|
key: option.key,
|
||||||
});
|
});
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Select view', {
|
||||||
|
panelType,
|
||||||
|
viewName: option.value,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Select view', {
|
||||||
|
panelType,
|
||||||
|
viewName: option.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
ref.current.blur();
|
ref.current.blur();
|
||||||
}
|
}
|
||||||
@ -259,6 +299,17 @@ function ExplorerOptions({
|
|||||||
viewName: newViewName,
|
viewName: newViewName,
|
||||||
setNewViewName,
|
setNewViewName,
|
||||||
});
|
});
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Save view successful', {
|
||||||
|
panelType,
|
||||||
|
viewName: newViewName,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Save view successful', {
|
||||||
|
panelType,
|
||||||
|
viewName: newViewName,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Remove this and move this to scss file
|
// TODO: Remove this and move this to scss file
|
||||||
@ -499,7 +550,7 @@ function ExplorerOptions({
|
|||||||
|
|
||||||
export interface ExplorerOptionsProps {
|
export interface ExplorerOptionsProps {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onExport: (dashboard: Dashboard | null) => void;
|
onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void;
|
||||||
query: Query | null;
|
query: Query | null;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
sourcepage: DataSource;
|
sourcepage: DataSource;
|
||||||
|
@ -41,7 +41,7 @@ function ExportPanelContainer({
|
|||||||
} = useMutation(createDashboard, {
|
} = useMutation(createDashboard, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.payload) {
|
if (data.payload) {
|
||||||
onExport(data?.payload);
|
onExport(data?.payload, true);
|
||||||
}
|
}
|
||||||
refetch();
|
refetch();
|
||||||
},
|
},
|
||||||
@ -55,7 +55,7 @@ function ExportPanelContainer({
|
|||||||
({ uuid }) => uuid === selectedDashboardId,
|
({ uuid }) => uuid === selectedDashboardId,
|
||||||
);
|
);
|
||||||
|
|
||||||
onExport(currentSelectedDashboard || null);
|
onExport(currentSelectedDashboard || null, false);
|
||||||
}, [data, selectedDashboardId, onExport]);
|
}, [data, selectedDashboardId, onExport]);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
|
@ -40,7 +40,7 @@ function ExportPanel({
|
|||||||
|
|
||||||
export interface ExportPanelProps {
|
export interface ExportPanelProps {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onExport: (dashboard: Dashboard | null) => void;
|
onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void;
|
||||||
query: Query | null;
|
query: Query | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import './DashboardEmptyState.styles.scss';
|
|||||||
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Typography } from 'antd';
|
import { Button, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer';
|
import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
@ -36,6 +37,12 @@ export default function DashboardEmptyState(): JSX.Element {
|
|||||||
|
|
||||||
const onEmptyWidgetHandler = useCallback(() => {
|
const onEmptyWidgetHandler = useCallback(() => {
|
||||||
handleToggleDashboardSlider(true);
|
handleToggleDashboardSlider(true);
|
||||||
|
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
dashboardName: selectedDashboard?.data.title,
|
||||||
|
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [handleToggleDashboardSlider]);
|
}, [handleToggleDashboardSlider]);
|
||||||
return (
|
return (
|
||||||
<section className="dashboard-empty-state">
|
<section className="dashboard-empty-state">
|
||||||
|
@ -3,6 +3,7 @@ import './GridCardLayout.styles.scss';
|
|||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Form, Input, Modal, Typography } from 'antd';
|
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||||
import { useForm } from 'antd/es/form/Form';
|
import { useForm } from 'antd/es/form/Form';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -15,7 +16,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
|||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { defaultTo } from 'lodash-es';
|
import { defaultTo, isUndefined } from 'lodash-es';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import {
|
import {
|
||||||
Check,
|
Check,
|
||||||
@ -27,7 +28,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { sortLayout } from 'providers/Dashboard/util';
|
import { sortLayout } from 'providers/Dashboard/util';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
||||||
import { ItemCallback, Layout } from 'react-grid-layout';
|
import { ItemCallback, Layout } from 'react-grid-layout';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
@ -126,6 +127,18 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
|||||||
setDashboardLayout(sortLayout(layouts));
|
setDashboardLayout(sortLayout(layouts));
|
||||||
}, [layouts]);
|
}, [layouts]);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||||
|
logEvent('Dashboard Detail: Opened', {
|
||||||
|
dashboardId: data.uuid,
|
||||||
|
dashboardName: data.title,
|
||||||
|
numberOfPanels: data.widgets?.length,
|
||||||
|
numberOfVariables: Object.keys(data?.variables || {}).length || 0,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
const onSaveHandler = (): void => {
|
const onSaveHandler = (): void => {
|
||||||
if (!selectedDashboard) return;
|
if (!selectedDashboard) return;
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ function WidgetHeader({
|
|||||||
);
|
);
|
||||||
}, [widget.id, widget.panelTypes, widget.query]);
|
}, [widget.id, widget.panelTypes, widget.query]);
|
||||||
|
|
||||||
const onCreateAlertsHandler = useCreateAlerts(widget);
|
const onCreateAlertsHandler = useCreateAlerts(widget, 'dashboardView');
|
||||||
|
|
||||||
const onDownloadHandler = useCallback((): void => {
|
const onDownloadHandler = useCallback((): void => {
|
||||||
const csv = unparse(tableProcessedDataRef.current);
|
const csv = unparse(tableProcessedDataRef.current);
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { TableProps } from 'antd/lib';
|
import { TableProps } from 'antd/lib';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
@ -34,7 +35,7 @@ import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
|||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { get, isEmpty } from 'lodash-es';
|
import { get, isEmpty, isUndefined } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
ArrowDownWideNarrow,
|
ArrowDownWideNarrow,
|
||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
@ -60,6 +61,7 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -269,6 +271,7 @@ function DashboardsList(): JSX.Element {
|
|||||||
|
|
||||||
const onNewDashboardHandler = useCallback(async () => {
|
const onNewDashboardHandler = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
logEvent('Dashboard List: Create dashboard clicked', {});
|
||||||
setNewDashboardState({
|
setNewDashboardState({
|
||||||
...newDashboardState,
|
...newDashboardState,
|
||||||
loading: true,
|
loading: true,
|
||||||
@ -305,6 +308,8 @@ function DashboardsList(): JSX.Element {
|
|||||||
}, [newDashboardState, t]);
|
}, [newDashboardState, t]);
|
||||||
|
|
||||||
const onModalHandler = (uploadedGrafana: boolean): void => {
|
const onModalHandler = (uploadedGrafana: boolean): void => {
|
||||||
|
logEvent('Dashboard List: Import JSON clicked', {});
|
||||||
|
|
||||||
setIsImportJSONModalVisible((state) => !state);
|
setIsImportJSONModalVisible((state) => !state);
|
||||||
setUploadedGrafana(uploadedGrafana);
|
setUploadedGrafana(uploadedGrafana);
|
||||||
};
|
};
|
||||||
@ -441,6 +446,10 @@ function DashboardsList(): JSX.Element {
|
|||||||
} else {
|
} else {
|
||||||
history.push(getLink());
|
history.push(getLink());
|
||||||
}
|
}
|
||||||
|
logEvent('Dashboard List: Clicked on dashboard', {
|
||||||
|
dashboardId: dashboard.id,
|
||||||
|
dashboardName: dashboard.name,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -619,6 +628,21 @@ function DashboardsList(): JSX.Element {
|
|||||||
hideOnSinglePage: true,
|
hideOnSinglePage: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!logEventCalledRef.current &&
|
||||||
|
!isDashboardListLoading &&
|
||||||
|
!isUndefined(dashboardListResponse)
|
||||||
|
) {
|
||||||
|
logEvent('Dashboard List: Page visited', {
|
||||||
|
number: dashboardListResponse?.length,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isDashboardListLoading]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboards-list-container">
|
<div className="dashboards-list-container">
|
||||||
<div className="dashboards-list-view-content">
|
<div className="dashboards-list-view-content">
|
||||||
@ -705,6 +729,9 @@ function DashboardsList(): JSX.Element {
|
|||||||
type="text"
|
type="text"
|
||||||
className="new-dashboard"
|
className="new-dashboard"
|
||||||
icon={<Plus size={14} />}
|
icon={<Plus size={14} />}
|
||||||
|
onClick={(): void => {
|
||||||
|
logEvent('Dashboard List: New dashboard clicked', {});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
New Dashboard
|
New Dashboard
|
||||||
</Button>
|
</Button>
|
||||||
@ -745,6 +772,9 @@ function DashboardsList(): JSX.Element {
|
|||||||
type="primary"
|
type="primary"
|
||||||
className="periscope-btn primary btn"
|
className="periscope-btn primary btn"
|
||||||
icon={<Plus size={14} />}
|
icon={<Plus size={14} />}
|
||||||
|
onClick={(): void => {
|
||||||
|
logEvent('Dashboard List: New dashboard clicked', {});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
New dashboard
|
New dashboard
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -5,6 +5,7 @@ import { ExclamationCircleTwoTone } from '@ant-design/icons';
|
|||||||
import MEditor, { Monaco } from '@monaco-editor/react';
|
import MEditor, { Monaco } from '@monaco-editor/react';
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
|
import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
@ -67,6 +68,8 @@ function ImportJSON({
|
|||||||
const onClickLoadJsonHandler = async (): Promise<void> => {
|
const onClickLoadJsonHandler = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setDashboardCreating(true);
|
setDashboardCreating(true);
|
||||||
|
logEvent('Dashboard List: Import and next clicked', {});
|
||||||
|
|
||||||
const dashboardData = JSON.parse(editorValue) as DashboardData;
|
const dashboardData = JSON.parse(editorValue) as DashboardData;
|
||||||
|
|
||||||
if (dashboardData?.layout) {
|
if (dashboardData?.layout) {
|
||||||
@ -86,6 +89,10 @@ function ImportJSON({
|
|||||||
dashboardId: response.payload.uuid,
|
dashboardId: response.payload.uuid,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
logEvent('Dashboard List: New dashboard imported successfully', {
|
||||||
|
dashboardId: response.payload?.uuid,
|
||||||
|
dashboardName: response.payload?.data?.title,
|
||||||
|
});
|
||||||
} else if (response.error === 'feature usage exceeded') {
|
} else if (response.error === 'feature usage exceeded') {
|
||||||
setIsFeatureAlert(true);
|
setIsFeatureAlert(true);
|
||||||
notifications.error({
|
notifications.error({
|
||||||
@ -180,6 +187,9 @@ function ImportJSON({
|
|||||||
type="default"
|
type="default"
|
||||||
className="periscope-btn"
|
className="periscope-btn"
|
||||||
icon={<MonitorDot size={14} />}
|
icon={<MonitorDot size={14} />}
|
||||||
|
onClick={(): void => {
|
||||||
|
logEvent('Dashboard List: Upload JSON file clicked', {});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{' '}
|
{' '}
|
||||||
{t('upload_json_file')}
|
{t('upload_json_file')}
|
||||||
|
@ -172,7 +172,9 @@ function LogsExplorerList({
|
|||||||
!isFetching &&
|
!isFetching &&
|
||||||
logs.length === 0 &&
|
logs.length === 0 &&
|
||||||
!isError &&
|
!isError &&
|
||||||
isFilterApplied && <EmptyLogsSearch />}
|
isFilterApplied && (
|
||||||
|
<EmptyLogsSearch dataSource={DataSource.LOGS} panelType="LIST" />
|
||||||
|
)}
|
||||||
|
|
||||||
{isError && !isLoading && !isFetching && <LogsError />}
|
{isError && !isLoading && !isFetching && <LogsError />}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import './LogsExplorerViews.styles.scss';
|
import './LogsExplorerViews.styles.scss';
|
||||||
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
@ -37,7 +38,14 @@ import { useNotifications } from 'hooks/useNotifications';
|
|||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
||||||
import { cloneDeep, defaultTo, isEmpty, omit, set } from 'lodash-es';
|
import {
|
||||||
|
cloneDeep,
|
||||||
|
defaultTo,
|
||||||
|
isEmpty,
|
||||||
|
isUndefined,
|
||||||
|
omit,
|
||||||
|
set,
|
||||||
|
} from 'lodash-es';
|
||||||
import { Sliders } from 'lucide-react';
|
import { Sliders } from 'lucide-react';
|
||||||
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -310,6 +318,19 @@ function LogsExplorerViews({
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isUndefined(data?.payload)) {
|
||||||
|
const currentData = data?.payload?.data?.newResult?.data?.result || [];
|
||||||
|
logEvent('Logs Explorer: Page visited', {
|
||||||
|
panelType,
|
||||||
|
isEmpty: !currentData?.[0]?.list,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [data?.payload]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutate: updateDashboard,
|
mutate: updateDashboard,
|
||||||
isLoading: isUpdateDashboardLoading,
|
isLoading: isUpdateDashboardLoading,
|
||||||
@ -324,7 +345,7 @@ function LogsExplorerViews({
|
|||||||
}, [currentQuery]);
|
}, [currentQuery]);
|
||||||
|
|
||||||
const handleExport = useCallback(
|
const handleExport = useCallback(
|
||||||
(dashboard: Dashboard | null): void => {
|
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
|
||||||
if (!dashboard || !panelType) return;
|
if (!dashboard || !panelType) return;
|
||||||
|
|
||||||
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
||||||
@ -346,6 +367,12 @@ function LogsExplorerViews({
|
|||||||
options.selectColumns,
|
options.selectColumns,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logEvent('Logs Explorer: Add to dashboard successful', {
|
||||||
|
panelType,
|
||||||
|
isNewDashboard,
|
||||||
|
dashboardName: dashboard?.data?.title,
|
||||||
|
});
|
||||||
|
|
||||||
updateDashboard(updatedDashboard, {
|
updateDashboard(updatedDashboard, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Col } from 'antd';
|
import { Col } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import Graph from 'container/GridCardLayout/GridCard';
|
import Graph from 'container/GridCardLayout/GridCard';
|
||||||
@ -11,7 +12,7 @@ import {
|
|||||||
convertRawQueriesToTraceSelectedTags,
|
convertRawQueriesToTraceSelectedTags,
|
||||||
resourceAttributesToTagFilterItems,
|
resourceAttributesToTagFilterItems,
|
||||||
} from 'hooks/useResourceAttribute/utils';
|
} from 'hooks/useResourceAttribute/utils';
|
||||||
import { useMemo, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
@ -97,6 +98,24 @@ function DBCall(): JSX.Element {
|
|||||||
[servicename, tagFilterItems],
|
[servicename, tagFilterItems],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
const selectedEnvironments = queries.find(
|
||||||
|
(val) => val.tagKey === 'resource_deployment_environment',
|
||||||
|
)?.tagValue;
|
||||||
|
|
||||||
|
logEvent('APM: Service detail page visited', {
|
||||||
|
selectedEnvironments,
|
||||||
|
resourceAttributeUsed: !!queries.length,
|
||||||
|
section: 'dbMetrics',
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const apmToTraceQuery = useGetAPMToTracesQueries({
|
const apmToTraceQuery = useGetAPMToTracesQueries({
|
||||||
servicename,
|
servicename,
|
||||||
isDBCall: true,
|
isDBCall: true,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Col } from 'antd';
|
import { Col } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import Graph from 'container/GridCardLayout/GridCard';
|
import Graph from 'container/GridCardLayout/GridCard';
|
||||||
@ -13,7 +14,7 @@ import {
|
|||||||
convertRawQueriesToTraceSelectedTags,
|
convertRawQueriesToTraceSelectedTags,
|
||||||
resourceAttributesToTagFilterItems,
|
resourceAttributesToTagFilterItems,
|
||||||
} from 'hooks/useResourceAttribute/utils';
|
} from 'hooks/useResourceAttribute/utils';
|
||||||
import { useMemo, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
@ -114,6 +115,23 @@ function External(): JSX.Element {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
const selectedEnvironments = queries.find(
|
||||||
|
(val) => val.tagKey === 'resource_deployment_environment',
|
||||||
|
)?.tagValue;
|
||||||
|
|
||||||
|
logEvent('APM: Service detail page visited', {
|
||||||
|
selectedEnvironments,
|
||||||
|
resourceAttributeUsed: !!queries.length,
|
||||||
|
section: 'externalMetrics',
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const externalCallRPSWidget = useMemo(
|
const externalCallRPSWidget = useMemo(
|
||||||
() =>
|
() =>
|
||||||
getWidgetQueryBuilder({
|
getWidgetQueryBuilder({
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import getTopLevelOperations, {
|
import getTopLevelOperations, {
|
||||||
ServiceDataProps,
|
ServiceDataProps,
|
||||||
} from 'api/metrics/getTopLevelOperations';
|
} from 'api/metrics/getTopLevelOperations';
|
||||||
@ -17,7 +18,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||||
import { defaultTo } from 'lodash-es';
|
import { defaultTo } from 'lodash-es';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
@ -81,6 +82,23 @@ function Application(): JSX.Element {
|
|||||||
[handleSetTimeStamp],
|
[handleSetTimeStamp],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
const selectedEnvironments = queries.find(
|
||||||
|
(val) => val.tagKey === 'resource_deployment_environment',
|
||||||
|
)?.tagValue;
|
||||||
|
|
||||||
|
logEvent('APM: Service detail page visited', {
|
||||||
|
selectedEnvironments,
|
||||||
|
resourceAttributeUsed: !!queries.length,
|
||||||
|
section: 'overview',
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: topLevelOperations,
|
data: topLevelOperations,
|
||||||
error: topLevelOperationsError,
|
error: topLevelOperationsError,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './ComponentSlider.styles.scss';
|
import './ComponentSlider.styles.scss';
|
||||||
|
|
||||||
import { Card, Modal } from 'antd';
|
import { Card, Modal } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
@ -20,6 +21,13 @@ function DashboardGraphSlider(): JSX.Element {
|
|||||||
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
handleToggleDashboardSlider(false);
|
handleToggleDashboardSlider(false);
|
||||||
|
logEvent('Dashboard Detail: New panel type selected', {
|
||||||
|
// dashboardId: '',
|
||||||
|
// dashboardName: '',
|
||||||
|
// numberOfPanels: 0, // todo - at this point we don't know these attributes
|
||||||
|
panelType: name,
|
||||||
|
widgetId: id,
|
||||||
|
});
|
||||||
const queryParamsLog = {
|
const queryParamsLog = {
|
||||||
graphType: name,
|
graphType: name,
|
||||||
widgetId: id,
|
widgetId: id,
|
||||||
@ -47,7 +55,6 @@ function DashboardGraphSlider(): JSX.Element {
|
|||||||
PANEL_TYPES_INITIAL_QUERY[name],
|
PANEL_TYPES_INITIAL_QUERY[name],
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (name === PANEL_TYPES.LIST) {
|
if (name === PANEL_TYPES.LIST) {
|
||||||
history.push(
|
history.push(
|
||||||
`${history.location.pathname}/new?${createQueryParams(queryParamsLog)}`,
|
`${history.location.pathname}/new?${createQueryParams(queryParamsLog)}`,
|
||||||
|
@ -2,6 +2,7 @@ import './Description.styles.scss';
|
|||||||
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd';
|
import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
||||||
import { dashboardHelpMessage } from 'components/facingIssueBtn/util';
|
import { dashboardHelpMessage } from 'components/facingIssueBtn/util';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
@ -126,6 +127,12 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
|||||||
|
|
||||||
const onEmptyWidgetHandler = useCallback(() => {
|
const onEmptyWidgetHandler = useCallback(() => {
|
||||||
handleToggleDashboardSlider(true);
|
handleToggleDashboardSlider(true);
|
||||||
|
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
dashboardName: selectedDashboard?.data.title,
|
||||||
|
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [handleToggleDashboardSlider]);
|
}, [handleToggleDashboardSlider]);
|
||||||
|
|
||||||
const handleLockDashboardToggle = (): void => {
|
const handleLockDashboardToggle = (): void => {
|
||||||
|
@ -2,6 +2,7 @@ import './QuerySection.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import PromQLIcon from 'assets/Dashboard/PromQl';
|
import PromQLIcon from 'assets/Dashboard/PromQl';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
@ -14,7 +15,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { defaultTo } from 'lodash-es';
|
import { defaultTo, isUndefined } from 'lodash-es';
|
||||||
import { Atom, Play, Terminal } from 'lucide-react';
|
import { Atom, Play, Terminal } from 'lucide-react';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import {
|
import {
|
||||||
@ -122,6 +123,18 @@ function QuerySection({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRunQuery = (): void => {
|
const handleRunQuery = (): void => {
|
||||||
|
const widgetId = urlQuery.get('widgetId');
|
||||||
|
const isNewPanel = isUndefined(widgets?.find((e) => e.id === widgetId));
|
||||||
|
|
||||||
|
logEvent('Panel Edit: Stage and run query', {
|
||||||
|
dataSource: currentQuery.builder?.queryData?.[0]?.dataSource,
|
||||||
|
panelType: selectedWidget.panelTypes,
|
||||||
|
queryType: currentQuery.queryType,
|
||||||
|
widgetId: selectedWidget.id,
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
dashboardName: selectedDashboard?.data.title,
|
||||||
|
isNewPanel,
|
||||||
|
});
|
||||||
handleStageQuery(currentQuery);
|
handleStageQuery(currentQuery);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ function RightContainer({
|
|||||||
const selectedGraphType =
|
const selectedGraphType =
|
||||||
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
||||||
|
|
||||||
const onCreateAlertsHandler = useCreateAlerts(selectedWidget);
|
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
|
||||||
|
|
||||||
const allowThreshold = panelTypeVsThreshold[selectedGraph];
|
const allowThreshold = panelTypeVsThreshold[selectedGraph];
|
||||||
const allowSoftMinMax = panelTypeVsSoftMinMax[selectedGraph];
|
const allowSoftMinMax = panelTypeVsSoftMinMax[selectedGraph];
|
||||||
|
@ -3,6 +3,7 @@ import './NewWidget.styles.scss';
|
|||||||
|
|
||||||
import { WarningOutlined } from '@ant-design/icons';
|
import { WarningOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd';
|
import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
||||||
import { chartHelpMessage } from 'components/facingIssueBtn/util';
|
import { chartHelpMessage } from 'components/facingIssueBtn/util';
|
||||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||||
@ -31,7 +32,7 @@ import {
|
|||||||
getPreviousWidgets,
|
getPreviousWidgets,
|
||||||
getSelectedWidgetIndex,
|
getSelectedWidgetIndex,
|
||||||
} from 'providers/Dashboard/util';
|
} from 'providers/Dashboard/util';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { generatePath, useParams } from 'react-router-dom';
|
import { generatePath, useParams } from 'react-router-dom';
|
||||||
@ -101,6 +102,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
|
|
||||||
const [isNewDashboard, setIsNewDashboard] = useState<boolean>(false);
|
const [isNewDashboard, setIsNewDashboard] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = query.get('widgetId');
|
const widgetId = query.get('widgetId');
|
||||||
const selectedWidget = widgets?.find((e) => e.id === widgetId);
|
const selectedWidget = widgets?.find((e) => e.id === widgetId);
|
||||||
@ -108,6 +111,18 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
if (isWidgetNotPresent) {
|
if (isWidgetNotPresent) {
|
||||||
setIsNewDashboard(true);
|
setIsNewDashboard(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
logEvent('Panel Edit: Page visited', {
|
||||||
|
panelType: selectedWidget?.panelTypes,
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
widgetId: selectedWidget?.id,
|
||||||
|
dashboardName: selectedDashboard?.data.title,
|
||||||
|
isNewPanel: !!isWidgetNotPresent,
|
||||||
|
dataSource: currentQuery.builder.queryData?.[0]?.dataSource,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -482,7 +497,20 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSaveDashboard = useCallback((): void => {
|
const onSaveDashboard = useCallback((): void => {
|
||||||
|
const widgetId = query.get('widgetId');
|
||||||
|
const selectWidget = widgets?.find((e) => e.id === widgetId);
|
||||||
|
|
||||||
|
logEvent('Panel Edit: Save changes', {
|
||||||
|
panelType: selectedWidget.panelTypes,
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
widgetId: selectedWidget.id,
|
||||||
|
dashboardName: selectedDashboard?.data.title,
|
||||||
|
queryType: currentQuery.queryType,
|
||||||
|
isNewPanel: isUndefined(selectWidget),
|
||||||
|
dataSource: currentQuery.builder.queryData?.[0]?.dataSource,
|
||||||
|
});
|
||||||
setSaveModal(true);
|
setSaveModal(true);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isQueryBuilderActive = useIsFeatureDisabled(
|
const isQueryBuilderActive = useIsFeatureDisabled(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import './NoLogs.styles.scss';
|
import './NoLogs.styles.scss';
|
||||||
|
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { ArrowUpRight } from 'lucide-react';
|
import { ArrowUpRight } from 'lucide-react';
|
||||||
@ -21,6 +22,11 @@ export default function NoLogs({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (cloudUser) {
|
if (cloudUser) {
|
||||||
|
if (dataSource === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Explorer: Navigate to onboarding', {});
|
||||||
|
} else if (dataSource === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Explorer: Navigate to onboarding', {});
|
||||||
|
}
|
||||||
history.push(
|
history.push(
|
||||||
dataSource === 'traces'
|
dataSource === 'traces'
|
||||||
? ROUTES.GET_STARTED_APPLICATION_MONITORING
|
? ROUTES.GET_STARTED_APPLICATION_MONITORING
|
||||||
|
@ -3,11 +3,19 @@ import './styles.scss';
|
|||||||
import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { Card, Modal, Table, Typography } from 'antd';
|
import { Card, Modal, Table, Typography } from 'antd';
|
||||||
import { ExpandableConfig } from 'antd/es/table/interface';
|
import { ExpandableConfig } from 'antd/es/table/interface';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import savePipeline from 'api/pipeline/post';
|
import savePipeline from 'api/pipeline/post';
|
||||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { isUndefined } from 'lodash-es';
|
||||||
import cloneDeep from 'lodash-es/cloneDeep';
|
import cloneDeep from 'lodash-es/cloneDeep';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -466,6 +474,16 @@ function PipelineListsView({
|
|||||||
getExpandIcon(expanded, onExpand, record),
|
getExpandIcon(expanded, onExpand, record),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isUndefined(currPipelineData)) {
|
||||||
|
logEvent('Logs Pipelines: List page visited', {
|
||||||
|
number: currPipelineData?.length,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
}, [currPipelineData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import localStorageGet from 'api/browser/localstorage/get';
|
import localStorageGet from 'api/browser/localstorage/get';
|
||||||
import localStorageSet from 'api/browser/localstorage/set';
|
import localStorageSet from 'api/browser/localstorage/set';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||||
import useErrorNotification from 'hooks/useErrorNotification';
|
import useErrorNotification from 'hooks/useErrorNotification';
|
||||||
import { useQueryService } from 'hooks/useQueryService';
|
import { useQueryService } from 'hooks/useQueryService';
|
||||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||||
import { useMemo, useState } from 'react';
|
import { isUndefined } from 'lodash-es';
|
||||||
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
@ -45,6 +47,26 @@ function ServiceTraces(): JSX.Element {
|
|||||||
setSkipOnboarding(true);
|
setSkipOnboarding(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||||
|
const selectedEnvironments = queries.find(
|
||||||
|
(val) => val.tagKey === 'resource_deployment_environment',
|
||||||
|
)?.tagValue;
|
||||||
|
|
||||||
|
const rps = data.reduce((total, service) => total + service.callRate, 0);
|
||||||
|
|
||||||
|
logEvent('APM: List page visited', {
|
||||||
|
numberOfServices: data?.length,
|
||||||
|
selectedEnvironments,
|
||||||
|
resourceAttributeUsed: !!queries.length,
|
||||||
|
rps,
|
||||||
|
});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
services.length === 0 &&
|
services.length === 0 &&
|
||||||
isLoading === false &&
|
isLoading === false &&
|
||||||
|
@ -4,6 +4,7 @@ import './SideNav.styles.scss';
|
|||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Button, Tooltip } from 'antd';
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -179,6 +180,11 @@ function SideNav({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onClickShortcuts = (e: MouseEvent): void => {
|
const onClickShortcuts = (e: MouseEvent): void => {
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: '/shortcuts',
|
||||||
|
menuLabel: 'Keyboard Shortcuts',
|
||||||
|
});
|
||||||
if (isCtrlMetaKey(e)) {
|
if (isCtrlMetaKey(e)) {
|
||||||
openInNewTab('/shortcuts');
|
openInNewTab('/shortcuts');
|
||||||
} else {
|
} else {
|
||||||
@ -187,6 +193,10 @@ function SideNav({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onClickGetStarted = (event: MouseEvent): void => {
|
const onClickGetStarted = (event: MouseEvent): void => {
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: '/get-started',
|
||||||
|
menuLabel: 'Get Started',
|
||||||
|
});
|
||||||
if (isCtrlMetaKey(event)) {
|
if (isCtrlMetaKey(event)) {
|
||||||
openInNewTab('/get-started');
|
openInNewTab('/get-started');
|
||||||
} else {
|
} else {
|
||||||
@ -313,6 +323,10 @@ function SideNav({
|
|||||||
} else if (item) {
|
} else if (item) {
|
||||||
onClickHandler(item?.key as string, event);
|
onClickHandler(item?.key as string, event);
|
||||||
}
|
}
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: item.key,
|
||||||
|
menuLabel: item.label,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -440,6 +454,10 @@ function SideNav({
|
|||||||
isActive={activeMenuKey === item?.key}
|
isActive={activeMenuKey === item?.key}
|
||||||
onClick={(event: MouseEvent): void => {
|
onClick={(event: MouseEvent): void => {
|
||||||
handleUserManagentMenuItemClick(item?.key as string, event);
|
handleUserManagentMenuItemClick(item?.key as string, event);
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: item.key,
|
||||||
|
menuLabel: item.label,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -456,6 +474,10 @@ function SideNav({
|
|||||||
} else {
|
} else {
|
||||||
history.push(`${inviteMemberMenuItem.key}`);
|
history.push(`${inviteMemberMenuItem.key}`);
|
||||||
}
|
}
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: inviteMemberMenuItem.key,
|
||||||
|
menuLabel: inviteMemberMenuItem.label,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -470,6 +492,10 @@ function SideNav({
|
|||||||
userSettingsMenuItem?.key as string,
|
userSettingsMenuItem?.key as string,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
|
logEvent('Sidebar: Menu clicked', {
|
||||||
|
menuRoute: userSettingsMenuItem.key,
|
||||||
|
menuLabel: 'User',
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -155,7 +155,9 @@ function TimeSeriesView({
|
|||||||
chartData[0]?.length === 0 &&
|
chartData[0]?.length === 0 &&
|
||||||
!isLoading &&
|
!isLoading &&
|
||||||
!isError &&
|
!isError &&
|
||||||
isFilterApplied && <EmptyLogsSearch />}
|
isFilterApplied && (
|
||||||
|
<EmptyLogsSearch dataSource={dataSource} panelType="TIME_SERIES" />
|
||||||
|
)}
|
||||||
|
|
||||||
{chartData &&
|
{chartData &&
|
||||||
chartData[0] &&
|
chartData[0] &&
|
||||||
|
@ -156,7 +156,9 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
|
|||||||
<NoLogs dataSource={DataSource.TRACES} />
|
<NoLogs dataSource={DataSource.TRACES} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDataPresent && isFilterApplied && <EmptyLogsSearch />}
|
{isDataPresent && isFilterApplied && (
|
||||||
|
<EmptyLogsSearch dataSource={DataSource.TRACES} panelType="LIST" />
|
||||||
|
)}
|
||||||
|
|
||||||
{!isError && transformedQueryTableData.length !== 0 && (
|
{!isError && transformedQueryTableData.length !== 0 && (
|
||||||
<ResizeTable
|
<ResizeTable
|
||||||
|
@ -105,7 +105,9 @@ function TracesView({ isFilterApplied }: TracesViewProps): JSX.Element {
|
|||||||
!isFetching &&
|
!isFetching &&
|
||||||
(tableData || []).length === 0 &&
|
(tableData || []).length === 0 &&
|
||||||
!isError &&
|
!isError &&
|
||||||
isFilterApplied && <EmptyLogsSearch />}
|
isFilterApplied && (
|
||||||
|
<EmptyLogsSearch dataSource={DataSource.TRACES} panelType="TRACE" />
|
||||||
|
)}
|
||||||
|
|
||||||
{(tableData || []).length !== 0 && (
|
{(tableData || []).length !== 0 && (
|
||||||
<ResizeTable
|
<ResizeTable
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||||
import { prepareQueryRangePayload } from 'lib/dashboard/prepareQueryRangePayload';
|
import { prepareQueryRangePayload } from 'lib/dashboard/prepareQueryRangePayload';
|
||||||
@ -17,7 +19,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { getGraphType } from 'utils/getGraphType';
|
import { getGraphType } from 'utils/getGraphType';
|
||||||
|
|
||||||
const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||||
const queryRangeMutation = useMutation(getQueryRangeFormat);
|
const queryRangeMutation = useMutation(getQueryRangeFormat);
|
||||||
|
|
||||||
const { selectedTime: globalSelectedInterval } = useSelector<
|
const { selectedTime: globalSelectedInterval } = useSelector<
|
||||||
@ -32,6 +34,24 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
|||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
if (!widget) return;
|
if (!widget) return;
|
||||||
|
|
||||||
|
if (caller === 'panelView') {
|
||||||
|
logEvent('Panel Edit: Create alert', {
|
||||||
|
panelType: widget.panelTypes,
|
||||||
|
dashboardName: selectedDashboard?.data?.title,
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
widgetId: widget.id,
|
||||||
|
queryType: widget.query.queryType,
|
||||||
|
});
|
||||||
|
} else if (caller === 'dashboardView') {
|
||||||
|
logEvent('Dashboard Detail: Panel action', {
|
||||||
|
action: MenuItemKeys.CreateAlerts,
|
||||||
|
panelType: widget.panelTypes,
|
||||||
|
dashboardName: selectedDashboard?.data?.title,
|
||||||
|
dashboardId: selectedDashboard?.uuid,
|
||||||
|
widgetId: widget.id,
|
||||||
|
queryType: widget.query.queryType,
|
||||||
|
});
|
||||||
|
}
|
||||||
const { queryPayload } = prepareQueryRangePayload({
|
const { queryPayload } = prepareQueryRangePayload({
|
||||||
query: widget.query,
|
query: widget.query,
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
@ -57,6 +77,7 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
notifications,
|
notifications,
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
TableProps,
|
TableProps,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import {
|
import {
|
||||||
getViewDetailsUsingViewKey,
|
getViewDetailsUsingViewKey,
|
||||||
showErrorNotification,
|
showErrorNotification,
|
||||||
@ -30,7 +31,7 @@ import {
|
|||||||
Trash2,
|
Trash2,
|
||||||
X,
|
X,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { ChangeEvent, useEffect, useState } from 'react';
|
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
@ -143,6 +144,22 @@ function SaveView(): JSX.Element {
|
|||||||
viewName: newViewName,
|
viewName: newViewName,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current && !isLoading) {
|
||||||
|
if (sourcepage === DataSource.TRACES) {
|
||||||
|
logEvent('Traces Views: Views visited', {
|
||||||
|
number: viewsData?.data.data.length,
|
||||||
|
});
|
||||||
|
} else if (sourcepage === DataSource.LOGS) {
|
||||||
|
logEvent('Logs Views: Views visited', {
|
||||||
|
number: viewsData?.data.data.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [viewsData?.data.data, isLoading]);
|
||||||
const onUpdateQueryHandler = (): void => {
|
const onUpdateQueryHandler = (): void => {
|
||||||
updateViewAsync(
|
updateViewAsync(
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
VerticalAlignTopOutlined,
|
VerticalAlignTopOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Button, Flex, Tooltip, Typography } from 'antd';
|
import { Button, Flex, Tooltip, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
|
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
|
||||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
@ -197,6 +198,11 @@ export function Filter(props: FilterProps): JSX.Element {
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if (selectedFilters) {
|
||||||
|
logEvent('Traces Explorer: Sidebar filter used', {
|
||||||
|
selectedFilters,
|
||||||
|
});
|
||||||
|
}
|
||||||
redirectWithQueryBuilderData(preparedQuery);
|
redirectWithQueryBuilderData(preparedQuery);
|
||||||
},
|
},
|
||||||
[currentQuery, redirectWithQueryBuilderData, selectedFilters],
|
[currentQuery, redirectWithQueryBuilderData, selectedFilters],
|
||||||
|
@ -3,6 +3,7 @@ import './TracesExplorer.styles.scss';
|
|||||||
import { FilterOutlined } from '@ant-design/icons';
|
import { FilterOutlined } from '@ant-design/icons';
|
||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { Button, Card, Tabs, Tooltip } from 'antd';
|
import { Button, Card, Tabs, Tooltip } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
|
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
@ -25,7 +26,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { cloneDeep, isEmpty, set } from 'lodash-es';
|
import { cloneDeep, isEmpty, set } from 'lodash-es';
|
||||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
@ -135,7 +136,7 @@ function TracesExplorer(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleExport = useCallback(
|
const handleExport = useCallback(
|
||||||
(dashboard: Dashboard | null): void => {
|
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
|
||||||
if (!dashboard || !panelType) return;
|
if (!dashboard || !panelType) return;
|
||||||
|
|
||||||
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
||||||
@ -157,6 +158,12 @@ function TracesExplorer(): JSX.Element {
|
|||||||
options.selectColumns,
|
options.selectColumns,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logEvent('Traces Explorer: Add to dashboard successful', {
|
||||||
|
panelType,
|
||||||
|
isNewDashboard,
|
||||||
|
dashboardName: dashboard?.data?.title,
|
||||||
|
});
|
||||||
|
|
||||||
updateDashboard(updatedDashboard, {
|
updateDashboard(updatedDashboard, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
@ -223,6 +230,13 @@ function TracesExplorer(): JSX.Element {
|
|||||||
currentPanelType,
|
currentPanelType,
|
||||||
]);
|
]);
|
||||||
const [isOpen, setOpen] = useState<boolean>(true);
|
const [isOpen, setOpen] = useState<boolean>(true);
|
||||||
|
const logEventCalledRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!logEventCalledRef.current) {
|
||||||
|
logEvent('Traces Explorer: Page visited', {});
|
||||||
|
logEventCalledRef.current = true;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user