diff --git a/.github/workflows/appsmithctl.yml b/.github/workflows/appsmithctl.yml deleted file mode 100644 index 41a87e961ba6..000000000000 --- a/.github/workflows/appsmithctl.yml +++ /dev/null @@ -1,99 +0,0 @@ -# This workflow is responsible for building, testing & packaging the Appsmithctl CLI util -name: Build Appsmithctl CLI util Workflow - -on: - # This line enables manual triggering of this workflow. - workflow_dispatch: - workflow_call: - inputs: - pr: - description: "This is the PR number in case the workflow is being called in a pull request" - required: false - type: number - - pull_request: - branches: [release, master] - paths: - - "deploy/docker/fs/opt/appsmith/utils/**" - -# Change the working directory for all the jobs in this workflow -defaults: - run: - working-directory: deploy/docker/fs/opt/appsmith/utils/ - -jobs: - build: - runs-on: ubuntu-latest - # Only run this workflow for internally triggered events - if: | - github.event.pull_request.head.repo.full_name == github.repository || - github.event_name == 'push' || - github.event_name == 'workflow_dispatch' || - github.event_name == 'repository_dispatch' - - steps: - # The checkout steps MUST happen first because the default directory is set according to the code base. - # GitHub Action expects all future commands to be executed in the code directory. Hence, we need to check out - # the code before doing anything else. - - # Check out merge commit with the base branch in case this workflow is invoked via pull request - - name: Checkout the merged commit from PR and base branch - if: inputs.pr != 0 - uses: actions/checkout@v4 - with: - ref: refs/pull/${{ inputs.pr }}/merge - - # Checkout the code in the current branch in case the workflow is called because of a branch push event - - name: Checkout the head commit of the branch - if: inputs.pr == 0 - uses: actions/checkout@v4 - - - name: Figure out the PR number - run: echo ${{ inputs.pr }} - - - name: Print the Github event - run: echo ${{ github.event_name }} - - # In case this is second attempt try restoring status of the prior attempt from cache - - name: Restore the previous run result - uses: actions/cache@v4 - with: - path: | - ~/appsmithctl_run_result - key: ${{ github.run_id }}-${{ github.job }}-appsmithctl-util - - # Fetch prior run result - - name: Get the previous run result - id: appsmithctl_run_result - run: cat ~/appsmithctl_run_result 2>/dev/null || echo 'default' - - # In case of prior failure run the job - - if: steps.appsmithctl_run_result.outputs.appsmithctl_run_result != 'success' - run: echo "I'm alive!" && exit 0 - - - name: Use Node.js - if: steps.appsmithctl_run_result.outputs.appsmithctl_run_result != 'success' - uses: actions/setup-node@v4 - with: - node-version-file: app/client/package.json - - # Install all the dependencies - - name: Install dependencies - if: steps.appsmithctl_run_result.outputs.appsmithctl_run_result != 'success' - run: yarn install --immutable - - # Run the Jest tests only if the workflow has been invoked in a PR - - name: Run the jest tests - if: steps.appsmithctl_run_result.outputs.appsmithctl_run_result != 'success' - run: yarn run test - - # Set status = failure - - name: Set result as failed if there are build failures - if: failure() - run: | - echo "appsmithctl_run_result=failed" >> $GITHUB_OUTPUT > ~/appsmithctl_run_result - exit 1; - - # Set status = success - - name: Save the status of the run - run: echo "appsmithctl_run_result=success" >> $GITHUB_OUTPUT > ~/appsmithctl_run_result diff --git a/.github/workflows/docs/integration-tests-command.md b/.github/workflows/docs/integration-tests-command.md index af61be594b37..69234894af9a 100644 --- a/.github/workflows/docs/integration-tests-command.md +++ b/.github/workflows/docs/integration-tests-command.md @@ -22,10 +22,6 @@ The `client-build` job builds the client-side codebase. It uses the configuratio The `rts-build` job builds the "rts" (real-time suggestions) package of the client-side codebase. It uses the configuration defined in the `.github/workflows/rts-build.yml` file. -### `test-appsmithctl` - -The `test-appsmithctl` job runs tests for the `appsmithctl` utility. It uses the configuration from the `.github/workflows/appsmithctl.yml` file. - ### `build-docker-image` The `build-docker-image` job builds and pushes the Docker image for the application. It depends on the successful completion of the `client-build`, `server-build`, and `rts-build` jobs. The Docker image is built with support for both `linux/arm64` and `linux/amd64` platforms. diff --git a/.github/workflows/pr-cypress.yml b/.github/workflows/pr-cypress.yml index b64c4c6cf195..186ca9a63116 100644 --- a/.github/workflows/pr-cypress.yml +++ b/.github/workflows/pr-cypress.yml @@ -45,14 +45,6 @@ jobs: with: pr: ${{ github.event.number }} - test-appsmithctl: - if: success() - name: appsmithctl - uses: ./.github/workflows/appsmithctl.yml - secrets: inherit - with: - pr: ${{ github.event.number }} - build-docker-image: needs: [client-build, server-build, rts-build] # Only run if the build step is successful diff --git a/CODEOWNERS b/CODEOWNERS index 6f6f143cc2f3..2aead47b820c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -27,12 +27,12 @@ app/client/src/widgets/wds/* @appsmithorg/wds-team @appsmithorg/anvil-team app/client/src/layoutSystems/anvil/* @appsmithorg/anvil-team # App viewers pod -app/client/src/widgets/* @appsmithorg/app-viewers -app/client/src/components/propertyControls/* @appsmithorg/app-viewers -app/client/src/sagas/OneClickBindingSaga.ts @appsmithorg/app-viewers -app/client/src/WidgetQueryGenerators/* @appsmithorg/app-viewers -app/client/src/components/editorComponents/WidgetQueryGeneratorForm/* @appsmithorg/app-viewers -app/client/src/pages/AppViewer/* @appsmithorg/app-viewers +app/client/src/widgets/* @appsmithorg/widgets-blocks +app/client/src/components/propertyControls/* @appsmithorg/widgets-blocks +app/client/src/sagas/OneClickBindingSaga.ts @appsmithorg/widgets-blocks +app/client/src/WidgetQueryGenerators/* @appsmithorg/widgets-blocks +app/client/src/components/editorComponents/WidgetQueryGeneratorForm/* @appsmithorg/widgets-blocks +app/client/src/pages/AppViewer/* @appsmithorg/widgets-blocks # New Developers Pod app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/* @nilanshbansal @@ -195,6 +195,7 @@ app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/**/* @sh # DevOps & Shri deploy/**/* @sharat87 @pratapaprasanna .github/workflows/*.yml @sharat87 +app/client/packages/ctl/**/* @sharat87 @pratapaprasanna app/server/**/pom.xml @sharat87 app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/BaseAppsmithRepositoryCEImpl.java @sharat87 app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/params/QueryAllParams.java @sharat87 diff --git a/Dockerfile b/Dockerfile index be72a28da060..b5374338cf42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,17 +29,11 @@ COPY ./app/client/build editor/ # Add RTS - Application Layer COPY ./app/client/packages/rts/dist rts/ -ENV PATH /opt/bin:/opt/appsmith/utils/node_modules/.bin:/opt/java/bin:/opt/node/bin:$PATH +ENV PATH /opt/bin:/opt/java/bin:/opt/node/bin:$PATH RUN < { agHelper.GetNClick(propPane._navigateToType("URL")); agHelper.TypeText( propPane._actionSelectorFieldByLabel("Enter URL"), - "https://www.appsmith.com", + "https://www.google.com", ); agHelper.GetNClick(propPane._actionSelectorPopupClose); propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');}}`, ); // Add third action @@ -80,7 +80,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { // Validate the code propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, ); // Delete the third action @@ -97,7 +97,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { // Assert the code for the remaining actions propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, ); // Delete the first action @@ -113,7 +113,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { // Assert the code for the remaining actions propPane.ValidateJSFieldValue( "onClick", - `{{navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, + `{{navigateTo('https://www.google.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, ); }); @@ -132,13 +132,13 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { agHelper.GetNClick(propPane._navigateToType("URL")); agHelper.TypeText( propPane._actionSelectorFieldByLabel("Enter URL"), - "https://www.appsmith.com", + "https://www.google.com", ); agHelper.GetNClick(propPane._actionSelectorPopupClose); propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');}}`, ); // Add third action @@ -165,7 +165,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, ); // Delete the third action @@ -197,7 +197,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, ); }); @@ -216,13 +216,13 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { agHelper.GetNClick(propPane._navigateToType("URL")); agHelper.TypeText( propPane._actionSelectorFieldByLabel("Enter URL"), - "https://www.appsmith.com", + "https://www.google.com", ); agHelper.GetNClick(propPane._actionSelectorPopupClose); propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');}}`, ); // Add third action @@ -249,7 +249,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { propPane.ValidateJSFieldValue( "onClick", - `{{showAlert('Hello!', '');navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, + `{{showAlert('Hello!', '');navigateTo('https://www.google.com', {}, 'SAME_WINDOW');storeValue('secret-key', 'secret-value');copyToClipboard('text to copy');}}`, ); // Delete the third action @@ -286,7 +286,7 @@ describe("UI to Code", { tags: ["@tag.JS", "@tag.Binding"] }, () => { propPane.ValidateJSFieldValue( "onClick", - `{{navigateTo('https://www.appsmith.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, + `{{navigateTo('https://www.google.com', {}, 'SAME_WINDOW');copyToClipboard('text to copy');}}`, ); }); diff --git a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts index 94d2acc069d9..ee10834b2a82 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OneClickBinding/PropertyControl_spec.ts @@ -149,7 +149,7 @@ describe( propPane.MoveToTab("Content"); [1, 2, 3, 4, 5].forEach(() => { - apiPage.CreateAndFillApi("https://www.appsmith.com/"); + apiPage.CreateAndFillApi("https://www.google.com/"); }); EditorNavigation.SelectEntityByName("Table1", EntityType.Widget); diff --git a/app/client/cypress/e2e/Regression/ServerSide/ActionExecution/Block_Execution.ts b/app/client/cypress/e2e/Regression/ServerSide/ActionExecution/Block_Execution.ts index a77b917e526c..cd65a25a8440 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/ActionExecution/Block_Execution.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/ActionExecution/Block_Execution.ts @@ -9,7 +9,7 @@ describe( "Block Action Execution when no field is present", { tags: ["@tag.Datasource"] }, () => { - const url = "https://www.appsmith.com/"; + const url = "https://www.google.com/"; it("1. Ensure API Run button is disabled when no url is present", () => { apiPage.CreateApi("FirstAPI", "GET"); apiPage.AssertRunButtonDisability(true); diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts index e174cd171cfa..e19bc35a8463 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts @@ -47,6 +47,7 @@ describe( dataSources._dropdownOption, "worldCountryInfo", ); + agHelper.GetNClick(dataSources._generatePageBtn); GenerateCRUDNValidateDeployPage("ABW", "Aruba", "North America", "Code"); @@ -93,6 +94,7 @@ describe( assertHelper.AssertNetworkStatus("@getDatasourceStructure"); //Making sure table dropdown is populated agHelper.GetNClick(dataSources._selectTableDropdown, 0, true); agHelper.GetNClickByContains(dataSources._dropdownOption, "customers"); + agHelper.GetNClick(dataSources._generatePageBtn); GenerateCRUDNValidateDeployPage( "103", @@ -110,6 +112,7 @@ describe( it("3. Generate CRUD page from datasource present in ACTIVE section", function () { EditorNavigation.SelectEntityByName(dsName, EntityType.Datasource); dataSources.SelectTableFromPreviewSchemaList("employees"); + agHelper.GetNClick(dataSources._datasourceCardGeneratePageBtn); GenerateCRUDNValidateDeployPage( "1002", @@ -311,9 +314,6 @@ describe( col3Text: string, jsonFromHeader: string, ) { - agHelper.GetNClick( - `${dataSources._generatePageBtn}, ${dataSources._datasourceCardGeneratePageBtn}`, - ); assertHelper.AssertNetworkStatus("@replaceLayoutWithCRUDPage", 201); agHelper.AssertContains("Successfully generated a page"); //assertHelper.AssertNetworkStatus("@getActions", 200);//Since failing sometimes diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts index ce5773313e82..7cc2f30a82c7 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts @@ -399,9 +399,7 @@ describe( col3Text: string, jsonFromHeader: string, ) { - agHelper.GetNClick( - `${dataSources._generatePageBtn}, ${dataSources._datasourceCardGeneratePageBtn}`, - ); + agHelper.GetNClick(dataSources._datasourceCardGeneratePageBtn); assertHelper.AssertNetworkStatus("@replaceLayoutWithCRUDPage", 201); agHelper.AssertContains("Successfully generated a page"); //assertHelper.AssertNetworkStatus("@getActions", 200);//Since failing sometimes diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres1_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres1_Spec.ts index 8177679f27db..292e4289d05e 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres1_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres1_Spec.ts @@ -38,6 +38,7 @@ describe( assertHelper.AssertNetworkStatus("@getDatasourceStructure"); //Making sure table dropdown is populated agHelper.GetNClick(dataSources._selectTableDropdown, 0, true); agHelper.GetNClickByContains(dataSources._dropdownOption, "film"); + agHelper.GetNClick(dataSources._generatePageBtn); GenerateCRUDNValidateDeployPage( "ACADEMY DINOSAUR", @@ -86,6 +87,7 @@ describe( assertHelper.AssertNetworkStatus("@getDatasourceStructure"); //Making sure table dropdown is populated agHelper.GetNClick(dataSources._selectTableDropdown, 0, true); agHelper.GetNClickByContains(dataSources._dropdownOption, "suppliers"); + agHelper.GetNClick(dataSources._generatePageBtn); GenerateCRUDNValidateDeployPage( "Exotic Liquids", @@ -104,6 +106,7 @@ describe( it("3. Generate CRUD page from datasource present in ACTIVE section", function () { EditorNavigation.SelectEntityByName(dsName, EntityType.Datasource); dataSources.SelectTableFromPreviewSchemaList("public.orders"); + agHelper.GetNClick(dataSources._datasourceCardGeneratePageBtn); GenerateCRUDNValidateDeployPage( "VINET", @@ -135,9 +138,6 @@ describe( col3Text: string, jsonFromHeader: string, ) { - agHelper.GetNClick( - `${dataSources._generatePageBtn}, ${dataSources._datasourceCardGeneratePageBtn}`, - ); assertHelper.AssertNetworkStatus("@replaceLayoutWithCRUDPage", 201); agHelper.AssertContains("Successfully generated a page"); //assertHelper.AssertNetworkStatus("@getActions", 200);//Since failing sometimes diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/S3_Spec.js b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/S3_Spec.js index cb1be5c1cd5b..97a620c1060c 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/S3_Spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/S3_Spec.js @@ -52,7 +52,7 @@ describe( 200, ); - agHelper.AssertContains("Generate from data"); + agHelper.AssertContains("Generate a page based on your data"); agHelper.GetNClick(generatePage.selectTableDropdown); agHelper.GetNClickByContains( generatePage.dropdownOption, diff --git a/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/APIOnLoad_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/APIOnLoad_Spec.ts index f5fe03d92b18..c1b71039bdbb 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/APIOnLoad_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/OnLoadTests/APIOnLoad_Spec.ts @@ -46,7 +46,7 @@ describe( it("2. Shows when API failed to load on page load.", function () { cy.fixture("testdata").then(function (dataSet: any) { apiPage.CreateAndFillApi( - "https://www.appsmith.com/" + dataSet.methods, + "https://www.google.com/" + dataSet.methods, "PageLoadApi2", ); }); diff --git a/app/client/packages/design-system/widgets/src/components/Sheet/src/Sheet.tsx b/app/client/packages/design-system/widgets/src/components/Sheet/src/Sheet.tsx index 3b5261f223a8..1b19dc7ca10b 100644 --- a/app/client/packages/design-system/widgets/src/components/Sheet/src/Sheet.tsx +++ b/app/client/packages/design-system/widgets/src/components/Sheet/src/Sheet.tsx @@ -15,7 +15,9 @@ export function _Sheet(props: SheetProps, ref: Ref) { children, className, isOpen, + onEnter, onEntered, + onExit, onExited, onOpenChange, position = "start", @@ -31,7 +33,9 @@ export function _Sheet(props: SheetProps, ref: Ref) { void; onEntered?: () => void; + onExit?: () => void; onExited?: () => void; } diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/src/Sidebar.tsx b/app/client/packages/design-system/widgets/src/components/Sidebar/src/Sidebar.tsx index c8b231cef3cb..60b628f8daec 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/src/Sidebar.tsx +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/src/Sidebar.tsx @@ -1,30 +1,65 @@ import clsx from "clsx"; import * as React from "react"; -import { type Ref, useRef } from "react"; +import { type Ref, useRef, useState } from "react"; +import { CSSTransition } from "react-transition-group"; + import { Sheet } from "../../Sheet"; -import { useSidebar } from "./use-sidebar"; import styles from "./styles.module.css"; +import { useSidebar } from "./use-sidebar"; import type { SidebarProps } from "./types"; -import { CSSTransition } from "react-transition-group"; + +import { SidebarContent } from "./SidebarContent"; const _Sidebar = (props: SidebarProps, ref: Ref) => { const { children, className, collapsible = "offcanvas", - onEntered, - onExited, + onEnter: onEnterProp, + onEntered: onEnteredProp, + onExit: onExitProp, + onExited: onExitedProp, side = "start", + title, variant = "sidebar", ...rest } = props; - const { isMobile, setOpen, state } = useSidebar(); + const [isAnimating, setIsAnimating] = useState(false); + const { isMobile, setState, state } = useSidebar(); const sidebarRef = useRef(); + const onEnter = () => { + setIsAnimating(true); + onEnterProp?.(); + }; + + const onEntered = () => { + setIsAnimating(false); + onEnteredProp?.(); + }; + + const onExit = () => { + setIsAnimating(true); + onExitProp?.(); + }; + + const onExited = () => { + setIsAnimating(false); + onExitedProp?.(); + }; + + const content = ( + + {typeof children === "function" + ? children({ isAnimating, state }) + : children} + + ); + if (collapsible === "none") { return (
- {children} + {content}
); } @@ -33,38 +68,52 @@ const _Sidebar = (props: SidebarProps, ref: Ref) => { return ( setState(isOpen ? "expanded" : "collapsed")} position={side} > - {children} + {content} ); } return ( -
-
-
-
{children}
+
+
+
+
{content}
+
-
+ ); }; diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarContent.tsx b/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarContent.tsx index bb90417a91b1..f72ee85dc5c9 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarContent.tsx +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarContent.tsx @@ -1,13 +1,24 @@ import clsx from "clsx"; -import React, { type ComponentProps, type Ref } from "react"; +import React, { type Ref } from "react"; +import { Flex } from "../../Flex"; +import { Text } from "../../Text"; +import { Button } from "../../Button"; import styles from "./styles.module.css"; +import { useSidebar } from "./use-sidebar"; + +interface SidebarContentProps { + title?: string; + className?: string; + children: React.ReactNode; +} const _SidebarContent = ( - props: ComponentProps<"div">, + props: SidebarContentProps, ref: Ref, ) => { - const { className, ...rest } = props; + const { children, className, title, ...rest } = props; + const { isMobile, setState, state } = useSidebar(); return (
+ > + + + {Boolean(title) && {title}} + {!isMobile && ( +
); }; diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarProvider.tsx b/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarProvider.tsx index 35d688f23837..16dcc26a5a85 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarProvider.tsx +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/src/SidebarProvider.tsx @@ -1,11 +1,15 @@ import clsx from "clsx"; import React, { type Ref, useCallback, useState } from "react"; +import type { + SidebarContextType, + SidebarProviderProps, + SidebarState, +} from "./types"; import styles from "./styles.module.css"; import { SidebarContext } from "./context"; import { useIsMobile } from "./use-mobile"; import { SIDEBAR_CONSTANTS } from "./constants"; -import type { SidebarContextType, SidebarProviderProps } from "./types"; export const _SidebarProvider = ( props: SidebarProviderProps, @@ -14,32 +18,32 @@ export const _SidebarProvider = ( const { children, className, - defaultOpen = true, - isOpen: openProp, - onOpen: setOpenProp, + defaultState = "expanded", + onStateChange: setStateProp, + state: stateProp, style, ...rest } = props; const isMobile = useIsMobile(); - const [_open, _setOpen] = useState(defaultOpen); - const open = openProp ?? _open; - const setOpen = useCallback( - (value: boolean | ((value: boolean) => boolean)) => { - const openState = typeof value === "function" ? value(open) : value; + const [_state, _setState] = useState(defaultState); + const state = stateProp ?? _state; + const setState = useCallback( + (value: SidebarState | ((value: SidebarState) => SidebarState)) => { + const computedState = typeof value === "function" ? value(state) : value; - if (setOpenProp) { - setOpenProp(openState); + if (setStateProp) { + setStateProp(computedState); } else { - _setOpen(openState); + _setState(computedState); } }, - [setOpenProp, open], + [setStateProp, state], ); const toggleSidebar = React.useCallback(() => { - return isMobile ? setOpen((open) => !open) : setOpen((open) => !open); - }, [isMobile, setOpen]); + return state === "collapsed" ? setState("expanded") : setState("collapsed"); + }, [setState, state]); React.useEffect( function handleKeyboardShortcuts() { @@ -60,17 +64,14 @@ export const _SidebarProvider = ( [toggleSidebar, isMobile], ); - const state = open ? "expanded" : "collapsed"; - const contextValue = React.useMemo( () => ({ state, - open, - setOpen, + setState, isMobile, toggleSidebar, }), - [state, open, setOpen, isMobile, toggleSidebar], + [state, setState, isMobile, toggleSidebar], ); return ( diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/src/styles.module.css b/app/client/packages/design-system/widgets/src/components/Sidebar/src/styles.module.css index e3fe71fce134..91c640d923b5 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/src/styles.module.css +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/src/styles.module.css @@ -37,7 +37,7 @@ height: 100%; width: var(--sidebar-width); background-color: transparent; - transition: width 300ms linear; + transition: width 300ms ease-in-out; } .mainSidebar[data-side="right"] .fakeSidebar { @@ -76,9 +76,14 @@ height: 100%; width: var(--sidebar-width); transition: - left 300ms linear, - right 300ms linear, - width 300ms linear; + left 300ms ease-in-out, + right 300ms ease-in-out, + width 300ms ease-in-out; + background-color: var(--color-bg-elevation-2); +} + +[data-state="full-width"] .sidebar { + width: 100%; } @container (min-width: 768px) { @@ -131,6 +136,20 @@ border-inline-start: var(--border-width-1) solid var(--color-bd-elevation-1); } +.mainSidebar[data-state="full-width"][data-side="start"]:is( + [data-variant="sidebar"] + ) + .sidebar { + border-inline-end: none; +} + +.mainSidebar[data-state="full-width"][data-side="end"]:is( + [data-variant="sidebar"] + ) + .sidebar { + border-inline-start: none; +} + /** *----------------------------------------------------- * SIDEBAR CONTAINER @@ -166,6 +185,13 @@ height: 100%; } +.sidebarContentInner { + flex-grow: 1; + overflow-y: auto; + overflow-x: hidden; + width: 100%; +} + /** *----------------------------------------------------- * SIDEBAR INSET @@ -201,3 +227,12 @@ margin-left: 0; } } + +/** + *----------------------------------------------------- + * SIDEBAR HEADER + *----------------------------------------------------- + */ +.sidebarHeader { + border-bottom: var(--border-width-1) solid var(--color-bd-elevation-1); +} diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/src/types.ts b/app/client/packages/design-system/widgets/src/components/Sidebar/src/types.ts index 1d4e130126e5..00f84b13757a 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/src/types.ts +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/src/types.ts @@ -1,28 +1,37 @@ import type { ReactNode } from "react"; export interface SidebarContextType { - state: "expanded" | "collapsed"; - open: boolean; - setOpen: (open: boolean) => void; + state: SidebarState; + setState: (state: SidebarState) => void; isMobile: boolean; toggleSidebar: () => void; } export interface SidebarProviderProps { - defaultOpen?: boolean; - isOpen?: boolean; - onOpen?: (open: boolean) => void; + defaultState?: SidebarState; + state?: SidebarState; + onStateChange?: (state: SidebarState) => void; children: ReactNode; className?: string; style?: React.CSSProperties; } +export type SidebarState = "collapsed" | "expanded" | "full-width"; + export interface SidebarProps { side?: "start" | "end"; variant?: "sidebar" | "floating" | "inset"; collapsible?: "offcanvas" | "icon" | "none"; + onEnter?: () => void; + onExit?: () => void; onEntered?: () => void; onExited?: () => void; - children: ReactNode; + children: + | React.ReactNode + | ((props: { + isAnimating: boolean; + state: SidebarState; + }) => React.ReactNode); className?: string; + title?: string; } diff --git a/app/client/packages/design-system/widgets/src/components/Sidebar/stories/Sidebar.stories.tsx b/app/client/packages/design-system/widgets/src/components/Sidebar/stories/Sidebar.stories.tsx index 0aa33cfc529e..b460c3c4740e 100644 --- a/app/client/packages/design-system/widgets/src/components/Sidebar/stories/Sidebar.stories.tsx +++ b/app/client/packages/design-system/widgets/src/components/Sidebar/stories/Sidebar.stories.tsx @@ -2,7 +2,6 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { Sidebar, - SidebarContent, SidebarTrigger, SidebarProvider, SidebarInset, @@ -23,6 +22,7 @@ const meta: Meta = { }, }, args: { + title: "Sidebar", side: "start", variant: "sidebar", }, @@ -34,9 +34,7 @@ const meta: Meta = { }} > - - - + @@ -123,3 +121,46 @@ export const VariantInset: Story = { ), }; + +export const WithRenderProps: Story = { + render: (args) => ( + + + {({ isAnimating, state }) => ( + + + Sidebar State + + + + + + {state} + + {isAnimating ? "(Animating)" : ""} + + + + + + )} + + + + + + + + ), +}; diff --git a/app/client/packages/rts/build.js b/app/client/packages/rts/build.js index e645a0f7ff4a..414147574c65 100644 --- a/app/client/packages/rts/build.js +++ b/app/client/packages/rts/build.js @@ -44,7 +44,7 @@ const getWorkflowDependencies = () => { const bundle = async () => { return esbuild .build({ - entryPoints: ["src/server.ts"], + entryPoints: ["src/server.ts", "src/ctl/index.js"], bundle: true, sourcemap: true, platform: "node", diff --git a/app/client/packages/rts/package.json b/app/client/packages/rts/package.json index a52d4bf06803..28c80dda5bbe 100644 --- a/app/client/packages/rts/package.json +++ b/app/client/packages/rts/package.json @@ -23,10 +23,14 @@ "@opentelemetry/semantic-conventions": "^1.27.0", "@shared/ast": "workspace:^", "axios": "^1.7.4", + "dotenv": "10.0.0", "express": "^4.20.0", "express-validator": "^6.14.2", "http-status-codes": "^2.2.0", "loglevel": "^1.8.1", + "mongodb": "^5.8.0", + "nodemailer": "6.9.9", + "readline-sync": "1.4.10", "socket.io": "^4.6.2" }, "devDependencies": { diff --git a/app/client/packages/rts/src/ctl/.eslintrc.json b/app/client/packages/rts/src/ctl/.eslintrc.json new file mode 100644 index 000000000000..91a11ef80351 --- /dev/null +++ b/app/client/packages/rts/src/ctl/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "extends": ["../../../../.eslintrc.base.json"], + "rules": { + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/strict-boolean-expressions": "off", + "@typescript-eslint/no-explicit-any": "off", + "testing-library/no-debugging-utils": "off", + "@typescript-eslint/no-var-requires": "off", + "padding-line-between-statements": "off", + "no-console": "off", + "@typescript-eslint/promise-function-async": "off", + "@typescript-eslint/no-unused-vars": "off", + "sort-destructure-keys/sort-destructure-keys": "off" + } +} diff --git a/app/client/packages/rts/src/ctl/README.md b/app/client/packages/rts/src/ctl/README.md new file mode 100644 index 000000000000..e41fc9f9851c --- /dev/null +++ b/app/client/packages/rts/src/ctl/README.md @@ -0,0 +1,3 @@ +# `appsmithctl` + +This is the source for the `appsmithctl` command in Appsmith containers. diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/backup.js b/app/client/packages/rts/src/ctl/backup.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/backup.js rename to app/client/packages/rts/src/ctl/backup.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/backup.test.js b/app/client/packages/rts/src/ctl/backup.test.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/backup.test.js rename to app/client/packages/rts/src/ctl/backup.test.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/check_replica_set.js b/app/client/packages/rts/src/ctl/check_replica_set.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/check_replica_set.js rename to app/client/packages/rts/src/ctl/check_replica_set.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/constants.js b/app/client/packages/rts/src/ctl/constants.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/constants.js rename to app/client/packages/rts/src/ctl/constants.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/export_db.js b/app/client/packages/rts/src/ctl/export_db.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/export_db.js rename to app/client/packages/rts/src/ctl/export_db.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/import_db.js b/app/client/packages/rts/src/ctl/import_db.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/import_db.js rename to app/client/packages/rts/src/ctl/import_db.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/index.js b/app/client/packages/rts/src/ctl/index.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/index.js rename to app/client/packages/rts/src/ctl/index.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/logger.js b/app/client/packages/rts/src/ctl/logger.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/logger.js rename to app/client/packages/rts/src/ctl/logger.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/mailer.js b/app/client/packages/rts/src/ctl/mailer.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/mailer.js rename to app/client/packages/rts/src/ctl/mailer.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/mongo_shell_utils.js b/app/client/packages/rts/src/ctl/mongo_shell_utils.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/mongo_shell_utils.js rename to app/client/packages/rts/src/ctl/mongo_shell_utils.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/move-to-postgres.mjs b/app/client/packages/rts/src/ctl/move-to-postgres.mjs similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/move-to-postgres.mjs rename to app/client/packages/rts/src/ctl/move-to-postgres.mjs diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/restore.js b/app/client/packages/rts/src/ctl/restore.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/restore.js rename to app/client/packages/rts/src/ctl/restore.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/utils.js b/app/client/packages/rts/src/ctl/utils.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/utils.js rename to app/client/packages/rts/src/ctl/utils.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/utils.test.js b/app/client/packages/rts/src/ctl/utils.test.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/utils.test.js rename to app/client/packages/rts/src/ctl/utils.test.js diff --git a/deploy/docker/fs/opt/appsmith/utils/bin/version.js b/app/client/packages/rts/src/ctl/version.js similarity index 100% rename from deploy/docker/fs/opt/appsmith/utils/bin/version.js rename to app/client/packages/rts/src/ctl/version.js diff --git a/app/client/src/ce/RouteBuilder.ts b/app/client/src/ce/RouteBuilder.ts index b8df54bcd135..d61b619d4c45 100644 --- a/app/client/src/ce/RouteBuilder.ts +++ b/app/client/src/ce/RouteBuilder.ts @@ -1,8 +1,6 @@ import { ADD_PATH, ADMIN_SETTINGS_PATH, - GEN_TEMPLATE_FORM_ROUTE, - GEN_TEMPLATE_URL, getViewerCustomPath, getViewerPath, TEMPLATES_PATH, @@ -122,12 +120,6 @@ export const saasEditorApiIdURL = ( }`, }); -export const generateTemplateFormURL = (props: URLBuilderParams): string => - urlBuilder.build({ - ...props, - suffix: `${GEN_TEMPLATE_URL}${GEN_TEMPLATE_FORM_ROUTE}`, - }); - export const onboardingCheckListUrl = (props: URLBuilderParams): string => urlBuilder.build({ ...props, diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index a4ef8ae1d025..0ecaa63d846e 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -1070,6 +1070,17 @@ const CurlImportActionErrorTypes = { SUBMIT_CURL_FORM_ERROR: "SUBMIT_CURL_FORM_ERROR", }; +const GeneratePageActionTypes = { + SET_GENERATE_PAGE_MODAL_OPEN: "SET_GENERATE_PAGE_MODAL_OPEN", + SET_GENERATE_PAGE_MODAL_CLOSE: "SET_GENERATE_PAGE_MODAL_CLOSE", + SUBMIT_GENERATE_PAGE_FORM_INIT: "SUBMIT_GENERATE_PAGE_FORM_INIT", + SUBMIT_GENERATE_PAGE_FORM_SUCCESS: "SUBMIT_GENERATE_PAGE_FORM_SUCCESS", +}; + +const GeneratePageActionErrorTypes = { + SUBMIT_GENERATE_PAGE_FORM_ERROR: "SUBMIT_GENERATE_PAGE_FORM_ERROR", +}; + const BatchUpdateActionTypes = { BATCHED_UPDATE: "BATCHED_UPDATE", EXECUTE_BATCH: "EXECUTE_BATCH", @@ -1276,12 +1287,13 @@ export const ReduxActionTypes = { ...AppSettingsActionTypes, ...BatchUpdateActionTypes, ...BuildingBlocksActionTypes, - ...DatasourceEditorActionTypes, ...CurlImportActionTypes, + ...DatasourceEditorActionTypes, ...ErrorManagementActionTypes, ...ExplorerActionTypes, ...EvaluationActionTypes, ...FeatureFlagActionTypes, + ...GeneratePageActionTypes, ...GitActionTypes, ...HelpActionTypes, ...IDEActionTypes, @@ -1324,6 +1336,7 @@ export const ReduxActionErrorTypes = { ...DatasourceEditorActionErrorTypes, ...EvaluationActionErrorTypes, ...FeatureFlagActionErrorTypes, + ...GeneratePageActionErrorTypes, ...GitActionErrorTypes, ...IDEActionErrorTypes, ...ImportExportActionErrorTypes, diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index b5a102028ede..f525c40f0d38 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -752,7 +752,10 @@ export const BUILD_FROM_SCRATCH_ACTION_TITLE = () => "Build with drag & drop"; export const GENERATE_PAGE_ACTION_TITLE = () => "Generate page with data"; -export const GENERATE_PAGE_FORM_TITLE = () => "Generate from data"; +export const GENERATE_PAGE_FORM_TITLE = () => + "Generate a page based on your data"; +export const GENERATE_PAGE_FORM_SUB_TITLE = () => + "Use your datasource's schema to generate a simple CRUD page."; export const GEN_CRUD_SUCCESS_MESSAGE = () => "Hurray! Your application is ready for use."; diff --git a/app/client/src/ce/constants/routes/appRoutes.ts b/app/client/src/ce/constants/routes/appRoutes.ts index 8031209435be..f4b2465b6f2d 100644 --- a/app/client/src/ce/constants/routes/appRoutes.ts +++ b/app/client/src/ce/constants/routes/appRoutes.ts @@ -70,10 +70,6 @@ export const APP_LIBRARIES_EDITOR_PATH = `/libraries`; export const APP_PACKAGES_EDITOR_PATH = `/packages`; export const APP_SETTINGS_EDITOR_PATH = `/settings`; export const SAAS_GSHEET_EDITOR_ID_PATH = `/saas/google-sheets-plugin/datasources/:datasourceId`; -export const GEN_TEMPLATE_URL = "generate-page"; -export const GENERATE_TEMPLATE_PATH = `/${GEN_TEMPLATE_URL}`; -export const GEN_TEMPLATE_FORM_ROUTE = "/form"; -export const GENERATE_TEMPLATE_FORM_PATH = `${GENERATE_TEMPLATE_PATH}${GEN_TEMPLATE_FORM_ROUTE}`; export const BUILDER_CHECKLIST_PATH = `/checklist`; export const ADMIN_SETTINGS_PATH = "/settings"; export const ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH = "/settings/general"; @@ -124,10 +120,6 @@ export const matchViewerForkPath = (pathName: string) => match(`${VIEWER_PATH}${VIEWER_FORK_PATH}`)(pathName) || match(`${VIEWER_CUSTOM_PATH}${VIEWER_FORK_PATH}`)(pathName) || match(`${VIEWER_PATH_DEPRECATED}${VIEWER_FORK_PATH}`)(pathName); -export const matchGeneratePagePath = (pathName: string) => - match(`${BUILDER_PATH}${GENERATE_TEMPLATE_FORM_PATH}`)(pathName) || - match(`${BUILDER_CUSTOM_PATH}${GENERATE_TEMPLATE_FORM_PATH}`)(pathName) || - match(`${BUILDER_PATH_DEPRECATED}${GENERATE_TEMPLATE_FORM_PATH}`)(pathName); export const matchAppLibrariesPath = (pathName: string) => match(`${BUILDER_PATH}${APP_LIBRARIES_EDITOR_PATH}`)(pathName); diff --git a/app/client/src/ce/hooks/datasourceEditorHooks.tsx b/app/client/src/ce/hooks/datasourceEditorHooks.tsx index d0e9fdc54813..5dde79d1f35d 100644 --- a/app/client/src/ce/hooks/datasourceEditorHooks.tsx +++ b/app/client/src/ce/hooks/datasourceEditorHooks.tsx @@ -1,4 +1,3 @@ -import { generateTemplateFormURL } from "ee/RouteBuilder"; import { GENERATE_NEW_PAGE_BUTTON_TEXT, createMessage, @@ -18,7 +17,7 @@ import type { ApiDatasourceForm } from "entities/Datasource/RestAPIForm"; import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton"; import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks"; import React from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { getCurrentApplicationId, getCurrentBasePageId, @@ -26,10 +25,10 @@ import { } from "selectors/editorSelectors"; import { getIsAnvilEnabledInCurrentApplication } from "layoutSystems/anvil/integrations/selectors"; import { isEnabledForPreviewData } from "utils/editorContextUtils"; -import history from "utils/history"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { EditorNames } from "./"; import { getCurrentApplication } from "ee/selectors/applicationSelectors"; +import { openGeneratePageModal } from "pages/Editor/GeneratePage/store/generatePageActions"; export interface HeaderActionProps { datasource: Datasource | ApiDatasourceForm | undefined; @@ -47,7 +46,7 @@ export const useHeaderActions = ( showReconnectButton = false, }: HeaderActionProps, ) => { - const basePageId = useSelector(getCurrentBasePageId); + const dispatch = useDispatch(); const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); const releaseDragDropBuildingBlocks = useFeatureFlag( FEATURE_FLAG.release_drag_drop_building_blocks_enabled, @@ -97,13 +96,10 @@ export const useHeaderActions = ( } AnalyticsUtil.logEvent("DATASOURCE_CARD_GEN_CRUD_PAGE_ACTION"); - history.push( - generateTemplateFormURL({ - basePageId, - params: { - datasourceId: (datasource as Datasource).id, - new_page: true, - }, + dispatch( + openGeneratePageModal({ + datasourceId: (datasource as Datasource).id, + new_page: true, }), ); }; diff --git a/app/client/src/ce/pages/Editor/IDE/MainPane/useRoutes.tsx b/app/client/src/ce/pages/Editor/IDE/MainPane/useRoutes.tsx index d0fdc4ecda05..cac3ae6f84a8 100644 --- a/app/client/src/ce/pages/Editor/IDE/MainPane/useRoutes.tsx +++ b/app/client/src/ce/pages/Editor/IDE/MainPane/useRoutes.tsx @@ -12,7 +12,6 @@ import { BUILDER_PATH_DEPRECATED, DATA_SOURCES_EDITOR_ID_PATH, DATA_SOURCES_EDITOR_LIST_PATH, - GENERATE_TEMPLATE_FORM_PATH, INTEGRATION_EDITOR_PATH, JS_COLLECTION_EDITOR_PATH, JS_COLLECTION_ID_PATH, @@ -33,7 +32,6 @@ import { import DatasourceForm from "pages/Editor/SaaSEditor/DatasourceForm"; import DataSourceEditor from "pages/Editor/DataSourceEditor"; import DatasourceBlankState from "pages/Editor/DataSourceEditor/DatasourceBlankState"; -import GeneratePage from "pages/Editor/GeneratePage"; import type { RouteProps } from "react-router"; import { useSelector } from "react-redux"; import { combinedPreviewModeSelector } from "selectors/editorSelectors"; @@ -139,12 +137,6 @@ function useRoutes(path: string): RouteReturnType[] { exact: true, path: `${path}${SAAS_EDITOR_DATASOURCE_ID_PATH}`, }, - { - key: "GeneratePage", - component: isPreviewMode ? WidgetsEditor : GeneratePage, - exact: true, - path: `${path}${GENERATE_TEMPLATE_FORM_PATH}`, - }, ]; } diff --git a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts index 07c0dc443c9c..381429411c13 100644 --- a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts +++ b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.test.ts @@ -258,6 +258,57 @@ describe("getLintAnnotations()", () => { }, ]); }); + + it("should use provided lintlength when available", () => { + const value = `{{ variable }}`; + const errors: LintError[] = [ + { + errorType: PropertyEvaluationErrorType.LINT, + raw: "variable", + severity: Severity.WARNING, + errorMessage: { + name: "LintingError", + message: "Lint error message.", + }, + errorSegment: "variable", + originalBinding: "variable", + variables: ["variable"], + code: "W001", + line: 0, + ch: 3, + lintLength: 8, // Provided lint length + }, + ]; + + const annotations = getLintAnnotations(value, errors, {}); + + expect(annotations[0].to?.ch).toBe(13); + }); + + it("should calculate lintlength when not provided", () => { + const value = `{{ variable }}`; + const errors: LintError[] = [ + { + errorType: PropertyEvaluationErrorType.LINT, + raw: "variable", + severity: Severity.WARNING, + errorMessage: { + name: "LintingError", + message: "Lint error message.", + }, + errorSegment: "variable", + originalBinding: "variable", + variables: ["variable"], + code: "W001", + line: 0, + ch: 3, + }, + ]; + + const annotations = getLintAnnotations(value, errors, {}); + + expect(annotations[0].to?.ch).toBe(13); + }); }); describe("getFirstNonEmptyPosition", () => { diff --git a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts index 65a7e34c27a0..125e41ebaf8c 100644 --- a/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts +++ b/app/client/src/components/editorComponents/CodeEditor/lintHelpers.ts @@ -135,6 +135,7 @@ export const getLintAnnotations = ( code, errorMessage, line, + lintLength, originalBinding, severity, variables, @@ -157,16 +158,20 @@ export const getLintAnnotations = ( }); } - let variableLength = 1; + let calculatedLintLength = 1; + // If lint length is provided, then skip the length calculation logic + if (lintLength && lintLength > 0) { + calculatedLintLength = lintLength; + } // Find the variable with minimal length - if (variables) { + else if (variables) { for (const variable of variables) { if (variable) { - variableLength = - variableLength === 1 + calculatedLintLength = + calculatedLintLength === 1 ? String(variable).length - : Math.min(String(variable).length, variableLength); + : Math.min(String(variable).length, calculatedLintLength); } } } @@ -205,7 +210,7 @@ export const getLintAnnotations = ( }; const to = { line: from.line, - ch: from.ch + variableLength, + ch: from.ch + calculatedLintLength, }; annotations.push({ diff --git a/app/client/src/components/editorComponents/GlobalSearch/utils.tsx b/app/client/src/components/editorComponents/GlobalSearch/utils.tsx index f32a6c644075..4413d68053e6 100644 --- a/app/client/src/components/editorComponents/GlobalSearch/utils.tsx +++ b/app/client/src/components/editorComponents/GlobalSearch/utils.tsx @@ -28,7 +28,7 @@ import { createNewAPIBasedOnParentEntity, createNewJSCollectionBasedOnParentEntity, } from "ee/actions/helpers"; -import { openCurlImportModal } from "pages/Editor/CurlImport/helpers"; +import { openCurlImportModal } from "pages/Editor/CurlImport/store/curlImportActions"; export type SelectEvent = | React.MouseEvent diff --git a/app/client/src/pages/Editor/CurlImport/ModalControls.tsx b/app/client/src/pages/Editor/CurlImport/ModalControls.tsx index df0e15ed55b3..79ff227c6818 100644 --- a/app/client/src/pages/Editor/CurlImport/ModalControls.tsx +++ b/app/client/src/pages/Editor/CurlImport/ModalControls.tsx @@ -6,7 +6,10 @@ import { } from "selectors/curlImportSelectors"; import { submit } from "redux-form"; import { CURL_IMPORT_FORM } from "ee/constants/forms"; -import { closeCurlImportModal, openCurlImportModal } from "./helpers"; +import { + closeCurlImportModal, + openCurlImportModal, +} from "./store/curlImportActions"; import CurlLogo from "assets/images/Curl-logo.svg"; import { createMessage, IMPORT_BTN_LABEL } from "ee/constants/messages"; import { diff --git a/app/client/src/pages/Editor/CurlImport/helpers.ts b/app/client/src/pages/Editor/CurlImport/helpers.ts index 70d08206ecec..59e135cd1c71 100644 --- a/app/client/src/pages/Editor/CurlImport/helpers.ts +++ b/app/client/src/pages/Editor/CurlImport/helpers.ts @@ -1,6 +1,5 @@ import { submitCurlImportForm } from "../../../actions/importActions"; import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers"; -import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; export interface CurlImportFormValues { curl: string; @@ -17,15 +16,3 @@ export const curlImportSubmitHandler = ( ) => { dispatch(submitCurlImportForm(values)); }; - -export const openCurlImportModal = () => { - return { - type: ReduxActionTypes.SET_CURL_MODAL_OPEN, - }; -}; - -export const closeCurlImportModal = () => { - return { - type: ReduxActionTypes.SET_CURL_MODAL_CLOSE, - }; -}; diff --git a/app/client/src/pages/Editor/CurlImport/store/curlImportActions.ts b/app/client/src/pages/Editor/CurlImport/store/curlImportActions.ts new file mode 100644 index 000000000000..16e971b680e2 --- /dev/null +++ b/app/client/src/pages/Editor/CurlImport/store/curlImportActions.ts @@ -0,0 +1,21 @@ +import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers"; +import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; + +export interface CurlImportFormValues { + curl: string; + contextId: string; + name: string; + contextType: ActionParentEntityTypeInterface; +} + +export const openCurlImportModal = () => { + return { + type: ReduxActionTypes.SET_CURL_MODAL_OPEN, + }; +}; + +export const closeCurlImportModal = () => { + return { + type: ReduxActionTypes.SET_CURL_MODAL_CLOSE, + }; +}; diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx index 478693bb169d..f98787ab40b0 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx @@ -52,7 +52,6 @@ const FieldWrapper = styled.div` export const ViewModeWrapper = styled.div` display: flex; flex-direction: column; - border-bottom: 1px solid var(--ads-v2-color-border); padding: var(--ads-v2-spaces-7) 0; gap: var(--ads-v2-spaces-4); overflow: auto; diff --git a/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx b/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx index d80513b94a52..e1501da44948 100644 --- a/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx @@ -2,8 +2,6 @@ import React, { useMemo, useState } from "react"; import { AddButtonWrapper, EntityClassNames } from "../Entity"; import EntityAddButton from "../Entity/AddButton"; import styled from "styled-components"; -import history from "utils/history"; -import { generateTemplateFormURL } from "ee/RouteBuilder"; import { useParams } from "react-router"; import { useDispatch } from "react-redux"; import type { ExplorerURLParams } from "ee/pages/Editor/Explorer/helpers"; @@ -32,6 +30,7 @@ import { LayoutSystemFeatures, useLayoutSystemFeatures, } from "layoutSystems/common/useLayoutSystemFeatures"; +import { openGeneratePageModal } from "pages/Editor/GeneratePage/store/generatePageActions"; const Wrapper = styled.div` .title { @@ -85,7 +84,7 @@ function AddPageContextMenu({ items.push({ title: createMessage(GENERATE_PAGE_ACTION_TITLE), icon: "database-2-line", - onClick: () => history.push(generateTemplateFormURL({ basePageId })), + onClick: () => dispatch(openGeneratePageModal()), "data-testid": "generate-page", key: "GENERATE_PAGE", }); diff --git a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx index 84178b81ac4e..ef53e385e747 100644 --- a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx +++ b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GeneratePageForm.tsx @@ -11,23 +11,25 @@ import { } from "ee/selectors/entitiesSelector"; import type { Datasource } from "entities/Datasource"; -import { fetchDatasourceStructure } from "actions/datasourceActions"; +import { + fetchDatasourceStructure, + setDatasourceViewModeFlag, +} from "actions/datasourceActions"; import { generateTemplateToUpdatePage } from "actions/pageActions"; -import { useLocation } from "react-router"; import { INTEGRATION_TABS } from "constants/routes"; import history from "utils/history"; -import { getQueryParams } from "utils/URLUtils"; -import { getIsGeneratingTemplatePage } from "selectors/pageListSelectors"; +import { + getGeneratePageModalParams, + getIsGeneratingTemplatePage, +} from "selectors/pageListSelectors"; import DataSourceOption, { CONNECT_NEW_DATASOURCE_OPTION_ID, DatasourceImage, } from "../DataSourceOption"; -import { getQueryStringfromObject } from "ee/entities/URLRedirect/URLAssembly"; import type { DropdownOption } from "@appsmith/ads-old"; import { Button, Icon, Text, Select, Option, Tooltip } from "@appsmith/ads"; import GoogleSheetForm from "./GoogleSheetForm"; import { - GENERATE_PAGE_FORM_TITLE, createMessage, GEN_CRUD_DATASOURCE_DROPDOWN_LABEL, } from "ee/constants/messages"; @@ -70,6 +72,7 @@ import equal from "fast-deep-equal"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { getHasCreateDatasourcePermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; +import { closeGeneratePageModal } from "../../store/generatePageActions"; // ---------- Styles ---------- @@ -77,24 +80,11 @@ const TooltipWrapper = styled.div` margin-left: 6px; `; -const Wrapper = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; - border: none; -`; - const FormWrapper = styled.div` display: flex; flex-direction: column; `; -const DescWrapper = styled.div` - flex: 1; - display: flex; - flex-direction: column; -`; - const Row = styled.p` display: flex; flex-direction: row; @@ -218,7 +208,7 @@ const DatasourceOptionSelectedView = (props: any) => { function GeneratePageForm() { const dispatch = useDispatch(); - const querySearch = useLocation().search; + const params = useSelector(getGeneratePageModalParams); const basePageId = useSelector(getCurrentBasePageId); const pageId = useSelector(getCurrentPageId); @@ -539,10 +529,9 @@ function GeneratePageForm() { ]); useEffect(() => { - if (querySearch) { - const queryParams = getQueryParams(); - const datasourceId = queryParams.datasourceId; - const generateNewPage = queryParams.new_page; + if (params?.datasourceId || params?.new_page) { + const datasourceId = params.datasourceId; + const generateNewPage = params.new_page; if (datasourceId) { if (generateNewPage || numberOfEntities > 0) { @@ -552,15 +541,9 @@ function GeneratePageForm() { } setDatasourceIdToBeSelected(datasourceId); - delete queryParams.datasourceId; - delete queryParams.new_page; - const redirectURL = - window.location.pathname + getQueryStringfromObject(queryParams); - - history.replace(redirectURL); } } - }, [numberOfEntities, querySearch, setDatasourceIdToBeSelected]); + }, [numberOfEntities, params, setDatasourceIdToBeSelected]); const routeToCreateNewDatasource = () => { AnalyticsUtil.logEvent("GEN_CRUD_PAGE_CREATE_NEW_DATASOURCE"); @@ -577,6 +560,7 @@ function GeneratePageForm() { AnalyticsUtil.logEvent("NAVIGATE_TO_CREATE_NEW_DATASOURCE_PAGE", { entryPoint, }); + dispatch(closeGeneratePageModal()); }; const generatePageAction = (data: GeneratePagePayload) => { @@ -602,6 +586,7 @@ function GeneratePageForm() { AnalyticsUtil.logEvent("GEN_CRUD_PAGE_FORM_SUBMIT"); dispatch(generateTemplateToUpdatePage(payload)); + dispatch(closeGeneratePageModal()); }; const handleFormSubmit = () => { @@ -625,6 +610,8 @@ function GeneratePageForm() { }); history.push(redirectURL); + dispatch(setDatasourceViewModeFlag(false)); + dispatch(closeGeneratePageModal()); }; // if the datasource has basic information to connect to db it is considered as a valid structure hence isValid true. @@ -682,250 +669,247 @@ function GeneratePageForm() { !selectedTable.value || !showSubmitButton || isSelectedTableEmpty; return ( -
- - - {GENERATE_PAGE_FORM_TITLE()} - - - + + + + + + {selectedDatasource.value ? ( - + + + {tableDropdownErrorMsg && ( + + {tableDropdownErrorMsg} + + )} - {selectedDatasource.value ? ( - - - - - {tableDropdownErrorMsg && ( - - {tableDropdownErrorMsg} - - )} - - ) : null} - {showEditDatasourceBtn && ( -
- + ) : null} + {showEditDatasourceBtn && ( +
+ +
+ )} + {!isGoogleSheetPlugin ? ( + <> + {showSearchableColumn && ( + + + Select a searchable {pluginField.COLUMN} from the selected  + {pluginField.TABLE} + + + + + + + + + {selectedTableColumnOptions.length === 0 + ? `* Optional (No searchable ${pluginField.COLUMN} to select)` + : "* Optional"} + + + )} +
+
- )} - {!isGoogleSheetPlugin ? ( - <> - {showSearchableColumn && ( - - - Select a searchable {pluginField.COLUMN} from the - selected  - {pluginField.TABLE} - - - - - - - - - {selectedTableColumnOptions.length === 0 - ? `* Optional (No searchable ${pluginField.COLUMN} to select)` - : "* Optional"} - - - )} -
- -
- - ) : ( - void; - disabled: boolean; - isLoading: boolean; - }) => ( - - )} - selectedDatasource={selectedDatasource} - selectedSpreadsheet={selectedTable} - sheetColumnsHeaderProps={sheetColumnsHeaderProps} - sheetsListProps={sheetsListProps} - spreadSheetsProps={spreadSheetsProps} - /> - )} - -
+ + ) : ( + void; + disabled: boolean; + isLoading: boolean; + }) => ( + + )} + selectedDatasource={selectedDatasource} + selectedSpreadsheet={selectedTable} + sheetColumnsHeaderProps={sheetColumnsHeaderProps} + sheetsListProps={sheetsListProps} + spreadSheetsProps={spreadSheetsProps} + /> + )} +
); } diff --git a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GoogleSheetForm.tsx b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GoogleSheetForm.tsx index d5e146788b13..afd4b55c8220 100644 --- a/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GoogleSheetForm.tsx +++ b/app/client/src/pages/Editor/GeneratePage/components/GeneratePageForm/GoogleSheetForm.tsx @@ -301,6 +301,9 @@ function GoogleSheetForm(props: Props) {