mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-24 07:04:26 +08:00
feat: add clone query functionality (#4617)
* feat: add clone query functionality * feat: update ui
This commit is contained in:
parent
0c4149225f
commit
f9b3ca01f9
@ -152,7 +152,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
height: 1rem;
|
height: 0.2rem;
|
||||||
width: 0.2rem;
|
width: 0.2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user