diff --git a/app/client/src/PluginActionEditor/PluginActionContext.tsx b/app/client/src/PluginActionEditor/PluginActionContext.tsx new file mode 100644 index 000000000000..0b99f9dc363d --- /dev/null +++ b/app/client/src/PluginActionEditor/PluginActionContext.tsx @@ -0,0 +1,62 @@ +import React, { + type ReactNode, + createContext, + useContext, + useMemo, +} from "react"; +import type { Action } from "entities/Action"; +import type { Plugin } from "api/PluginApi"; +import type { Datasource } from "entities/Datasource"; + +interface PluginActionContextType { + action: Action; + editorConfig: unknown[]; + settingsConfig: unknown[]; + plugin: Plugin; + datasource?: Datasource; +} + +// No need to export this context to use it. Use the hook defined below instead +const PluginActionContext = createContext(null); + +interface ChildrenProps { + children: ReactNode[]; +} + +export const PluginActionContextProvider = ( + props: ChildrenProps & PluginActionContextType, +) => { + const { action, children, datasource, editorConfig, plugin, settingsConfig } = + props; + + // using useMemo to avoid unnecessary renders + const contextValue = useMemo( + () => ({ + action, + datasource, + editorConfig, + plugin, + settingsConfig, + }), + [action, datasource, editorConfig, plugin, settingsConfig], + ); + + return ( + + {children} + + ); +}; + +// By using this hook, you are guaranteed that the states are correctly +// typed and set. +// Without this, consumers of the context would need to keep doing a null check +export const usePluginActionContext = () => { + const context = useContext(PluginActionContext); + if (!context) { + throw new Error( + "usePluginActionContext must be used within usePluginActionContextProvider", + ); + } + return context; +}; diff --git a/app/client/src/PluginActionEditor/PluginActionEditor.tsx b/app/client/src/PluginActionEditor/PluginActionEditor.tsx new file mode 100644 index 000000000000..d8e480403064 --- /dev/null +++ b/app/client/src/PluginActionEditor/PluginActionEditor.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { useLocation } from "react-router"; +import { identifyEntityFromPath } from "../navigation/FocusEntity"; +import { useSelector } from "react-redux"; +import { + getActionByBaseId, + getDatasource, + getEditorConfig, + getPlugin, + getPluginSettingConfigs, +} from "ee/selectors/entitiesSelector"; +import { PluginActionContextProvider } from "./PluginActionContext"; +import { get } from "lodash"; +import EntityNotFoundPane from "pages/Editor/EntityNotFoundPane"; +import { getIsEditorInitialized } from "selectors/editorSelectors"; +import Spinner from "components/editorComponents/Spinner"; +import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; +import { Text } from "@appsmith/ads"; + +interface ChildrenProps { + children: React.ReactNode[]; +} + +const PluginActionEditor = (props: ChildrenProps) => { + const { pathname } = useLocation(); + + const isEditorInitialized = useSelector(getIsEditorInitialized); + + const entity = identifyEntityFromPath(pathname); + const action = useSelector((state) => getActionByBaseId(state, entity.id)); + + const pluginId = get(action, "pluginId", ""); + const plugin = useSelector((state) => getPlugin(state, pluginId)); + + const datasourceId = get(action, "datasource.id", ""); + const datasource = useSelector((state) => getDatasource(state, datasourceId)); + + const settingsConfig = useSelector((state) => + getPluginSettingConfigs(state, pluginId), + ); + + const editorConfig = useSelector((state) => getEditorConfig(state, pluginId)); + + if (!isEditorInitialized) { + return ( + + + + ); + } + + if (!action) { + return ; + } + + if (!plugin) { + return ( + + + Plugin not installed! + + + ); + } + + if (!settingsConfig || !editorConfig) { + return ( + + + Editor config not found! + + + ); + } + + return ( + + {props.children} + + ); +}; + +export default PluginActionEditor; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx new file mode 100644 index 000000000000..17db33e698a3 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const PluginActionForm = () => { + return
; +}; + +export default PluginActionForm; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/index.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/index.ts new file mode 100644 index 000000000000..bb106d466ee2 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/index.ts @@ -0,0 +1 @@ +export { default } from "./PluginActionForm"; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponsePane.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponsePane.tsx new file mode 100644 index 000000000000..5a0be861970c --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponsePane.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const PluginActionResponsePane = () => { + return
; +}; + +export default PluginActionResponsePane; diff --git a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx new file mode 100644 index 000000000000..64542de3c66f --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const PluginActionToolbar = () => { + return
; +}; + +export default PluginActionToolbar; diff --git a/app/client/src/PluginActionEditor/index.ts b/app/client/src/PluginActionEditor/index.ts new file mode 100644 index 000000000000..0a58d00bdaae --- /dev/null +++ b/app/client/src/PluginActionEditor/index.ts @@ -0,0 +1,8 @@ +export { default as PluginActionEditor } from "./PluginActionEditor"; +export { + PluginActionContextProvider, + usePluginActionContext, +} from "./PluginActionContext"; +export { default as PluginActionToolbar } from "./components/PluginActionToolbar"; +export { default as PluginActionForm } from "./components/PluginActionForm"; +export { default as PluginActionResponsePane } from "./components/PluginActionResponsePane"; diff --git a/app/client/src/ce/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx b/app/client/src/ce/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx new file mode 100644 index 000000000000..6ed514390c08 --- /dev/null +++ b/app/client/src/ce/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { + PluginActionEditor, + PluginActionToolbar, + PluginActionForm, + PluginActionResponsePane, +} from "PluginActionEditor"; + +const AppPluginActionEditor = () => { + return ( + + + + + + ); +}; + +export default AppPluginActionEditor; diff --git a/app/client/src/ee/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx b/app/client/src/ee/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx new file mode 100644 index 000000000000..2d974cc98581 --- /dev/null +++ b/app/client/src/ee/pages/Editor/AppPluginActionEditor/AppPluginActionEditor.tsx @@ -0,0 +1,3 @@ +import { default as CE_AppPluginActionEditor } from "ce/pages/Editor/AppPluginActionEditor/AppPluginActionEditor"; + +export default CE_AppPluginActionEditor; diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index d7affd3e552f..5241a672b2bf 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -38,6 +38,7 @@ import { resolveIcon } from "../utils"; import { ENTITY_ICON_SIZE, EntityIcon } from "../Explorer/ExplorerIcons"; import { getIDEViewMode } from "selectors/ideSelectors"; import { EditorViewMode } from "ee/entities/IDE/constants"; +import { AppPluginActionEditor } from "pages/Editor/AppPluginActionEditor"; type ApiEditorWrapperProps = RouteComponentProps; @@ -177,6 +178,14 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) { return ; }, [action?.name, isConverting]); + const isActionRedesignEnabled = useFeatureFlag( + FEATURE_FLAG.release_actions_redesign_enabled, + ); + + if (isActionRedesignEnabled) { + return ; + } + return ( ; @@ -188,6 +189,14 @@ function QueryEditor(props: QueryEditorProps) { ); }, [action?.name, isConverting]); + const isActionRedesignEnabled = useFeatureFlag( + FEATURE_FLAG.release_actions_redesign_enabled, + ); + + if (isActionRedesignEnabled) { + return ; + } + return (