Yevhen Shevchenko 8c2f33c95a
feat: Connect Query builder with graph (#2611)
* fix: having value data type

* feat: connect new builder to dashboard

* Fix/query builder filters (#2623)

* feat: rename query data type

* fix: remove reset of groupBy

* fix: filters search

* fix: calls autocomplete times

* fix: response mapper

* fix: removee unnecessary field

* fix: no check ts types for old query builder

* fix: disable check utils old builder
2023-05-02 19:38:03 +05:30

294 lines
8.4 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unused-vars */
import { Button, Tabs } from 'antd';
import TextToolTip from 'components/TextToolTip';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import { QueryBuilder } from 'container/QueryBuilder';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { cloneDeep, isEqual } from 'lodash-es';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { connect, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import {
UpdateQuery,
UpdateQueryProps,
} from 'store/actions/dashboard/updateQuery';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { Query, Widgets } from 'types/api/dashboard/getAll';
import { EQueryType } from 'types/common/dashboard';
import DashboardReducer from 'types/reducer/dashboards';
import { v4 as uuid } from 'uuid';
import {
WIDGET_CLICKHOUSE_QUERY_KEY_NAME,
WIDGET_PROMQL_QUERY_KEY_NAME,
} from './constants';
import ClickHouseQueryContainer from './QueryBuilder/clickHouse';
import PromQLQueryContainer from './QueryBuilder/promQL';
import TabHeader from './TabHeader';
import { IHandleUpdatedQuery } from './types';
import { getQueryKey } from './utils/getQueryKey';
import { showUnstagedStashConfirmBox } from './utils/userSettings';
function QuerySection({
handleUnstagedChanges,
updateQuery,
selectedGraph,
}: QueryProps): JSX.Element {
const { queryBuilderData, initQueryBuilderData } = useQueryBuilder();
const [localQueryChanges, setLocalQueryChanges] = useState<Query>({} as Query);
const [rctTabKey, setRctTabKey] = useState<
Record<keyof typeof EQueryType, string>
>({
QUERY_BUILDER: uuid(),
CLICKHOUSE: uuid(),
PROM: uuid(),
});
const { dashboards, isLoadingQueryResult } = useSelector<
AppState,
DashboardReducer
>((state) => state.dashboards);
const [selectedDashboards] = dashboards;
const { search } = useLocation();
const { widgets } = selectedDashboards.data;
const urlQuery = useMemo(() => new URLSearchParams(search), [search]);
const getWidget = useCallback(() => {
const widgetId = urlQuery.get('widgetId');
return widgets?.find((e) => e.id === widgetId);
}, [widgets, urlQuery]);
const selectedWidget = getWidget() as Widgets;
const [queryCategory, setQueryCategory] = useState<EQueryType>(
selectedWidget.query.queryType,
);
const { query } = selectedWidget || {};
useEffect(() => {
initQueryBuilderData(query.builder);
setLocalQueryChanges(cloneDeep(query) as Query);
}, [query, initQueryBuilderData]);
const queryDiff = (
queryA: Query,
queryB: Query,
queryCategory: EQueryType,
): boolean => {
const keyOfConcern = getQueryKey(queryCategory);
return !isEqual(queryA[keyOfConcern], queryB[keyOfConcern]);
};
useEffect(() => {
handleUnstagedChanges(
queryDiff(query, localQueryChanges, parseInt(`${queryCategory}`, 10)),
);
}, [handleUnstagedChanges, localQueryChanges, query, queryCategory]);
const regenRctKeys = (): void => {
setRctTabKey((prevState) => {
const newState = prevState;
Object.keys(newState).forEach((key) => {
newState[key as keyof typeof EQueryType] = uuid();
});
return cloneDeep(newState);
});
};
const handleStageQuery = (): void => {
updateQuery({
updatedQuery: {
...localQueryChanges,
builder: queryBuilderData,
},
widgetId: urlQuery.get('widgetId') || '',
yAxisUnit: selectedWidget.yAxisUnit,
});
};
const handleQueryCategoryChange = (qCategory: string): void => {
// If true, then it means that the user has made some changes and haven't staged them
const unstagedChanges = queryDiff(
query,
localQueryChanges,
parseInt(`${queryCategory}`, 10),
);
if (unstagedChanges && showUnstagedStashConfirmBox()) {
// eslint-disable-next-line no-alert
window.confirm(
"You are trying to navigate to different tab with unstaged changes. Your current changes will be purged. Press 'Stage & Run Query' to stage them.",
);
return;
}
setQueryCategory(parseInt(`${qCategory}`, 10));
const newLocalQuery = {
...cloneDeep(query),
queryType: parseInt(`${qCategory}`, 10),
};
setLocalQueryChanges(newLocalQuery);
regenRctKeys();
updateQuery({
updatedQuery: newLocalQuery,
widgetId: urlQuery.get('widgetId') || '',
yAxisUnit: selectedWidget.yAxisUnit,
});
};
const handleLocalQueryUpdate = ({
updatedQuery,
}: IHandleUpdatedQuery): void => {
setLocalQueryChanges(cloneDeep(updatedQuery));
};
const items = [
{
key: EQueryType.QUERY_BUILDER.toString(),
label: 'Query Builder',
tab: (
<TabHeader
tabName="Query Builder"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.QUERY_BUILDER,
)}
/>
),
children: <QueryBuilder panelType={selectedGraph} />,
},
{
key: EQueryType.CLICKHOUSE.toString(),
label: 'ClickHouse Query',
tab: (
<TabHeader
tabName="ClickHouse Query"
hasUnstagedChanges={queryDiff(
query,
localQueryChanges,
EQueryType.CLICKHOUSE,
)}
/>
),
children: (
<ClickHouseQueryContainer
key={rctTabKey.CLICKHOUSE}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]}
/>
),
},
{
key: EQueryType.PROM.toString(),
label: 'PromQL',
tab: (
<TabHeader
tabName="PromQL"
hasUnstagedChanges={queryDiff(query, localQueryChanges, EQueryType.PROM)}
/>
),
children: (
<PromQLQueryContainer
key={rctTabKey.PROM}
queryData={localQueryChanges}
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery });
}}
promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]}
/>
),
},
];
return (
<>
<div style={{ display: 'flex' }}>
<Tabs
type="card"
style={{ width: '100%' }}
defaultActiveKey={queryCategory.toString()}
activeKey={queryCategory.toString()}
onChange={handleQueryCategoryChange}
tabBarExtraContent={
<span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<TextToolTip
{...{
text: `This will temporarily save the current query and graph state. This will persist across tab change`,
}}
/>
<Button
loading={isLoadingQueryResult}
type="primary"
onClick={handleStageQuery}
>
Stage & Run Query
</Button>
</span>
}
items={items}
/>
</div>
{/* {localQueryChanges.map((e, index) => (
// <Query
// name={e.name}
// currentIndex={index}
// selectedTime={selectedTime}
// key={JSON.stringify(e)}
// queryInput={e}
// updatedLocalQuery={handleLocalQueryUpdate}
// queryCategory={queryCategory}
// />
<QueryBuilder
key={`${JSON.stringify(e)}`}
name={e.name}
updateQueryData={(updatedQuery) =>
handleLocalQueryUpdate({ currentIndex: index, updatedQuery })
}
onDelete={() => handleDeleteQuery({ currentIndex: index })}
queryData={e}
queryCategory={queryCategory}
/>
))} */}
</>
);
}
interface DispatchProps {
// createQuery: ({
// widgetId,
// }: CreateQueryProps) => (dispatch: Dispatch<AppActions>) => void;
updateQuery: (
props: UpdateQueryProps,
) => (dispatch: Dispatch<AppActions>) => void;
// getQueryResults: (
// props: GetQueryResultsProps,
// ) => (dispatch: Dispatch<AppActions>) => void;
// updateQueryType: (
// props: UpdateQueryTypeProps,
// ) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
// createQuery: bindActionCreators(CreateQuery, dispatch),
updateQuery: bindActionCreators(UpdateQuery, dispatch),
// getQueryResults: bindActionCreators(GetQueryResults, dispatch),
// updateQueryType: bindActionCreators(UpdateQueryType, dispatch),
});
interface QueryProps extends DispatchProps {
selectedGraph: GRAPH_TYPES;
selectedTime: timePreferance;
handleUnstagedChanges: (arg0: boolean) => void;
}
export default connect(null, mapDispatchToProps)(QuerySection);