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 {