2025-05-16 15:07:00 +05:30
|
|
|
import './QueryAddOns.styles.scss';
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
import { Button, Radio, RadioChangeEvent } from 'antd';
|
|
|
|
|
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
|
2025-06-07 12:45:01 +05:30
|
|
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
2025-05-11 17:47:30 +05:30
|
|
|
import { GroupByFilter } from 'container/QueryBuilder/filters/GroupByFilter/GroupByFilter';
|
|
|
|
|
import { OrderByFilter } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter';
|
2025-06-05 04:05:33 +05:30
|
|
|
import { ReduceToFilter } from 'container/QueryBuilder/filters/ReduceToFilter/ReduceToFilter';
|
2025-05-11 17:47:30 +05:30
|
|
|
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
|
|
|
|
import { BarChart2, ScrollText, X } from 'lucide-react';
|
2025-06-07 12:45:01 +05:30
|
|
|
import { useCallback, useEffect, useState } from 'react';
|
2025-05-11 17:47:30 +05:30
|
|
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
|
|
|
|
import { DataSource } from 'types/common/queryBuilder';
|
|
|
|
|
|
2025-05-14 16:16:34 +05:30
|
|
|
import HavingFilter from './HavingFilter/HavingFilter';
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
interface AddOn {
|
|
|
|
|
icon: React.ReactNode;
|
|
|
|
|
label: string;
|
|
|
|
|
key: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-07 12:45:01 +05:30
|
|
|
const ADD_ONS_KEYS = {
|
|
|
|
|
GROUP_BY: 'group_by',
|
|
|
|
|
HAVING: 'having',
|
|
|
|
|
ORDER_BY: 'order_by',
|
|
|
|
|
LIMIT: 'limit',
|
|
|
|
|
LEGEND_FORMAT: 'legend_format',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ADD_ONS = [
|
|
|
|
|
{
|
2025-05-11 17:47:30 +05:30
|
|
|
icon: <BarChart2 size={14} />,
|
|
|
|
|
label: 'Group By',
|
|
|
|
|
key: 'group_by',
|
|
|
|
|
},
|
2025-06-07 12:45:01 +05:30
|
|
|
{
|
2025-05-11 17:47:30 +05:30
|
|
|
icon: <ScrollText size={14} />,
|
|
|
|
|
label: 'Having',
|
|
|
|
|
key: 'having',
|
|
|
|
|
},
|
2025-06-07 12:45:01 +05:30
|
|
|
{
|
2025-05-11 17:47:30 +05:30
|
|
|
icon: <ScrollText size={14} />,
|
|
|
|
|
label: 'Order By',
|
|
|
|
|
key: 'order_by',
|
|
|
|
|
},
|
2025-06-07 12:45:01 +05:30
|
|
|
{
|
2025-05-11 17:47:30 +05:30
|
|
|
icon: <ScrollText size={14} />,
|
|
|
|
|
label: 'Limit',
|
|
|
|
|
key: 'limit',
|
|
|
|
|
},
|
2025-06-07 12:45:01 +05:30
|
|
|
{
|
2025-05-11 17:47:30 +05:30
|
|
|
icon: <ScrollText size={14} />,
|
|
|
|
|
label: 'Legend format',
|
|
|
|
|
key: 'legend_format',
|
|
|
|
|
},
|
2025-06-07 12:45:01 +05:30
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const REDUCE_TO = {
|
|
|
|
|
icon: <ScrollText size={14} />,
|
|
|
|
|
label: 'Reduce to',
|
|
|
|
|
key: 'reduce_to',
|
2025-05-11 17:47:30 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function QueryAddOns({
|
|
|
|
|
query,
|
|
|
|
|
version,
|
|
|
|
|
isListViewPanel,
|
2025-06-05 04:05:33 +05:30
|
|
|
showReduceTo,
|
2025-06-07 12:45:01 +05:30
|
|
|
panelType,
|
2025-06-07 13:22:06 +05:30
|
|
|
index,
|
2025-05-11 17:47:30 +05:30
|
|
|
}: {
|
|
|
|
|
query: IBuilderQuery;
|
|
|
|
|
version: string;
|
|
|
|
|
isListViewPanel: boolean;
|
2025-06-05 04:05:33 +05:30
|
|
|
showReduceTo: boolean;
|
2025-06-07 12:45:01 +05:30
|
|
|
panelType: PANEL_TYPES | null;
|
2025-06-07 13:22:06 +05:30
|
|
|
index: number;
|
2025-05-11 17:47:30 +05:30
|
|
|
}): JSX.Element {
|
2025-06-07 12:45:01 +05:30
|
|
|
const [addOns, setAddOns] = useState<AddOn[]>(ADD_ONS);
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
const [selectedViews, setSelectedViews] = useState<AddOn[]>([]);
|
|
|
|
|
|
|
|
|
|
const { handleChangeQueryData } = useQueryOperations({
|
2025-06-07 13:22:06 +05:30
|
|
|
index,
|
2025-05-11 17:47:30 +05:30
|
|
|
query,
|
|
|
|
|
entityVersion: '',
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-07 12:45:01 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
if (panelType === PANEL_TYPES.VALUE) {
|
|
|
|
|
// Filter out all add-ons except legend format
|
|
|
|
|
setAddOns((prevAddOns) =>
|
|
|
|
|
prevAddOns.filter((addOn) => addOn.key === ADD_ONS_KEYS.LEGEND_FORMAT),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
setAddOns(Object.values(ADD_ONS));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add reduce to if showReduceTo is true
|
|
|
|
|
if (showReduceTo) {
|
|
|
|
|
setAddOns((prevAddOns) => [...prevAddOns, REDUCE_TO]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, [panelType]);
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
const handleOptionClick = (e: RadioChangeEvent): void => {
|
|
|
|
|
if (selectedViews.find((view) => view.key === e.target.value.key)) {
|
|
|
|
|
setSelectedViews(
|
|
|
|
|
selectedViews.filter((view) => view.key !== e.target.value.key),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
setSelectedViews([...selectedViews, e.target.value]);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleChangeGroupByKeys = useCallback(
|
|
|
|
|
(value: IBuilderQuery['groupBy']) => {
|
|
|
|
|
handleChangeQueryData('groupBy', value);
|
|
|
|
|
},
|
|
|
|
|
[handleChangeQueryData],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const handleChangeOrderByKeys = useCallback(
|
|
|
|
|
(value: IBuilderQuery['orderBy']) => {
|
|
|
|
|
handleChangeQueryData('orderBy', value);
|
|
|
|
|
},
|
|
|
|
|
[handleChangeQueryData],
|
|
|
|
|
);
|
|
|
|
|
|
2025-06-05 04:05:33 +05:30
|
|
|
const handleChangeReduceTo = useCallback(
|
|
|
|
|
(value: IBuilderQuery['reduceTo']) => {
|
|
|
|
|
handleChangeQueryData('reduceTo', value);
|
|
|
|
|
},
|
|
|
|
|
[handleChangeQueryData],
|
|
|
|
|
);
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
const handleRemoveView = useCallback(
|
|
|
|
|
(key: string): void => {
|
|
|
|
|
setSelectedViews(selectedViews.filter((view) => view.key !== key));
|
|
|
|
|
},
|
|
|
|
|
[selectedViews],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="query-add-ons">
|
|
|
|
|
{selectedViews.length > 0 && (
|
|
|
|
|
<div className="selected-add-ons-content">
|
|
|
|
|
{selectedViews.find((view) => view.key === 'group_by') && (
|
|
|
|
|
<div className="add-on-content">
|
|
|
|
|
<div className="periscope-input-with-label">
|
|
|
|
|
<div className="label">Group By</div>
|
|
|
|
|
<div className="input">
|
|
|
|
|
<GroupByFilter
|
|
|
|
|
disabled={
|
|
|
|
|
query.dataSource === DataSource.METRICS &&
|
|
|
|
|
!query.aggregateAttribute.key
|
|
|
|
|
}
|
|
|
|
|
query={query}
|
|
|
|
|
onChange={handleChangeGroupByKeys}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
|
|
|
|
className="close-btn periscope-btn ghost"
|
|
|
|
|
icon={<X size={16} />}
|
|
|
|
|
onClick={(): void => handleRemoveView('group_by')}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{selectedViews.find((view) => view.key === 'having') && (
|
|
|
|
|
<div className="add-on-content">
|
2025-05-26 03:28:56 +05:30
|
|
|
<div className="periscope-input-with-label">
|
|
|
|
|
<div className="label">Having</div>
|
|
|
|
|
<div className="input">
|
|
|
|
|
<HavingFilter
|
|
|
|
|
onClose={(): void => {
|
|
|
|
|
setSelectedViews(
|
|
|
|
|
selectedViews.filter((view) => view.key !== 'having'),
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-05-11 17:47:30 +05:30
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{selectedViews.find((view) => view.key === 'limit') && (
|
|
|
|
|
<div className="add-on-content">
|
|
|
|
|
<InputWithLabel
|
2025-06-05 04:05:33 +05:30
|
|
|
label="Limit"
|
2025-05-26 03:28:56 +05:30
|
|
|
placeholder="Enter limit"
|
2025-05-11 17:47:30 +05:30
|
|
|
onClose={(): void => {
|
|
|
|
|
setSelectedViews(selectedViews.filter((view) => view.key !== 'limit'));
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{selectedViews.find((view) => view.key === 'order_by') && (
|
|
|
|
|
<div className="add-on-content">
|
|
|
|
|
<div className="periscope-input-with-label">
|
|
|
|
|
<div className="label">Order By</div>
|
|
|
|
|
<div className="input">
|
|
|
|
|
<OrderByFilter
|
|
|
|
|
entityVersion={version}
|
|
|
|
|
query={query}
|
|
|
|
|
onChange={handleChangeOrderByKeys}
|
|
|
|
|
isListViewPanel={isListViewPanel}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
|
|
|
|
className="close-btn periscope-btn ghost"
|
|
|
|
|
icon={<X size={16} />}
|
|
|
|
|
onClick={(): void => handleRemoveView('order_by')}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-06-05 04:05:33 +05:30
|
|
|
|
|
|
|
|
{selectedViews.find((view) => view.key === 'reduce_to') && showReduceTo && (
|
|
|
|
|
<div className="add-on-content">
|
|
|
|
|
<div className="periscope-input-with-label">
|
|
|
|
|
<div className="label">Reduce to</div>
|
|
|
|
|
<div className="input">
|
|
|
|
|
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
className="close-btn periscope-btn ghost"
|
|
|
|
|
icon={<X size={16} />}
|
|
|
|
|
onClick={(): void => handleRemoveView('reduce_to')}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2025-05-11 17:47:30 +05:30
|
|
|
{selectedViews.find((view) => view.key === 'legend_format') && (
|
|
|
|
|
<div className="add-on-content">
|
|
|
|
|
<InputWithLabel
|
2025-06-05 04:05:33 +05:30
|
|
|
label="Legend format"
|
2025-05-11 17:47:30 +05:30
|
|
|
placeholder="Write legend format"
|
|
|
|
|
onClose={(): void => {
|
|
|
|
|
setSelectedViews(
|
|
|
|
|
selectedViews.filter((view) => view.key !== 'legend_format'),
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="add-ons-list">
|
|
|
|
|
<Radio.Group
|
|
|
|
|
className="add-ons-tabs"
|
|
|
|
|
onChange={handleOptionClick}
|
|
|
|
|
value={selectedViews}
|
|
|
|
|
>
|
2025-06-07 12:45:01 +05:30
|
|
|
{addOns.map((addOn) => (
|
|
|
|
|
<Radio.Button
|
|
|
|
|
key={addOn.label}
|
|
|
|
|
className={
|
|
|
|
|
selectedViews.find((view) => view.key === addOn.key)
|
|
|
|
|
? 'selected-view tab'
|
|
|
|
|
: 'tab'
|
|
|
|
|
}
|
|
|
|
|
value={addOn}
|
|
|
|
|
>
|
|
|
|
|
<div className="add-on-tab-title">
|
|
|
|
|
{addOn.icon}
|
|
|
|
|
{addOn.label}
|
|
|
|
|
</div>
|
|
|
|
|
</Radio.Button>
|
|
|
|
|
))}
|
2025-05-11 17:47:30 +05:30
|
|
|
</Radio.Group>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default QueryAddOns;
|