feat: export panel in logs explorer is added (#2993)

* feat: export panel in logs explorer is added

* chore: notification is updated when dashboard id is not found while updating

* fix: error handling for export panel

* fix: layout gap

* refactor: remove log

* fix: updating log page from update button

* fix: redirect with correct operator

* fix: redirect wioth query data

* fix: refetch list

---------

Co-authored-by: Yevhen Shevchenko <y.shevchenko@seedium.io>
This commit is contained in:
Palash Gupta 2023-07-07 20:59:35 +05:30 committed by GitHub
parent e3d26d3f10
commit 2e85bd0264
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 152 additions and 15 deletions

View File

@ -84,10 +84,6 @@ function ExportPanel({
);
}
ExportPanel.defaultProps = {
isLoading: false,
};
interface OnClickProps {
key: string;
}
@ -98,4 +94,6 @@ export interface ExportPanelProps {
query: Query | null;
}
ExportPanel.defaultProps = { isLoading: false };
export default ExportPanel;

View File

@ -7,3 +7,9 @@ export const TabsStyled = styled(Tabs)`
background-color: ${themeColors.lightBlack};
}
`;
export const ActionsWrapper = styled.div`
display: flex;
justify-content: flex-end;
margin-bottom: 1rem;
`;

View File

@ -1,8 +1,12 @@
import { TabsProps } from 'antd';
import axios from 'axios';
import TabLabel from 'components/TabLabel';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import ExportPanel from 'container/ExportPanel';
import LogExplorerDetailedView from 'container/LogExplorerDetailedView';
import LogsExplorerChart from 'container/LogsExplorerChart';
import LogsExplorerList from 'container/LogsExplorerList';
@ -10,11 +14,16 @@ import LogsExplorerList from 'container/LogsExplorerList';
// import LogsExplorerTable from 'container/LogsExplorerTable';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { generatePath, useHistory } from 'react-router-dom';
import { Dashboard } from 'types/api/dashboard/getAll';
import { ILog } from 'types/api/logs/log';
import {
IBuilderQuery,
@ -23,9 +32,12 @@ import {
} from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { TabsStyled } from './LogsExplorerViews.styled';
import { ActionsWrapper, TabsStyled } from './LogsExplorerViews.styled';
function LogsExplorerViews(): JSX.Element {
const { notifications } = useNotifications();
const history = useHistory();
const { queryData: pageSize } = useUrlQueryData(
queryParamNamesMap.pageSize,
DEFAULT_PER_PAGE_VALUE,
@ -105,6 +117,16 @@ function LogsExplorerViews(): JSX.Element {
return modifiedQuery;
}, [stagedQuery, currentStagedQueryData]);
const exportDefaultQuery = useMemo(
() =>
updateAllQueriesOperators(
currentQuery || initialQueriesMap.logs,
PANEL_TYPES.TIME_SERIES,
DataSource.LOGS,
),
[currentQuery, updateAllQueriesOperators],
);
const listChartData = useGetExplorerQueryRange(
listChartQuery,
PANEL_TYPES.TIME_SERIES,
@ -218,6 +240,66 @@ function LogsExplorerViews(): JSX.Element {
],
);
const {
mutate: updateDashboard,
isLoading: isUpdateDashboardLoading,
} = useUpdateDashboard();
const handleExport = useCallback(
(dashboard: Dashboard | null): void => {
if (!dashboard) return;
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
dashboard,
exportDefaultQuery,
);
updateDashboard(updatedDashboard, {
onSuccess: (data) => {
if (data.error) {
const message =
data.error === 'feature usage exceeded' ? (
<span>
Panel limit exceeded for {DataSource.LOGS} in community edition. Please
checkout our paid plans{' '}
<a
href="https://signoz.io/pricing"
rel="noreferrer noopener"
target="_blank"
>
here
</a>
</span>
) : (
data.error
);
notifications.error({
message,
});
return;
}
const dashboardEditView = `${generatePath(ROUTES.DASHBOARD, {
dashboardId: data?.payload?.uuid,
})}/new?${QueryParams.graphType}=graph&${QueryParams.widgetId}=empty&${
queryParamNamesMap.compositeQuery
}=${encodeURIComponent(JSON.stringify(exportDefaultQuery))}`;
history.push(dashboardEditView);
},
onError: (error) => {
if (axios.isAxiosError(error)) {
notifications.error({
message: error.message,
});
}
},
});
},
[exportDefaultQuery, history, notifications, updateDashboard],
);
useEffect(() => {
const shouldChangeView = isMultipleQueries || isGroupByExist;
@ -238,7 +320,7 @@ function LogsExplorerViews(): JSX.Element {
}, [data]);
useEffect(() => {
if (requestData?.id !== stagedQuery?.id) {
if (requestData?.id !== stagedQuery?.id || isFetching) {
const newRequestData = getRequestData(stagedQuery, {
page: 1,
log: null,
@ -248,7 +330,7 @@ function LogsExplorerViews(): JSX.Element {
setPage(1);
setRequestData(newRequestData);
}
}, [stagedQuery, requestData, getRequestData, pageSize]);
}, [stagedQuery, requestData, getRequestData, pageSize, isFetching]);
const tabsItems: TabsProps['items'] = useMemo(
() => [
@ -333,6 +415,15 @@ function LogsExplorerViews(): JSX.Element {
return (
<>
<LogsExplorerChart isLoading={isFetching} data={chartData} />
{stagedQuery && (
<ActionsWrapper>
<ExportPanel
query={exportDefaultQuery}
isLoading={isUpdateDashboardLoading}
onExport={handleExport}
/>
</ActionsWrapper>
)}
<TabsStyled
items={tabsItems}
defaultActiveKey={panelType || PANEL_TYPES.LIST}

View File

@ -19,7 +19,7 @@ export const useGetExplorerQueryRange = (
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error> => {
const { isEnabledQuery } = useQueryBuilder();
const { selectedTime: globalSelectedInterval } = useSelector<
const { selectedTime: globalSelectedInterval, minTime, maxTime } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
@ -51,7 +51,7 @@ export const useGetExplorerQueryRange = (
{
...options,
retry: false,
queryKey: [key, globalSelectedInterval, requestData],
queryKey: [key, globalSelectedInterval, requestData, minTime, maxTime],
enabled: isEnabled,
},
);

View File

@ -0,0 +1,19 @@
import axios from 'axios';
import { useNotifications } from './useNotifications';
const useAxiosError = (): UseAxiosError => {
const { notifications } = useNotifications();
return (error: unknown): void => {
if (axios.isAxiosError(error)) {
notifications.error({
message: error.message,
});
}
};
};
type UseAxiosError = (error: unknown) => void;
export default useAxiosError;

View File

@ -8,7 +8,7 @@ import { WrapperStyled } from './styles';
function LogsExplorer(): JSX.Element {
return (
<WrapperStyled>
<Row gutter={[0, 28]}>
<Row gutter={[0, 16]}>
<Col xs={24}>
<LogExplorerQuerySection />
</Col>

View File

@ -23,9 +23,9 @@ import { getTabsItems } from './utils';
function TracesExplorer(): JSX.Element {
const { notifications } = useNotifications();
const {
currentQuery,
stagedQuery,
panelType,
updateAllQueriesOperators,
redirectWithQueryBuilderData,
@ -77,11 +77,11 @@ function TracesExplorer(): JSX.Element {
const exportDefaultQuery = useMemo(
() =>
updateAllQueriesOperators(
stagedQuery || initialQueriesMap.traces,
currentQuery || initialQueriesMap.traces,
PANEL_TYPES.TIME_SERIES,
DataSource.TRACES,
),
[stagedQuery, updateAllQueriesOperators],
[currentQuery, updateAllQueriesOperators],
);
const { mutate: updateDashboard, isLoading } = useUpdateDashboard();
@ -97,6 +97,29 @@ function TracesExplorer(): JSX.Element {
updateDashboard(updatedDashboard, {
onSuccess: (data) => {
if (data.error) {
const message =
data.error === 'feature usage exceeded' ? (
<span>
Panel limit exceeded for {DataSource.TRACES} in community edition.
Please checkout our paid plans{' '}
<a
href="https://signoz.io/pricing"
rel="noreferrer noopener"
target="_blank"
>
here
</a>
</span>
) : (
data.error
);
notifications.error({
message,
});
return;
}
const dashboardEditView = `${generatePath(ROUTES.DASHBOARD, {
dashboardId: data?.payload?.uuid,
})}/new?${QueryParams.graphType}=graph&${QueryParams.widgetId}=empty&${
@ -159,7 +182,7 @@ function TracesExplorer(): JSX.Element {
<Container>
<ActionsWrapper>
<ExportPanel
query={stagedQuery}
query={exportDefaultQuery}
isLoading={isLoading}
onExport={handleExport}
/>