feat: add clone query functionality (#4617)

* feat: add clone query functionality

* feat: update ui
This commit is contained in:
Yunus M 2024-03-07 11:41:29 +05:30 committed by GitHub
parent 0c4149225f
commit f9b3ca01f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 126 additions and 52 deletions

View File

@ -152,7 +152,7 @@
}
::-webkit-scrollbar {
height: 1rem;
height: 0.2rem;
width: 0.2rem;
}
}

View File

@ -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 (
<div className="qb-entity-options">
<div className="left-col-items">
<div className="options periscope-btn-group">
<Button.Group className="options-group">
<Button
value="search"
className="periscope-btn collapse"
onClick={onCollapseEntity}
>
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
</Button>
<Button
value="query-builder"
className="periscope-btn visibility-toggle"
onClick={onToggleVisibility}
disabled={isListViewPanel}
>
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
</Button>
<Col span={24}>
<div className="qb-entity-options">
<div className="left-col-items">
<div className="options periscope-btn-group">
<Button.Group>
<Button
value="search"
className="periscope-btn collapse"
onClick={onCollapseEntity}
>
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
</Button>
<Button
value="query-builder"
className="periscope-btn visibility-toggle"
onClick={onToggleVisibility}
disabled={isListViewPanel}
>
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
</Button>
<Button
className={cx(
'periscope-btn',
entityType === 'query' ? 'query-name' : 'formula-name',
{entityType === 'query' && (
<Tooltip title={`Clone Query ${entityData.queryName}`}>
<Button className={cx('periscope-btn')} onClick={handleCloneEntity}>
<Copy size={14} />
</Button>
</Tooltip>
)}
>
{entityData.queryName}
</Button>
{showFunctions &&
isMetricsDataSource &&
query &&
onQueryFunctionsUpdates && (
<QueryFunctions
queryFunctions={query.functions}
onChange={onQueryFunctionsUpdates}
/>
)}
</Button.Group>
<Button
className={cx(
'periscope-btn',
entityType === 'query' ? 'query-name' : 'formula-name',
)}
>
{entityData.queryName}
</Button>
{showFunctions &&
isMetricsDataSource &&
query &&
onQueryFunctionsUpdates && (
<QueryFunctions
queryFunctions={query.functions}
onChange={onQueryFunctionsUpdates}
/>
)}
</Button.Group>
</div>
{isCollapsed && (
<div className="title">
<span className="entityType"> {entityType} </span> -{' '}
<span className="entityData"> {entityData.queryName} </span>
</div>
)}
</div>
{isCollapsed && (
<div className="title">
<span className="entityType"> {entityType} </span> -{' '}
<span className="entityData"> {entityData.queryName} </span>
</div>
{showDeleteButton && (
<Button className="periscope-btn ghost" onClick={onDelete}>
<Trash2 size={14} />
</Button>
)}
</div>
{showDeleteButton && (
<Button className="periscope-btn ghost" onClick={onDelete}>
<Trash2 size={14} />
</Button>
)}
</div>
</Col>
);
}
@ -104,4 +132,5 @@ QBEntityOptions.defaultProps = {
isMetricsDataSource: false,
onQueryFunctionsUpdates: undefined,
showFunctions: false,
onCloneQuery: noop,
};

View File

@ -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}

View File

@ -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;

View File

@ -68,6 +68,7 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
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,

View File

@ -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,