mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 01:15:52 +08:00
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:
parent
e3d26d3f10
commit
2e85bd0264
@ -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;
|
||||
|
@ -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;
|
||||
`;
|
||||
|
@ -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}
|
||||
|
@ -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,
|
||||
},
|
||||
);
|
||||
|
19
frontend/src/hooks/useAxiosError.tsx
Normal file
19
frontend/src/hooks/useAxiosError.tsx
Normal 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;
|
@ -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>
|
||||
|
@ -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}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user