chore: Handling the updation of action name in the plugin action toolbar (#36560)

## Description

Handling the updation of action name in the plugin action toolbar in the
new modularised flow.

Fixes [#36498](https://github.com/appsmithorg/appsmith/issues/36498)

## Automation

/ok-to-test tags="@tag.All"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11071023786>
> Commit: 73647e50cfeb6919b30c674f8f3a3a219f6f98c8
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11071023786&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Fri, 27 Sep 2024 14:15:24 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new component for editing plugin action names, enhancing
user experience in managing plugin actions.
- Added optional icon size property to the editable text component for
improved customization.
- Enhanced the `CommonEditorForm` and `QueryEditorHeader` components to
display plugin-specific information and saving status.

- **Bug Fixes**
- Streamlined action dispatching logic, improving reliability in saving
actions.

- **Documentation**
- Updated interfaces and prop types for better clarity and type safety
in the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ankita Kinger 2024-09-27 19:48:05 +05:30 committed by GitHub
parent b1ed82dbcf
commit 5995e4292a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 194 additions and 93 deletions

View File

@ -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<SaveActionNameParams>;
}
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 (
<ActionNameEditorWrapper>
<ActionNameEditor
actionConfig={action}
disabled={!isChangePermitted}
enableFontStyling={plugin?.type === PluginType.API}
icon={icon}
saveActionName={props.saveActionName}
saveStatus={saveStatus}
/>
</ActionNameEditorWrapper>
);
};
export default PluginActionNameEditor;

View File

@ -6,3 +6,8 @@ export {
export { default as PluginActionToolbar } from "./components/PluginActionToolbar"; export { default as PluginActionToolbar } from "./components/PluginActionToolbar";
export { default as PluginActionForm } from "./components/PluginActionForm"; export { default as PluginActionForm } from "./components/PluginActionForm";
export { default as PluginActionResponse } from "./components/PluginActionResponse"; export { default as PluginActionResponse } from "./components/PluginActionResponse";
export type {
SaveActionNameParams,
PluginActionNameEditorProps,
} from "./components/PluginActionNameEditor";
export { default as PluginActionNameEditor } from "./components/PluginActionNameEditor";

View File

@ -1,19 +1,13 @@
import React, { memo } from "react"; import React, { memo } from "react";
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import EditableText, { import EditableText, {
EditInteractionKind, EditInteractionKind,
} from "components/editorComponents/EditableText"; } from "components/editorComponents/EditableText";
import { removeSpecialChars } from "utils/helpers"; import { removeSpecialChars } from "utils/helpers";
import type { AppState } from "ee/reducers";
import { saveActionName } from "actions/pluginActionActions";
import { Flex } from "@appsmith/ads"; import { Flex } from "@appsmith/ads";
import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector";
import NameEditorComponent, { import NameEditorComponent, {
IconBox, IconBox,
IconWrapper,
NameWrapper, NameWrapper,
} from "components/utils/NameEditorComponent"; } from "components/utils/NameEditorComponent";
import { import {
@ -21,14 +15,13 @@ import {
ACTION_NAME_PLACEHOLDER, ACTION_NAME_PLACEHOLDER,
createMessage, createMessage,
} from "ee/constants/messages"; } from "ee/constants/messages";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import type { ReduxAction } from "ee/constants/ReduxActionConstants"; 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 { interface ActionNameEditorProps {
/* /*
This prop checks if page is API Pane or Query Pane or Curl Pane This prop checks if page is API Pane or Query Pane or Curl Pane
@ -38,38 +31,34 @@ interface ActionNameEditorProps {
*/ */
enableFontStyling?: boolean; enableFontStyling?: boolean;
disabled?: boolean; disabled?: boolean;
saveActionName?: ( saveActionName: (
params: SaveActionNameParams, params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>; ) => ReduxAction<SaveActionNameParams>;
actionConfig?: Action | ModuleInstance;
icon?: JSX.Element;
saveStatus: { isSaving: boolean; error: boolean };
} }
function ActionNameEditor(props: ActionNameEditorProps) { function ActionNameEditor(props: ActionNameEditorProps) {
const params = useParams<{ baseApiId?: string; baseQueryId?: string }>(); const {
actionConfig,
disabled = false,
enableFontStyling = false,
icon = "",
saveActionName,
saveStatus,
} = props;
const currentActionConfig = useSelector((state: AppState) => const isActionRedesignEnabled = useFeatureFlag(
getActionByBaseId(state, params.baseApiId || params.baseQueryId || ""), FEATURE_FLAG.release_actions_redesign_enabled,
);
const currentPlugin = useSelector((state: AppState) =>
getPlugin(state, currentActionConfig?.pluginId || ""),
);
const saveStatus = useSelector((state) =>
getSavingStatusForActionName(state, currentActionConfig?.id || ""),
); );
return ( return (
<NameEditorComponent <NameEditorComponent
/** id={actionConfig?.id}
* This component is used by module editor in EE which uses a different
* action to save the name of an action. The current callers of this component
* pass the existing saveAction action but as fallback the saveActionName is used here
* as a guard.
*/
dispatchAction={props.saveActionName || saveActionName}
id={currentActionConfig?.id}
idUndefinedErrorMessage={ACTION_ID_NOT_FOUND_IN_URL} idUndefinedErrorMessage={ACTION_ID_NOT_FOUND_IN_URL}
name={currentActionConfig?.name} name={actionConfig?.name}
onSaveName={saveActionName}
saveStatus={saveStatus} saveStatus={saveStatus}
> >
{({ {({
@ -85,28 +74,22 @@ function ActionNameEditor(props: ActionNameEditorProps) {
isNew: boolean; isNew: boolean;
saveStatus: { isSaving: boolean; error: boolean }; saveStatus: { isSaving: boolean; error: boolean };
}) => ( }) => (
<NameWrapper enableFontStyling={props.enableFontStyling}> <NameWrapper enableFontStyling={enableFontStyling}>
<Flex <Flex
alignItems="center" alignItems="center"
gap="spaces-3" gap="spaces-3"
overflow="hidden" overflow="hidden"
width="100%" width="100%"
> >
{currentPlugin && ( {icon && <IconBox className="t--plugin-icon-box">{icon}</IconBox>}
<IconBox>
<IconWrapper
alt={currentPlugin.name}
src={getAssetUrl(currentPlugin?.iconLocation)}
/>
</IconBox>
)}
<EditableText <EditableText
className="t--action-name-edit-field" className="t--action-name-edit-field"
defaultValue={currentActionConfig ? currentActionConfig.name : ""} defaultValue={actionConfig ? actionConfig.name : ""}
disabled={props.disabled} disabled={disabled}
editInteractionKind={EditInteractionKind.SINGLE} editInteractionKind={EditInteractionKind.SINGLE}
errorTooltipClass="t--action-name-edit-error" errorTooltipClass="t--action-name-edit-error"
forceDefault={forceUpdate} forceDefault={forceUpdate}
iconSize={isActionRedesignEnabled ? "sm" : "md"}
isEditingDefault={isNew} isEditingDefault={isNew}
isInvalid={isInvalidNameForEntity} isInvalid={isInvalidNameForEntity}
onTextChanged={handleNameChange} onTextChanged={handleNameChange}

View File

@ -6,7 +6,13 @@ import {
} from "@blueprintjs/core"; } from "@blueprintjs/core";
import styled from "styled-components"; import styled from "styled-components";
import _ from "lodash"; import _ from "lodash";
import { Button, Spinner, toast, Tooltip } from "@appsmith/ads"; import {
Button,
Spinner,
toast,
Tooltip,
type ButtonSizes,
} from "@appsmith/ads";
import { INVALID_NAME_ERROR, createMessage } from "ee/constants/messages"; import { INVALID_NAME_ERROR, createMessage } from "ee/constants/messages";
export enum EditInteractionKind { export enum EditInteractionKind {
@ -39,6 +45,7 @@ interface EditableTextProps {
minLines?: number; minLines?: number;
customErrorTooltip?: string; customErrorTooltip?: string;
useFullWidth?: boolean; useFullWidth?: boolean;
iconSize?: ButtonSizes;
} }
// using the !important keyword here is mandatory because a style is being applied to that element using the style attribute // using the !important keyword here is mandatory because a style is being applied to that element using the style attribute
@ -129,6 +136,7 @@ export function EditableText(props: EditableTextProps) {
errorTooltipClass, errorTooltipClass,
forceDefault, forceDefault,
hideEditIcon, hideEditIcon,
iconSize = "md",
isEditingDefault, isEditingDefault,
isInvalid, isInvalid,
maxLength, maxLength,
@ -275,7 +283,7 @@ export function EditableText(props: EditableTextProps) {
className="t--action-name-edit-icon" className="t--action-name-edit-icon"
isIconButton isIconButton
kind="tertiary" kind="tertiary"
size="md" size={iconSize}
startIcon="pencil-line" startIcon="pencil-line"
/> />
))} ))}

View File

@ -11,6 +11,8 @@ import {
} from "ee/constants/messages"; } from "ee/constants/messages";
import styled from "styled-components"; import styled from "styled-components";
import { Classes } from "@blueprintjs/core"; import { Classes } from "@blueprintjs/core";
import type { SaveActionNameParams } from "PluginActionEditor";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
export const NameWrapper = styled.div<{ enableFontStyling?: boolean }>` export const NameWrapper = styled.div<{ enableFontStyling?: boolean }>`
min-width: 50%; min-width: 50%;
@ -71,9 +73,9 @@ interface NameEditorProps {
children: (params: any) => JSX.Element; children: (params: any) => JSX.Element;
id?: string; id?: string;
name?: string; name?: string;
// TODO: Fix this the next time the file is edited onSaveName: (
// eslint-disable-next-line @typescript-eslint/no-explicit-any params: SaveActionNameParams,
dispatchAction: (a: any) => any; ) => ReduxAction<SaveActionNameParams>;
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
suffixErrorMessage?: (params?: any) => string; suffixErrorMessage?: (params?: any) => string;
@ -90,10 +92,10 @@ interface NameEditorProps {
function NameEditor(props: NameEditorProps) { function NameEditor(props: NameEditorProps) {
const { const {
dispatchAction,
id: entityId, id: entityId,
idUndefinedErrorMessage, idUndefinedErrorMessage,
name: entityName, name: entityName,
onSaveName,
saveStatus, saveStatus,
suffixErrorMessage = ACTION_NAME_CONFLICT_ERROR, suffixErrorMessage = ACTION_NAME_CONFLICT_ERROR,
} = props; } = props;
@ -131,8 +133,8 @@ function NameEditor(props: NameEditorProps) {
const handleNameChange = useCallback( const handleNameChange = useCallback(
(name: string) => { (name: string) => {
if (name !== entityName && !isInvalidNameForEntity(name)) { if (name !== entityName && !isInvalidNameForEntity(name) && entityId) {
dispatch(dispatchAction({ id: entityId, name })); dispatch(onSaveName({ id: entityId, name }));
} }
}, },
[dispatch, isInvalidNameForEntity, entityId, entityName], [dispatch, isInvalidNameForEntity, entityId, entityName],

View File

@ -1,11 +1,7 @@
import type { ReduxAction } from "ee/constants/ReduxActionConstants"; import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { PaginationField } from "api/ActionAPI"; import type { PaginationField } from "api/ActionAPI";
import React, { createContext, useMemo } from "react"; import React, { createContext, useMemo } from "react";
import type { SaveActionNameParams } from "PluginActionEditor";
interface SaveActionNameParams {
id: string;
name: string;
}
interface ApiEditorContextContextProps { interface ApiEditorContextContextProps {
moreActionsMenu?: React.ReactNode; moreActionsMenu?: React.ReactNode;
@ -15,7 +11,7 @@ interface ApiEditorContextContextProps {
// TODO: Fix this the next time the file is edited // TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
settingsConfig: any; settingsConfig: any;
saveActionName?: ( saveActionName: (
params: SaveActionNameParams, params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>; ) => ReduxAction<SaveActionNameParams>;
closeEditorLink?: React.ReactNode; closeEditorLink?: React.ReactNode;

View File

@ -35,6 +35,9 @@ import {
InfoFields, InfoFields,
RequestTabs, RequestTabs,
} from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm"; } 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` const Form = styled.form`
position: relative; position: relative;
@ -245,6 +248,18 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
currentActionConfig?.userPermissions, 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) => const plugin = useSelector((state: AppState) =>
getPlugin(state, pluginId ?? ""), getPlugin(state, pluginId ?? ""),
); );
@ -281,9 +296,12 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
<FormRow className="form-row-header"> <FormRow className="form-row-header">
<NameWrapper className="t--nameOfApi"> <NameWrapper className="t--nameOfApi">
<ActionNameEditor <ActionNameEditor
actionConfig={currentActionConfig}
disabled={!isChangePermitted} disabled={!isChangePermitted}
enableFontStyling enableFontStyling
icon={icon}
saveActionName={saveActionName} saveActionName={saveActionName}
saveStatus={saveStatus}
/> />
</NameWrapper> </NameWrapper>
<ActionButtons className="t--formActionButtons"> <ActionButtons className="t--formActionButtons">

View File

@ -8,7 +8,11 @@ import {
getPluginSettingConfigs, getPluginSettingConfigs,
getPlugins, getPlugins,
} from "ee/selectors/entitiesSelector"; } from "ee/selectors/entitiesSelector";
import { deleteAction, runAction } from "actions/pluginActionActions"; import {
deleteAction,
runAction,
saveActionName,
} from "actions/pluginActionActions";
import AnalyticsUtil from "ee/utils/AnalyticsUtil"; import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import Editor from "./Editor"; import Editor from "./Editor";
import BackToCanvas from "components/common/BackToCanvas"; import BackToCanvas from "components/common/BackToCanvas";
@ -151,15 +155,7 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) {
}); });
dispatch(runAction(action?.id ?? "", paginationField)); dispatch(runAction(action?.id ?? "", paginationField));
}, },
[ [action?.id, apiName, pageName, plugins, pluginId, datasourceId, dispatch],
action?.id,
apiName,
pageName,
getPageName,
plugins,
pluginId,
datasourceId,
],
); );
const actionRightPaneBackLink = useMemo(() => { const actionRightPaneBackLink = useMemo(() => {
@ -173,13 +169,13 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) {
pageName, pageName,
}); });
dispatch(deleteAction({ id: action?.id ?? "", name: apiName })); dispatch(deleteAction({ id: action?.id ?? "", name: apiName }));
}, [getPageName, pages, basePageId, apiName]); }, [pages, basePageId, apiName, action?.id, dispatch, pageName]);
const notification = useMemo(() => { const notification = useMemo(() => {
if (!isConverting) return null; if (!isConverting) return null;
return <ConvertEntityNotification icon={icon} name={action?.name || ""} />; return <ConvertEntityNotification icon={icon} name={action?.name || ""} />;
}, [action?.name, isConverting]); }, [action?.name, isConverting, icon]);
const isActionRedesignEnabled = useFeatureFlag( const isActionRedesignEnabled = useFeatureFlag(
FEATURE_FLAG.release_actions_redesign_enabled, FEATURE_FLAG.release_actions_redesign_enabled,
@ -196,6 +192,7 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) {
handleRunClick={handleRunClick} handleRunClick={handleRunClick}
moreActionsMenu={moreActionsMenu} moreActionsMenu={moreActionsMenu}
notification={notification} notification={notification}
saveActionName={saveActionName}
settingsConfig={settingsConfig} settingsConfig={settingsConfig}
> >
<Disabler isDisabled={isConverting}> <Disabler isDisabled={isConverting}>

View File

@ -16,6 +16,8 @@ import {
import { Tooltip } from "@appsmith/ads"; import { Tooltip } from "@appsmith/ads";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { getSavingStatusForActionName } from "selectors/actionSelectors"; import { getSavingStatusForActionName } from "selectors/actionSelectors";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { SaveActionNameParams } from "PluginActionEditor";
export const searchHighlightSpanClassName = "token"; export const searchHighlightSpanClassName = "token";
export const searchTokenizationDelimiter = "!!"; export const searchTokenizationDelimiter = "!!";
@ -84,7 +86,7 @@ export interface EntityNameProps {
name: string; name: string;
isEditing?: boolean; isEditing?: boolean;
onChange?: (name: string) => void; onChange?: (name: string) => void;
updateEntityName: (name: string) => void; updateEntityName: (name: string) => ReduxAction<SaveActionNameParams>;
entityId: string; entityId: string;
searchKeyword?: string; searchKeyword?: string;
className?: string; className?: string;
@ -164,10 +166,10 @@ export const EntityName = React.memo(
return ( return (
<NameEditorComponent <NameEditorComponent
dispatchAction={handleUpdateName}
id={props.entityId} id={props.entityId}
idUndefinedErrorMessage={ACTION_ID_NOT_FOUND_IN_URL} idUndefinedErrorMessage={ACTION_ID_NOT_FOUND_IN_URL}
name={updatedName} name={updatedName}
onSaveName={handleUpdateName}
saveStatus={saveStatus} saveStatus={saveStatus}
suffixErrorMessage={ENTITY_EXPLORER_ACTION_NAME_CONFLICT_ERROR} suffixErrorMessage={ENTITY_EXPLORER_ACTION_NAME_CONFLICT_ERROR}
> >

View File

@ -25,11 +25,8 @@ import NameEditorComponent, {
} from "components/utils/NameEditorComponent"; } from "components/utils/NameEditorComponent";
import { getSavingStatusForJSObjectName } from "selectors/actionSelectors"; import { getSavingStatusForJSObjectName } from "selectors/actionSelectors";
import type { ReduxAction } from "ee/constants/ReduxActionConstants"; import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { SaveActionNameParams } from "PluginActionEditor";
export interface SaveActionNameParams {
id: string;
name: string;
}
export interface JSObjectNameEditorProps { export interface JSObjectNameEditorProps {
/* /*
This prop checks if page is API Pane or Query Pane or Curl Pane This prop checks if page is API Pane or Query Pane or Curl Pane
@ -64,10 +61,10 @@ export function JSObjectNameEditor(props: JSObjectNameEditorProps) {
return ( return (
<NameEditorComponent <NameEditorComponent
dispatchAction={props.saveJSObjectName}
id={currentJSObjectConfig?.id} id={currentJSObjectConfig?.id}
idUndefinedErrorMessage={JSOBJECT_ID_NOT_FOUND_IN_URL} idUndefinedErrorMessage={JSOBJECT_ID_NOT_FOUND_IN_URL}
name={currentJSObjectConfig?.name} name={currentJSObjectConfig?.name}
onSaveName={props.saveJSObjectName}
saveStatus={saveStatus} saveStatus={saveStatus}
> >
{({ {({

View File

@ -1,18 +1,14 @@
import type { ReduxAction } from "ee/constants/ReduxActionConstants"; import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { SaveActionNameParams } from "PluginActionEditor";
import React, { createContext, useMemo } from "react"; import React, { createContext, useMemo } from "react";
interface SaveActionNameParams {
id: string;
name: string;
}
interface QueryEditorContextContextProps { interface QueryEditorContextContextProps {
moreActionsMenu?: React.ReactNode; moreActionsMenu?: React.ReactNode;
onCreateDatasourceClick?: () => void; onCreateDatasourceClick?: () => void;
onEntityNotFoundBackClick?: () => void; onEntityNotFoundBackClick?: () => void;
changeQueryPage?: (baseQueryId: string) => void; changeQueryPage?: (baseQueryId: string) => void;
actionRightPaneBackLink?: React.ReactNode; actionRightPaneBackLink?: React.ReactNode;
saveActionName?: ( saveActionName: (
params: SaveActionNameParams, params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>; ) => ReduxAction<SaveActionNameParams>;
closeEditorLink?: React.ReactNode; closeEditorLink?: React.ReactNode;

View File

@ -13,6 +13,7 @@ import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { import {
getActionByBaseId, getActionByBaseId,
getPlugin,
getPluginNameFromId, getPluginNameFromId,
} from "ee/selectors/entitiesSelector"; } from "ee/selectors/entitiesSelector";
import { QueryEditorContext } from "./QueryEditorContext"; import { QueryEditorContext } from "./QueryEditorContext";
@ -21,6 +22,9 @@ import type { Datasource } from "entities/Datasource";
import type { AppState } from "ee/reducers"; import type { AppState } from "ee/reducers";
import { SQL_DATASOURCES } from "constants/QueryEditorConstants"; import { SQL_DATASOURCES } from "constants/QueryEditorConstants";
import DatasourceSelector from "./DatasourceSelector"; import DatasourceSelector from "./DatasourceSelector";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { ActionUrlIcon } from "../Explorer/ExplorerIcons";
const NameWrapper = styled.div` const NameWrapper = styled.div`
display: flex; display: flex;
@ -79,6 +83,18 @@ const QueryEditorHeader = (props: Props) => {
currentActionConfig?.userPermissions, 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 // get the current action's plugin name
const currentActionPluginName = useSelector((state: AppState) => const currentActionPluginName = useSelector((state: AppState) =>
getPluginNameFromId(state, currentActionConfig?.pluginId || ""), getPluginNameFromId(state, currentActionConfig?.pluginId || ""),
@ -106,8 +122,11 @@ const QueryEditorHeader = (props: Props) => {
<StyledFormRow> <StyledFormRow>
<NameWrapper> <NameWrapper>
<ActionNameEditor <ActionNameEditor
actionConfig={currentActionConfig}
disabled={!isChangePermitted} disabled={!isChangePermitted}
icon={icon}
saveActionName={saveActionName} saveActionName={saveActionName}
saveStatus={saveStatus}
/> />
</NameWrapper> </NameWrapper>
<ActionsWrapper> <ActionsWrapper>

View File

@ -42,6 +42,7 @@ import { ENTITY_ICON_SIZE, EntityIcon } from "../Explorer/ExplorerIcons";
import { getIDEViewMode } from "selectors/ideSelectors"; import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "ee/entities/IDE/constants"; import { EditorViewMode } from "ee/entities/IDE/constants";
import { AppPluginActionEditor } from "../AppPluginActionEditor"; import { AppPluginActionEditor } from "../AppPluginActionEditor";
import { saveActionName } from "actions/pluginActionActions";
type QueryEditorProps = RouteComponentProps<QueryEditorRouteParams>; type QueryEditorProps = RouteComponentProps<QueryEditorRouteParams>;
@ -126,6 +127,7 @@ function QueryEditor(props: QueryEditorProps) {
}, [ }, [
action?.id, action?.id,
action?.name, action?.name,
action?.pluginType,
isChangePermitted, isChangePermitted,
isDeletePermitted, isDeletePermitted,
basePageId, basePageId,
@ -143,7 +145,7 @@ function QueryEditor(props: QueryEditorProps) {
changeQuery({ baseQueryId: baseQueryId, basePageId, applicationId }), changeQuery({ baseQueryId: baseQueryId, basePageId, applicationId }),
); );
}, },
[basePageId, applicationId], [basePageId, applicationId, dispatch],
); );
const onCreateDatasourceClick = useCallback(() => { const onCreateDatasourceClick = useCallback(() => {
@ -159,13 +161,7 @@ function QueryEditor(props: QueryEditorProps) {
AnalyticsUtil.logEvent("NAVIGATE_TO_CREATE_NEW_DATASOURCE_PAGE", { AnalyticsUtil.logEvent("NAVIGATE_TO_CREATE_NEW_DATASOURCE_PAGE", {
entryPoint, entryPoint,
}); });
}, [ }, [basePageId]);
basePageId,
history,
integrationEditorURL,
DatasourceCreateEntryPoints,
AnalyticsUtil,
]);
// custom function to return user to integrations page if action is not found // custom function to return user to integrations page if action is not found
const onEntityNotFoundBackClick = useCallback( const onEntityNotFoundBackClick = useCallback(
@ -176,7 +172,7 @@ function QueryEditor(props: QueryEditorProps) {
selectedTab: INTEGRATION_TABS.ACTIVE, selectedTab: INTEGRATION_TABS.ACTIVE,
}), }),
), ),
[basePageId, history, integrationEditorURL], [basePageId],
); );
const notification = useMemo(() => { const notification = useMemo(() => {
@ -189,7 +185,7 @@ function QueryEditor(props: QueryEditorProps) {
withPadding withPadding
/> />
); );
}, [action?.name, isConverting]); }, [action?.name, isConverting, icon]);
const isActionRedesignEnabled = useFeatureFlag( const isActionRedesignEnabled = useFeatureFlag(
FEATURE_FLAG.release_actions_redesign_enabled, FEATURE_FLAG.release_actions_redesign_enabled,
@ -207,6 +203,7 @@ function QueryEditor(props: QueryEditorProps) {
notification={notification} notification={notification}
onCreateDatasourceClick={onCreateDatasourceClick} onCreateDatasourceClick={onCreateDatasourceClick}
onEntityNotFoundBackClick={onEntityNotFoundBackClick} onEntityNotFoundBackClick={onEntityNotFoundBackClick}
saveActionName={saveActionName}
> >
<Disabler isDisabled={isConverting}> <Disabler isDisabled={isConverting}>
<Editor <Editor