From f9b3ca01f96d93c06c00d7aed89f77bf628fb2f1 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Thu, 7 Mar 2024 11:41:29 +0530 Subject: [PATCH] feat: add clone query functionality (#4617) * feat: add clone query functionality * feat: update ui --- .../QueryBuilder/QueryBuilder.styles.scss | 2 +- .../QBEntityOptions/QBEntityOptions.tsx | 129 +++++++++++------- .../QueryBuilder/components/Query/Query.tsx | 3 +- .../filters/QueryBuilderSearch/style.ts | 1 + frontend/src/providers/QueryBuilder.tsx | 42 ++++++ frontend/src/types/common/queryBuilder.ts | 1 + 6 files changed, 126 insertions(+), 52 deletions(-) diff --git a/frontend/src/container/QueryBuilder/QueryBuilder.styles.scss b/frontend/src/container/QueryBuilder/QueryBuilder.styles.scss index b23521ad68..dbb7a962ef 100644 --- a/frontend/src/container/QueryBuilder/QueryBuilder.styles.scss +++ b/frontend/src/container/QueryBuilder/QueryBuilder.styles.scss @@ -152,7 +152,7 @@ } ::-webkit-scrollbar { - height: 1rem; + height: 0.2rem; width: 0.2rem; } } diff --git a/frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.tsx b/frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.tsx index 6f765c7b0e..8730506a88 100644 --- a/frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.tsx +++ b/frontend/src/container/QueryBuilder/components/QBEntityOptions/QBEntityOptions.tsx @@ -1,8 +1,18 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import './QBEntityOptions.styles.scss'; -import { Button } from 'antd'; +import { Button, Col, Tooltip } from 'antd'; +import { noop } from 'antd/lib/_util/warning'; import cx from 'classnames'; -import { ChevronDown, ChevronRight, Eye, EyeOff, Trash2 } from 'lucide-react'; +import { isFunction } from 'lodash-es'; +import { + ChevronDown, + ChevronRight, + Copy, + Eye, + EyeOff, + Trash2, +} from 'lucide-react'; import { IBuilderQuery, QueryFunctionProps, @@ -18,6 +28,7 @@ interface QBEntityOptionsProps { entityType: string; entityData: any; onDelete: () => void; + onCloneQuery?: (type: string, query: IBuilderQuery) => void; onToggleVisibility: () => void; onCollapseEntity: () => void; onQueryFunctionsUpdates?: (functions: QueryFunctionProps[]) => void; @@ -33,68 +44,85 @@ export default function QBEntityOptions({ entityType, entityData, onDelete, + onCloneQuery, onToggleVisibility, onCollapseEntity, showDeleteButton, onQueryFunctionsUpdates, isListViewPanel, }: QBEntityOptionsProps): JSX.Element { + const handleCloneEntity = (): void => { + if (isFunction(onCloneQuery)) { + onCloneQuery(entityType, entityData); + } + }; + return ( -
-
-
- - - + +
+
+
+ + + - + )} - > - {entityData.queryName} - - {showFunctions && - isMetricsDataSource && - query && - onQueryFunctionsUpdates && ( - - )} - + + + {showFunctions && + isMetricsDataSource && + query && + onQueryFunctionsUpdates && ( + + )} + +
+ + {isCollapsed && ( +
+ {entityType} -{' '} + {entityData.queryName} +
+ )}
- {isCollapsed && ( -
- {entityType} -{' '} - {entityData.queryName} -
+ {showDeleteButton && ( + )}
- - {showDeleteButton && ( - - )} -
+ ); } @@ -104,4 +132,5 @@ QBEntityOptions.defaultProps = { isMetricsDataSource: false, onQueryFunctionsUpdates: undefined, showFunctions: false, + onCloneQuery: noop, }; diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx index 335b998334..1bb761fde7 100644 --- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx +++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx @@ -54,7 +54,7 @@ export const Query = memo(function Query({ showFunctions = false, version, }: QueryProps): JSX.Element { - const { panelType, currentQuery } = useQueryBuilder(); + const { panelType, currentQuery, cloneQuery } = useQueryBuilder(); const { pathname } = useLocation(); const [isCollapse, setIsCollapsed] = useState(false); @@ -331,6 +331,7 @@ export const Query = memo(function Query({ entityData={query} onToggleVisibility={handleToggleDisableQuery} onDelete={handleDeleteQuery} + onCloneQuery={cloneQuery} onCollapseEntity={handleToggleCollapsQuery} query={query} onQueryFunctionsUpdates={handleQueryFunctionsUpdates} diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/style.ts b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/style.ts index 55a11cc76d..5e010ff34a 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/style.ts +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/style.ts @@ -18,6 +18,7 @@ export const StyledCheckOutlined = styled(CheckOutlined)` export const TagContainer = styled(Tag)` &&& { + display: inline-block; border-radius: 3px; padding: 0.1rem 0.2rem; font-weight: 300; diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index 80bc673a83..1fde9fc224 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -68,6 +68,7 @@ export const QueryBuilderContext = createContext({ removeQueryBuilderEntityByIndex: () => {}, removeQueryTypeItemByIndex: () => {}, addNewBuilderQuery: () => {}, + cloneQuery: () => {}, addNewFormula: () => {}, addNewQueryItem: () => {}, redirectWithQueryBuilderData: () => {}, @@ -307,6 +308,23 @@ export function QueryBuilderProvider({ [initialDataSource], ); + const cloneNewBuilderQuery = useCallback( + (queries: IBuilderQuery[], query: IBuilderQuery): IBuilderQuery => { + const existNames = queries.map((item) => item.queryName); + const clonedQuery: IBuilderQuery = { + ...query, + queryName: createNewBuilderItemName({ existNames, sourceNames: alphabet }), + expression: createNewBuilderItemName({ + existNames, + sourceNames: alphabet, + }), + }; + + return clonedQuery; + }, + [], + ); + const createNewBuilderFormula = useCallback((formulas: IBuilderFormula[]) => { const existNames = formulas.map((item) => item.queryName); @@ -373,6 +391,28 @@ export function QueryBuilderProvider({ }); }, [createNewBuilderQuery]); + const cloneQuery = useCallback( + (type: string, query: IBuilderQuery): void => { + setCurrentQuery((prevState) => { + if (prevState.builder.queryData.length >= MAX_QUERIES) return prevState; + + const clonedQuery = cloneNewBuilderQuery( + prevState.builder.queryData, + query, + ); + + return { + ...prevState, + builder: { + ...prevState.builder, + queryData: [...prevState.builder.queryData, clonedQuery], + }, + }; + }); + }, + [cloneNewBuilderQuery], + ); + const addNewFormula = useCallback(() => { setCurrentQuery((prevState) => { if (prevState.builder.queryFormulas.length >= MAX_FORMULAS) return prevState; @@ -647,6 +687,7 @@ export function QueryBuilderProvider({ handleSetConfig, removeQueryBuilderEntityByIndex, removeQueryTypeItemByIndex, + cloneQuery, addNewBuilderQuery, addNewFormula, addNewQueryItem, @@ -671,6 +712,7 @@ export function QueryBuilderProvider({ handleSetConfig, removeQueryBuilderEntityByIndex, removeQueryTypeItemByIndex, + cloneQuery, addNewBuilderQuery, addNewFormula, addNewQueryItem, diff --git a/frontend/src/types/common/queryBuilder.ts b/frontend/src/types/common/queryBuilder.ts index 731de0650d..02ea8beebb 100644 --- a/frontend/src/types/common/queryBuilder.ts +++ b/frontend/src/types/common/queryBuilder.ts @@ -211,6 +211,7 @@ export type QueryBuilderContextType = { ) => void; addNewBuilderQuery: () => void; addNewFormula: () => void; + cloneQuery: (type: string, query: IBuilderQuery) => void; addNewQueryItem: (type: EQueryType.PROM | EQueryType.CLICKHOUSE) => void; redirectWithQueryBuilderData: ( query: Query,