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 { ::-webkit-scrollbar {
height: 1rem; height: 0.2rem;
width: 0.2rem; width: 0.2rem;
} }
} }

View File

@ -1,8 +1,18 @@
/* eslint-disable sonarjs/no-duplicate-string */
import './QBEntityOptions.styles.scss'; 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 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 { import {
IBuilderQuery, IBuilderQuery,
QueryFunctionProps, QueryFunctionProps,
@ -18,6 +28,7 @@ interface QBEntityOptionsProps {
entityType: string; entityType: string;
entityData: any; entityData: any;
onDelete: () => void; onDelete: () => void;
onCloneQuery?: (type: string, query: IBuilderQuery) => void;
onToggleVisibility: () => void; onToggleVisibility: () => void;
onCollapseEntity: () => void; onCollapseEntity: () => void;
onQueryFunctionsUpdates?: (functions: QueryFunctionProps[]) => void; onQueryFunctionsUpdates?: (functions: QueryFunctionProps[]) => void;
@ -33,68 +44,85 @@ export default function QBEntityOptions({
entityType, entityType,
entityData, entityData,
onDelete, onDelete,
onCloneQuery,
onToggleVisibility, onToggleVisibility,
onCollapseEntity, onCollapseEntity,
showDeleteButton, showDeleteButton,
onQueryFunctionsUpdates, onQueryFunctionsUpdates,
isListViewPanel, isListViewPanel,
}: QBEntityOptionsProps): JSX.Element { }: QBEntityOptionsProps): JSX.Element {
const handleCloneEntity = (): void => {
if (isFunction(onCloneQuery)) {
onCloneQuery(entityType, entityData);
}
};
return ( return (
<div className="qb-entity-options"> <Col span={24}>
<div className="left-col-items"> <div className="qb-entity-options">
<div className="options periscope-btn-group"> <div className="left-col-items">
<Button.Group className="options-group"> <div className="options periscope-btn-group">
<Button <Button.Group>
value="search" <Button
className="periscope-btn collapse" value="search"
onClick={onCollapseEntity} className="periscope-btn collapse"
> onClick={onCollapseEntity}
{isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />} >
</Button> {isCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
<Button </Button>
value="query-builder" <Button
className="periscope-btn visibility-toggle" value="query-builder"
onClick={onToggleVisibility} className="periscope-btn visibility-toggle"
disabled={isListViewPanel} onClick={onToggleVisibility}
> disabled={isListViewPanel}
{entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />} >
</Button> {entityData.disabled ? <EyeOff size={16} /> : <Eye size={16} />}
</Button>
<Button {entityType === 'query' && (
className={cx( <Tooltip title={`Clone Query ${entityData.queryName}`}>
'periscope-btn', <Button className={cx('periscope-btn')} onClick={handleCloneEntity}>
entityType === 'query' ? 'query-name' : 'formula-name', <Copy size={14} />
</Button>
</Tooltip>
)} )}
>
{entityData.queryName}
</Button>
{showFunctions && <Button
isMetricsDataSource && className={cx(
query && 'periscope-btn',
onQueryFunctionsUpdates && ( entityType === 'query' ? 'query-name' : 'formula-name',
<QueryFunctions )}
queryFunctions={query.functions} >
onChange={onQueryFunctionsUpdates} {entityData.queryName}
/> </Button>
)}
</Button.Group> {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> </div>
{isCollapsed && ( {showDeleteButton && (
<div className="title"> <Button className="periscope-btn ghost" onClick={onDelete}>
<span className="entityType"> {entityType} </span> -{' '} <Trash2 size={14} />
<span className="entityData"> {entityData.queryName} </span> </Button>
</div>
)} )}
</div> </div>
</Col>
{showDeleteButton && (
<Button className="periscope-btn ghost" onClick={onDelete}>
<Trash2 size={14} />
</Button>
)}
</div>
); );
} }
@ -104,4 +132,5 @@ QBEntityOptions.defaultProps = {
isMetricsDataSource: false, isMetricsDataSource: false,
onQueryFunctionsUpdates: undefined, onQueryFunctionsUpdates: undefined,
showFunctions: false, showFunctions: false,
onCloneQuery: noop,
}; };

View File

@ -54,7 +54,7 @@ export const Query = memo(function Query({
showFunctions = false, showFunctions = false,
version, version,
}: QueryProps): JSX.Element { }: QueryProps): JSX.Element {
const { panelType, currentQuery } = useQueryBuilder(); const { panelType, currentQuery, cloneQuery } = useQueryBuilder();
const { pathname } = useLocation(); const { pathname } = useLocation();
const [isCollapse, setIsCollapsed] = useState(false); const [isCollapse, setIsCollapsed] = useState(false);
@ -331,6 +331,7 @@ export const Query = memo(function Query({
entityData={query} entityData={query}
onToggleVisibility={handleToggleDisableQuery} onToggleVisibility={handleToggleDisableQuery}
onDelete={handleDeleteQuery} onDelete={handleDeleteQuery}
onCloneQuery={cloneQuery}
onCollapseEntity={handleToggleCollapsQuery} onCollapseEntity={handleToggleCollapsQuery}
query={query} query={query}
onQueryFunctionsUpdates={handleQueryFunctionsUpdates} onQueryFunctionsUpdates={handleQueryFunctionsUpdates}

View File

@ -18,6 +18,7 @@ export const StyledCheckOutlined = styled(CheckOutlined)`
export const TagContainer = styled(Tag)` export const TagContainer = styled(Tag)`
&&& { &&& {
display: inline-block;
border-radius: 3px; border-radius: 3px;
padding: 0.1rem 0.2rem; padding: 0.1rem 0.2rem;
font-weight: 300; font-weight: 300;

View File

@ -68,6 +68,7 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
removeQueryBuilderEntityByIndex: () => {}, removeQueryBuilderEntityByIndex: () => {},
removeQueryTypeItemByIndex: () => {}, removeQueryTypeItemByIndex: () => {},
addNewBuilderQuery: () => {}, addNewBuilderQuery: () => {},
cloneQuery: () => {},
addNewFormula: () => {}, addNewFormula: () => {},
addNewQueryItem: () => {}, addNewQueryItem: () => {},
redirectWithQueryBuilderData: () => {}, redirectWithQueryBuilderData: () => {},
@ -307,6 +308,23 @@ export function QueryBuilderProvider({
[initialDataSource], [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 createNewBuilderFormula = useCallback((formulas: IBuilderFormula[]) => {
const existNames = formulas.map((item) => item.queryName); const existNames = formulas.map((item) => item.queryName);
@ -373,6 +391,28 @@ export function QueryBuilderProvider({
}); });
}, [createNewBuilderQuery]); }, [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(() => { const addNewFormula = useCallback(() => {
setCurrentQuery((prevState) => { setCurrentQuery((prevState) => {
if (prevState.builder.queryFormulas.length >= MAX_FORMULAS) return prevState; if (prevState.builder.queryFormulas.length >= MAX_FORMULAS) return prevState;
@ -647,6 +687,7 @@ export function QueryBuilderProvider({
handleSetConfig, handleSetConfig,
removeQueryBuilderEntityByIndex, removeQueryBuilderEntityByIndex,
removeQueryTypeItemByIndex, removeQueryTypeItemByIndex,
cloneQuery,
addNewBuilderQuery, addNewBuilderQuery,
addNewFormula, addNewFormula,
addNewQueryItem, addNewQueryItem,
@ -671,6 +712,7 @@ export function QueryBuilderProvider({
handleSetConfig, handleSetConfig,
removeQueryBuilderEntityByIndex, removeQueryBuilderEntityByIndex,
removeQueryTypeItemByIndex, removeQueryTypeItemByIndex,
cloneQuery,
addNewBuilderQuery, addNewBuilderQuery,
addNewFormula, addNewFormula,
addNewQueryItem, addNewQueryItem,

View File

@ -211,6 +211,7 @@ export type QueryBuilderContextType = {
) => void; ) => void;
addNewBuilderQuery: () => void; addNewBuilderQuery: () => void;
addNewFormula: () => void; addNewFormula: () => void;
cloneQuery: (type: string, query: IBuilderQuery) => void;
addNewQueryItem: (type: EQueryType.PROM | EQueryType.CLICKHOUSE) => void; addNewQueryItem: (type: EQueryType.PROM | EQueryType.CLICKHOUSE) => void;
redirectWithQueryBuilderData: ( redirectWithQueryBuilderData: (
query: Query, query: Query,