diff --git a/app/client/cypress/support/Objects/FeatureFlags.ts b/app/client/cypress/support/Objects/FeatureFlags.ts index fdf01f2bc83f..b8385241ddda 100644 --- a/app/client/cypress/support/Objects/FeatureFlags.ts +++ b/app/client/cypress/support/Objects/FeatureFlags.ts @@ -5,7 +5,6 @@ import produce from "immer"; const defaultFlags = { release_side_by_side_ide_enabled: true, rollout_remove_feature_walkthrough_enabled: false, // remove this flag from here when it's removed from code - release_actions_redesign_enabled: true, release_git_modularisation_enabled: true, }; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx deleted file mode 100644 index 3c1018cfc12e..000000000000 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import styled from "styled-components"; -import { Tab, TabPanel, Tabs, TabsList } from "@appsmith/ads"; -import FormLabel from "components/editorComponents/FormLabel"; -import type { AutoGeneratedHeader } from "pages/Editor/APIEditor/helpers"; -import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import React from "react"; -import { API_EDITOR_TABS } from "../../../../constants/CommonApiConstants"; -import { DatasourceConfig } from "./components/DatasourceConfig"; -import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray"; -import ApiAuthentication from "./components/ApiAuthentication"; -import ActionSettings from "pages/Editor/ActionSettings"; -import { API_EDITOR_TAB_TITLES, createMessage } from "ee/constants/messages"; -import { useSelectedFormTab } from "./hooks/useSelectedFormTab"; -import { getHeadersCount, getParamsCount } from "./utils"; -import type { Property } from "entities/Action"; - -const SettingsWrapper = styled.div` - padding: var(--ads-v2-spaces-4) 0; - height: 100%; - - ${FormLabel} { - padding: 0; - } -`; -const StyledTabPanel = styled(TabPanel)` - height: calc(100% - 50px); - overflow: auto; -`; - -/** - * @deprecated This component will be deleted along with APIEditor/CommonEditorForm. - */ -export function RequestTabs(props: { - autogeneratedHeaders: AutoGeneratedHeader[] | undefined; - datasourceHeaders: Property[]; - actionConfigurationHeaders: Property[]; - actionName: string; - pushFields: boolean; - theme: EditorTheme.LIGHT; - datasourceParams: Property[]; - actionConfigurationParams: Property[]; - bodyUIComponent: React.ReactNode; - paginationUiComponent: React.ReactNode; - formName: string; - showSettings: boolean; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - actionSettingsConfig?: any; -}) { - const [value, onValueChange] = useSelectedFormTab(); - const headersCount = getHeadersCount( - props.actionConfigurationHeaders, - props.datasourceHeaders, - props.autogeneratedHeaders, - ); - - const paramsCount = getParamsCount( - props.actionConfigurationParams, - props.datasourceHeaders, - ); - - return ( - - - {Object.values(API_EDITOR_TABS) - .filter((tab) => { - return !(!props.showSettings && tab === API_EDITOR_TABS.SETTINGS); - }) - .map((tab) => ( - - {createMessage(API_EDITOR_TAB_TITLES[tab])} - - ))} - - - - - - - - - - - {props.bodyUIComponent} - - - {props.paginationUiComponent} - - - - - {props.showSettings ? ( - - - - - - ) : null} - - ); -} diff --git a/app/client/src/pages/Editor/APIEditor/helpers.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/utils/autoGeneratedHeaders.ts similarity index 62% rename from app/client/src/pages/Editor/APIEditor/helpers.ts rename to app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/utils/autoGeneratedHeaders.ts index 1d095f0ee381..33cd0f0a3034 100644 --- a/app/client/src/pages/Editor/APIEditor/helpers.ts +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/utils/autoGeneratedHeaders.ts @@ -1,29 +1,4 @@ -export const sortedDatasourcesHandler = ( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - datasources: Record, - currentDatasourceId: string, -) => { - // this function sorts the datasources list, with the current action's datasource first, followed by others. - let sortedArr = []; - - sortedArr = datasources.filter( - (d: { id: string }) => d?.id === currentDatasourceId, - ); - - sortedArr = [ - ...sortedArr, - ...datasources.filter((d: { id: string }) => d?.id !== currentDatasourceId), - ]; - - return sortedArr; -}; - -export interface AutoGeneratedHeader { - key: string; - value: string; - isInvalid: boolean; -} +import type { AutoGeneratedHeader } from "entities/Action"; // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/app/client/src/pages/Editor/QueryEditor/QueriesBlankState.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/QueriesBlankState.tsx similarity index 100% rename from app/client/src/pages/Editor/QueryEditor/QueriesBlankState.tsx rename to app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/QueriesBlankState.tsx diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiFormatSegmentedResponse.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiFormatSegmentedResponse.tsx index d83a11b15f16..91da821f37db 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiFormatSegmentedResponse.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/ApiFormatSegmentedResponse.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useState } from "react"; import { isArray, isString } from "lodash"; import { isHtml } from "../utils"; import ReadOnlyEditor from "components/editorComponents/ReadOnlyEditor"; -import { SegmentedControlContainer } from "pages/Editor/QueryEditor/EditorJSONtoForm"; import { Flex, SegmentedControl } from "@appsmith/ads"; import type { ActionResponse } from "api/ActionAPI"; import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; @@ -12,6 +11,16 @@ import { useDispatch } from "react-redux"; import styled from "styled-components"; import { ResponseFormatTabs } from "./ResponseFormatTabs"; +const SegmentedControlContainer = styled.div` + padding: 0 var(--ads-v2-spaces-7); + padding-top: var(--ads-v2-spaces-4); + display: flex; + flex-direction: column; + gap: var(--ads-v2-spaces-4); + overflow-y: clip; + overflow-x: scroll; +`; + const ResponseBodyContainer = styled.div` overflow-y: clip; height: 100%; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/Response.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/Response.tsx index 296032decbb7..c14056cf2b77 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/Response.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/Response.tsx @@ -7,7 +7,6 @@ import pluralize from "pluralize"; import { Callout, Tooltip, type CalloutLinkProps } from "@appsmith/ads"; import type { ActionResponse } from "api/ActionAPI"; -import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView"; import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import { type Action } from "entities/Action"; import { PluginType } from "entities/Plugin"; @@ -16,14 +15,7 @@ import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; import { actionResponseDisplayDataFormats } from "pages/Editor/utils"; import { scrollbarWidth } from "utils/helpers"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { - openPluginActionSettings, - setPluginActionEditorSelectedTab, -} from "PluginActionEditor/store"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; - -import { EDITOR_TABS } from "constants/QueryEditorConstants"; +import { openPluginActionSettings } from "../../../../store"; import { createMessage, PREPARED_STATEMENT_WARNING, @@ -39,6 +31,7 @@ import { RESPONSE_TABLE_HEIGHT_OFFSET } from "./constants"; import * as Styled from "./styles"; import { checkForPreparedStatement, parseActionResponse } from "./utils"; +import ActionExecutionInProgressView from "./components/ActionExecutionInProgressView"; interface ResponseProps { action: Action; @@ -51,10 +44,6 @@ interface ResponseProps { } export function Response(props: ResponseProps) { - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - const { action, actionResponse, @@ -148,11 +137,7 @@ export function Response(props: ResponseProps) { const preparedStatementCalloutLinks: CalloutLinkProps[] = useMemo(() => { const navigateToSettings = () => { - if (isActionRedesignEnabled) { - dispatch(openPluginActionSettings(true)); - } else { - dispatch(setPluginActionEditorSelectedTab(EDITOR_TABS.SETTINGS)); - } + dispatch(openPluginActionSettings(true)); }; return [ @@ -161,7 +146,7 @@ export function Response(props: ResponseProps) { children: createMessage(PREPARED_STATEMENT_WARNING.LINK), }, ]; - }, [dispatch, isActionRedesignEnabled]); + }, [dispatch]); const handleContentTypeChange = useEventCallback((e?: Event) => { if (e?.target && e.target instanceof HTMLElement) { diff --git a/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/components/ActionExecutionInProgressView.tsx similarity index 91% rename from app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx rename to app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/components/ActionExecutionInProgressView.tsx index 1627c0c1f31f..853ec041db90 100644 --- a/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Response/components/ActionExecutionInProgressView.tsx @@ -7,8 +7,8 @@ import { import ActionAPI from "api/ActionAPI"; import { Button, Spinner, Text } from "@appsmith/ads"; import styled from "styled-components"; -import type { EditorTheme } from "./CodeEditor/EditorConfig"; -import LoadingOverlayScreen from "./LoadingOverlayScreen"; +import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; +import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen"; const Wrapper = styled.div` position: relative; diff --git a/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx b/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx index 0c3da50abf16..cbc5bb9ae1b0 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from "react"; import { Link } from "@appsmith/ads"; -import ActionSettings from "pages/Editor/ActionSettings"; +import ActionSettings from "../PluginActionToolbar/components/ActionSettings"; import { usePluginActionContext } from "../../PluginActionContext"; import styled from "styled-components"; import { diff --git a/app/client/src/pages/Editor/ActionSettings.tsx b/app/client/src/PluginActionEditor/components/PluginActionToolbar/components/ActionSettings.tsx similarity index 94% rename from app/client/src/pages/Editor/ActionSettings.tsx rename to app/client/src/PluginActionEditor/components/PluginActionToolbar/components/ActionSettings.tsx index 908bb8180b0e..cccd1dfe1404 100644 --- a/app/client/src/pages/Editor/ActionSettings.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionToolbar/components/ActionSettings.tsx @@ -1,11 +1,11 @@ import React from "react"; import type { ControlProps } from "components/formControls/BaseControl"; -import FormControl from "./FormControl"; +import FormControl from "pages/Editor/FormControl"; import log from "loglevel"; import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import styled from "styled-components"; import { Text } from "@appsmith/ads"; -import CenteredWrapper from "../../components/designSystems/appsmith/CenteredWrapper"; +import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; interface ActionSettingsProps { // TODO: Fix this the next time the file is edited diff --git a/app/client/src/ce/entities/FeatureFlag.ts b/app/client/src/ce/entities/FeatureFlag.ts index a662c4807e10..9e8d0b90cef0 100644 --- a/app/client/src/ce/entities/FeatureFlag.ts +++ b/app/client/src/ce/entities/FeatureFlag.ts @@ -26,7 +26,6 @@ export const FEATURE_FLAG = { "ab_one_click_learning_popover_enabled", release_side_by_side_ide_enabled: "release_side_by_side_ide_enabled", ab_appsmith_ai_query: "ab_appsmith_ai_query", - release_actions_redesign_enabled: "release_actions_redesign_enabled", rollout_remove_feature_walkthrough_enabled: "rollout_remove_feature_walkthrough_enabled", rollout_eslint_enabled: "rollout_eslint_enabled", @@ -82,7 +81,6 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = { ab_one_click_learning_popover_enabled: false, release_side_by_side_ide_enabled: false, ab_appsmith_ai_query: false, - release_actions_redesign_enabled: false, rollout_remove_feature_walkthrough_enabled: true, rollout_eslint_enabled: false, rollout_side_by_side_enabled: false, diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx index dfd6dc18c8a5..1c850a191c4f 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx @@ -154,13 +154,6 @@ const PluginActionEditor = lazy(async () => ), ); -const ApiEditor = lazy(async () => - retryPromise( - async () => - import(/* webpackChunkName: "APIEditor" */ "pages/Editor/APIEditor"), - ), -); - const AddQuery = lazy(async () => retryPromise( async () => @@ -169,30 +162,20 @@ const AddQuery = lazy(async () => ), ), ); -const QueryEditor = lazy(async () => - retryPromise( - async () => - import(/* webpackChunkName: "QueryEditor" */ "pages/Editor/QueryEditor"), - ), -); const QueryEmpty = lazy(async () => retryPromise( async () => import( - /* webpackChunkName: "QueryEmpty" */ "pages/Editor/QueryEditor/QueriesBlankState" + /* webpackChunkName: "QueryEmpty" */ "../../../../../../PluginActionEditor/components/PluginActionForm/components/UQIEditor/QueriesBlankState" ), ), ); export const useQueryEditorRoutes = (path: string): UseRoutes => { - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - const skeleton = useMemo(() => , []); - const newComponents = useMemo( + return useMemo( () => [ { key: "AddQuery", @@ -237,82 +220,6 @@ export const useQueryEditorRoutes = (path: string): UseRoutes => { ], [path, skeleton], ); - - const oldComponents = useMemo( - () => [ - { - key: "ApiEditor", - component: (args: object) => { - return ( - - - - ); - }, - exact: true, - path: [ - BUILDER_PATH + API_EDITOR_ID_PATH, - BUILDER_CUSTOM_PATH + API_EDITOR_ID_PATH, - BUILDER_PATH_DEPRECATED + API_EDITOR_ID_PATH, - ], - }, - { - key: "AddQuery", - exact: true, - component: () => ( - - - - ), - path: [`${path}${ADD_PATH}`, `${path}/:baseQueryId${ADD_PATH}`], - }, - { - key: "SAASEditor", - component: (args: object) => { - return ( - - - - ); - }, - exact: true, - path: [ - BUILDER_PATH + SAAS_EDITOR_API_ID_PATH, - BUILDER_CUSTOM_PATH + SAAS_EDITOR_API_ID_PATH, - BUILDER_PATH_DEPRECATED + SAAS_EDITOR_API_ID_PATH, - ], - }, - { - key: "QueryEditor", - component: (args: object) => { - return ( - - - - ); - }, - exact: true, - path: [path + "/:baseQueryId"], - }, - { - key: "QueryEmpty", - component: () => ( - - - - ), - exact: true, - path: [path], - }, - ], - [path, skeleton], - ); - - if (isActionRedesignEnabled) { - return newComponents; - } - - return oldComponents; }; export const useAddQueryListItems = () => { diff --git a/app/client/src/components/editorComponents/ActionNameEditor.tsx b/app/client/src/components/editorComponents/ActionNameEditor.tsx deleted file mode 100644 index 807b676908bf..000000000000 --- a/app/client/src/components/editorComponents/ActionNameEditor.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { memo } from "react"; - -import EditableText, { - EditInteractionKind, -} from "components/editorComponents/EditableText"; -import { removeSpecialChars } from "utils/helpers"; - -import { Flex } from "@appsmith/ads"; -import NameEditorComponent, { - IconBox, - NameWrapper, -} from "components/utils/NameEditorComponent"; -import { - ACTION_ID_NOT_FOUND_IN_URL, - ACTION_NAME_PLACEHOLDER, - createMessage, -} from "ee/constants/messages"; -import type { ReduxAction } from "actions/ReduxActionTypes"; -import type { SaveActionNameParams } from "PluginActionEditor"; -import type { Action } from "entities/Action"; -import type { ModuleInstance } from "ee/constants/ModuleInstanceConstants"; - -interface ActionNameEditorProps { - /* - This prop checks if page is API Pane or Query Pane or Curl Pane - So, that we can toggle between ads editable-text component and existing editable-text component - Right now, it's optional so that it doesn't impact any other pages other than API Pane. - In future, when default component will be ads editable-text, then we can remove this prop. - */ - enableFontStyling?: boolean; - disabled?: boolean; - saveActionName: ( - params: SaveActionNameParams, - ) => ReduxAction; - actionConfig?: Action | ModuleInstance; - icon?: JSX.Element; - saveStatus: { isSaving: boolean; error: boolean }; -} - -function ActionNameEditor(props: ActionNameEditorProps) { - const { - actionConfig, - disabled = false, - enableFontStyling = false, - icon = "", - saveActionName, - saveStatus, - } = props; - - return ( - - {({ - forceUpdate, - handleNameChange, - isInvalidNameForEntity, - isNew, - saveStatus, - }: { - forceUpdate: boolean; - handleNameChange: (value: string) => void; - isInvalidNameForEntity: (value: string) => string | boolean; - isNew: boolean; - saveStatus: { isSaving: boolean; error: boolean }; - }) => ( - - - {icon && {icon}} - - - - )} - - ); -} - -export default memo(ActionNameEditor); diff --git a/app/client/src/components/editorComponents/ActionRightPane/index.tsx b/app/client/src/components/editorComponents/ActionRightPane/index.tsx deleted file mode 100644 index 456484c3035b..000000000000 --- a/app/client/src/components/editorComponents/ActionRightPane/index.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React, { useMemo } from "react"; -import styled from "styled-components"; -import { getTypographyByKey } from "@appsmith/ads-old"; -import { useSelector } from "react-redux"; -import type { AppState } from "ee/reducers"; -import { getDependenciesFromInverseDependencies } from "../Debugger/helpers"; -import { - CollapsibleGroup, - CollapsibleGroupContainer, -} from "components/common/Collapsible"; - -const SideBar = styled.div` - height: 100%; - width: 100%; - - & > a { - margin-top: 0; - margin-left: 0; - } - - .icon-text { - display: flex; - - .connection-type { - ${getTypographyByKey("p1")} - } - } - - .icon-text:nth-child(2) { - padding-top: ${(props) => props.theme.spaces[7]}px; - } - - .description { - ${getTypographyByKey("p1")} - margin-left: ${(props) => props.theme.spaces[2] + 1}px; - padding-bottom: ${(props) => props.theme.spaces[7]}px; - } - - @-webkit-keyframes slide-left { - 0% { - -webkit-transform: translateX(100%); - transform: translateX(100%); - } - 100% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - } - @keyframes slide-left { - 0% { - -webkit-transform: translateX(100%); - transform: translateX(100%); - } - 100% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - } -`; - -const Wrapper = styled.div` - border-left: 1px solid var(--ads-v2-color-border); - padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-4); - overflow: hidden; - border-bottom: 0; - display: flex; - width: ${(props) => props.theme.actionSidePane.width}px; - margin-top: 10px; - /* margin-left: var(--ads-v2-spaces-7); */ -`; - -export function useEntityDependencies(actionName: string) { - const deps = useSelector((state: AppState) => state.evaluations.dependencies); - const entityDependencies = useMemo( - () => - getDependenciesFromInverseDependencies( - deps.inverseDependencyMap, - actionName, - ), - [actionName, deps.inverseDependencyMap], - ); - const hasDependencies = - entityDependencies && - (entityDependencies?.directDependencies.length > 0 || - entityDependencies?.inverseDependencies.length > 0); - - return { - hasDependencies, - entityDependencies, - }; -} - -function ActionSidebar({ - additionalSections, -}: { - additionalSections?: React.ReactNode; -}) { - if (!additionalSections) { - return null; - } - - return ( - - - - {additionalSections && ( - - {additionalSections} - - )} - - - - ); -} - -export default ActionSidebar; diff --git a/app/client/src/components/formControls/DynamicTextFieldControl.tsx b/app/client/src/components/formControls/DynamicTextFieldControl.tsx index 0725efb17eae..b7c772cf37e9 100644 --- a/app/client/src/components/formControls/DynamicTextFieldControl.tsx +++ b/app/client/src/components/formControls/DynamicTextFieldControl.tsx @@ -20,7 +20,6 @@ import { import { actionPathFromName } from "components/formControls/utils"; import type { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { getSqlEditorModeFromPluginName } from "components/editorComponents/CodeEditor/sql/config"; -import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors"; import { Flex } from "@appsmith/ads"; const Wrapper = styled.div` @@ -110,14 +109,12 @@ const mapStateToProps = (state: AppState, props: DynamicTextFieldProps) => { const pluginId = valueSelector(state, "datasource.pluginId"); const responseTypes = getPluginResponseTypes(state); const pluginName = getPluginNameFromId(state, pluginId); - const { release_actions_redesign_enabled } = selectFeatureFlags(state); return { actionName, pluginId, responseType: responseTypes[pluginId], pluginName, - isActionRedesignEnabled: release_actions_redesign_enabled, }; }; diff --git a/app/client/src/entities/Action/index.ts b/app/client/src/entities/Action/index.ts index 3c370f5c17d9..a7619968a1f5 100644 --- a/app/client/src/entities/Action/index.ts +++ b/app/client/src/entities/Action/index.ts @@ -2,7 +2,6 @@ import type { EmbeddedRestDatasource } from "entities/Datasource"; import type { DynamicPath } from "utils/DynamicBindingUtils"; import _ from "lodash"; import type { LayoutOnLoadActionErrors } from "constants/AppsmithActionConstants/ActionConstants"; -import type { AutoGeneratedHeader } from "pages/Editor/APIEditor/helpers"; import type { EventLocation } from "ee/utils/analyticsUtilTypes"; import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers"; import { @@ -82,6 +81,12 @@ export interface BodyFormData { type: string; } +export interface AutoGeneratedHeader { + key: string; + value: string; + isInvalid: boolean; +} + export interface ApiActionConfig extends Omit { headers: Property[]; autoGeneratedHeaders?: AutoGeneratedHeader[]; diff --git a/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx b/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx deleted file mode 100644 index 6f85505c5ed6..000000000000 --- a/app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import type { ReduxAction } from "actions/ReduxActionTypes"; -import type { PaginationField } from "api/ActionAPI"; -import React, { createContext, useMemo } from "react"; -import type { SaveActionNameParams } from "PluginActionEditor"; - -interface ApiEditorContextContextProps { - moreActionsMenu?: React.ReactNode; - handleRunClick: (paginationField?: PaginationField) => void; - actionRightPaneBackLink?: React.ReactNode; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingsConfig: any; - saveActionName: ( - params: SaveActionNameParams, - ) => ReduxAction; - showRightPaneTabbedSection?: boolean; - actionRightPaneAdditionSections?: React.ReactNode; - notification?: React.ReactNode | string; -} - -type ApiEditorContextProviderProps = - React.PropsWithChildren; - -export const ApiEditorContext = createContext( - {} as ApiEditorContextContextProps, -); - -export function ApiEditorContextProvider({ - actionRightPaneAdditionSections, - actionRightPaneBackLink, - children, - handleRunClick, - moreActionsMenu, - notification, - saveActionName, - settingsConfig, - showRightPaneTabbedSection, -}: ApiEditorContextProviderProps) { - const value = useMemo( - () => ({ - actionRightPaneAdditionSections, - actionRightPaneBackLink, - showRightPaneTabbedSection, - handleRunClick, - moreActionsMenu, - saveActionName, - settingsConfig, - notification, - }), - [ - actionRightPaneBackLink, - actionRightPaneAdditionSections, - showRightPaneTabbedSection, - handleRunClick, - moreActionsMenu, - saveActionName, - settingsConfig, - notification, - ], - ); - - return ( - - {children} - - ); -} diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx deleted file mode 100644 index 20083d77bc2d..000000000000 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ /dev/null @@ -1,352 +0,0 @@ -import React, { useContext } from "react"; -import { useSelector } from "react-redux"; -import styled from "styled-components"; -import FormLabel from "components/editorComponents/FormLabel"; -import FormRow from "components/editorComponents/FormRow"; -import type { ActionResponse, PaginationField } from "api/ActionAPI"; -import type { Action, PaginationType } from "entities/Action"; -import ApiResponseView from "components/editorComponents/ApiResponseView"; -import type { AppState } from "ee/reducers"; -import ActionNameEditor from "components/editorComponents/ActionNameEditor"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import { Button } from "@appsmith/ads"; -import { useParams } from "react-router"; -import equal from "fast-deep-equal/es6"; -import { getPlugin } from "ee/selectors/entitiesSelector"; -import type { AutoGeneratedHeader } from "./helpers"; -import { noop } from "lodash"; -import { DEFAULT_DATASOURCE_NAME } from "PluginActionEditor/constants/ApiEditorConstants"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { - getHasExecuteActionPermission, - getHasManageActionPermission, -} from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import { ApiEditorContext } from "./ApiEditorContext"; -import RunHistory from "ee/components/RunHistory"; -import { HintMessages } from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/HintMessages"; -import { InfoFields } from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/InfoFields"; -import { RequestTabs } from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs"; -import { getSavingStatusForActionName } from "selectors/actionSelectors"; -import { getAssetUrl } from "ee/utils/airgapHelpers"; -import { ActionUrlIcon } from "../Explorer/ExplorerIcons"; - -const Form = styled.form` - position: relative; - display: flex; - flex-direction: column; - flex: 1; - overflow: hidden; - width: 100%; - - ${FormLabel} { - padding: ${(props) => props.theme.spaces[3]}px; - } - - ${FormRow} { - align-items: center; - - ${FormLabel} { - padding: 0; - width: 100%; - } - } - - .api-info-row { - input { - margin-left: ${(props) => props.theme.spaces[1] + 1}px; - } - } -`; - -const MainConfiguration = styled.div` - z-index: 7; - padding: 0 var(--ads-v2-spaces-7); - - .api-info-row { - padding-top: var(--ads-v2-spaces-5); - } - - .form-row-header { - padding-top: var(--ads-v2-spaces-5); - } -`; - -const ActionButtons = styled.div` - justify-self: flex-end; - display: flex; - align-items: center; - gap: var(--ads-v2-spaces-3); -`; - -const HelpSection = styled.div` - padding: var(--ads-v2-spaces-4) var(--ads-v2-spaces-7); -`; - -const SecondaryWrapper = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - width: 100%; -`; - -const TabbedViewContainer = styled.div` - flex: 1; - overflow: auto; - position: relative; - height: 100%; - padding: 0 var(--ads-v2-spaces-7); -`; - -const Wrapper = styled.div` - display: flex; - flex-direction: row; - height: 100%; - position: relative; - overflow: hidden; -`; - -const MainContainer = styled.div` - display: flex; - position: relative; - height: 100%; - flex-direction: column; - /* padding: var(--ads-v2-spaces-7); */ -`; - -export interface CommonFormProps { - actionResponse?: ActionResponse; - pluginId: string; - onRunClick: (paginationField?: PaginationField) => void; - isRunning: boolean; - isDeleting: boolean; - paginationType: PaginationType; - appName: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - actionConfigurationHeaders?: any; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - actionConfigurationParams?: any; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - datasourceHeaders?: any; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - datasourceParams?: any; - actionName: string; - apiName: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingsConfig: any; - hintMessages?: Array; - autoGeneratedActionConfigHeaders?: AutoGeneratedHeader[]; -} - -type CommonFormPropsWithExtraParams = CommonFormProps & { - formName: string; - // Body Tab Component which is passed on from the Parent Component - bodyUIComponent: JSX.Element; - // Pagination Tab Component which is passed on from the Parent Component - paginationUIComponent: JSX.Element; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleSubmit: any; - httpsMethods: { value: string }[]; -}; - -export const NameWrapper = styled.div` - display: flex; - align-items: center; - - input { - margin: 0; - box-sizing: border-box; - } -`; - -const StyledNotificationWrapper = styled.div` - padding-top: var(--ads-v2-spaces-5); -`; - -/** - * Commons editor form which is being used by API and GraphQL. Since most of the things were common to both so picking out the common part was a better option. For now Body and Pagination component are being passed on by the using component. - * @param props type CommonFormPropsWithExtraParams - * @returns Editor with respect to which type is using it - */ -function CommonEditorForm(props: CommonFormPropsWithExtraParams) { - const { - actionRightPaneAdditionSections, - moreActionsMenu, - notification, - saveActionName, - } = useContext(ApiEditorContext); - - const { - actionConfigurationHeaders, - actionConfigurationParams, - actionResponse, - autoGeneratedActionConfigHeaders, - formName, - handleSubmit, - hintMessages, - isRunning, - onRunClick, - pluginId, - settingsConfig, - } = props; - - const params = useParams<{ baseApiId?: string; baseQueryId?: string }>(); - - // passing lodash's equality function to ensure that this selector does not cause a rerender multiple times. - // it checks each value to make sure none has changed before recomputing the actions. - const actions: Action[] = useSelector( - (state: AppState) => state.entities.actions.map((action) => action.config), - equal, - ); - - const currentActionConfig: Action | undefined = actions.find( - (action) => - action.baseId === params.baseApiId || action.id === params.baseQueryId, - ); - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const isChangePermitted = getHasManageActionPermission( - isFeatureEnabled, - currentActionConfig?.userPermissions, - ); - const isExecutePermitted = getHasExecuteActionPermission( - isFeatureEnabled, - 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 ?? ""), - ); - - if (!currentActionConfig) return null; - - // this gets the url of the current action's datasource - const actionDatasourceUrl = - currentActionConfig?.datasource?.datasourceConfiguration?.url || ""; - const actionDatasourceUrlPath = - currentActionConfig?.actionConfiguration?.path || ""; - // this gets the name of the current action's datasource - const actionDatasourceName = currentActionConfig?.datasource.name || ""; - - // if the url is empty and the action's datasource name is the default datasource name (this means the api does not have a datasource attached) - // or the user does not have permission, - // we block action execution. - const blockExecution = - (!actionDatasourceUrl && - !actionDatasourceUrlPath && - actionDatasourceName === DEFAULT_DATASOURCE_NAME) || - !isExecutePermitted; - - const theme = EditorTheme.LIGHT; - - return ( - -
- - - - - - - {moreActionsMenu} - - - - {notification && ( - - {notification} - - )} - - - - - {hintMessages && ( - - - - )} - -
- - - - - - - -
- {actionRightPaneAdditionSections} -
-
-
- ); -} - -export default CommonEditorForm; diff --git a/app/client/src/pages/Editor/APIEditor/Editor.tsx b/app/client/src/pages/Editor/APIEditor/Editor.tsx deleted file mode 100644 index 01502aa54855..000000000000 --- a/app/client/src/pages/Editor/APIEditor/Editor.tsx +++ /dev/null @@ -1,276 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import { submit } from "redux-form"; -import RestApiEditorForm from "./RestAPIForm"; -import type { AppState } from "ee/reducers"; -import type { RouteComponentProps } from "react-router"; -import type { - ActionData, - ActionDataState, -} from "ee/reducers/entityReducers/actionsReducer"; -import _ from "lodash"; -import { getCurrentApplication } from "ee/selectors/applicationSelectors"; -import { - getCurrentApplicationId, - getCurrentPageName, -} from "selectors/editorSelectors"; -import { type Plugin, PluginPackageName } from "entities/Plugin"; -import type { Action, PaginationType } from "entities/Action"; -import Spinner from "components/editorComponents/Spinner"; -import type { CSSProperties } from "styled-components"; -import styled from "styled-components"; -import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; -import { - changeApi, - isActionDeleting, - isActionRunning, - isPluginActionCreating, -} from "PluginActionEditor/store"; -import * as Sentry from "@sentry/react"; -import EntityNotFoundPane from "pages/Editor/EntityNotFoundPane"; -import type { ApplicationPayload } from "entities/Application"; -import { - getActionByBaseId, - getPageList, - getPlugins, -} from "ee/selectors/entitiesSelector"; -import history from "utils/history"; -import { saasEditorApiIdURL } from "ee/RouteBuilder"; -import GraphQLEditorForm from "./GraphQL/GraphQLEditorForm"; -import type { APIEditorRouteParams } from "constants/routes"; -import { ApiEditorContext } from "./ApiEditorContext"; - -const LoadingContainer = styled(CenteredWrapper)` - height: 50%; -`; - -interface ReduxStateProps { - actions: ActionDataState; - isRunning: boolean; - isDeleting: boolean; - isCreating: boolean; - apiId: string; - apiName: string; - currentApplication?: ApplicationPayload; - currentPageName: string | undefined; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pages: any; - plugins: Plugin[]; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pluginId: any; - apiAction: Action | ActionData | undefined; - paginationType: PaginationType; - applicationId: string; -} - -interface OwnProps { - isEditorInitialized: boolean; -} - -interface ReduxActionProps { - submitForm: (name: string) => void; - changeAPIPage: (apiId: string, isSaas: boolean) => void; -} - -function getPackageNameFromPluginId(pluginId: string, plugins: Plugin[]) { - const plugin = plugins.find((plugin: Plugin) => plugin.id === pluginId); - - return plugin?.packageName; -} - -type Props = ReduxActionProps & - ReduxStateProps & - RouteComponentProps & - OwnProps; - -class ApiEditor extends React.Component { - static contextType = ApiEditorContext; - context!: React.ContextType; - - componentDidMount() { - const type = this.getFormName(); - - if (this.props.apiId) { - this.props.changeAPIPage(this.props.apiId, type === "SAAS"); - } - } - - getFormName = () => { - const plugins = this.props.plugins; - const pluginId = this.props.pluginId; - const plugin = - plugins && - plugins.find((plug) => { - if (plug.id === pluginId) return plug; - }); - - return plugin && plugin.type; - }; - - componentDidUpdate(prevProps: Props) { - if (prevProps.apiId !== this.props.apiId) { - const type = this.getFormName(); - - this.props.changeAPIPage(this.props.apiId || "", type === "SAAS"); - } - } - - getPluginUiComponentOfId = ( - id: string, - plugins: Plugin[], - ): string | undefined => { - const plugin = plugins.find((plugin) => plugin.id === id); - - if (!plugin) return undefined; - - return plugin.uiComponent; - }; - - getPluginUiComponentOfName = (plugins: Plugin[]): string | undefined => { - const plugin = plugins.find( - (plugin) => plugin.packageName === PluginPackageName.REST_API, - ); - - if (!plugin) return undefined; - - return plugin.uiComponent; - }; - - render() { - const { - isCreating, - isDeleting, - isEditorInitialized, - isRunning, - match: { - params: { baseApiId }, - }, - paginationType, - pluginId, - plugins, - } = this.props; - - if (!pluginId && baseApiId) { - return ; - } - - if (isCreating || !isEditorInitialized) { - return ( - - - - ); - } - - let formUiComponent: string | undefined; - - if (baseApiId) { - if (pluginId) { - formUiComponent = this.getPluginUiComponentOfId(pluginId, plugins); - } else { - formUiComponent = this.getPluginUiComponentOfName(plugins); - } - } - - return ( -
- {formUiComponent === "ApiEditorForm" && ( - - )} - {formUiComponent === "GraphQLEditorForm" && ( - - )} - {formUiComponent === "SaaSEditorForm" && - history.push( - saasEditorApiIdURL({ - basePageId: this.props.match.params.basePageId, - pluginPackageName: - getPackageNameFromPluginId( - this.props.pluginId, - this.props.plugins, - ) ?? "", - baseApiId: this.props.match.params.baseApiId || "", - }), - )} -
- ); - } -} - -const formStyles: CSSProperties = { - position: "relative", - display: "flex", - flexDirection: "column", - flexGrow: "1", - overflow: "auto", -}; - -// TODO: Fix this the next time the file is edited -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { - const apiAction = getActionByBaseId(state, props?.match?.params?.baseApiId); - const apiName = apiAction?.name ?? ""; - const apiId = apiAction?.id ?? ""; - const isCreating = isPluginActionCreating(state); - const isDeleting = isActionDeleting(apiId)(state); - const isRunning = isActionRunning(apiId)(state); - const pluginId = _.get(apiAction, "pluginId", ""); - - return { - actions: state.entities.actions, - currentApplication: getCurrentApplication(state), - currentPageName: getCurrentPageName(state), - pages: getPageList(state), - apiId, - apiName, - plugins: getPlugins(state), - pluginId, - paginationType: _.get(apiAction, "actionConfiguration.paginationType"), - apiAction, - isRunning, - isDeleting, - isCreating, - applicationId: getCurrentApplicationId(state), - }; -}; - -// TODO: Fix this the next time the file is edited -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({ - submitForm: (name: string) => dispatch(submit(name)), - changeAPIPage: (actionId: string, isSaas: boolean) => - dispatch(changeApi(actionId, isSaas)), -}); - -export default Sentry.withProfiler( - connect(mapStateToProps, mapDispatchToProps)(ApiEditor), -); diff --git a/app/client/src/pages/Editor/APIEditor/GraphQL/GraphQLEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/GraphQL/GraphQLEditorForm.tsx deleted file mode 100644 index 27fa575fb872..000000000000 --- a/app/client/src/pages/Editor/APIEditor/GraphQL/GraphQLEditorForm.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import type { InjectedFormProps } from "redux-form"; -import { formValueSelector, reduxForm } from "redux-form"; -import { API_EDITOR_FORM_NAME } from "ee/constants/forms"; -import type { Action } from "entities/Action"; -import type { AppState } from "ee/reducers"; -import get from "lodash/get"; -import { - getActionByBaseId, - getActionData, -} from "ee/selectors/entitiesSelector"; -import type { CommonFormProps } from "../CommonEditorForm"; -import CommonEditorForm from "../CommonEditorForm"; -import Pagination from "PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/Pagination"; -import { GRAPHQL_HTTP_METHOD_OPTIONS } from "PluginActionEditor/constants/GraphQLEditorConstants"; -import PostBodyData from "PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData"; - -type APIFormProps = { - actionConfigurationBody: string; -} & CommonFormProps; - -type Props = APIFormProps & InjectedFormProps; - -/** - * Graphql Editor form which uses the Common Editor and pass on the differentiating components from the API Editor. - * @param props using type Props - * @returns Graphql Editor Area which is used to editor APIs using GraphQL datasource. - */ -function GraphQLEditorForm(props: Props) { - const { actionName } = props; - - return ( - } - formName={API_EDITOR_FORM_NAME} - httpsMethods={GRAPHQL_HTTP_METHOD_OPTIONS} - paginationUIComponent={ - - } - /> - ); -} - -const selector = formValueSelector(API_EDITOR_FORM_NAME); - -export default connect( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state: AppState, props: { pluginId: string; match?: any }) => { - const actionConfigurationHeaders = - selector(state, "actionConfiguration.headers") || []; - const actionConfigurationParams = - selector(state, "actionConfiguration.queryParameters") || []; - let datasourceFromAction = selector(state, "datasource"); - - if (datasourceFromAction && datasourceFromAction.hasOwnProperty("id")) { - datasourceFromAction = state.entities.datasources.list.find( - (d) => d.id === datasourceFromAction.id, - ); - } - - const { baseApiId, baseQueryId } = props.match?.params || {}; - const baseActionId = baseQueryId || baseApiId; - const action = getActionByBaseId(state, baseActionId); - const apiId = action?.id ?? ""; - const actionName = action?.name ?? ""; - const hintMessages = action?.messages; - - const datasourceHeaders = - get(datasourceFromAction, "datasourceConfiguration.headers") || []; - const datasourceParams = - get(datasourceFromAction, "datasourceConfiguration.queryParameters") || - []; - - const actionConfigurationBody = - selector(state, "actionConfiguration.body") || ""; - - const actionResponse = getActionData(state, apiId); - - return { - actionName, - actionResponse, - actionConfigurationHeaders, - actionConfigurationParams, - actionConfigurationBody, - datasourceHeaders, - datasourceParams, - hintMessages, - }; - }, -)( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reduxForm({ - form: API_EDITOR_FORM_NAME, - enableReinitialize: true, - })(GraphQLEditorForm), -); diff --git a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx b/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx deleted file mode 100644 index 893fe98c2524..000000000000 --- a/app/client/src/pages/Editor/APIEditor/RestAPIForm.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import type { InjectedFormProps } from "redux-form"; -import { formValueSelector, reduxForm } from "redux-form"; -import { API_EDITOR_FORM_NAME } from "ee/constants/forms"; -import type { Action } from "entities/Action"; -import PostBodyData from "PluginActionEditor/components/PluginActionForm/components/ApiEditor/PostBodyData"; -import type { AppState } from "ee/reducers"; -import { getApiName } from "selectors/formSelectors"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import get from "lodash/get"; -import { getAction, getActionResponses } from "ee/selectors/entitiesSelector"; -import type { CommonFormProps } from "./CommonEditorForm"; -import CommonEditorForm from "./CommonEditorForm"; -import Pagination from "PluginActionEditor/components/PluginActionForm/components/ApiEditor/Pagination"; -import { getCurrentEnvironmentId } from "ee/selectors/environmentSelectors"; -import { HTTP_METHOD_OPTIONS } from "PluginActionEditor/constants/CommonApiConstants"; - -type APIFormProps = { - httpMethodFromForm: string; -} & CommonFormProps; - -type Props = APIFormProps & InjectedFormProps; - -function ApiEditorForm(props: Props) { - const { actionName } = props; - const theme = EditorTheme.LIGHT; - - return ( - - } - formName={API_EDITOR_FORM_NAME} - httpsMethods={HTTP_METHOD_OPTIONS} - paginationUIComponent={ - - } - /> - ); -} - -const selector = formValueSelector(API_EDITOR_FORM_NAME); - -export default connect((state: AppState) => { - const httpMethodFromForm = selector(state, "actionConfiguration.httpMethod"); - const actionConfigurationHeaders = - selector(state, "actionConfiguration.headers") || []; - const autoGeneratedActionConfigHeaders = - selector(state, "actionConfiguration.autoGeneratedHeaders") || []; - const actionConfigurationParams = - selector(state, "actionConfiguration.queryParameters") || []; - let datasourceFromAction = selector(state, "datasource"); - - if (datasourceFromAction && datasourceFromAction.hasOwnProperty("id")) { - datasourceFromAction = state.entities.datasources.list.find( - (d) => d.id === datasourceFromAction.id, - ); - } - - // get messages from action itself - const actionId = selector(state, "id"); - const action = getAction(state, actionId); - const currentEnvironment = getCurrentEnvironmentId(state); - const hintMessages = action?.messages; - - const datasourceHeaders = - get( - datasourceFromAction, - `datasourceStorages.${currentEnvironment}.datasourceConfiguration.headers`, - ) || []; - const datasourceParams = - get( - datasourceFromAction, - `datasourceStorages.${currentEnvironment}.datasourceConfiguration.queryParameters`, - ) || []; - - const apiId = selector(state, "id"); - const currentActionDatasourceId = selector(state, "datasource.id"); - - const actionName = getApiName(state, apiId) || ""; - - const responses = getActionResponses(state); - const actionResponse = responses[apiId]; - - return { - actionName, - actionResponse, - apiId, - httpMethodFromForm, - actionConfigurationHeaders, - actionConfigurationParams, - autoGeneratedActionConfigHeaders, - currentActionDatasourceId, - datasourceHeaders, - datasourceParams, - hintMessages, - }; -})( - reduxForm({ - form: API_EDITOR_FORM_NAME, - enableReinitialize: true, - })(ApiEditorForm), -); diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx deleted file mode 100644 index 8e97ac8ec2c7..000000000000 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useCallback, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import type { RouteComponentProps } from "react-router"; - -import { - getIsActionConverting, - getPageList, - getPluginSettingConfigs, - getPlugins, -} from "ee/selectors/entitiesSelector"; -import { runAction, saveActionName } from "actions/pluginActionActions"; -import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import Editor from "./Editor"; -import BackToCanvas from "components/common/BackToCanvas"; -import MoreActionsMenu from "../Explorer/Actions/MoreActionsMenu"; -import { - getIsEditorInitialized, - getPagePermissions, -} from "selectors/editorSelectors"; -import { getActionByBaseId } from "ee/selectors/entitiesSelector"; -import type { APIEditorRouteParams } from "constants/routes"; -import { - getHasCreateActionPermission, - getHasDeleteActionPermission, - getHasManageActionPermission, -} from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { ApiEditorContextProvider } from "./ApiEditorContext"; -import type { PaginationField } from "api/ActionAPI"; -import { get, keyBy } from "lodash"; -import ConvertToModuleInstanceCTA from "ee/pages/Editor/EntityEditor/ConvertToModuleInstanceCTA"; -import { MODULE_TYPE } from "ee/constants/ModuleConstants"; -import Disabler from "pages/common/Disabler"; -import ConvertEntityNotification from "ee/pages/common/ConvertEntityNotification"; -import { Icon } from "@appsmith/ads"; -import { resolveIcon } from "../utils"; -import { ENTITY_ICON_SIZE, EntityIcon } from "../Explorer/ExplorerIcons"; -import { getIDEViewMode } from "selectors/ideSelectors"; -import { EditorViewMode } from "ee/entities/IDE/constants"; - -type ApiEditorWrapperProps = RouteComponentProps; - -// TODO: Fix this the next time the file is edited -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getPageName(pages: any, basePageId: string) { - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const page = pages.find((page: any) => page.basePageId === basePageId); - - return page ? page.pageName : ""; -} - -function ApiEditorWrapper(props: ApiEditorWrapperProps) { - const { baseApiId = "", basePageId } = props.match.params; - const dispatch = useDispatch(); - const isEditorInitialized = useSelector(getIsEditorInitialized); - const action = useSelector((state) => getActionByBaseId(state, baseApiId)); - const apiName = action?.name || ""; - const pluginId = get(action, "pluginId", ""); - const datasourceId = action?.datasource.id || ""; - const plugins = useSelector(getPlugins); - const pages = useSelector(getPageList); - const pageName = getPageName(pages, basePageId); - const settingsConfig = useSelector((state) => - getPluginSettingConfigs(state, pluginId), - ); - const pagePermissions = useSelector(getPagePermissions); - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const isConverting = useSelector((state) => - getIsActionConverting(state, action?.id || ""), - ); - const editorMode = useSelector(getIDEViewMode); - const pluginGroups = useMemo(() => keyBy(plugins, "id"), [plugins]); - const icon = resolveIcon({ - iconLocation: pluginGroups[pluginId]?.iconLocation || "", - pluginType: action?.pluginType || "", - moduleType: action?.actionConfiguration?.body?.moduleType, - }) || ( - - - - ); - - const isChangePermitted = getHasManageActionPermission( - isFeatureEnabled, - action?.userPermissions, - ); - const isDeletePermitted = getHasDeleteActionPermission( - isFeatureEnabled, - action?.userPermissions, - ); - const isCreatePermitted = getHasCreateActionPermission( - isFeatureEnabled, - pagePermissions, - ); - - const moreActionsMenu = useMemo(() => { - const convertToModuleProps = { - canCreateModuleInstance: isCreatePermitted, - canDeleteEntity: isDeletePermitted, - entityId: action?.id || "", - moduleType: MODULE_TYPE.QUERY, - }; - - return ( - <> - - ) - } - /> - {editorMode !== EditorViewMode.SplitScreen && ( - - )} - - ); - }, [ - action?.id, - action?.name, - isChangePermitted, - isDeletePermitted, - basePageId, - isCreatePermitted, - editorMode, - ]); - - const handleRunClick = useCallback( - (paginationField?: PaginationField) => { - const pluginName = plugins.find((plugin) => plugin.id === pluginId)?.name; - - AnalyticsUtil.logEvent("RUN_API_CLICK", { - apiName, - apiID: action?.id, - pageName: pageName, - datasourceId, - pluginName: pluginName, - isMock: false, // as mock db exists only for postgres and mongo plugins - }); - dispatch(runAction(action?.id ?? "", paginationField)); - }, - [action?.id, apiName, pageName, plugins, pluginId, datasourceId, dispatch], - ); - - const actionRightPaneBackLink = useMemo(() => { - return ; - }, [basePageId]); - - const notification = useMemo(() => { - if (!isConverting) return null; - - return ; - }, [action?.name, isConverting, icon]); - - return ( - - - - - - ); -} - -export default ApiEditorWrapper; diff --git a/app/client/src/pages/Editor/IDE/EditorPane/JS/JSRender.test.tsx b/app/client/src/pages/Editor/IDE/EditorPane/JS/JSRender.test.tsx index fa0c0f25f28c..565e73551df8 100644 --- a/app/client/src/pages/Editor/IDE/EditorPane/JS/JSRender.test.tsx +++ b/app/client/src/pages/Editor/IDE/EditorPane/JS/JSRender.test.tsx @@ -1,4 +1,3 @@ -import localStorage from "utils/localStorage"; import { render, waitFor } from "test/testUtils"; import { Route } from "react-router-dom"; import { BUILDER_PATH } from "ee/constants/routes/appRoutes"; @@ -17,7 +16,6 @@ const FeatureFlags = { const basePageId = "0123456789abcdef00000000"; describe("IDE Render: JS", () => { - localStorage.setItem("SPLITPANE_ANNOUNCEMENT", "false"); describe("JS Blank State", () => { it("Renders Fullscreen Blank State", async () => { const { findByText, getByRole, getByText } = render( @@ -132,7 +130,7 @@ describe("IDE Render: JS", () => { }, }); - const { container, getAllByText, getByRole, getByTestId } = render( + const { getAllByText, getByRole, getByTestId } = render( , @@ -147,13 +145,13 @@ describe("IDE Render: JS", () => { async () => { const elements = getAllByText("JSObject1"); // Use the common test ID or selector - expect(elements).toHaveLength(3); // Wait until there are exactly 3 elements + expect(elements).toHaveLength(2); // Wait until there are exactly 2 elements }, { timeout: 3000, interval: 500 }, ); - // There will be 3 JSObject1 text (Left pane list, editor tab and Editor form) - expect(getAllByText("JSObject1").length).toEqual(3); + // There will be 2 JSObject1 text (Left pane list and editor tab) + expect(getAllByText("JSObject1").length).toEqual(2); // Left pane active state expect( getByTestId("t--entity-item-JSObject1").classList.contains("active"), @@ -162,13 +160,12 @@ describe("IDE Render: JS", () => { expect( getByTestId("t--ide-tab-jsobject1").classList.contains("active"), ).toBe(true); - // Check if the form is rendered - expect(container.querySelector(".js-editor-tab")).not.toBeNull(); - // Check if the code and settings tabs is visible - getByRole("tab", { name: /code/i }); - getByRole("tab", { name: /settings/i }); - // Check if run button is visible + // Check toolbar elements + getByRole("button", { name: /myFun1/i }); getByRole("button", { name: /run/i }); + getByTestId("t--js-settings-trigger"); + getByTestId("t--more-action-trigger"); + // Check if the Add new button is shown getByTestId("t--add-item"); }); @@ -190,7 +187,7 @@ describe("IDE Render: JS", () => { ideView: EditorViewMode.SplitScreen, }); - const { container, getAllByText, getByRole, getByTestId } = render( + const { getAllByText, getByRole, getByTestId } = render( , @@ -206,19 +203,17 @@ describe("IDE Render: JS", () => { getByTestId("t--widgets-editor"); // Check if js is rendered in side by side - expect(getAllByText("JSObject2").length).toBe(2); + expect(getAllByText("JSObject2").length).toBe(1); // Tabs active state expect( getByTestId("t--ide-tab-jsobject2").classList.contains("active"), ).toBe(true); - // Check if the form is rendered - expect(container.querySelector(".js-editor-tab")).not.toBeNull(); - // Check if the code and settings tabs is visible - getByRole("tab", { name: /code/i }); - getByRole("tab", { name: /settings/i }); - // Check if run button is visible + // Check toolbar elements + getByRole("button", { name: /myFun1/i }); getByRole("button", { name: /run/i }); + getByTestId("t--more-action-trigger"); + // Check if the Add new button is shown getByTestId("t--ide-tabs-add-button"); }); diff --git a/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx b/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx index 65314ae7a727..77393a10131f 100644 --- a/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx +++ b/app/client/src/pages/Editor/IDE/EditorPane/Query/QueryRender.test.tsx @@ -6,10 +6,8 @@ import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages"; import { BUILDER_PATH } from "ee/constants/routes/appRoutes"; import { EditorEntityTab, EditorViewMode } from "ee/entities/IDE/constants"; import { APIFactory } from "test/factories/Actions/API"; -import localStorage from "utils/localStorage"; import { PostgresFactory } from "test/factories/Actions/Postgres"; import { sagasToRunForTests } from "test/sagas"; -import userEvent from "@testing-library/user-event"; import { getIDETestState } from "test/factories/AppIDEFactoryUtils"; import { PageFactory } from "test/factories/PageFactory"; import { screen, waitFor } from "@testing-library/react"; @@ -22,7 +20,6 @@ const FeatureFlags = { const basePageId = "0123456789abcdef00000000"; describe("IDE URL rendering of Queries", () => { - localStorage.setItem("SPLITPANE_ANNOUNCEMENT", "false"); describe("Query Blank State", () => { it("Renders Fullscreen Blank State", async () => { const { findByText, getByRole, getByText } = render( @@ -144,7 +141,7 @@ describe("IDE URL rendering of Queries", () => { }, }); - const { getAllByText, getByRole, getByTestId } = render( + const { getAllByRole, getAllByText, getByRole, getByTestId } = render( , @@ -159,13 +156,13 @@ describe("IDE URL rendering of Queries", () => { async () => { const elements = getAllByText("Api1"); // Use the common test ID or selector - expect(elements).toHaveLength(3); // Wait until there are exactly 3 elements + expect(elements).toHaveLength(2); // Wait until there are exactly 3 elements }, { timeout: 3000, interval: 500 }, ); - // There will be 3 Api1 text (Left pane list, editor tab and Editor form) - expect(getAllByText("Api1").length).toEqual(3); + // There will be 2 Api1 text (Left pane list, editor tab) + expect(getAllByText("Api1").length).toEqual(2); // Left pane active state expect( getByTestId("t--entity-item-Api1").classList.contains("active"), @@ -175,11 +172,11 @@ describe("IDE URL rendering of Queries", () => { true, ); // Check if the form is rendered - getByTestId("t--action-form-API"); + getByTestId("t--api-editor-form"); // Check if the params tabs is visible getByRole("tab", { name: /params/i }); // Check if run button is visible - getByRole("button", { name: /run/i }); + expect(getAllByRole("button", { name: /run/i })).toHaveLength(2); // Check if the Add new button is shown getByTestId("t--add-item"); }); @@ -201,7 +198,7 @@ describe("IDE URL rendering of Queries", () => { ideView: EditorViewMode.SplitScreen, }); - const { getAllByText, getByRole, getByTestId } = render( + const { getAllByRole, getAllByText, getByTestId } = render( , @@ -217,15 +214,15 @@ describe("IDE URL rendering of Queries", () => { getByTestId("t--widgets-editor"); // Check if api is rendered in side by side - expect(getAllByText("Api2").length).toBe(2); + expect(getAllByText("Api2").length).toBe(1); // Tabs active state expect(getByTestId("t--ide-tab-api2").classList.contains("active")).toBe( true, ); // Check if the form is rendered - getByTestId("t--action-form-API"); + getByTestId("t--api-editor-form"); // Check if run button is visible - getByRole("button", { name: /run/i }); + expect(getAllByRole("button", { name: /run/i }).length).toBe(2); // Check if the Add new button is shown getByTestId("t--ide-tabs-add-button"); }); @@ -358,12 +355,12 @@ describe("IDE URL rendering of Queries", () => { async () => { const elements = getAllByText("Query1"); // Use the common test ID or selector - expect(elements).toHaveLength(3); // Wait until there are exactly 3 elements + expect(elements).toHaveLength(2); // Wait until there are exactly 3 elements }, { timeout: 3000, interval: 500 }, ); - // There will be 3 Query1 text (Left pane list, editor tab and Editor form) - expect(getAllByText("Query1").length).toBe(3); + // There will be 2 Query1 text (Left pane list, editor tab) + expect(getAllByText("Query1").length).toBe(2); // Left pane active state expect( getByTestId("t--entity-item-Query1").classList.contains("active"), @@ -372,11 +369,8 @@ describe("IDE URL rendering of Queries", () => { expect( getByTestId("t--ide-tab-query1").classList.contains("active"), ).toBe(true); - - await userEvent.click(getByRole("tab", { name: "Query" })); - // Check if the form is rendered - getByTestId("t--action-form-DB"); + getByTestId("t--uqi-editor-form"); // Check if run button is visible getByRole("button", { name: /run/i }); // Check if the Add new button is shown @@ -417,16 +411,14 @@ describe("IDE URL rendering of Queries", () => { getByTestId("t--widgets-editor"); // Check if api is rendered in side by side - expect(getAllByText("Query2").length).toBe(2); + expect(getAllByText("Query2").length).toBe(1); // Tabs active state expect( getByTestId("t--ide-tab-query2").classList.contains("active"), ).toBe(true); - await userEvent.click(getByRole("tab", { name: "Query" })); - // Check if the form is rendered - getByTestId("t--action-form-DB"); + getByTestId("t--uqi-editor-form"); // Check if run button is visible getByRole("button", { name: /run/i }); // Check if the Add new button is shown @@ -449,7 +441,7 @@ describe("IDE URL rendering of Queries", () => { }, }); - const { container, getByTestId, getByText } = render( + const { getByTestId, getByText } = render( , @@ -461,8 +453,6 @@ describe("IDE URL rendering of Queries", () => { }, ); - screen.logTestingPlaygroundURL(container); - // Create options are rendered getByText(createMessage(EDITOR_PANE_TEXTS.queries_create_from_existing)); getByText("New datasource"); @@ -562,8 +552,8 @@ describe("IDE URL rendering of Queries", () => { }, ); - // There will be 3 Query1 text (Left pane list, editor tab and Editor form) - expect(getAllByText("Sheets1").length).toBe(3); + // There will be 2 Query1 text (Left pane list, editor tab) + expect(getAllByText("Sheets1").length).toBe(2); // Left pane active state expect( getByTestId("t--entity-item-Sheets1").classList.contains("active"), @@ -573,10 +563,8 @@ describe("IDE URL rendering of Queries", () => { getByTestId("t--ide-tab-sheets1").classList.contains("active"), ).toBe(true); - await userEvent.click(getByRole("tab", { name: "Query" })); - // Check if the form is rendered - getByTestId("t--action-form-SAAS"); + getByTestId("t--uqi-editor-form"); // Check if run button is visible getByRole("button", { name: /run/i }); // Check if the Add new button is shown @@ -618,18 +606,16 @@ describe("IDE URL rendering of Queries", () => { getByTestId("t--widgets-editor"); // Check if api is rendered in side by side - expect(getAllByText("Sheets2").length).toBe(2); + expect(getAllByText("Sheets2").length).toBe(1); // Tabs active state expect( getByTestId("t--ide-tab-sheets2").classList.contains("active"), ).toBe(true); - await userEvent.click(getByRole("tab", { name: "Query" })); - screen.logTestingPlaygroundURL(container); // Check if the form is rendered - getByTestId("t--action-form-SAAS"); + getByTestId("t--uqi-editor-form"); // Check if run button is visible getByRole("button", { name: /run/i }); // Check if the Add new button is shown @@ -653,7 +639,7 @@ describe("IDE URL rendering of Queries", () => { }, }); - const { container, getByTestId, getByText } = render( + const { getByTestId, getByText } = render( , @@ -665,8 +651,6 @@ describe("IDE URL rendering of Queries", () => { }, ); - screen.logTestingPlaygroundURL(container); - // Create options are rendered getByText(createMessage(EDITOR_PANE_TEXTS.queries_create_from_existing)); getByText("New datasource"); diff --git a/app/client/src/pages/Editor/IDE/EditorPane/components/Announcement.tsx b/app/client/src/pages/Editor/IDE/EditorPane/components/Announcement.tsx deleted file mode 100644 index 54181cab50b4..000000000000 --- a/app/client/src/pages/Editor/IDE/EditorPane/components/Announcement.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useState } from "react"; -import { AnnouncementModal, Button } from "@appsmith/ads"; -import localStorage, { LOCAL_STORAGE_KEYS } from "utils/localStorage"; -import { SPLITPANE_ANNOUNCEMENT, createMessage } from "ee/constants/messages"; -import { getAssetUrl } from "ee/utils/airgapHelpers"; -import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; - -const Announcement = () => { - const localStorageFlag = - localStorage.getItem(LOCAL_STORAGE_KEYS.SPLITPANE_ANNOUNCEMENT) || "true"; - const [show, setShow] = useState(JSON.parse(localStorageFlag)); - - const tryClickHandler = () => { - setShow(false); - localStorage.setItem(LOCAL_STORAGE_KEYS.SPLITPANE_ANNOUNCEMENT, "false"); - }; - - const learnClickHandler = () => { - window.open( - "https://community.appsmith.com/content/blog/discover-ide-20-building-more-efficient-ide", - "_blank", - ); - }; - - const featureIsOutOfBeta = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - - const modalFooter = () => ( - <> - - - - ); - - // If the feature is out of beta, don't show the announcement - if (featureIsOutOfBeta) { - return null; - } - - return ( - - ); -}; - -export { Announcement }; diff --git a/app/client/src/pages/Editor/IDE/EditorTabs/Editortabs.test.tsx b/app/client/src/pages/Editor/IDE/EditorTabs/Editortabs.test.tsx index 2fdf6d83369b..507c72cdc623 100644 --- a/app/client/src/pages/Editor/IDE/EditorTabs/Editortabs.test.tsx +++ b/app/client/src/pages/Editor/IDE/EditorTabs/Editortabs.test.tsx @@ -15,7 +15,6 @@ const FeatureFlags = { }; describe("EditorTabs render checks", () => { - localStorage.setItem("SPLITPANE_ANNOUNCEMENT", "false"); const page = PageFactory.build(); const renderComponent = (url: string, state: Partial) => diff --git a/app/client/src/pages/Editor/IDE/EditorTabs/index.tsx b/app/client/src/pages/Editor/IDE/EditorTabs/index.tsx index 0f07bf982c06..00deacf7bec2 100644 --- a/app/client/src/pages/Editor/IDE/EditorTabs/index.tsx +++ b/app/client/src/pages/Editor/IDE/EditorTabs/index.tsx @@ -17,7 +17,6 @@ import Container from "./Container"; import { useCurrentEditorState, useIDETabClickHandlers } from "../hooks"; import { SCROLL_AREA_OPTIONS, TabSelectors } from "./constants"; import { AddButton } from "./AddButton"; -import { Announcement } from "../EditorPane/components/Announcement"; import { useLocation } from "react-router"; import { identifyEntityFromPath } from "navigation/FocusEntity"; import { List } from "./List"; @@ -162,9 +161,6 @@ const EditorTabs = () => { {isListViewActive && ideViewMode === EditorViewMode.SplitScreen && ( )} - - {/* Announcement modal */} - {ideViewMode === EditorViewMode.SplitScreen && } ); }; diff --git a/app/client/src/pages/Editor/IDE/hooks.ts b/app/client/src/pages/Editor/IDE/hooks.ts index 6d356af8626b..ccfe8c31e57c 100644 --- a/app/client/src/pages/Editor/IDE/hooks.ts +++ b/app/client/src/pages/Editor/IDE/hooks.ts @@ -31,8 +31,6 @@ import { useEditorType } from "ee/hooks"; import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks"; import { useBoolean } from "usehooks-ts"; import { isWidgetActionConnectionPresent } from "selectors/onboardingSelectors"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import localStorage, { LOCAL_STORAGE_KEYS } from "utils/localStorage"; export const useCurrentEditorState = () => { @@ -212,12 +210,8 @@ export const useShowSideBySideNudge: () => [boolean, () => void] = () => { LOCAL_STORAGE_KEYS.NUDGE_SHOWN_SPLIT_PANE, ); - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - const { setFalse, value } = useBoolean( - widgetBindingsExist && isActionRedesignEnabled && !localStorageFlag, + widgetBindingsExist && !localStorageFlag, ); const dismissNudge = useCallback(() => { diff --git a/app/client/src/pages/Editor/JSEditor/Form.tsx b/app/client/src/pages/Editor/JSEditor/Form.tsx index 68028eb4ebaf..1cc944e0226c 100644 --- a/app/client/src/pages/Editor/JSEditor/Form.tsx +++ b/app/client/src/pages/Editor/JSEditor/Form.tsx @@ -55,14 +55,14 @@ import { type JSActionDropdownOption, convertJSActionToDropdownOption, getJSActionOption, + type OnUpdateSettingsProps, } from "./JSEditorToolbar"; -import type { JSFunctionSettingsProps } from "./JSEditorToolbar/components/old/JSFunctionSettings"; interface JSFormProps { jsCollectionData: JSCollectionData; contextMenu: React.ReactNode; showSettings?: boolean; - onUpdateSettings: JSFunctionSettingsProps["onUpdateSettings"]; + onUpdateSettings: (props: OnUpdateSettingsProps) => void; saveJSObjectName: JSObjectNameEditorProps["saveJSObjectName"]; backLink?: React.ReactNode; hideContextMenuOnEditor?: boolean; diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorContextMenu.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorContextMenu.tsx index 801b41e6ab2a..27fc6be61268 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorContextMenu.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorContextMenu.tsx @@ -12,8 +12,6 @@ import { MenuTrigger, Text, } from "@appsmith/ads"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; export interface ContextMenuOption { id?: string; @@ -37,10 +35,6 @@ export function JSEditorContextMenu({ onMenuClose, options, }: EntityContextMenuProps) { - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - if (options.length === 0) { return null; } @@ -59,8 +53,8 @@ export function JSEditorContextMenu({ data-testid="t--more-action-trigger" isIconButton kind="tertiary" - size={isActionRedesignEnabled ? "sm" : "md"} - startIcon={isActionRedesignEnabled ? "more-2-fill" : "context-menu"} + size={"sm"} + startIcon={"more-2-fill"} /> diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx index b7ab3ad48447..ec3b5865022d 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx @@ -1,7 +1,5 @@ import React from "react"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import type { JSEditorTab } from "reducers/uiReducers/jsPaneReducer"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { type BlockCompletion, CodeEditorBorder, @@ -12,7 +10,6 @@ import { } from "components/editorComponents/CodeEditor/EditorConfig"; import type { CodeEditorGutter } from "components/editorComponents/CodeEditor"; import type { JSAction, JSCollection } from "entities/JSCollection"; -import { OldJSEditorForm } from "./old/JSEditorForm"; import type { OnUpdateSettingsProps } from "../JSEditorToolbar"; import LazyCodeEditor from "components/editorComponents/LazyCodeEditor"; import { Flex } from "@appsmith/ads"; @@ -33,29 +30,6 @@ interface Props { } export const JSEditorForm = (props: Props) => { - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - - if (!isActionRedesignEnabled) { - return ( - - ); - } - return ( void; - value: JSEditorTab; - showSettings: undefined | boolean; - blockCompletions: Array; - customGutter: CodeEditorGutter; - currentJSCollection: JSCollection; - changePermitted: boolean; - onChange: (valueOrEvent: React.ChangeEvent | string) => void; - theme: EditorTheme.LIGHT; - actions: JSAction[]; - onUpdateSettings?: (props: OnUpdateSettingsProps) => void; -} - -export function OldJSEditorForm(props: Props) { - return ( - - - - - Code - - {props.showSettings && ( - - Settings - - )} - - -
- -
-
- {props.showSettings && ( - -
- -
-
- )} -
-
- ); -} diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx index 99efd4a252c4..dd8a655d2ed5 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx @@ -34,17 +34,7 @@ const defaultProps = { }; describe("JSEditorToolbar", () => { - it("renders JSHeader when action redesign is disabled", () => { - mockUseFeatureFlag.mockReturnValue(false); - render(); - // Old header shows the name of the JS object - // since we don't provide the name via props, it has the placeholder text - expect( - screen.getByText("Name of the JS Object in camelCase"), - ).toBeInTheDocument(); - }); - - it("renders IDEToolbar with JSFunctionRun and JSFunctionSettings when action redesign is enabled", () => { + it("renders IDEToolbar with JSFunctionRun and JSFunctionSettings", () => { mockUseFeatureFlag.mockReturnValue(true); render(); diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx index 10fd5f3f9f09..e48a11848870 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx @@ -1,17 +1,13 @@ import React, { useState } from "react"; import { IDEToolbar, ToolbarSettingsPopover } from "IDE"; import { JSFunctionRun } from "./components/JSFunctionRun"; -import type { JSActionDropdownOption } from "./types"; +import type { JSActionDropdownOption, OnUpdateSettingsProps } from "./types"; import type { SaveActionNameParams } from "PluginActionEditor"; import type { ReduxAction } from "actions/ReduxActionTypes"; import type { JSAction, JSCollection } from "entities/JSCollection"; import type { DropdownOnSelect } from "@appsmith/ads-old"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { createMessage, JS_EDITOR_SETTINGS } from "ee/constants/messages"; -import { JSHeader } from "./JSHeader"; import { JSFunctionSettings } from "./components/JSFunctionSettings"; -import type { JSFunctionSettingsProps } from "./components/old/JSFunctionSettings"; import { convertJSActionsToDropdownOptions } from "./utils"; import { JSObjectNameEditor } from "./JSObjectNameEditor"; @@ -33,7 +29,7 @@ interface Props { onSelect: DropdownOnSelect; jsActions: JSAction[]; selected: JSActionDropdownOption; - onUpdateSettings: JSFunctionSettingsProps["onUpdateSettings"]; + onUpdateSettings: (props: OnUpdateSettingsProps) => void; showNameEditor?: boolean; showSettings: boolean; } @@ -41,23 +37,12 @@ interface Props { /** * JSEditorToolbar component. * - * This component renders a toolbar for the JS editor. It conditionally renders - * different components based on the `release_actions_redesign_enabled` feature flag. + * This component renders a toolbar for the JS editor. * */ export const JSEditorToolbar = (props: Props) => { - // Check if the action redesign feature flag is enabled - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - const [isOpen, setIsOpen] = useState(false); - // If the action redesign is not enabled, render the JSHeader component - if (!isActionRedesignEnabled) { - return ; - } - // Render the IDEToolbar with JSFunctionRun and JSFunctionSettings components return ( diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSHeader.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSHeader.tsx deleted file mode 100644 index adff92647f4a..000000000000 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSHeader.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from "react"; -import { JSFunctionRun } from "./components/JSFunctionRun"; -import type { JSActionDropdownOption } from "./types"; -import { ActionButtons, NameWrapper, StyledFormRow } from "../styledComponents"; -import type { SaveActionNameParams } from "PluginActionEditor"; -import type { ReduxAction } from "actions/ReduxActionTypes"; -import type { JSAction, JSCollection } from "entities/JSCollection"; -import type { DropdownOnSelect } from "@appsmith/ads-old"; -import { JSObjectNameEditor } from "./JSObjectNameEditor"; -import { Flex } from "@appsmith/ads"; -import { convertJSActionsToDropdownOptions } from "./utils"; - -interface Props { - changePermitted: boolean; - hideEditIconOnEditor?: boolean; - saveJSObjectName: ( - params: SaveActionNameParams, - ) => ReduxAction; - hideContextMenuOnEditor?: boolean; - contextMenu: React.ReactNode; - disableRunFunctionality: boolean; - executePermitted: boolean; - loading: boolean; - jsCollection: JSCollection; - onButtonClick: ( - event: React.MouseEvent | KeyboardEvent, - ) => void; - onSelect: DropdownOnSelect; - jsActions: JSAction[]; - selected: JSActionDropdownOption; -} - -export const JSHeader = (props: Props) => { - return ( - - - - - - - {!props.hideContextMenuOnEditor && props.contextMenu} - - - - - ); -}; diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx index 8b573866d645..5748a2a9a598 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx @@ -1,7 +1,5 @@ import React, { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import type { ReduxAction } from "actions/ReduxActionTypes"; import { getSavingStatusForJSObjectName } from "selectors/actionSelectors"; import { getAssetUrl } from "ee/utils/airgapHelpers"; @@ -14,7 +12,6 @@ import { getJsCollectionByBaseId, getPlugin, } from "ee/selectors/entitiesSelector"; -import { JSObjectNameEditor as OldJSObjectNameEditor } from "./old/JSObjectNameEditor"; import { EditableName, useIsRenaming } from "IDE"; export interface SaveActionNameParams { @@ -102,10 +99,6 @@ export const JSObjectNameEditor = ({ [currentJSObjectConfig, saveJSObjectName], ); - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - const icon = useMemo(() => { if (!currentPlugin) return null; @@ -119,15 +112,6 @@ export const JSObjectNameEditor = ({ ); }, [currentPlugin]); - if (!isActionRedesignEnabled) { - return ( - - ); - } - return ( ReduxAction; -} - -export function JSObjectNameEditor(props: JSObjectNameEditorProps) { - const params = useParams<{ - baseCollectionId?: string; - baseQueryId?: string; - }>(); - - const currentJSObjectConfig = useSelector((state: AppState) => - getJsCollectionByBaseId(state, params.baseCollectionId || ""), - ); - - const currentPlugin = useSelector((state: AppState) => - getPlugin(state, currentJSObjectConfig?.pluginId || ""), - ); - - const saveStatus = useSelector((state) => - getSavingStatusForJSObjectName(state, currentJSObjectConfig?.id || ""), - ); - - return ( - - {({ - forceUpdate, - handleNameChange, - isInvalidNameForEntity, - isNew, - saveStatus, - }: { - forceUpdate: boolean; - handleNameChange: (value: string) => void; - isInvalidNameForEntity: (value: string) => string | boolean; - isNew: boolean; - saveStatus: { isSaving: boolean; error: boolean }; - }) => ( - - - {currentPlugin && ( - - - - )} - - - - )} - - ); -} - -export default JSObjectNameEditor; diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.test.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.test.tsx deleted file mode 100644 index 5be884df8a7d..000000000000 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.test.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from "react"; -import "@testing-library/jest-dom"; -import { render, screen, fireEvent } from "test/testUtils"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { JSObjectFactory } from "test/factories/Actions/JSObject"; - -import { convertJSActionsToDropdownOptions } from "../utils"; -import { JSFunctionRun } from "./JSFunctionRun"; -import { JS_FUNCTION_RUN_NAME_LENGTH } from "./constants"; - -jest.mock("utils/hooks/useFeatureFlag"); -const mockUseFeatureFlag = useFeatureFlag as jest.Mock; - -const JSObject = JSObjectFactory.build(); - -const mockProps = { - disabled: false, - isLoading: false, - jsCollection: JSObject, - onButtonClick: jest.fn(), - onSelect: jest.fn(), - options: convertJSActionsToDropdownOptions(JSObject.actions), - selected: { - label: JSObject.actions[0].name, - value: JSObject.actions[0].name, - data: JSObject.actions[0], - }, - showTooltip: false, -}; - -describe("JSFunctionRun", () => { - it("renders OldJSFunctionRun when feature flag is disabled", () => { - mockUseFeatureFlag.mockReturnValue(false); - render(); - expect(screen.getByText("myFun1")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "Run" })).toBeInTheDocument(); - }); - - it("renders new JSFunctionRun when feature flag is enabled", () => { - mockUseFeatureFlag.mockReturnValue(true); - render(); - // Assert the Function select is a popup menu - expect(screen.getByText("myFun1")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: "myFun1" })).toHaveAttribute( - "aria-haspopup", - "menu", - ); - }); - - // This test is skipped because menu does not open in the test environment - // eslint-disable-next-line jest/no-disabled-tests - it.skip("calls onSelect when a menu item is selected", () => { - mockUseFeatureFlag.mockReturnValue(true); - render(); - // click the button to open the menu - fireEvent.click(screen.getByRole("button", { name: "myFun1" })); - - fireEvent.click(screen.getByText("myFun2")); - expect(mockProps.onSelect).toHaveBeenCalledWith("myFun2"); - }); - - it("disables the button when props.disabled is true", () => { - mockUseFeatureFlag.mockReturnValue(true); - render(); - expect(screen.getByRole("button", { name: "myFun1" })).toBeDisabled(); - }); - - // This test is skipped because tooltip does not show in the test environment - // eslint-disable-next-line jest/no-disabled-tests - it.skip("shows tooltip when showTooltip is true", () => { - mockUseFeatureFlag.mockReturnValue(true); - render(); - fireEvent.mouseOver(screen.getByText("Run")); - expect( - screen.getByText("No JS function to run in TestCollection"), - ).toBeInTheDocument(); - }); - - it("calls onButtonClick when run button is clicked", () => { - mockUseFeatureFlag.mockReturnValue(true); - render(); - fireEvent.click(screen.getByText("Run")); - expect(mockProps.onButtonClick).toHaveBeenCalled(); - }); - - it("truncates long names to 30 characters", () => { - mockUseFeatureFlag.mockReturnValue(true); - const options = [ - { - label: - "aReallyReallyLongFunctionNameThatConveysALotOfMeaningAndCannotBeShortenedAtAllBecauseItConveysALotOfMeaningAndCannotBeShortened", - value: "1", - }, - ]; - const [selected] = options; - const jsCollection = { name: "CollectionName" }; - const params = { options, selected, jsCollection } as Parameters< - typeof JSFunctionRun - >[0]; - - render(); - - expect(screen.getByTestId("t--js-function-run").textContent?.length).toBe( - JS_FUNCTION_RUN_NAME_LENGTH, - ); - }); -}); diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.tsx index 7f644567c6a1..bf6f174d3f1f 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun.tsx @@ -1,9 +1,6 @@ import React, { useCallback } from "react"; import { truncate } from "lodash"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { JSFunctionRun as OldJSFunctionRun } from "./old/JSFunctionRun"; import type { JSCollection } from "entities/JSCollection"; import { Button, @@ -37,9 +34,6 @@ interface Props { */ export const JSFunctionRun = (props: Props) => { const { onSelect } = props; - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); // Callback function to handle function selection from the dropdown menu const onFunctionSelect = useCallback( @@ -51,11 +45,6 @@ export const JSFunctionRun = (props: Props) => { [onSelect], ); - if (!isActionRedesignEnabled) { - return ; - } - - // Render the new version of the component return ( diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionSettings.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionSettings.tsx index 1e221470f0b5..ced51a6e9f5b 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionSettings.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionSettings.tsx @@ -1,22 +1,18 @@ import React, { useCallback, useState } from "react"; import { Flex, Switch, Text } from "@appsmith/ads"; -import JSFunctionSettingsView, { - type JSFunctionSettingsProps, -} from "./old/JSFunctionSettings"; import type { JSAction } from "entities/JSCollection"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { createMessage, JS_EDITOR_SETTINGS, NO_JS_FUNCTIONS, } from "ee/constants/messages"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; +import type { OnUpdateSettingsProps } from "../types"; interface Props { disabled: boolean; actions: JSAction[]; - onUpdateSettings: JSFunctionSettingsProps["onUpdateSettings"]; + onUpdateSettings: (props: OnUpdateSettingsProps) => void; } interface FunctionSettingsRowProps extends Omit { @@ -73,22 +69,6 @@ const FunctionSettingRow = (props: FunctionSettingsRowProps) => { * It conditionally renders the old or new version of the component based on a feature flag. */ export const JSFunctionSettings = (props: Props) => { - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - - // If the feature flag is disabled, render the old version of the component - if (!isActionRedesignEnabled) { - return ( - - ); - } - - // Render the new version of the component return ( diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionRun.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionRun.tsx deleted file mode 100644 index c67ff6e363e7..000000000000 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionRun.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import type { JSCollection } from "entities/JSCollection"; -import type { SelectProps } from "@appsmith/ads"; -import { Button, Option, Select, Tooltip, Text } from "@appsmith/ads"; -import { createMessage, NO_JS_FUNCTION_TO_RUN } from "ee/constants/messages"; -import type { JSActionDropdownOption } from "../../types"; -import { RUN_BUTTON_DEFAULTS, testLocators } from "../../constants"; - -interface Props { - disabled: boolean; - isLoading: boolean; - jsCollection: JSCollection; - onButtonClick: (event: React.MouseEvent) => void; - onSelect: SelectProps["onSelect"]; - options: JSActionDropdownOption[]; - selected: JSActionDropdownOption; - showTooltip: boolean; -} - -export interface DropdownWithCTAWrapperProps { - isDisabled: boolean; -} - -const DropdownWithCTAWrapper = styled.div` - display: flex; - gap: var(--ads-v2-spaces-3); -`; - -const OptionWrapper = styled.div` - display: flex; - justify-content: space-between; - width: 100%; -`; - -const OptionLabelWrapper = styled.div<{ fullSize?: boolean }>` - width: ${(props) => (props?.fullSize ? "100%" : "80%")}; - overflow: hidden; -`; - -const OptionLabel = styled(Text)` - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -`; - -export function JSFunctionRun({ - disabled, - isLoading, - jsCollection, - onButtonClick, - onSelect, - options, - selected, - showTooltip, -}: Props) { - return ( - - - - {/* this span exists to make the disabled button visible to the tooltip */} - - - - - - ); -} diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionSettings.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionSettings.tsx deleted file mode 100644 index 07c2235c605c..000000000000 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/components/old/JSFunctionSettings.tsx +++ /dev/null @@ -1,316 +0,0 @@ -import { - createMessage, - FUNCTION_SETTINGS_HEADING, - NO_JS_FUNCTIONS, -} from "ee/constants/messages"; -import type { JSAction } from "entities/JSCollection"; -import React, { useCallback, useState } from "react"; -import styled from "styled-components"; -import { - CONFIRM_BEFORE_CALLING_HEADING, - SETTINGS_HEADINGS, -} from "../../../constants"; -import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import { Icon, Switch, Tooltip } from "@appsmith/ads"; -import RemoveConfirmationModal from "../../../RemoveConfirmBeforeCallingDialog"; -import type { OnUpdateSettingsProps } from "../../types"; - -interface SettingsHeadingProps { - text: string; - hasInfo?: boolean; - info?: string; - grow: boolean; - headingCount: number; - hidden?: boolean; -} - -interface SettingsItemProps { - headingCount: number; - action: JSAction; - disabled?: boolean; - onUpdateSettings?: (props: OnUpdateSettingsProps) => void; - renderAdditionalColumns?: ( - action: JSAction, - headingCount: number, - ) => React.ReactNode; -} - -export interface JSFunctionSettingsProps { - actions: JSAction[]; - disabled?: boolean; - onUpdateSettings: SettingsItemProps["onUpdateSettings"]; - renderAdditionalColumns?: SettingsItemProps["renderAdditionalColumns"]; - additionalHeadings?: typeof SETTINGS_HEADINGS; -} - -const SettingRow = styled.div<{ isHeading?: boolean; noBorder?: boolean }>` - display: flex; - padding: 8px; - ${(props) => - !props.noBorder && - ` - border-bottom: solid 1px var(--ads-v2-color-border); - `} - - ${(props) => - props.isHeading && - ` - background: var(--ads-v2-color-bg-subtle); - font-size: ${props.theme.typography.h5.fontSize}px; - `}; -`; - -const StyledIcon = styled(Icon)` - width: max-content; - height: max-content; -`; - -export const SettingColumn = styled.div<{ - headingCount: number; - grow?: boolean; - isHeading?: boolean; - hidden?: boolean; -}>` - visibility: ${(props) => (props.hidden ? "hidden" : "visible")}; - display: flex; - align-items: center; - flex-grow: ${(props) => (props.grow ? 1 : 0)}; - padding: 5px 12px; - width: ${({ headingCount }) => `calc(100% / ${headingCount})`}; - - ${(props) => - props.isHeading && - ` - font-weight: ${props.theme.fontWeights[2]}; - font-size: ${props.theme.fontSizes[2]}px; - margin-right: 9px; - `} - - ${StyledIcon} { - margin-left: 8px; - } -`; - -const JSFunctionSettingsWrapper = styled.div` - height: 100%; - overflow: hidden; -`; - -const SettingsContainer = styled.div` - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - & > h3 { - margin: 20px 0; - font-size: ${(props) => props.theme.fontSizes[5]}px; - font-weight: ${(props) => props.theme.fontWeights[2]}; - color: var(--ads-v2-color-fg-emphasis); - } - overflow: hidden; -`; - -const SettingsRowWrapper = styled.div` - border-radius: var(--ads-v2-border-radius); - height: 100%; - overflow: hidden; -`; -const SettingsHeaderWrapper = styled.div``; -const SettingsBodyWrapper = styled.div` - overflow: auto; - max-height: calc(100% - 48px); -`; -const SwitchWrapper = styled.div` - margin-left: 6ch; -`; - -function SettingsHeading({ - grow, - hasInfo, - headingCount, - hidden, - info, - text, -}: SettingsHeadingProps) { - return ( - - ); -} - -function SettingsItem({ - action, - headingCount, - onUpdateSettings, - renderAdditionalColumns, -}: SettingsItemProps) { - const [showConfirmationModal, setShowConfirmationModal] = useState(false); - - const [executeOnPageLoad, setExecuteOnPageLoad] = useState( - String(!!action.executeOnLoad), - ); - const [confirmBeforeExecute, setConfirmBeforeExecute] = useState( - String(!!action.confirmBeforeExecute), - ); - - const onChangeExecuteOnPageLoad = (value: string) => { - setExecuteOnPageLoad(value); - onUpdateSettings?.({ - value: value === "true", - propertyName: "executeOnLoad", - action, - }); - - AnalyticsUtil.logEvent("JS_OBJECT_SETTINGS_CHANGED", { - toggleSetting: "ON_PAGE_LOAD", - toggleValue: value, - }); - }; - const onChangeConfirmBeforeExecute = (value: string) => { - setConfirmBeforeExecute(value); - onUpdateSettings?.({ - value: value === "true", - propertyName: "confirmBeforeExecute", - action, - }); - - AnalyticsUtil.logEvent("JS_OBJECT_SETTINGS_CHANGED", { - toggleSetting: "CONFIRM_BEFORE_RUN", - toggleValue: value, - }); - }; - - const showConfirmBeforeExecute = action.confirmBeforeExecute; - - const onRemoveConfirm = useCallback(() => { - setShowConfirmationModal(false); - onChangeConfirmBeforeExecute("false"); - }, []); - - const onCancel = useCallback(() => { - setShowConfirmationModal(false); - }, []); - - return ( - - - {action.name} - - - - - onChangeExecuteOnPageLoad(String(isSelected)) - } - /> - - - - - {showConfirmBeforeExecute ? ( - setShowConfirmationModal(true)} - /> - ) : null} - - - {renderAdditionalColumns?.(action, headingCount)} - - - ); -} - -function JSFunctionSettingsView({ - actions, - additionalHeadings = [], - disabled = false, - onUpdateSettings, - renderAdditionalColumns, -}: JSFunctionSettingsProps) { - const showConfirmBeforeExecuteOption = actions.some( - (action) => action.confirmBeforeExecute === true, - ); - const headings = [...SETTINGS_HEADINGS, ...additionalHeadings]; - - headings.forEach((heading) => { - if (heading.key === CONFIRM_BEFORE_CALLING_HEADING.key) { - CONFIRM_BEFORE_CALLING_HEADING.hidden = !showConfirmBeforeExecuteOption; - } - }); - - return ( - - -

{createMessage(FUNCTION_SETTINGS_HEADING)}

- - - - {headings.map((setting, index) => ( - - - - {actions && actions.length ? ( - actions.map((action) => ( - - )) - ) : ( - - - {createMessage(NO_JS_FUNCTIONS)} - - - )} - - -
-
- ); -} - -export default JSFunctionSettingsView; diff --git a/app/client/src/pages/Editor/QueryEditor/DatasourceSelector.tsx b/app/client/src/pages/Editor/QueryEditor/DatasourceSelector.tsx deleted file mode 100644 index d0164bc3cd54..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/DatasourceSelector.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from "react"; -import { useSelector } from "react-redux"; -import { Icon } from "@appsmith/ads"; -import DropdownField from "components/editorComponents/form/fields/DropdownField"; -import { CREATE_NEW_DATASOURCE, createMessage } from "ee/constants/messages"; -import styled from "styled-components"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { - getHasCreateDatasourcePermission, - getHasManageActionPermission, -} from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import type { Action } from "entities/Action"; -import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers"; -import { getPluginImages } from "ee/selectors/entitiesSelector"; -import type { Datasource } from "entities/Datasource"; -import type { Plugin } from "entities/Plugin"; -import type { AppState } from "ee/reducers"; -import { getCurrentAppWorkspace } from "ee/selectors/selectedWorkspaceSelectors"; - -const DropdownSelect = styled.div` - font-size: 14px; - width: 230px; - - .rc-select-selector { - min-width: unset; - } -`; - -const CreateDatasource = styled.div` - display: flex; - gap: 8px; -`; - -interface Props { - formName: string; - currentActionConfig?: Action; - plugin?: Plugin; - dataSources: Datasource[]; - onCreateDatasourceClick: () => void; -} - -interface DATASOURCES_OPTIONS_TYPE { - label: string; - value: string; - image: string; -} - -const DatasourceSelector = (props: Props) => { - const { - currentActionConfig, - dataSources, - formName, - onCreateDatasourceClick, - plugin, - } = props; - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const userWorkspacePermissions = useSelector( - (state: AppState) => getCurrentAppWorkspace(state).userPermissions ?? [], - ); - const isChangePermitted = getHasManageActionPermission( - isFeatureEnabled, - currentActionConfig?.userPermissions, - ); - const canCreateDatasource = getHasCreateDatasourcePermission( - isFeatureEnabled, - userWorkspacePermissions, - ); - const showDatasourceSelector = doesPluginRequireDatasource(plugin); - const pluginImages = useSelector(getPluginImages); - - const DATASOURCES_OPTIONS: Array = - dataSources.reduce( - (acc: Array, dataSource: Datasource) => { - if (dataSource.pluginId === plugin?.id) { - acc.push({ - label: dataSource.name, - value: dataSource.id, - image: pluginImages[dataSource.pluginId], - }); - } - - return acc; - }, - [], - ); - - if (!showDatasourceSelector) return null; - - return ( - - - {canCreateDatasource && ( - // this additional div is here so that rc-select can render the child with the onClick correctly -
- onCreateDatasourceClick()}> - - {createMessage(CREATE_NEW_DATASOURCE)} - -
- )} -
-
- ); -}; - -export default DatasourceSelector; diff --git a/app/client/src/pages/Editor/QueryEditor/Editor.tsx b/app/client/src/pages/Editor/QueryEditor/Editor.tsx deleted file mode 100644 index 7057c92a470d..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/Editor.tsx +++ /dev/null @@ -1,376 +0,0 @@ -import React from "react"; -import type { RouteComponentProps } from "react-router"; -import { connect } from "react-redux"; -import { getFormValues } from "redux-form"; -import styled from "styled-components"; -import type { QueryEditorRouteParams } from "constants/routes"; -import QueryEditorForm from "./Form"; -import type { UpdateActionPropertyActionPayload } from "actions/pluginActionActions"; -import { - deleteAction, - runAction, - setActionResponseDisplayFormat, - setActionProperty, -} from "actions/pluginActionActions"; -import type { AppState } from "ee/reducers"; -import { getCurrentApplicationId } from "selectors/editorSelectors"; -import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms"; -import { type Plugin, UIComponentTypes } from "entities/Plugin"; -import type { Datasource } from "entities/Datasource"; -import { - getPluginIdsOfPackageNames, - getPlugins, - getActionByBaseId, - getActionResponses, - getDatasourceByPluginId, - getDBAndRemoteDatasources, -} from "ee/selectors/entitiesSelector"; -import { PLUGIN_PACKAGE_DBS } from "constants/QueryEditorConstants"; -import type { QueryAction, SaaSAction } from "entities/Action"; -import Spinner from "components/editorComponents/Spinner"; -import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; -import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import { initFormEvaluations } from "actions/evaluationActions"; -import { getUIComponent } from "./helpers"; -import type { Diff } from "deep-diff"; -import { diff } from "deep-diff"; -import EntityNotFoundPane from "pages/Editor/EntityNotFoundPane"; -import { getConfigInitialValues } from "components/formControls/utils"; -import { merge } from "lodash"; -import { getPathAndValueFromActionDiffObject } from "../../../utils/getPathAndValueFromActionDiffObject"; -import { getCurrentEnvironmentDetails } from "ee/selectors/environmentSelectors"; -import { QueryEditorContext } from "./QueryEditorContext"; -import { - isActionDeleting, - isActionRunning, - isPluginActionCreating, -} from "PluginActionEditor/store"; - -const EmptyStateContainer = styled.div` - display: flex; - height: 100%; - font-size: 20px; -`; - -const LoadingContainer = styled(CenteredWrapper)` - height: 50%; -`; - -interface ReduxDispatchProps { - runAction: (actionId: string) => void; - deleteAction: (id: string, name: string) => void; - initFormEvaluation: ( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - editorConfig: any, - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingConfig: any, - formId: string, - ) => void; - updateActionResponseDisplayFormat: ({ - field, - id, - value, - }: UpdateActionPropertyActionPayload) => void; - setActionProperty: ( - actionId: string, - propertyName: string, - value: string, - ) => void; -} - -interface ReduxStateProps { - plugins: Plugin[]; - dataSources: Datasource[]; - isRunning: boolean; - isDeleting: boolean; - formData: QueryAction | SaaSAction; - runErrorMessage: Record; - pluginId: string | undefined; - pluginIds: Array | undefined; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - responses: any; - isCreating: boolean; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - editorConfig: any; - uiComponent: UIComponentTypes; - applicationId: string; - actionId: string; - baseActionId: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - actionObjectDiff?: any; - isSaas: boolean; - datasourceId?: string; - currentEnvironmentId: string; - currentEnvironmentName: string; -} - -type StateAndRouteProps = RouteComponentProps; -type OwnProps = StateAndRouteProps & { - isEditorInitialized: boolean; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingsConfig: any; -}; -type Props = ReduxDispatchProps & ReduxStateProps & OwnProps; - -class QueryEditor extends React.Component { - static contextType = QueryEditorContext; - context!: React.ContextType; - - constructor(props: Props) { - super(props); - - // Call the first evaluations when the page loads - // call evaluations only for queries and not google sheets (which uses apiId) - if (this.props.match.params.baseQueryId) { - this.props.initFormEvaluation( - this.props.editorConfig, - this.props.settingsConfig, - this.props.match.params.baseQueryId, - ); - } - } - - componentDidMount() { - // if the current action is non existent, do not dispatch change query page action - // this action should only be dispatched when switching from an existent action. - if (!this.props.pluginId) return; - - this.context?.changeQueryPage?.(this.props.baseActionId); - - // fixes missing where key issue by populating the action with a where object when the component is mounted. - if (this.props.isSaas) { - const { path = "", value = "" } = { - ...getPathAndValueFromActionDiffObject(this.props.actionObjectDiff), - }; - - if (value && path) { - this.props.setActionProperty(this.props.actionId, path, value); - } - } - } - - handleDeleteClick = () => { - const { formData } = this.props; - - this.props.deleteAction(this.props.actionId, formData.name); - }; - - handleRunClick = () => { - const { dataSources } = this.props; - const datasource = dataSources.find( - (datasource) => datasource.id === this.props.datasourceId, - ); - const pluginName = this.props.plugins.find( - (plugin) => plugin.id === this.props.pluginId, - )?.name; - - AnalyticsUtil.logEvent("RUN_QUERY_CLICK", { - actionId: this.props.actionId, - dataSourceSize: dataSources.length, - environmentId: this.props.currentEnvironmentId, - environmentName: this.props.currentEnvironmentName, - pluginName: pluginName, - datasourceId: datasource?.id, - isMock: !!datasource?.isMock, - }); - this.props.runAction(this.props.actionId); - }; - - componentDidUpdate(prevProps: Props) { - // Update the page when the queryID is changed by changing the - // URL or selecting new query from the query pane - // reusing same logic for changing query panes for switching query editor datasources, since the operations are similar. - if ( - prevProps.baseActionId !== this.props.baseActionId || - prevProps.pluginId !== this.props.pluginId - ) { - this.context?.changeQueryPage?.(this.props.baseActionId); - } - } - - render() { - const { - actionId, - dataSources, - editorConfig, - isCreating, - isDeleting, - isEditorInitialized, - isRunning, - pluginId, - pluginIds, - responses, - runErrorMessage, - uiComponent, - updateActionResponseDisplayFormat, - } = this.props; - const { onCreateDatasourceClick, onEntityNotFoundBackClick } = this.context; - - // if the action can not be found, generate a entity not found page - if (!pluginId && actionId) { - return ; - } - - if (!pluginIds?.length) { - return ( - {"Plugin is not installed"} - ); - } - - if (isCreating || !isEditorInitialized) { - return ( - - - - ); - } - - return ( - - ); - } -} - -const mapStateToProps = (state: AppState, props: OwnProps): ReduxStateProps => { - const { baseApiId, baseQueryId } = props.match.params; - const baseActionId = baseQueryId || baseApiId || ""; - const { runErrorMessage } = state.ui.pluginActionEditor; - const { plugins } = state.entities; - - const { editorConfigs } = plugins; - - const action = getActionByBaseId(state, baseActionId) as - | QueryAction - | SaaSAction; - const actionId = action?.id; - - const formData = getFormValues(QUERY_EDITOR_FORM_NAME)(state) as - | QueryAction - | SaaSAction; - let pluginId; - - if (action) { - pluginId = action.pluginId; - } - - const isCreating = isPluginActionCreating(state); - const isDeleting = isActionDeleting(actionId)(state); - const isRunning = isActionRunning(actionId)(state); - - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let editorConfig: any; - - if (editorConfigs && pluginId) { - editorConfig = editorConfigs[pluginId]; - } - - const initialValues = {}; - - if (editorConfig) { - merge(initialValues, getConfigInitialValues(editorConfig)); - } - - if (props.settingsConfig) { - merge(initialValues, getConfigInitialValues(props.settingsConfig)); - } - - // initialValues contains merge of action, editorConfig, settingsConfig and will be passed to redux form - merge(initialValues, action); - - // @ts-expect-error: Types are not available - const actionObjectDiff: undefined | Diff[] = diff( - action, - initialValues, - ); - - const allPlugins = getPlugins(state); - let uiComponent = UIComponentTypes.DbEditorForm; - - if (!!pluginId) uiComponent = getUIComponent(pluginId, allPlugins); - - const currentEnvDetails = getCurrentEnvironmentDetails(state); - - return { - actionId, - baseActionId, - currentEnvironmentId: currentEnvDetails?.id || "", - currentEnvironmentName: currentEnvDetails?.name || "", - pluginId, - plugins: allPlugins, - runErrorMessage, - pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS), - dataSources: !!baseApiId - ? getDatasourceByPluginId(state, action?.pluginId) - : getDBAndRemoteDatasources(state), - responses: getActionResponses(state), - isCreating, - isRunning, - isDeleting, - isSaas: !!baseApiId, - formData, - editorConfig, - uiComponent, - applicationId: getCurrentApplicationId(state), - actionObjectDiff, - datasourceId: action?.datasource?.id, - }; -}; - -// TODO: Fix this the next time the file is edited -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapDispatchToProps = (dispatch: any): ReduxDispatchProps => ({ - deleteAction: (id: string, name: string) => - dispatch(deleteAction({ id, name })), - runAction: (actionId: string) => dispatch(runAction(actionId)), - initFormEvaluation: ( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - editorConfig: any, - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingsConfig: any, - formId: string, - ) => { - dispatch(initFormEvaluations(editorConfig, settingsConfig, formId)); - }, - updateActionResponseDisplayFormat: ({ - field, - id, - value, - }: UpdateActionPropertyActionPayload) => { - dispatch(setActionResponseDisplayFormat({ id, field, value })); - }, - setActionProperty: ( - actionId: string, - propertyName: string, - value: string, - ) => { - dispatch(setActionProperty({ actionId, propertyName, value })); - }, -}); - -export default connect(mapStateToProps, mapDispatchToProps)(QueryEditor); diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx deleted file mode 100644 index aa9b0a16851a..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx +++ /dev/null @@ -1,382 +0,0 @@ -import { useContext } from "react"; -import React, { useCallback } from "react"; -import type { InjectedFormProps } from "redux-form"; -import { noop } from "lodash"; -import type { Datasource } from "entities/Datasource"; -import type { Action, QueryAction, SaaSAction } from "entities/Action"; -import { useDispatch, useSelector } from "react-redux"; -import ActionSettings from "pages/Editor/ActionSettings"; -import { Button, Tab, TabPanel, Tabs, TabsList, Tooltip } from "@appsmith/ads"; -import styled from "styled-components"; -import FormRow from "components/editorComponents/FormRow"; -import { - createMessage, - DOCUMENTATION, - DOCUMENTATION_TOOLTIP, -} from "ee/constants/messages"; -import { useParams } from "react-router"; -import type { AppState } from "ee/reducers"; -import { thinScrollbar } from "constants/DefaultTheme"; -import type { ActionResponse } from "api/ActionAPI"; -import type { Plugin, UIComponentTypes } from "entities/Plugin"; -import { EDITOR_TABS, SQL_DATASOURCES } from "constants/QueryEditorConstants"; -import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer"; -import { - getPluginActionConfigSelectedTab, - setPluginActionEditorSelectedTab, -} from "PluginActionEditor/store"; -import type { SourceEntity } from "entities/AppsmithConsole"; -import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; -import { DocsLink, openDoc } from "constants/DocumentationLinks"; -import { QueryEditorContext } from "./QueryEditorContext"; -import QueryDebuggerTabs from "./QueryDebuggerTabs"; -import useShowSchema from "PluginActionEditor/components/PluginActionResponse/hooks/useShowSchema"; -import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers"; -import FormRender from "PluginActionEditor/components/PluginActionForm/components/UQIEditor/FormRender"; -import QueryEditorHeader from "./QueryEditorHeader"; -import RunHistory from "ee/components/RunHistory"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { getHasExecuteActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import { getPluginNameFromId } from "ee/selectors/entitiesSelector"; - -const QueryFormContainer = styled.form` - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - padding: var(--ads-v2-spaces-5) 0 0; - width: 100%; - .statementTextArea { - font-size: 14px; - line-height: 20px; - margin-top: 5px; - } - .queryInput { - max-width: 30%; - padding-right: 10px; - } - .executeOnLoad { - display: flex; - justify-content: flex-end; - margin-top: 10px; - } -`; - -const SettingsWrapper = styled.div` - ${thinScrollbar}; - height: 100%; -`; - -const SecondaryWrapper = styled.div` - display: flex; - flex-direction: column; - flex: 1; - overflow: hidden; -`; - -export const StyledFormRow = styled(FormRow)` - padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-5) - var(--ads-v2-spaces-7); - flex: 0; -`; - -const TabContainerView = styled.div` - display: flex; - align-items: start; - flex: 1; - overflow: auto; - ${thinScrollbar} - a { - font-size: 14px; - line-height: 20px; - margin-top: 12px; - } - position: relative; - - & > .ads-v2-tabs { - height: 100%; - - & > .ads-v2-tabs__panel { - height: calc(100% - 50px); - overflow-y: scroll; - } - } -`; - -const TabsListWrapper = styled.div` - padding: 0 var(--ads-v2-spaces-7); -`; - -const TabPanelWrapper = styled(TabPanel)` - padding: 0 var(--ads-v2-spaces-7); -`; - -const Wrapper = styled.div` - display: flex; - flex-direction: row; - height: calc(100% - 50px); - overflow: hidden; - width: 100%; -`; - -const DocumentationButton = styled(Button)` - position: absolute !important; - right: 24px; - margin: 7px 0 0; - z-index: 6; -`; - -export const SegmentedControlContainer = styled.div` - padding: 0 var(--ads-v2-spaces-7); - padding-top: var(--ads-v2-spaces-4); - display: flex; - flex-direction: column; - gap: var(--ads-v2-spaces-4); - overflow-y: clip; - overflow-x: scroll; -`; - -const StyledNotificationWrapper = styled.div` - padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-3) - var(--ads-v2-spaces-7); -`; - -interface QueryFormProps { - onDeleteClick: () => void; - onRunClick: () => void; - onCreateDatasourceClick: () => void; - isDeleting: boolean; - isRunning: boolean; - dataSources: Datasource[]; - uiComponent: UIComponentTypes; - actionResponse?: ActionResponse; - runErrorMessage: string | undefined; - location: { - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - state: any; - }; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - editorConfig?: any; - formName: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - settingConfig: any; - formData: SaaSAction | QueryAction; - responseDisplayFormat: { title: string; value: string }; - datasourceId: string; - showCloseEditor: boolean; -} - -interface ReduxProps { - actionName: string; - plugin?: Plugin; - pluginId: string; - documentationLink: string | undefined; - formEvaluationState: FormEvalOutput; -} - -export type EditorJSONtoFormProps = QueryFormProps & ReduxProps; - -type Props = EditorJSONtoFormProps & - InjectedFormProps; - -export function EditorJSONtoForm(props: Props) { - const { - actionName, - actionResponse, - dataSources, - documentationLink, - editorConfig, - formName, - handleSubmit, - isRunning, - onCreateDatasourceClick, - onRunClick, - plugin, - runErrorMessage, - settingConfig, - uiComponent, - } = props; - - const { actionRightPaneAdditionSections, notification } = - useContext(QueryEditorContext); - - const params = useParams<{ baseApiId?: string; baseQueryId?: string }>(); - // fetch the error count from the store. - const actions: Action[] = useSelector((state: AppState) => - state.entities.actions.map((action) => action.config), - ); - const currentActionConfig: Action | undefined = actions.find( - (action) => - action.baseId === params.baseApiId || - action.baseId === params.baseQueryId, - ); - - const pluginRequireDatasource = doesPluginRequireDatasource(plugin); - - const showSchema = - useShowSchema(currentActionConfig?.pluginId || "") && - pluginRequireDatasource; - - const dispatch = useDispatch(); - - const handleDocumentationClick = () => { - openDoc(DocsLink.QUERY, plugin?.documentationLink, plugin?.name); - }; - - // action source for analytics. - const actionSource: SourceEntity = { - type: SOURCE_ENTITY_TYPE.ACTION, - name: currentActionConfig ? currentActionConfig.name : "", - id: currentActionConfig ? currentActionConfig.id : "", - }; - - const selectedTab = useSelector(getPluginActionConfigSelectedTab); - - const setSelectedConfigTab = useCallback( - (selectedIndex: string) => { - dispatch(setPluginActionEditorSelectedTab(selectedIndex)); - }, - [dispatch], - ); - - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const isExecutePermitted = getHasExecuteActionPermission( - isFeatureEnabled, - currentActionConfig?.userPermissions, - ); - - // get the current action's plugin name - const currentActionPluginName = useSelector((state: AppState) => - getPluginNameFromId(state, currentActionConfig?.pluginId || ""), - ); - - let actionBody = ""; - - if (!!currentActionConfig?.actionConfiguration) { - if ("formData" in currentActionConfig?.actionConfiguration) { - // if the action has a formData (the action is postUQI e.g. Oracle) - actionBody = - currentActionConfig.actionConfiguration.formData?.body?.data || ""; - } else { - // if the action is pre UQI, the path is different e.g. mySQL - actionBody = currentActionConfig.actionConfiguration?.body || ""; - } - } - - // if (the body is empty and the action is an sql datasource) or the user does not have permission, block action execution. - const blockExecution = - (!actionBody && SQL_DATASOURCES.includes(currentActionPluginName)) || - !isExecutePermitted; - - // when switching between different redux forms, make sure this redux form has been initialized before rendering anything. - // the initialized prop below comes from redux-form. - if (!props.initialized) { - return null; - } - - return ( - - - {notification && ( - {notification} - )} - -
- - - - - - - Query - - - Settings - - - - - - - - - - - - - - - {documentationLink && ( - - { - e.stopPropagation(); - handleDocumentationClick(); - }} - size="sm" - startIcon="book-line" - > - {createMessage(DOCUMENTATION)} - - - )} - - - - -
- {actionRightPaneAdditionSections} -
-
- ); -} diff --git a/app/client/src/pages/Editor/QueryEditor/Form.tsx b/app/client/src/pages/Editor/QueryEditor/Form.tsx deleted file mode 100644 index 9691896d34d0..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/Form.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { formValueSelector, reduxForm } from "redux-form"; -import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms"; -import type { Action } from "entities/Action"; -import { connect } from "react-redux"; -import type { AppState } from "ee/reducers"; -import { - getPluginResponseTypes, - getPluginDocumentationLinks, - getPlugin, - getActionData, -} from "ee/selectors/entitiesSelector"; -import type { EditorJSONtoFormProps } from "./EditorJSONtoForm"; -import { EditorJSONtoForm } from "./EditorJSONtoForm"; -import { getFormEvaluationState } from "selectors/formSelectors"; -import { actionResponseDisplayDataFormats } from "../utils"; - -const valueSelector = formValueSelector(QUERY_EDITOR_FORM_NAME); -// TODO: Fix this the next time the file is edited -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapStateToProps = (state: AppState, props: any) => { - const actionId = valueSelector(state, "id"); - const actionName = valueSelector(state, "name"); - const pluginId = valueSelector(state, "datasource.pluginId"); - const selectedDbId = valueSelector(state, "datasource.id"); - const actionData = getActionData(state, actionId); - const { responseDataTypes, responseDisplayFormat } = - actionResponseDisplayDataFormats(actionData); - - const responseTypes = getPluginResponseTypes(state); - const documentationLinks = getPluginDocumentationLinks(state); - const plugin = getPlugin(state, pluginId); - // State to manage the evaluations for the form - let formEvaluationState = {}; - - // Fetching evaluations state only once the formData is populated - if (!!props.formData) { - formEvaluationState = getFormEvaluationState(state)[props.formData.id]; - } - - return { - actionName, - plugin, - pluginId, - selectedDbId, - responseDataTypes, - responseDisplayFormat, - responseType: responseTypes[pluginId], - documentationLink: documentationLinks[pluginId], - formName: QUERY_EDITOR_FORM_NAME, - formEvaluationState, - }; -}; - -export default connect(mapStateToProps)( - reduxForm({ - form: QUERY_EDITOR_FORM_NAME, - enableReinitialize: true, - })(EditorJSONtoForm), -); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx deleted file mode 100644 index 603fa1581327..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react"; -import configureStore from "redux-mock-store"; -import { Provider } from "react-redux"; -import { ThemeProvider } from "styled-components"; -import { unitTestBaseMockStore } from "layoutSystems/common/dropTarget/unitTestUtils"; -import { lightTheme } from "selectors/themeSelectors"; -import { BrowserRouter as Router } from "react-router-dom"; -import { EditorViewMode } from "ee/entities/IDE/constants"; -import "@testing-library/jest-dom/extend-expect"; -import QueryDebuggerTabs from "./QueryDebuggerTabs"; -import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; - -const mockStore = configureStore([]); - -const storeState = { - ...unitTestBaseMockStore, - evaluations: { - tree: {}, - }, - entities: { - plugins: { - list: [], - }, - datasources: { - structure: {}, - list: [], - }, - }, - ui: { - ...unitTestBaseMockStore.ui, - users: { - featureFlag: { - data: {}, - overriddenFlags: {}, - }, - }, - ide: { - view: EditorViewMode.FullScreen, - }, - debugger: { - context: { - errorCount: 0, - }, - }, - pluginActionEditor: { - debugger: { - open: true, - responseTabHeight: 200, - selectedTab: "response", - }, - }, - }, -}; - -describe("ApiResponseView", () => { - let store = mockStore(storeState); - - beforeEach(() => { - store = mockStore(storeState); - }); - - it("the container should have class select-text to enable the selection of text for user", () => { - const { container } = render( - - - - {}} - /> - - - , - ); - - expect( - container - .querySelector(".t--query-bottom-pane-container") - ?.classList.contains("select-text"), - ).toBe(true); - }); -}); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx b/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx deleted file mode 100644 index 1e2f67a8a63e..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/QueryEditorContext.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { ReduxAction } from "actions/ReduxActionTypes"; -import type { SaveActionNameParams } from "PluginActionEditor"; -import React, { createContext, useMemo } from "react"; - -interface QueryEditorContextContextProps { - moreActionsMenu?: React.ReactNode; - onCreateDatasourceClick?: () => void; - onEntityNotFoundBackClick?: () => void; - changeQueryPage?: (baseQueryId: string) => void; - actionRightPaneBackLink?: React.ReactNode; - saveActionName: ( - params: SaveActionNameParams, - ) => ReduxAction; - actionRightPaneAdditionSections?: React.ReactNode; - showSuggestedWidgets?: boolean; - notification?: string | React.ReactNode; -} - -type QueryEditorContextProviderProps = - React.PropsWithChildren; - -export const QueryEditorContext = createContext( - {} as QueryEditorContextContextProps, -); - -export function QueryEditorContextProvider({ - actionRightPaneAdditionSections, - actionRightPaneBackLink, - changeQueryPage, - children, - moreActionsMenu, - notification, - onCreateDatasourceClick, - onEntityNotFoundBackClick, - saveActionName, - showSuggestedWidgets, -}: QueryEditorContextProviderProps) { - const value = useMemo( - () => ({ - actionRightPaneBackLink, - actionRightPaneAdditionSections, - changeQueryPage, - moreActionsMenu, - onCreateDatasourceClick, - onEntityNotFoundBackClick, - saveActionName, - showSuggestedWidgets, - notification, - }), - [ - actionRightPaneBackLink, - actionRightPaneAdditionSections, - changeQueryPage, - moreActionsMenu, - onCreateDatasourceClick, - onEntityNotFoundBackClick, - saveActionName, - showSuggestedWidgets, - notification, - ], - ); - - return ( - - {children} - - ); -} diff --git a/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx b/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx deleted file mode 100644 index 443cff2032c8..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/QueryEditorHeader.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { useContext } from "react"; -import ActionNameEditor from "components/editorComponents/ActionNameEditor"; -import { Button } from "@appsmith/ads"; -import { StyledFormRow } from "./EditorJSONtoForm"; -import styled from "styled-components"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import { useActiveActionBaseId } from "ee/pages/Editor/Explorer/hooks"; -import { useSelector } from "react-redux"; -import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector"; -import { QueryEditorContext } from "./QueryEditorContext"; -import type { Plugin } from "entities/Plugin"; -import type { Datasource } from "entities/Datasource"; -import type { AppState } from "ee/reducers"; -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; - justify-content: space-between; - align-items: center; - width: 50%; - input { - margin: 0; - box-sizing: border-box; - } -`; - -const ActionsWrapper = styled.div` - display: flex; - align-items: center; - flex: 1 1 50%; - justify-content: flex-end; - gap: var(--ads-v2-spaces-3); - width: 50%; -`; - -interface Props { - plugin?: Plugin; - formName: string; - dataSources: Datasource[]; - onCreateDatasourceClick: () => void; - isRunDisabled?: boolean; - isRunning: boolean; - onRunClick: () => void; -} - -const QueryEditorHeader = (props: Props) => { - const { - dataSources, - formName, - isRunDisabled = false, - isRunning, - onCreateDatasourceClick, - onRunClick, - plugin, - } = props; - const { moreActionsMenu, saveActionName } = useContext(QueryEditorContext); - - const activeActionBaseId = useActiveActionBaseId(); - const currentActionConfig = useSelector((state) => - activeActionBaseId - ? getActionByBaseId(state, activeActionBaseId) - : undefined, - ); - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const isChangePermitted = getHasManageActionPermission( - isFeatureEnabled, - currentActionConfig?.userPermissions, - ); - - const isDatasourceSelectorEnabled = useFeatureFlag( - FEATURE_FLAG.release_ide_datasource_selector_enabled, - ); - - 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); - - return ( - - - - - - {moreActionsMenu} - {isDatasourceSelectorEnabled && ( - - )} - - - - ); -}; - -export default QueryEditorHeader; diff --git a/app/client/src/pages/Editor/QueryEditor/index.tsx b/app/client/src/pages/Editor/QueryEditor/index.tsx deleted file mode 100644 index 3710587cf08f..000000000000 --- a/app/client/src/pages/Editor/QueryEditor/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React, { useCallback, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import type { RouteComponentProps } from "react-router"; - -import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import Editor from "./Editor"; -import history from "utils/history"; -import MoreActionsMenu from "../Explorer/Actions/MoreActionsMenu"; -import BackToCanvas from "components/common/BackToCanvas"; -import { INTEGRATION_TABS } from "constants/routes"; -import { - getCurrentApplicationId, - getIsEditorInitialized, - getPagePermissions, -} from "selectors/editorSelectors"; -import { changeQuery } from "PluginActionEditor/store"; -import { DatasourceCreateEntryPoints } from "constants/Datasource"; -import { - getActionByBaseId, - getIsActionConverting, - getPluginImages, - getPluginSettingConfigs, -} from "ee/selectors/entitiesSelector"; -import { integrationEditorURL } from "ee/RouteBuilder"; -import { QueryEditorContextProvider } from "./QueryEditorContext"; -import type { QueryEditorRouteParams } from "constants/routes"; -import { - getHasCreateActionPermission, - getHasDeleteActionPermission, - getHasManageActionPermission, -} from "ee/utils/BusinessFeatures/permissionPageHelpers"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import Disabler from "pages/common/Disabler"; -import ConvertToModuleInstanceCTA from "ee/pages/Editor/EntityEditor/ConvertToModuleInstanceCTA"; -import { MODULE_TYPE } from "ee/constants/ModuleConstants"; -import ConvertEntityNotification from "ee/pages/common/ConvertEntityNotification"; -import { PluginType } from "entities/Plugin"; -import { Icon } from "@appsmith/ads"; -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 { saveActionName } from "actions/pluginActionActions"; - -type QueryEditorProps = RouteComponentProps; - -function QueryEditor(props: QueryEditorProps) { - const { baseApiId, basePageId, baseQueryId } = props.match.params; - const baseActionId = baseQueryId || baseApiId; - const dispatch = useDispatch(); - const action = useSelector((state) => - getActionByBaseId(state, baseActionId || ""), - ); - const pluginId = action?.pluginId || ""; - const isEditorInitialized = useSelector(getIsEditorInitialized); - const applicationId: string = useSelector(getCurrentApplicationId); - const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); - const settingsConfig = useSelector((state) => - getPluginSettingConfigs(state, pluginId), - ); - const pagePermissions = useSelector(getPagePermissions); - const isConverting = useSelector((state) => - getIsActionConverting(state, action?.id || ""), - ); - const pluginImages = useSelector(getPluginImages); - const editorMode = useSelector(getIDEViewMode); - const icon = resolveIcon({ - iconLocation: pluginImages[pluginId] || "", - pluginType: action?.pluginType || "", - moduleType: action?.actionConfiguration?.body?.moduleType, - }) || ( - - - - ); - - const isDeletePermitted = getHasDeleteActionPermission( - isFeatureEnabled, - action?.userPermissions, - ); - - const isChangePermitted = getHasManageActionPermission( - isFeatureEnabled, - action?.userPermissions, - ); - - const isCreatePermitted = getHasCreateActionPermission( - isFeatureEnabled, - pagePermissions, - ); - - const moreActionsMenu = useMemo(() => { - const convertToModuleProps = { - canCreateModuleInstance: isCreatePermitted, - canDeleteEntity: isDeletePermitted, - entityId: action?.id || "", - moduleType: MODULE_TYPE.QUERY, - }; - - return ( - <> - - ) - } - /> - {action?.pluginType !== PluginType.INTERNAL && - editorMode !== EditorViewMode.SplitScreen && ( - // Need to remove this check once workflow query is supported in module - - )} - - ); - }, [ - action?.id, - action?.name, - action?.pluginType, - isChangePermitted, - isDeletePermitted, - basePageId, - isCreatePermitted, - editorMode, - ]); - - const actionRightPaneBackLink = useMemo(() => { - return ; - }, [basePageId]); - - const changeQueryPage = useCallback( - (baseQueryId: string) => { - dispatch( - changeQuery({ baseQueryId: baseQueryId, basePageId, applicationId }), - ); - }, - [basePageId, applicationId, dispatch], - ); - - const onCreateDatasourceClick = useCallback(() => { - history.push( - integrationEditorURL({ - basePageId: basePageId, - selectedTab: INTEGRATION_TABS.NEW, - }), - ); - // Event for datasource creation click - const entryPoint = DatasourceCreateEntryPoints.QUERY_EDITOR; - - AnalyticsUtil.logEvent("NAVIGATE_TO_CREATE_NEW_DATASOURCE_PAGE", { - entryPoint, - }); - }, [basePageId]); - - // custom function to return user to integrations page if action is not found - const onEntityNotFoundBackClick = useCallback( - () => - history.push( - integrationEditorURL({ - basePageId: basePageId, - selectedTab: INTEGRATION_TABS.ACTIVE, - }), - ), - [basePageId], - ); - - const notification = useMemo(() => { - if (!isConverting) return null; - - return ( - - ); - }, [action?.name, isConverting, icon]); - - return ( - - - - - - ); -} - -export default QueryEditor; diff --git a/app/client/src/sagas/ApiPaneSagas.ts b/app/client/src/sagas/ApiPaneSagas.ts index 85162c278030..0f9aa00f1c94 100644 --- a/app/client/src/sagas/ApiPaneSagas.ts +++ b/app/client/src/sagas/ApiPaneSagas.ts @@ -45,6 +45,7 @@ import { import type { Action, ApiAction, + AutoGeneratedHeader, CreateApiActionDefaultsParams, } from "entities/Action"; import { type Plugin, PluginPackageName, PluginType } from "entities/Plugin"; @@ -60,8 +61,7 @@ import { getCurrentBasePageId } from "selectors/editorSelectors"; import { validateResponse } from "./ErrorSagas"; import type { CreateDatasourceSuccessAction } from "actions/datasourceActions"; import { removeTempDatasource } from "actions/datasourceActions"; -import type { AutoGeneratedHeader } from "pages/Editor/APIEditor/helpers"; -import { deriveAutoGeneratedHeaderState } from "pages/Editor/APIEditor/helpers"; +import { deriveAutoGeneratedHeaderState } from "../PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/utils/autoGeneratedHeaders"; import { TEMP_DATASOURCE_ID } from "constants/Datasource"; import type { FeatureFlags } from "ee/entities/FeatureFlag"; import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors"; diff --git a/app/client/src/utils/localStorage.tsx b/app/client/src/utils/localStorage.tsx index 8866e0d2d3eb..d005a172c35e 100644 --- a/app/client/src/utils/localStorage.tsx +++ b/app/client/src/utils/localStorage.tsx @@ -9,7 +9,6 @@ import { toast } from "@appsmith/ads"; export const LOCAL_STORAGE_KEYS = { CANVAS_CARDS_STATE: "CANVAS_CARDS_STATE", - SPLITPANE_ANNOUNCEMENT: "SPLITPANE_ANNOUNCEMENT", NUDGE_SHOWN_SPLIT_PANE: "NUDGE_SHOWN_SPLIT_PANE", };