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 (
-
-
-
-
-
-
+
+
+
+
+
+
+
-
- {showFunctions &&
- isMetricsDataSource &&
- query &&
- onQueryFunctionsUpdates && (
-
- )}
-
+
+ {entityData.queryName}
+
+
+ {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,