diff --git a/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx b/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx new file mode 100644 index 00000000000..a51d6006dec --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import ActionNameEditor from "components/editorComponents/ActionNameEditor"; +import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import { PluginType } from "entities/Action"; +import type { ReduxAction } from "ee/constants/ReduxActionConstants"; +import styled from "styled-components"; +import { getSavingStatusForActionName } from "selectors/actionSelectors"; +import { getAssetUrl } from "ee/utils/airgapHelpers"; +import { ActionUrlIcon } from "pages/Editor/Explorer/ExplorerIcons"; + +export interface SaveActionNameParams { + id: string; + name: string; +} + +export interface PluginActionNameEditorProps { + saveActionName: ( + params: SaveActionNameParams, + ) => ReduxAction; +} + +const ActionNameEditorWrapper = styled.div` + & .ads-v2-box { + gap: var(--ads-v2-spaces-2); + } + + && .t--action-name-edit-field { + font-size: 12px; + + .bp3-editable-text-content { + height: unset !important; + line-height: unset !important; + } + } + + & .t--plugin-icon-box { + height: 12px; + width: 12px; + + img { + width: 12px; + height: auto; + } + } +`; + +const PluginActionNameEditor = (props: PluginActionNameEditorProps) => { + const { action, plugin } = usePluginActionContext(); + + const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); + const isChangePermitted = getHasManageActionPermission( + isFeatureEnabled, + action?.userPermissions, + ); + + const saveStatus = useSelector((state) => + getSavingStatusForActionName(state, action?.id || ""), + ); + + const iconUrl = getAssetUrl(plugin?.iconLocation) || ""; + const icon = ActionUrlIcon(iconUrl); + + return ( + + + + ); +}; + +export default PluginActionNameEditor; diff --git a/app/client/src/PluginActionEditor/index.ts b/app/client/src/PluginActionEditor/index.ts index 20265c8bc5a..e8083e1b0f0 100644 --- a/app/client/src/PluginActionEditor/index.ts +++ b/app/client/src/PluginActionEditor/index.ts @@ -6,3 +6,8 @@ export { export { default as PluginActionToolbar } from "./components/PluginActionToolbar"; export { default as PluginActionForm } from "./components/PluginActionForm"; export { default as PluginActionResponse } from "./components/PluginActionResponse"; +export type { + SaveActionNameParams, + PluginActionNameEditorProps, +} from "./components/PluginActionNameEditor"; +export { default as PluginActionNameEditor } from "./components/PluginActionNameEditor"; diff --git a/app/client/src/components/editorComponents/ActionNameEditor.tsx b/app/client/src/components/editorComponents/ActionNameEditor.tsx index 8467855f479..1d9bf01aa7c 100644 --- a/app/client/src/components/editorComponents/ActionNameEditor.tsx +++ b/app/client/src/components/editorComponents/ActionNameEditor.tsx @@ -1,19 +1,13 @@ import React, { memo } from "react"; -import { useSelector } from "react-redux"; -import { useParams } from "react-router-dom"; import EditableText, { EditInteractionKind, } from "components/editorComponents/EditableText"; import { removeSpecialChars } from "utils/helpers"; -import type { AppState } from "ee/reducers"; -import { saveActionName } from "actions/pluginActionActions"; import { Flex } from "@appsmith/ads"; -import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector"; import NameEditorComponent, { IconBox, - IconWrapper, NameWrapper, } from "components/utils/NameEditorComponent"; import { @@ -21,14 +15,13 @@ import { ACTION_NAME_PLACEHOLDER, createMessage, } from "ee/constants/messages"; -import { getAssetUrl } from "ee/utils/airgapHelpers"; -import { getSavingStatusForActionName } from "selectors/actionSelectors"; import type { ReduxAction } from "ee/constants/ReduxActionConstants"; +import type { SaveActionNameParams } from "PluginActionEditor"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import type { Action } from "entities/Action"; +import type { ModuleInstance } from "ee/constants/ModuleInstanceConstants"; -interface SaveActionNameParams { - id: string; - name: string; -} interface ActionNameEditorProps { /* This prop checks if page is API Pane or Query Pane or Curl Pane @@ -38,38 +31,34 @@ interface ActionNameEditorProps { */ enableFontStyling?: boolean; disabled?: boolean; - saveActionName?: ( + saveActionName: ( params: SaveActionNameParams, ) => ReduxAction; + actionConfig?: Action | ModuleInstance; + icon?: JSX.Element; + saveStatus: { isSaving: boolean; error: boolean }; } function ActionNameEditor(props: ActionNameEditorProps) { - const params = useParams<{ baseApiId?: string; baseQueryId?: string }>(); - - const currentActionConfig = useSelector((state: AppState) => - getActionByBaseId(state, params.baseApiId || params.baseQueryId || ""), - ); - - const currentPlugin = useSelector((state: AppState) => - getPlugin(state, currentActionConfig?.pluginId || ""), - ); + const { + actionConfig, + disabled = false, + enableFontStyling = false, + icon = "", + saveActionName, + saveStatus, + } = props; - const saveStatus = useSelector((state) => - getSavingStatusForActionName(state, currentActionConfig?.id || ""), + const isActionRedesignEnabled = useFeatureFlag( + FEATURE_FLAG.release_actions_redesign_enabled, ); return ( {({ @@ -85,28 +74,22 @@ function ActionNameEditor(props: ActionNameEditorProps) { isNew: boolean; saveStatus: { isSaving: boolean; error: boolean }; }) => ( - + - {currentPlugin && ( - - - - )} + {icon && {icon}} ))} diff --git a/app/client/src/components/utils/NameEditorComponent.tsx b/app/client/src/components/utils/NameEditorComponent.tsx index b7aa2e03ce4..876a9c9d1fd 100644 --- a/app/client/src/components/utils/NameEditorComponent.tsx +++ b/app/client/src/components/utils/NameEditorComponent.tsx @@ -11,6 +11,8 @@ import { } from "ee/constants/messages"; import styled from "styled-components"; import { Classes } from "@blueprintjs/core"; +import type { SaveActionNameParams } from "PluginActionEditor"; +import type { ReduxAction } from "ee/constants/ReduxActionConstants"; export const NameWrapper = styled.div<{ enableFontStyling?: boolean }>` min-width: 50%; @@ -71,9 +73,9 @@ interface NameEditorProps { children: (params: any) => JSX.Element; id?: string; name?: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dispatchAction: (a: any) => any; + onSaveName: ( + params: SaveActionNameParams, + ) => ReduxAction; // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any suffixErrorMessage?: (params?: any) => string; @@ -90,10 +92,10 @@ interface NameEditorProps { function NameEditor(props: NameEditorProps) { const { - dispatchAction, id: entityId, idUndefinedErrorMessage, name: entityName, + onSaveName, saveStatus, suffixErrorMessage = ACTION_NAME_CONFLICT_ERROR, } = props; @@ -131,8 +133,8 @@ function NameEditor(props: NameEditorProps) { const handleNameChange = useCallback( (name: string) => { - if (name !== entityName && !isInvalidNameForEntity(name)) { - dispatch(dispatchAction({ id: entityId, name })); + if (name !== entityName && !isInvalidNameForEntity(name) && entityId) { + dispatch(onSaveName({ id: entityId, name })); } }, [dispatch, isInvalidNameForEntity, entityId, entityName], diff --git a/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx b/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx index a5d3aa5e235..1a2d9d51789 100644 --- a/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx +++ b/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx @@ -1,11 +1,7 @@ import type { ReduxAction } from "ee/constants/ReduxActionConstants"; import type { PaginationField } from "api/ActionAPI"; import React, { createContext, useMemo } from "react"; - -interface SaveActionNameParams { - id: string; - name: string; -} +import type { SaveActionNameParams } from "PluginActionEditor"; interface ApiEditorContextContextProps { moreActionsMenu?: React.ReactNode; @@ -15,7 +11,7 @@ interface ApiEditorContextContextProps { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any settingsConfig: any; - saveActionName?: ( + saveActionName: ( params: SaveActionNameParams, ) => ReduxAction; closeEditorLink?: React.ReactNode; diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index b542cdfbd04..979c4edf1cb 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -35,6 +35,9 @@ import { InfoFields, RequestTabs, } from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm"; +import { getSavingStatusForActionName } from "selectors/actionSelectors"; +import { getAssetUrl } from "ee/utils/airgapHelpers"; +import { ActionUrlIcon } from "../Explorer/ExplorerIcons"; const Form = styled.form` position: relative; @@ -245,6 +248,18 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { currentActionConfig?.userPermissions, ); + const currentPlugin = useSelector((state: AppState) => + getPlugin(state, currentActionConfig?.pluginId || ""), + ); + + const saveStatus = useSelector((state) => + getSavingStatusForActionName(state, currentActionConfig?.id || ""), + ); + + const iconUrl = getAssetUrl(currentPlugin?.iconLocation) || ""; + + const icon = ActionUrlIcon(iconUrl); + const plugin = useSelector((state: AppState) => getPlugin(state, pluginId ?? ""), ); @@ -281,9 +296,12 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index 247c2cfd408..31328d25055 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -8,7 +8,11 @@ import { getPluginSettingConfigs, getPlugins, } from "ee/selectors/entitiesSelector"; -import { deleteAction, runAction } from "actions/pluginActionActions"; +import { + deleteAction, + runAction, + saveActionName, +} from "actions/pluginActionActions"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import Editor from "./Editor"; import BackToCanvas from "components/common/BackToCanvas"; @@ -151,15 +155,7 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) { }); dispatch(runAction(action?.id ?? "", paginationField)); }, - [ - action?.id, - apiName, - pageName, - getPageName, - plugins, - pluginId, - datasourceId, - ], + [action?.id, apiName, pageName, plugins, pluginId, datasourceId, dispatch], ); const actionRightPaneBackLink = useMemo(() => { @@ -173,13 +169,13 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) { pageName, }); dispatch(deleteAction({ id: action?.id ?? "", name: apiName })); - }, [getPageName, pages, basePageId, apiName]); + }, [pages, basePageId, apiName, action?.id, dispatch, pageName]); const notification = useMemo(() => { if (!isConverting) return null; return ; - }, [action?.name, isConverting]); + }, [action?.name, isConverting, icon]); const isActionRedesignEnabled = useFeatureFlag( FEATURE_FLAG.release_actions_redesign_enabled, @@ -196,6 +192,7 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) { handleRunClick={handleRunClick} moreActionsMenu={moreActionsMenu} notification={notification} + saveActionName={saveActionName} settingsConfig={settingsConfig} > diff --git a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx index de937dd86a1..e0fb005bc00 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx @@ -16,6 +16,8 @@ import { import { Tooltip } from "@appsmith/ads"; import { useSelector } from "react-redux"; import { getSavingStatusForActionName } from "selectors/actionSelectors"; +import type { ReduxAction } from "ee/constants/ReduxActionConstants"; +import type { SaveActionNameParams } from "PluginActionEditor"; export const searchHighlightSpanClassName = "token"; export const searchTokenizationDelimiter = "!!"; @@ -84,7 +86,7 @@ export interface EntityNameProps { name: string; isEditing?: boolean; onChange?: (name: string) => void; - updateEntityName: (name: string) => void; + updateEntityName: (name: string) => ReduxAction; entityId: string; searchKeyword?: string; className?: string; @@ -164,10 +166,10 @@ export const EntityName = React.memo( return ( diff --git a/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx b/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx index f82860f134b..ff1a598bc1b 100644 --- a/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx @@ -25,11 +25,8 @@ import NameEditorComponent, { } from "components/utils/NameEditorComponent"; import { getSavingStatusForJSObjectName } from "selectors/actionSelectors"; import type { ReduxAction } from "ee/constants/ReduxActionConstants"; +import type { SaveActionNameParams } from "PluginActionEditor"; -export interface SaveActionNameParams { - id: string; - name: string; -} export interface JSObjectNameEditorProps { /* This prop checks if page is API Pane or Query Pane or Curl Pane @@ -64,10 +61,10 @@ export function JSObjectNameEditor(props: JSObjectNameEditorProps) { return ( {({ diff --git a/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx b/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx index 03c46a99d0d..c5089fc6bdf 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx @@ -1,18 +1,14 @@ import type { ReduxAction } from "ee/constants/ReduxActionConstants"; +import type { SaveActionNameParams } from "PluginActionEditor"; import React, { createContext, useMemo } from "react"; -interface SaveActionNameParams { - id: string; - name: string; -} - interface QueryEditorContextContextProps { moreActionsMenu?: React.ReactNode; onCreateDatasourceClick?: () => void; onEntityNotFoundBackClick?: () => void; changeQueryPage?: (baseQueryId: string) => void; actionRightPaneBackLink?: React.ReactNode; - saveActionName?: ( + saveActionName: ( params: SaveActionNameParams, ) => ReduxAction; closeEditorLink?: React.ReactNode; diff --git a/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx b/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx index af1d28c83a9..38f9a9b3d72 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx @@ -13,6 +13,7 @@ import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks"; import { useSelector } from "react-redux"; import { getActionByBaseId, + getPlugin, getPluginNameFromId, } from "ee/selectors/entitiesSelector"; import { QueryEditorContext } from "./QueryEditorContext"; @@ -21,6 +22,9 @@ import type { Datasource } from "entities/Datasource"; import type { AppState } from "ee/reducers"; import { SQL_DATASOURCES } from "constants/QueryEditorConstants"; import DatasourceSelector from "./DatasourceSelector"; +import { getSavingStatusForActionName } from "selectors/actionSelectors"; +import { getAssetUrl } from "ee/utils/airgapHelpers"; +import { ActionUrlIcon } from "../Explorer/ExplorerIcons"; const NameWrapper = styled.div` display: flex; @@ -79,6 +83,18 @@ const QueryEditorHeader = (props: Props) => { currentActionConfig?.userPermissions, ); + const currentPlugin = useSelector((state: AppState) => + getPlugin(state, currentActionConfig?.pluginId || ""), + ); + + const saveStatus = useSelector((state) => + getSavingStatusForActionName(state, currentActionConfig?.id || ""), + ); + + const iconUrl = getAssetUrl(currentPlugin?.iconLocation) || ""; + + const icon = ActionUrlIcon(iconUrl); + // get the current action's plugin name const currentActionPluginName = useSelector((state: AppState) => getPluginNameFromId(state, currentActionConfig?.pluginId || ""), @@ -106,8 +122,11 @@ const QueryEditorHeader = (props: Props) => { diff --git a/app/client/src/pages/Editor/QueryEditor/index.tsx b/app/client/src/pages/Editor/QueryEditor/index.tsx index f76cbffe1b5..03457a6228e 100644 --- a/app/client/src/pages/Editor/QueryEditor/index.tsx +++ b/app/client/src/pages/Editor/QueryEditor/index.tsx @@ -42,6 +42,7 @@ import { ENTITY_ICON_SIZE, EntityIcon } from "../Explorer/ExplorerIcons"; import { getIDEViewMode } from "selectors/ideSelectors"; import { EditorViewMode } from "ee/entities/IDE/constants"; import { AppPluginActionEditor } from "../AppPluginActionEditor"; +import { saveActionName } from "actions/pluginActionActions"; type QueryEditorProps = RouteComponentProps; @@ -126,6 +127,7 @@ function QueryEditor(props: QueryEditorProps) { }, [ action?.id, action?.name, + action?.pluginType, isChangePermitted, isDeletePermitted, basePageId, @@ -143,7 +145,7 @@ function QueryEditor(props: QueryEditorProps) { changeQuery({ baseQueryId: baseQueryId, basePageId, applicationId }), ); }, - [basePageId, applicationId], + [basePageId, applicationId, dispatch], ); const onCreateDatasourceClick = useCallback(() => { @@ -159,13 +161,7 @@ function QueryEditor(props: QueryEditorProps) { AnalyticsUtil.logEvent("NAVIGATE_TO_CREATE_NEW_DATASOURCE_PAGE", { entryPoint, }); - }, [ - basePageId, - history, - integrationEditorURL, - DatasourceCreateEntryPoints, - AnalyticsUtil, - ]); + }, [basePageId]); // custom function to return user to integrations page if action is not found const onEntityNotFoundBackClick = useCallback( @@ -176,7 +172,7 @@ function QueryEditor(props: QueryEditorProps) { selectedTab: INTEGRATION_TABS.ACTIVE, }), ), - [basePageId, history, integrationEditorURL], + [basePageId], ); const notification = useMemo(() => { @@ -189,7 +185,7 @@ function QueryEditor(props: QueryEditorProps) { withPadding /> ); - }, [action?.name, isConverting]); + }, [action?.name, isConverting, icon]); const isActionRedesignEnabled = useFeatureFlag( FEATURE_FLAG.release_actions_redesign_enabled, @@ -207,6 +203,7 @@ function QueryEditor(props: QueryEditorProps) { notification={notification} onCreateDatasourceClick={onCreateDatasourceClick} onEntityNotFoundBackClick={onEntityNotFoundBackClick} + saveActionName={saveActionName} >