From d494ca4ee1b36ea771bc8542ac621d392efa40d6 Mon Sep 17 00:00:00 2001 From: Ilia Date: Tue, 18 Mar 2025 12:41:11 +0100 Subject: [PATCH] feat: add generate schema button (#39751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Update JS and Plugin Action Toolbar to add new Schema generation CTA in them https://www.figma.com/design/mVEbXXryqv2oBxMcNg8yjC/Anvil-AI?node-id=3891-34025&t=AVP3gbWu07WzPfwc-0 Fixes #39726 ## Automation /ok-to-test tags="@tag.JS, @tag.Datasource" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: c0b76039714bf64155c0d41c6f72cda881bcd968 > Cypress dashboard. > Tags: `@tag.JS, @tag.Datasource` > Spec: >
Tue, 18 Mar 2025 11:30:10 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit - **New Features** - Introduced real-time tracking of schema generation processes with clear UI indicators and error messaging. - Expanded actionable events to support improved feedback during schema creation for plugin actions and JavaScript functions. - **Refactor** - Streamlined component exports and updated import paths for enhanced organization and consistency in the editor toolbars. --------- Co-authored-by: Hetu Nandu --- app/client/src/PluginActionEditor/index.ts | 2 +- .../store/pluginActionEditorSelectors.ts | 10 ++++ .../store/pluginEditorReducer.ts | 22 +++++++++ .../components/PluginActionToolbar.tsx | 14 +++--- .../src/ce/constants/ReduxActionConstants.tsx | 12 +++++ app/client/src/ce/constants/messages.ts | 1 + .../JSEditorToolbar/JSEditorToolbar.test.tsx | 0 .../JSEditorToolbar/JSEditorToolbar.tsx | 13 +++-- .../components/PluginActionToolbar.tsx | 1 + .../JSEditorToolbar/JSEditorToolbar.tsx | 1 + .../JSEditor/JSEditorToolbar/constants.ts | 1 + .../Editor/JSEditor/JSEditorToolbar/index.ts | 2 +- .../src/reducers/uiReducers/jsPaneReducer.ts | 48 ++++++++++++++++++- app/client/src/selectors/jsPaneSelectors.ts | 8 ++++ 14 files changed, 119 insertions(+), 16 deletions(-) rename app/client/src/{ => ce}/PluginActionEditor/components/PluginActionToolbar.tsx (79%) rename app/client/src/{ => ce}/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx (100%) rename app/client/src/{ => ce}/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx (84%) create mode 100644 app/client/src/ee/PluginActionEditor/components/PluginActionToolbar.tsx create mode 100644 app/client/src/ee/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx diff --git a/app/client/src/PluginActionEditor/index.ts b/app/client/src/PluginActionEditor/index.ts index 8a2cb80ae6..8b72d13c7a 100644 --- a/app/client/src/PluginActionEditor/index.ts +++ b/app/client/src/PluginActionEditor/index.ts @@ -3,7 +3,7 @@ export { PluginActionContextProvider, usePluginActionContext, } from "./PluginActionContext"; -export { default as PluginActionToolbar } from "./components/PluginActionToolbar"; +export { PluginActionToolbar } from "ee/PluginActionEditor/components/PluginActionToolbar"; export { default as PluginActionForm } from "./components/PluginActionForm"; export { default as PluginActionResponse } from "./components/PluginActionResponse"; export type { diff --git a/app/client/src/PluginActionEditor/store/pluginActionEditorSelectors.ts b/app/client/src/PluginActionEditor/store/pluginActionEditorSelectors.ts index d931e82106..f0af8494b5 100644 --- a/app/client/src/PluginActionEditor/store/pluginActionEditorSelectors.ts +++ b/app/client/src/PluginActionEditor/store/pluginActionEditorSelectors.ts @@ -20,6 +20,16 @@ export const isActionDirty = (id: string) => const getActionRunningState = (state: AppState) => state.ui.pluginActionEditor.isRunning; +const getActionSchemaGeneratingState = (state: AppState) => + state.ui.pluginActionEditor.isSchemaGenerating; + +export const isActionSchemaGenerating = (id: string) => + createSelector( + [getActionSchemaGeneratingState], + (isSchemaGeneratingMap) => + id in isSchemaGeneratingMap && isSchemaGeneratingMap[id], + ); + export const isActionRunning = (id: string) => createSelector( [getActionRunningState], diff --git a/app/client/src/PluginActionEditor/store/pluginEditorReducer.ts b/app/client/src/PluginActionEditor/store/pluginEditorReducer.ts index 3bcafcdc63..5cc203e7e3 100644 --- a/app/client/src/PluginActionEditor/store/pluginEditorReducer.ts +++ b/app/client/src/PluginActionEditor/store/pluginEditorReducer.ts @@ -22,6 +22,7 @@ export interface PluginActionEditorState { isCreating: boolean; isRunning: Record; isSaving: Record; + isSchemaGenerating: Record; isDeleting: Record; isDirty: Record; runErrorMessage: Record; @@ -34,6 +35,7 @@ const initialState: PluginActionEditorState = { isCreating: false, isRunning: {}, isSaving: {}, + isSchemaGenerating: {}, isDeleting: {}, isDirty: {}, runErrorMessage: {}, @@ -141,6 +143,26 @@ export const handlers = { set(state, ["isRunning", id], false); set(state, ["runErrorMessage", id], error.message); }, + [ReduxActionTypes.GENERATE_PLUGIN_ACTION_SCHEMA_REQUEST]: ( + state: PluginActionEditorState, + action: ReduxAction<{ + id: string; + }>, + ) => { + set(state, ["isSchemaGenerating", action.payload.id], true); + }, + [ReduxActionTypes.GENERATE_PLUGIN_ACTION_SCHEMA_SUCCESS]: ( + state: PluginActionEditorState, + action: ReduxAction<{ id: string }>, + ) => { + set(state, ["isSchemaGenerating", action.payload.id], false); + }, + [ReduxActionErrorTypes.GENERATE_PLUGIN_ACTION_SCHEMA_ERROR]: ( + state: PluginActionEditorState, + action: ReduxAction<{ id: string }>, + ) => { + set(state, ["isSchemaGenerating", action.payload.id], false); + }, [ReduxActionTypes.SET_PLUGIN_ACTION_EDITOR_FORM_SELECTED_TAB]: ( state: PluginActionEditorState, action: ReduxAction<{ selectedTab: string }>, diff --git a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/ce/PluginActionEditor/components/PluginActionToolbar.tsx similarity index 79% rename from app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx rename to app/client/src/ce/PluginActionEditor/components/PluginActionToolbar.tsx index d0ff690cf1..492de60eee 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx +++ b/app/client/src/ce/PluginActionEditor/components/PluginActionToolbar.tsx @@ -2,16 +2,16 @@ import React, { useCallback } from "react"; import { IDEToolbar } from "IDE"; import { Button, Tooltip } from "@appsmith/ads"; import { modText } from "utils/helpers"; -import { usePluginActionContext } from "../PluginActionContext"; +import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; import { useBlockExecution, useHandleRunClick, useAnalyticsOnRunClick, -} from "../hooks"; +} from "PluginActionEditor/hooks"; import { useSelector } from "react-redux"; -import { isActionRunning } from "../store"; -import PluginActionSettings from "./PluginActionSettings"; -import { PluginActionContextMenu } from "./PluginActionContextMenu"; +import { isActionRunning } from "PluginActionEditor/store"; +import PluginActionSettings from "PluginActionEditor/components/PluginActionSettings"; +import { PluginActionContextMenu } from "PluginActionEditor/components/PluginActionContextMenu"; interface PluginActionToolbarProps { runOptions?: React.ReactNode; @@ -19,7 +19,7 @@ interface PluginActionToolbarProps { menuContent?: React.ReactNode[] | React.ReactNode; } -const PluginActionToolbar = (props: PluginActionToolbarProps) => { +export const PluginActionToolbar = (props: PluginActionToolbarProps) => { const { action } = usePluginActionContext(); const { handleRunClick } = useHandleRunClick(); const { callRunActionAnalytics } = useAnalyticsOnRunClick(); @@ -63,5 +63,3 @@ const PluginActionToolbar = (props: PluginActionToolbarProps) => { ); }; - -export default PluginActionToolbar; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 6ff622ee5c..2df11a4696 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -710,6 +710,16 @@ const ActionExecutionTypes = { RUN_ACTION_REQUEST: "RUN_ACTION_REQUEST", RUN_ACTION_CANCELLED: "RUN_ACTION_CANCELLED", RUN_ACTION_SUCCESS: "RUN_ACTION_SUCCESS", + GENERATE_JS_FUNCTION_SCHEMA_REQUEST: "GENERATE_JS_FUNCTION_SCHEMA_REQUEST", + GENERATE_JS_FUNCTION_SCHEMA_CANCELLED: + "GENERATE_JS_FUNCTION_SCHEMA_CANCELLED", + GENERATE_JS_FUNCTION_SCHEMA_SUCCESS: "GENERATE_JS_FUNCTION_SCHEMA_SUCCESS", + GENERATE_PLUGIN_ACTION_SCHEMA_REQUEST: + "GENERATE_PLUGIN_ACTION_SCHEMA_REQUEST", + GENERATE_PLUGIN_ACTION_SCHEMA_CANCELLED: + "GENERATE_PLUGIN_ACTION_SCHEMA_CANCELLED", + GENERATE_PLUGIN_ACTION_SCHEMA_SUCCESS: + "GENERATE_PLUGIN_ACTION_SCHEMA_SUCCESS", CLEAR_ACTION_RESPONSE: "CLEAR_ACTION_RESPONSE", SHOW_ACTION_MODAL: "SHOW_ACTION_MODAL", CANCEL_ACTION_MODAL: "CANCEL_ACTION_MODAL", @@ -722,6 +732,8 @@ const ActionExecutionTypes = { const ActionExecutionErrorTypes = { RUN_ACTION_ERROR: "RUN_ACTION_ERROR", + GENERATE_JS_FUNCTION_SCHEMA_ERROR: "GENERATE_JS_FUNCTION_SCHEMA_ERROR", + GENERATE_PLUGIN_ACTION_SCHEMA_ERROR: "GENERATE_PLUGIN_ACTION_SCHEMA_ERROR", EXECUTE_PLUGIN_ACTION_ERROR: "EXECUTE_PLUGIN_ACTION_ERROR", }; diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 09033601d2..d74522682e 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -647,6 +647,7 @@ export const EXPORT_DEFAULT_BEGINNING = () => `Start object with export default`; export const ACTION_EXECUTION_FAILED = (actionName: string) => `The action "${actionName}" has failed.`; +export const CANNOT_GENERATE_SCHEMA = () => "Can't generate schema"; export const JS_EXECUTION_TRIGGERED = () => "Function triggered"; export const JS_EXECUTION_SUCCESS = () => "Function executed"; export const JS_EXECUTION_FAILURE = () => "Function execution failed"; diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx b/app/client/src/ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx similarity index 100% rename from app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx rename to app/client/src/ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.test.tsx diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx b/app/client/src/ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx similarity index 84% rename from app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx rename to app/client/src/ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx index e48a118488..8357b14813 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx +++ b/app/client/src/ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx @@ -1,15 +1,18 @@ import React, { useState } from "react"; import { IDEToolbar, ToolbarSettingsPopover } from "IDE"; -import { JSFunctionRun } from "./components/JSFunctionRun"; -import type { JSActionDropdownOption, OnUpdateSettingsProps } from "./types"; +import { JSFunctionRun } from "pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionRun"; +import type { + JSActionDropdownOption, + OnUpdateSettingsProps, +} from "pages/Editor/JSEditor/JSEditorToolbar/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 { createMessage, JS_EDITOR_SETTINGS } from "ee/constants/messages"; -import { JSFunctionSettings } from "./components/JSFunctionSettings"; -import { convertJSActionsToDropdownOptions } from "./utils"; -import { JSObjectNameEditor } from "./JSObjectNameEditor"; +import { JSFunctionSettings } from "pages/Editor/JSEditor/JSEditorToolbar/components/JSFunctionSettings"; +import { convertJSActionsToDropdownOptions } from "pages/Editor/JSEditor/JSEditorToolbar/utils"; +import { JSObjectNameEditor } from "pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor"; interface Props { changePermitted: boolean; diff --git a/app/client/src/ee/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/ee/PluginActionEditor/components/PluginActionToolbar.tsx new file mode 100644 index 0000000000..940a4bc546 --- /dev/null +++ b/app/client/src/ee/PluginActionEditor/components/PluginActionToolbar.tsx @@ -0,0 +1 @@ +export * from "ce/PluginActionEditor/components/PluginActionToolbar"; diff --git a/app/client/src/ee/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx b/app/client/src/ee/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx new file mode 100644 index 0000000000..61e2cd75e2 --- /dev/null +++ b/app/client/src/ee/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar.tsx @@ -0,0 +1 @@ +export * from "ce/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar"; diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/constants.ts b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/constants.ts index 27f3945f41..eb1e9be1ec 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/constants.ts +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/constants.ts @@ -8,6 +8,7 @@ export const RUN_BUTTON_DEFAULTS = { export const testLocators = { runJSAction: "run-js-action", runJSActionTestID: "t--run-js-action", + generateSchemaJSActionTestID: "t--generate-schema-js-action", }; export const NO_FUNCTION_DROPDOWN_OPTION = { label: "No function available", diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/index.ts b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/index.ts index 88dc44b167..6ec1f8ded0 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/index.ts +++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/index.ts @@ -1,4 +1,4 @@ -export { JSEditorToolbar } from "./JSEditorToolbar"; +export { JSEditorToolbar } from "ee/pages/Editor/JSEditor/JSEditorToolbar/JSEditorToolbar"; export { type OnUpdateSettingsProps, diff --git a/app/client/src/reducers/uiReducers/jsPaneReducer.ts b/app/client/src/reducers/uiReducers/jsPaneReducer.ts index 79fbcb633b..d9bd4ea5f3 100644 --- a/app/client/src/reducers/uiReducers/jsPaneReducer.ts +++ b/app/client/src/reducers/uiReducers/jsPaneReducer.ts @@ -4,7 +4,7 @@ import { ReduxActionTypes, ReduxActionErrorTypes, } from "ee/constants/ReduxActionConstants"; -import type { JSCollection } from "entities/JSCollection"; +import type { JSAction, JSCollection } from "entities/JSCollection"; import { ActionExecutionResizerHeight } from "PluginActionEditor/components/PluginActionResponse/constants"; export enum JSEditorTab { @@ -23,6 +23,7 @@ export interface JsPaneReduxState { isSaving: Record; isDeleting: Record; isDirty: Record; + isSchemaGenerating: Record; selectedConfigTab: JSEditorTab; debugger: JSPaneDebuggerState; } @@ -32,6 +33,7 @@ const initialState: JsPaneReduxState = { isSaving: {}, isDeleting: {}, isDirty: {}, + isSchemaGenerating: {}, selectedConfigTab: JSEditorTab.CODE, debugger: { open: false, @@ -175,6 +177,50 @@ const jsPaneReducer = createReducer(initialState, { isSaving: false, }; }, + [ReduxActionTypes.GENERATE_JS_FUNCTION_SCHEMA_REQUEST]: ( + state: JsPaneReduxState, + action: ReduxAction<{ + action: JSAction; + }>, + ) => { + if (!action.payload.action.collectionId) return state; + + return { + ...state, + isSchemaGenerating: { + ...state.isSchemaGenerating, + [action.payload.action.collectionId]: true, + }, + }; + }, + [ReduxActionTypes.GENERATE_JS_FUNCTION_SCHEMA_SUCCESS]: ( + state: JsPaneReduxState, + action: ReduxAction<{ action: JSAction }>, + ) => { + if (!action.payload.action.collectionId) return state; + + return { + ...state, + isSchemaGenerating: { + ...state.isSchemaGenerating, + [action.payload.action.collectionId]: false, + }, + }; + }, + [ReduxActionErrorTypes.GENERATE_JS_FUNCTION_SCHEMA_ERROR]: ( + state: JsPaneReduxState, + action: ReduxAction<{ action: JSAction }>, + ) => { + if (!action.payload.action.collectionId) return state; + + return { + ...state, + isSchemaGenerating: { + ...state.isSchemaGenerating, + [action.payload.action.collectionId]: false, + }, + }; + }, }); export default jsPaneReducer; diff --git a/app/client/src/selectors/jsPaneSelectors.ts b/app/client/src/selectors/jsPaneSelectors.ts index a41c0d31c0..abb35e542c 100644 --- a/app/client/src/selectors/jsPaneSelectors.ts +++ b/app/client/src/selectors/jsPaneSelectors.ts @@ -22,3 +22,11 @@ export const getLastJSTab = (state: AppState): FocusEntityInfo | undefined => { return identifyEntityFromPath(urlWithoutQueryParams); } }; + +export const getIsGeneratingSchema = (state: AppState, collectionId: string) => + state.ui.jsPane.isSchemaGenerating[collectionId]; + +export const getIsJSCollectionSaving = ( + state: AppState, + collectionId: string, +) => state.ui.jsPane.isSaving[collectionId];