From cf90a8e98c2d8d24340ed9fc632bdcc5b95e272b Mon Sep 17 00:00:00 2001 From: Aman Agarwal Date: Fri, 25 Apr 2025 09:38:07 +0530 Subject: [PATCH] fix: Updated the copy text if plugin not installed, disabled new query and edit for unavailable plugin ds (#40360) --- .../PluginActionEditor/PluginActionEditor.tsx | 7 ++- app/client/src/ce/constants/messages.ts | 3 + .../QueryAdd/useGroupedAddQueryOperations.tsx | 58 ++++++++++--------- .../GlobalSearch/GlobalSearchHooks.tsx | 5 +- .../editorComponents/GlobalSearch/index.tsx | 16 +++-- .../Editor/DataSourceEditor/DSFormHeader.tsx | 8 +++ .../DataSourceEditor/NewActionButton.tsx | 15 +++-- 7 files changed, 74 insertions(+), 38 deletions(-) diff --git a/app/client/src/PluginActionEditor/PluginActionEditor.tsx b/app/client/src/PluginActionEditor/PluginActionEditor.tsx index 674e248bdd..ef39a2f54d 100644 --- a/app/client/src/PluginActionEditor/PluginActionEditor.tsx +++ b/app/client/src/PluginActionEditor/PluginActionEditor.tsx @@ -15,6 +15,8 @@ import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; import { Text } from "@appsmith/ads"; import { useIsEditorInitialised } from "IDE/hooks"; import { useActionSettingsConfig } from "./hooks"; +import { createMessage, PLUGIN_NOT_INSTALLED } from "ee/constants/messages"; +import { ShowUpgradeMenuItem } from "ee/utils/licenseHelpers"; interface ChildrenProps { children: React.ReactNode | React.ReactNode[]; @@ -54,10 +56,11 @@ const PluginActionEditor = (props: ChildrenProps) => { if (!plugin) { return ( - + - Plugin not installed! + {createMessage(PLUGIN_NOT_INSTALLED)} + ); } diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index b554479be6..7967bb21fb 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -2693,3 +2693,6 @@ export const GOOGLE_RECAPTCHA_FAILED = () => "Google reCAPTCHA verification failed"; export const PASSWORD_INSUFFICIENT_STRENGTH = () => "Insufficient password strength"; + +export const PLUGIN_NOT_INSTALLED = () => + "Upgrade your plan to unlock access to these integrations."; diff --git a/app/client/src/ce/pages/AppIDE/components/QueryAdd/useGroupedAddQueryOperations.tsx b/app/client/src/ce/pages/AppIDE/components/QueryAdd/useGroupedAddQueryOperations.tsx index ffff528078..bbfac1fbdf 100644 --- a/app/client/src/ce/pages/AppIDE/components/QueryAdd/useGroupedAddQueryOperations.tsx +++ b/app/client/src/ce/pages/AppIDE/components/QueryAdd/useGroupedAddQueryOperations.tsx @@ -45,34 +45,40 @@ export const useAddQueryListItems = () => { ); const getListItems = (data: ActionOperation[]) => { - return data.map((fileOperation) => { - let title = - fileOperation.entityExplorerTitle || - fileOperation.dsName || - fileOperation.title; + return data + .map((fileOperation) => { + let title = + fileOperation.entityExplorerTitle || + fileOperation.dsName || + fileOperation.title; - title = - fileOperation.focusEntityType === FocusEntity.QUERY_MODULE_INSTANCE - ? fileOperation.title - : title; - const className = createAddClassName(title); - const icon = - fileOperation.icon || - (fileOperation.pluginId && - getPluginEntityIcon(pluginGroups[fileOperation.pluginId])); - - return { - startIcon: icon, - className: className, - title, - description: + title = fileOperation.focusEntityType === FocusEntity.QUERY_MODULE_INSTANCE - ? fileOperation.dsName - : "", - descriptionType: "inline", - onClick: onCreateItemClick.bind(null, fileOperation), - } as ListItemProps; - }); + ? fileOperation.title + : title; + const className = createAddClassName(title); + const icon = + fileOperation.icon || + (fileOperation.pluginId && + getPluginEntityIcon(pluginGroups[fileOperation.pluginId])); + + if (fileOperation.pluginId && !pluginGroups[fileOperation.pluginId]) { + return undefined; + } + + return { + startIcon: icon, + className: className, + title, + description: + fileOperation.focusEntityType === FocusEntity.QUERY_MODULE_INSTANCE + ? fileOperation.dsName + : "", + descriptionType: "inline", + onClick: onCreateItemClick.bind(null, fileOperation), + } as ListItemProps; + }) + .filter((item) => item !== undefined); }; return { getListItems }; diff --git a/app/client/src/components/editorComponents/GlobalSearch/GlobalSearchHooks.tsx b/app/client/src/components/editorComponents/GlobalSearch/GlobalSearchHooks.tsx index 7e1cc5b1ed..7098a4847a 100644 --- a/app/client/src/components/editorComponents/GlobalSearch/GlobalSearchHooks.tsx +++ b/app/client/src/components/editorComponents/GlobalSearch/GlobalSearchHooks.tsx @@ -64,6 +64,9 @@ export const useFilteredFileOperations = ({ }: FilterFileOperationsProps) => { const { appWideDS = [], otherDS = [] } = useAppWideAndOtherDatasource(); const plugins = useSelector(getPlugins); + const pluginById = useMemo(() => { + return keyBy(plugins, "id"); + }, [plugins]); const moduleOptions = useModuleOptions(); const workflowOptions = useWorkflowOptions(); @@ -106,7 +109,7 @@ export const useFilteredFileOperations = ({ return AiPlugin.id !== ds.pluginId; } - return true; + return !!pluginById[ds.pluginId]?.id; }); return useFilteredAndSortedFileOperations({ diff --git a/app/client/src/components/editorComponents/GlobalSearch/index.tsx b/app/client/src/components/editorComponents/GlobalSearch/index.tsx index d6df653cf0..2da1c9d52b 100644 --- a/app/client/src/components/editorComponents/GlobalSearch/index.tsx +++ b/app/client/src/components/editorComponents/GlobalSearch/index.tsx @@ -43,7 +43,7 @@ import type { ExplorerURLParams } from "ee/pages/Editor/Explorer/helpers"; import { getLastSelectedWidget } from "selectors/ui"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import useRecentEntities from "./useRecentEntities"; -import { noop } from "lodash"; +import { keyBy, noop } from "lodash"; import { getCurrentPageId, getPagePermissions, @@ -179,6 +179,9 @@ function GlobalSearch() { (state: AppState) => state.ui.globalSearch.filterContext.category, ); const plugins = useSelector(getPlugins); + const pluginById = useMemo(() => { + return keyBy(plugins, "id"); + }, [plugins]); const setCategory = useCallback( (category: SearchCategory) => { dispatch(setGlobalSearchFilterContext({ category: category })); @@ -233,10 +236,15 @@ function GlobalSearch() { }, [basePageIdToPageIdMap, params?.basePageId, reducerDatasources]); const filteredDatasources = useMemo(() => { - if (!query) return datasourcesList; + if (!query) + return datasourcesList.filter( + (datasource) => pluginById[datasource.pluginId]?.id, + ); - return datasourcesList.filter((datasource) => - isMatching(datasource.name, query), + return datasourcesList.filter( + (datasource) => + isMatching(datasource.name, query) && + pluginById[datasource.pluginId]?.id, ); }, [datasourcesList, query]); const recentEntities = useRecentEntities(); diff --git a/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx b/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx index f6122062b9..256d65d52c 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx @@ -28,6 +28,7 @@ import type { PluginType } from "entities/Plugin"; import { useLocation } from "react-router"; import { useHeaderActions } from "ee/hooks/datasourceEditorHooks"; import { getIDETypeByUrl } from "ee/entities/IDE/utils"; +import { getPlugin } from "ee/selectors/entitiesSelector"; export const ActionWrapper = styled.div` display: flex; @@ -118,6 +119,10 @@ export const DSFormHeader = (props: DSFormHeaderProps) => { const dispatch = useDispatch(); const location = useLocation(); const ideType = getIDETypeByUrl(location.pathname); + const plugin = useSelector((state) => + getPlugin(state, datasource?.pluginId || ""), + ); + const isEditDisabled = !plugin?.id; const deleteAction = () => { if (isDeleting) return; @@ -210,8 +215,11 @@ export const DSFormHeader = (props: DSFormHeaderProps) => { )}