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 {
|
||||
height: 1rem;
|
||||
height: 0.2rem;
|
||||
width: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user