diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/AggregateUI/Select.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/AggregateUI/Select.jsx index d579e0e672..2a9feb1d43 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/AggregateUI/Select.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/AggregateUI/Select.jsx @@ -24,7 +24,7 @@ export const SelectBox = ({ { const { columns, @@ -46,6 +46,7 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { }, [operation, handleOptionsChange, joinTableOptionsChange]); const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false); + const [currentAggregateKeyForDeleteConfirmation, setCurrentAggregateKeyForDeleteConfirmation] = useState(null); const addNewAggregateOption = () => { const currentAggregates = { ...(operationDetails?.aggregates || {}) }; @@ -79,11 +80,11 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { }; const value = getValue(operation, optionToUpdate, selectedValue); - const table_id = selectedValue.hasOwnProperty('tableId') ? selectedValue.tableId : selectedTableId; + const tableIdExist = selectedValue.hasOwnProperty('tableId'); const aggregateToUpdate = { ...currentAggregates[key], [optionToUpdate]: value, - table_id, + ...(tableIdExist && { table_id: selectedValue.tableId }), }; const updatedAggregates = { ...currentAggregates, @@ -92,48 +93,88 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { handleChange('aggregates', updatedAggregates); }; - const handleDeleteAggregate = (aggregateKey) => { - const currentAggregates = { ...(operationDetails?.aggregates || {}) }; - const numberOfAggregates = Object.keys(currentAggregates).length; + const showConfirmationMoalForLastFilledValue = (currentAggregates, aggregateKey) => { + if (!currentAggregates[aggregateKey]) { + return false; + } - if (numberOfAggregates > 1) { - delete currentAggregates[aggregateKey]; - try { - handleChange('aggregates', currentAggregates); - toast.success('Aggregate function deleted successfully!'); - return; - } catch (error) { - return toast.error('Could not delete aggregate function. Please try again!'); - } - } else { - const currentGroupBy = { ...(operationDetails?.group_by || {}) }; - const isValidGroupByPresent = Object.entries(currentGroupBy).some(([tableId, selectedColumn]) => { - if (tableId && selectedColumn.length >= 1) { - return true; - } - return false; - }); - if (isValidGroupByPresent) { - setShowDeleteConfirmation(true); - } else { - try { - delete currentAggregates[aggregateKey]; - handleChange('aggregates', currentAggregates); - toast.success('Aggregate function deleted successfully!'); - return; - } catch (error) { - return toast.error('Could not delete aggregate function. Please try again!'); + // Check if all values for the matched key are truthy + const allValuesTruthy = Object.values(currentAggregates[aggregateKey]).every((value) => Boolean(value)); + if (!allValuesTruthy) { + return false; + } + + // Check if the rest of the keys have values that are either all empty or partially filled + const keys = Object.keys(currentAggregates); + for (const key of keys) { + if (key !== aggregateKey) { + const values = Object.values(currentAggregates[key]); + const allEmpty = values.every((value) => value === ''); + const partiallyFilled = values.some((value) => value !== '') && values.some((value) => value === ''); + + if (!(allEmpty || partiallyFilled)) { + return false; } } } + + return true; }; - const executeAggregateDeletion = (aggregateKey) => { + const handleDeleteAggregate = (aggregateKey) => { + const currentAggregates = { ...(operationDetails?.aggregates || {}) }; + const numberOfAggregates = Object.keys(currentAggregates).length; + const currentGroupBy = { ...(operationDetails?.group_by || {}) }; + const showConfirmationModal = showConfirmationMoalForLastFilledValue(currentAggregates, aggregateKey); + + const isValidGroupByPresent = Object.values(currentGroupBy).some((selectedColumn) => selectedColumn.length >= 1); + + const deleteAggregate = () => { + delete currentAggregates[aggregateKey]; + handleChange('aggregates', currentAggregates); + return toast.success('Aggregate function deleted successfully!'); + }; + + const showError = () => { + return toast.error('Could not delete aggregate function. Please try again!'); + }; + try { + if (numberOfAggregates > 1) { + if (showConfirmationModal && isValidGroupByPresent) { + setCurrentAggregateKeyForDeleteConfirmation(aggregateKey); + setShowDeleteConfirmation(true); + return; + } else { + deleteAggregate(); + return; + } + } else { + if (isValidGroupByPresent) { + setCurrentAggregateKeyForDeleteConfirmation(aggregateKey); + setShowDeleteConfirmation(true); + return; + } else { + deleteAggregate(); + return; + } + } + } catch (error) { + showError(); + return; + } + }; + + const executeAggregateDeletion = () => { + const aggregateKey = currentAggregateKeyForDeleteConfirmation || ''; + + try { + if (!aggregateKey) { + throw new Error('Could not delete aggregate function. Please try again!'); + } const currentAggregates = { ...(operationDetails?.aggregates || {}) }; delete currentAggregates[aggregateKey]; - const currentGroupBy = { ...(operationDetails?.group_by || {}) }; - delete currentGroupBy?.[selectedTableId]; + const currentGroupBy = {}; handleChange('group_by', currentGroupBy); handleChange('aggregates', currentAggregates); @@ -244,35 +285,43 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { }, ]; + const getJoinTableOption = (value, tableId) => { + const valueToFilter = `${value}-${tableId}`; + let foundOption = null; // Use a variable to store the found option + + tableListOptions?.forEach((singleOption) => { + if (foundOption) return; // Exit early if foundOption is set + singleOption?.options?.some((option) => { + if (option.value === valueToFilter) { + foundOption = { + value: valueToFilter.split('-')[0], + label: option.tableName + '.' + option.label, + table: tableId, + }; + return true; // Exit the some loop early + } + return false; // Continue the some loop + }); + }); + + return foundOption || {}; + }; + + const getListRowsOption = (value) => { + const option = columnAccessorsOptions?.find((option) => option?.value === value); + return option || {}; + }; + const constructAggregateValue = (value, operation, option, tableId = '') => { if (option === 'aggFx') { const option = aggFxOptions.find((option) => option?.value === value); return option || {}; } if (option === 'column') { - switch (operation) { - case 'joinTable': { - const option = tableListOptions?.reduce((acc, singleOption) => { - const valueToFilter = `${value}-${tableId}`; - singleOption?.options?.find((option) => { - if (option.value === valueToFilter) { - acc = { - value: valueToFilter.split('-')[0], - label: option.tableName + '.' + option.label, - table: tableId, - }; - } - }); - return acc; - }, {}); - return option || {}; - } - case 'listRows': { - const option = columnAccessorsOptions?.find((option) => option?.value === value); - return option || {}; - } - default: - break; + if (operation === 'joinTable') { + return getJoinTableOption(value, tableId); + } else if (operation === 'listRows') { + return getListRowsOption(value); } } }; @@ -288,6 +337,18 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { ); }; + const selectedTableName = getTableName(selectedTableId); + + const isGroupByTableNameTruncated = (text) => { + const container = document.querySelector('.truncate-container'); + const textElement = document.createElement('span'); + textElement.innerText = text; + document.body.append(textElement); + const isTruncated = textElement?.offsetWidth > container?.offsetWidth; + document.body.removeChild(textElement); + return isTruncated; + }; + return ( <>
@@ -349,7 +410,9 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => { 'Deleting the aggregate function will also delete the group by conditions. Are you sure, you want to continue?' } // confirmButtonLoading={isDeletingQueryInProcess} - onConfirm={() => executeAggregateDeletion(aggregateKey)} + onConfirm={() => { + executeAggregateDeletion(); + }} onCancel={() => setShowDeleteConfirmation(false)} darkMode={darkMode} /> @@ -398,15 +461,22 @@ export const AggregateFilter = ({ darkMode, operation = '' }) => {
)} {operation === 'joinTable' && ( -
+
-
- {getTableName(selectedTableId)} -
-
+
+ {selectedTableName} +
+ +
{
{joinTableOptions?.joins?.map((table) => { - return ( -
-
- {getTableName(table.table)} + if (table.hasOwnProperty('table') && table.table) { + const tableName = getTableName(table.table); // Replace with your dynamic text + const isTextTruncated = isGroupByTableNameTruncated(tableName); + const tableNameEmpty = !tableName; + const showTooltip = tableNameEmpty || isTextTruncated; + const toolTipMessage = tableNameEmpty ? 'Please select joining table to see its name' : tableName; + return ( +
+ +
+ {tableName} +
+
+
+ handleGroupByChange(table.table, value)} + disabled={disableGroupBy() || tableNameEmpty} + darkMode={darkMode} + showTooltip={disableGroupBy()} + /> +
-
- handleGroupByChange(table.table, value)} - disabled={disableGroupBy()} - darkMode={darkMode} - showTooltip={disableGroupBy()} - /> -
-
- ); + ); + } + return null; })}
)} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/JoinTable.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/JoinTable.jsx index 6c5be5c738..5d94329479 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/JoinTable.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/JoinTable.jsx @@ -1,9 +1,9 @@ -import React, { useContext } from 'react'; +import React, { useCallback, useContext } from 'react'; import { Col, Container, Row } from 'react-bootstrap'; import { ButtonSolid } from '@/_ui/AppButton/AppButton'; import Trash from '@/_ui/Icon/solidIcons/Trash'; import AddRectangle from '@/_ui/Icon/bulkIcons/AddRectangle'; -import { clone } from 'lodash'; +import { clone, isEmpty } from 'lodash'; import { TooljetDatabaseContext } from '@/TooljetDatabase/index'; import DropDownSelect from './DropDownSelect'; import JoinConstraint from './JoinConstraint'; @@ -87,11 +87,14 @@ const SelectTableMenu = ({ darkMode }) => { return cleanedJoin; }; - const showSelectSection = () => { + const showSelectSection = useCallback(() => { const groupBy = joinTableOptions?.group_by || {}; - const isGroupByUsed = Object?.values(groupBy)?.some((condition) => condition?.length >= 1); - return isGroupByUsed ? false : true; - }; + const aggregates = joinTableOptions?.aggregates || {}; + const isGroupByUsed = Object?.values(groupBy)?.some((columnList) => columnList?.length >= 1); + //checking if isGroupby is valid or aggregates is not empty then hide select or else show select options + return isGroupByUsed || !isEmpty(aggregates) ? false : true; + }, [joinTableOptions]); + return (
{/* Join Section */} diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx index 0f1042144e..f3d3bf3796 100644 --- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx +++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx @@ -197,10 +197,10 @@ function DataSourceSelect({ closePopup && !isMulti && closePopup(); }; - let optionsCount = options.length; + let optionsCount = options?.length; - options.forEach((item) => { - if (item.options && item.options.length > 0) { + options?.forEach((item) => { + if (item?.options && item?.options?.length > 0) { optionsCount += item.options.length; } });