diff --git a/frontend/src/container/ExportPanel/index.tsx b/frontend/src/container/ExportPanel/index.tsx index 35ac124fae..c9c7d29383 100644 --- a/frontend/src/container/ExportPanel/index.tsx +++ b/frontend/src/container/ExportPanel/index.tsx @@ -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; diff --git a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styled.ts b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styled.ts index 4fd3046e3b..a73fe526fb 100644 --- a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styled.ts +++ b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styled.ts @@ -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; +`; diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index 560c498b3f..1875884c41 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -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' ? ( + + Panel limit exceeded for {DataSource.LOGS} in community edition. Please + checkout our paid plans{' '} + + here + + + ) : ( + 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 ( <> + {stagedQuery && ( + + + + )} , Error>, ): UseQueryResult, 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, }, ); diff --git a/frontend/src/hooks/useAxiosError.tsx b/frontend/src/hooks/useAxiosError.tsx new file mode 100644 index 0000000000..bad81754b3 --- /dev/null +++ b/frontend/src/hooks/useAxiosError.tsx @@ -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; diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx index bf9bf40d9e..3e6613ed77 100644 --- a/frontend/src/pages/LogsExplorer/index.tsx +++ b/frontend/src/pages/LogsExplorer/index.tsx @@ -8,7 +8,7 @@ import { WrapperStyled } from './styles'; function LogsExplorer(): JSX.Element { return ( - + diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index 3bcd8f7244..378e09d585 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -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' ? ( + + Panel limit exceeded for {DataSource.TRACES} in community edition. + Please checkout our paid plans{' '} + + here + + + ) : ( + 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 {