chore: Refactoring header actions on datasource page to support reusable queries on EE (#29442)

## Description

Refactoring header actions on datasource page to support reusable
queries on EE

#### PR fixes following issue(s)
Fixes [#28568](https://github.com/appsmithorg/appsmith/issues/28568)

#### Type of change
- Chore (housekeeping or task changes that don't impact user perception)

## Testing

#### How Has This Been Tested?
- [x] Manual
- [ ] JUnit
- [ ] Jest
- [x] Cypress

## Checklist:
#### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new header action management system in the data source
editor, allowing for dynamic action button generation based on user
permissions and editor type.
- Implemented a new editor type detection hook to improve editor
experience.

- **Improvements**
- Streamlined the data source editor interface by removing unused
permissions checks and simplifying action button logic.
- Enhanced the editor's routing system with additional constants for
deprecated paths.

- **Refactor**
- Refactored the `DSFormHeader` component to utilize the new
`useHeaderActions` hook for a cleaner and more maintainable codebase.
- Updated Redux state management to include a new action handler for
resetting editor state.

- **Style**
- Adjusted the layout of the `LeftPaneContainer` by removing the
`min-width` property for a more responsive design.

- **Chores**
- Cleaned up imports and unused variables across multiple files to
improve code clarity and maintainability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ankita Kinger 2023-12-08 17:12:14 +05:30 committed by GitHub
parent 95fa2328a8
commit 35b9e0b544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 178 additions and 93 deletions

View File

@ -0,0 +1,119 @@
import React from "react";
import { useSelector } from "react-redux";
import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton";
import { EditorNames } from "./";
import type { Datasource } from "entities/Datasource";
import type { ApiDatasourceForm } from "entities/Datasource/RestAPIForm";
import { Button } from "design-system";
import {
GENERATE_NEW_PAGE_BUTTON_TEXT,
createMessage,
} from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
import history from "utils/history";
import { generateTemplateFormURL } from "@appsmith/RouteBuilder";
import {
getCurrentApplication,
getCurrentPageId,
getPagePermissions,
} from "selectors/editorSelectors";
import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks";
import type { AppState } from "@appsmith/reducers";
import {
getHasCreatePagePermission,
hasCreateDSActionPermissionInApp,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
export interface HeaderActionProps {
datasource: Datasource | ApiDatasourceForm | undefined;
isPluginAuthorized: boolean;
pluginType: string;
showReconnectButton?: boolean;
}
export const useHeaderActions = (
editorType: string,
{
datasource,
isPluginAuthorized,
pluginType,
showReconnectButton = false,
}: HeaderActionProps,
) => {
const pageId = useSelector(getCurrentPageId);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const userAppPermissions = useSelector(
(state: AppState) => getCurrentApplication(state)?.userPermissions ?? [],
);
const pagePermissions = useSelector((state: AppState) =>
getPagePermissions(state),
);
const showGenerateButton = useShowPageGenerationOnHeader(
datasource as Datasource,
);
if (editorType === EditorNames.APPLICATION) {
const canCreateDatasourceActions = hasCreateDSActionPermissionInApp(
isFeatureEnabled,
datasource?.userPermissions ?? [],
pagePermissions,
);
const canCreatePages = getHasCreatePagePermission(
isFeatureEnabled,
userAppPermissions,
);
const canGeneratePage = canCreateDatasourceActions && canCreatePages;
const routeToGeneratePage = () => {
if (!showGenerateButton) {
// disable button when it doesn't support page generation
return;
}
AnalyticsUtil.logEvent("DATASOURCE_CARD_GEN_CRUD_PAGE_ACTION");
history.push(
generateTemplateFormURL({
pageId,
params: {
datasourceId: (datasource as Datasource).id,
new_page: true,
},
}),
);
};
const newActionButton = (
<NewActionButton
datasource={datasource as Datasource}
disabled={!canCreateDatasourceActions || !isPluginAuthorized}
eventFrom="datasource-pane"
pluginType={pluginType}
/>
);
const generatePageButton =
showGenerateButton && !showReconnectButton ? (
<Button
className={"t--generate-template"}
isDisabled={!canGeneratePage}
kind="secondary"
onClick={(e: any) => {
e.stopPropagation();
e.preventDefault();
routeToGeneratePage();
}}
size="md"
>
{createMessage(GENERATE_NEW_PAGE_BUTTON_TEXT)}
</Button>
) : null;
return {
newActionButton,
generatePageButton,
};
}
return {};
};

View File

@ -0,0 +1,27 @@
import {
BUILDER_BASE_PATH_DEPRECATED,
BUILDER_VIEWER_PATH_PREFIX,
} from "constants/routes";
import { matchPath } from "react-router";
export const EditorNames = {
APPLICATION: "appEditor",
};
export interface EditorType {
[key: string]: string;
}
export const editorType: EditorType = {
[BUILDER_VIEWER_PATH_PREFIX]: EditorNames.APPLICATION,
};
export const useEditorType = (path: string) => {
const basePath = matchPath(path, {
path: [BUILDER_VIEWER_PATH_PREFIX, BUILDER_BASE_PATH_DEPRECATED],
});
return basePath
? editorType[basePath.path]
: editorType[BUILDER_VIEWER_PATH_PREFIX];
};

View File

@ -42,6 +42,18 @@ export const initialState: EditorReduxState = {
};
export const handlers = {
[ReduxActionTypes.RESET_EDITOR_REQUEST]: (state: EditorReduxState) => {
return {
...state,
currentPageId: undefined,
currentPageName: undefined,
currentLayoutId: undefined,
currentApplicationId: undefined,
pageWidgetId: undefined,
pageActions: undefined,
layoutOnLoadActionErrors: undefined,
};
},
[ReduxActionTypes.RESET_EDITOR_SUCCESS]: (state: EditorReduxState) => {
return { ...state, initialized: false };
},

View File

@ -4,6 +4,7 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { match } = require("path-to-regexp");
export const BUILDER_BASE_PATH_DEPRECATED = "/applications";
export const BUILDER_VIEWER_PATH_PREFIX = "/app/";
export const BUILDER_PATH = `${BUILDER_VIEWER_PATH_PREFIX}:applicationSlug/:pageSlug(.*\-):pageId/edit`;
export const BUILDER_CUSTOM_PATH = `${BUILDER_VIEWER_PATH_PREFIX}:customSlug(.*\-):pageId/edit`;

View File

@ -0,0 +1 @@
export * from "ce/hooks/datasourceEditorHooks";

View File

@ -0,0 +1 @@
export * from "ce/hooks";

View File

@ -1,6 +1,5 @@
import React, { useState } from "react";
import FormTitle from "./FormTitle";
import NewActionButton from "./NewActionButton";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
import type { Datasource } from "entities/Datasource";
import {
@ -9,7 +8,6 @@ import {
CONTEXT_DELETE,
EDIT,
createMessage,
GENERATE_NEW_PAGE_BUTTON_TEXT,
} from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { useDispatch, useSelector } from "react-redux";
@ -20,21 +18,15 @@ import { MenuWrapper, StyledMenu } from "components/utils/formComponents";
import styled from "styled-components";
import { Button, MenuContent, MenuItem, MenuTrigger } from "design-system";
import { DatasourceEditEntryPoints } from "constants/Datasource";
import { useShowPageGenerationOnHeader } from "./hooks";
import { generateTemplateFormURL } from "@appsmith/RouteBuilder";
import { getCurrentPageId } from "selectors/editorSelectors";
import history from "utils/history";
import {
DB_NOT_SUPPORTED,
isEnvironmentConfigured,
} from "@appsmith/utils/Environments";
import { getHasCreatePagePermission } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import type { AppState } from "@appsmith/reducers";
import { getCurrentApplication } from "@appsmith/selectors/applicationSelectors";
import { getCurrentEnvironmentId } from "@appsmith/selectors/environmentSelectors";
import type { PluginType } from "entities/Action";
import { useEditorType } from "@appsmith/hooks";
import { useHistory } from "react-router";
import { useHeaderActions } from "@appsmith/hooks/datasourceEditorHooks";
export const ActionWrapper = styled.div`
display: flex;
@ -84,7 +76,6 @@ export const PluginImage = (props: any) => {
};
interface DSFormHeaderProps {
canCreateDatasourceActions: boolean;
canDeleteDatasource: boolean;
canManageDatasource: boolean;
datasource: Datasource | ApiDatasourceForm | undefined;
@ -105,7 +96,6 @@ interface DSFormHeaderProps {
export const DSFormHeader = (props: DSFormHeaderProps) => {
const {
canCreateDatasourceActions,
canDeleteDatasource,
canManageDatasource,
datasource,
@ -123,16 +113,8 @@ export const DSFormHeader = (props: DSFormHeaderProps) => {
const [confirmDelete, setConfirmDelete] = useState(false);
const dispatch = useDispatch();
const pageId = useSelector(getCurrentPageId);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const userAppPermissions = useSelector(
(state: AppState) => getCurrentApplication(state)?.userPermissions ?? [],
);
const canCreatePages = getHasCreatePagePermission(
isFeatureEnabled,
userAppPermissions,
);
const history = useHistory();
const editorType = useEditorType(history.location.pathname);
const deleteAction = () => {
if (isDeleting) return;
@ -142,10 +124,6 @@ export const DSFormHeader = (props: DSFormHeaderProps) => {
const onCloseMenu = debounce(() => setConfirmDelete(false), 20);
const showGenerateButton = useShowPageGenerationOnHeader(
datasource as Datasource,
);
const renderMenuOptions = () => {
return [
<MenuItem
@ -172,8 +150,6 @@ export const DSFormHeader = (props: DSFormHeaderProps) => {
];
};
const canGeneratePage = canCreateDatasourceActions && canCreatePages;
const currentEnv = useSelector(getCurrentEnvironmentId);
const envSupportedDs = !DB_NOT_SUPPORTED.includes(pluginType as PluginType);
@ -184,22 +160,12 @@ export const DSFormHeader = (props: DSFormHeaderProps) => {
: true)
);
const routeToGeneratePage = () => {
if (!showGenerateButton) {
// disable button when it doesn't support page generation
return;
}
AnalyticsUtil.logEvent("DATASOURCE_CARD_GEN_CRUD_PAGE_ACTION");
history.push(
generateTemplateFormURL({
pageId,
params: {
datasourceId: (datasource as Datasource).id,
new_page: true,
},
}),
);
};
const headerActions = useHeaderActions(editorType, {
datasource,
isPluginAuthorized,
pluginType,
showReconnectButton,
});
return (
<Header noBottomBorder={!!noBottomBorder}>
@ -254,27 +220,12 @@ export const DSFormHeader = (props: DSFormHeaderProps) => {
>
{createMessage(EDIT)}
</Button>
<NewActionButton
datasource={datasource as Datasource}
disabled={!canCreateDatasourceActions || !isPluginAuthorized}
eventFrom="datasource-pane"
pluginType={pluginType}
/>
{showGenerateButton && !showReconnectButton && (
<Button
className={"t--generate-template"}
isDisabled={!canGeneratePage}
kind="secondary"
onClick={(e: any) => {
e.stopPropagation();
e.preventDefault();
routeToGeneratePage();
}}
size="md"
>
{createMessage(GENERATE_NEW_PAGE_BUTTON_TEXT)}
</Button>
)}
{headerActions && headerActions.newActionButton
? headerActions.newActionButton
: null}
{headerActions && headerActions.generatePageButton
? headerActions.generatePageButton
: null}
</ActionWrapper>
)}
</Header>

View File

@ -42,7 +42,6 @@ import { DatasourceComponentTypes } from "api/PluginApi";
import DatasourceSaasForm from "../SaaSEditor/DatasourceForm";
import {
getCurrentApplicationId,
getPagePermissions,
selectURLSlugs,
} from "selectors/editorSelectors";
import { saasEditorDatasourceIdURL } from "@appsmith/RouteBuilder";
@ -103,14 +102,12 @@ import { isGACEnabled } from "@appsmith/utils/planHelpers";
import {
getHasDeleteDatasourcePermission,
getHasManageDatasourcePermission,
hasCreateDSActionPermissionInApp,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import DatasourceTabs from "../DatasourceInfo/DatasorceTabs";
import DatasourceInformation, { ViewModeWrapper } from "./DatasourceSection";
import { getIsAppSidebarEnabled } from "../../../selectors/ideSelectors";
interface ReduxStateProps {
canCreateDatasourceActions: boolean;
canDeleteDatasource: boolean;
canManageDatasource: boolean;
datasourceButtonConfiguration: string[] | undefined;
@ -899,7 +896,6 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
render() {
const {
canCreateDatasourceActions,
canDeleteDatasource,
canManageDatasource,
datasource,
@ -968,7 +964,6 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
{isAppSidebarEnabled || !!isOnboardingFlow ? null : <CloseEditor />}
{!isInsideReconnectModal && (
<DSFormHeader
canCreateDatasourceActions={canCreateDatasourceActions}
canDeleteDatasource={canDeleteDatasource}
canManageDatasource={canManageDatasource}
datasource={datasource}
@ -1118,13 +1113,6 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
isFeatureEnabled,
datasourcePermissions,
);
const pagePermissions = getPagePermissions(state);
const canCreateDatasourceActions = hasCreateDSActionPermissionInApp(
isFeatureEnabled,
datasourcePermissions,
pagePermissions,
);
// Debugger render flag
const showDebugger = showDebuggerFlag(state);
const pluginPackageName = plugin?.packageName ?? "";
@ -1166,7 +1154,6 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
const isAppSidebarEnabled = getIsAppSidebarEnabled(state);
return {
canCreateDatasourceActions,
canDeleteDatasource,
canManageDatasource,
datasourceButtonConfiguration,

View File

@ -23,7 +23,6 @@ import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
export const LeftPaneContainer = styled.div`
height: 100%;
min-width: 150px;
border-right: 1px solid var(--ads-v2-color-border);
background: var(--ads-v2-color-bg);
`;

View File

@ -33,7 +33,6 @@ import {
getCurrentApplicationId,
getGsheetProjectID,
getGsheetToken,
getPagePermissions,
} from "selectors/editorSelectors";
import DatasourceAuth from "pages/common/datasourceAuth";
import EntityNotFoundPane from "../EntityNotFoundPane";
@ -86,7 +85,6 @@ import { DEFAULT_ENV_ID } from "@appsmith/api/ApiUtils";
import {
getHasDeleteDatasourcePermission,
getHasManageDatasourcePermission,
hasCreateDSActionPermissionInApp,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { selectFeatureFlagCheck } from "@appsmith/selectors/featureFlagsSelectors";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
@ -104,7 +102,6 @@ interface StateProps extends JSONtoFormProps {
applicationId: string;
canManageDatasource?: boolean;
canDeleteDatasource?: boolean;
canCreateDatasourceActions?: boolean;
isSaving: boolean;
isTesting: boolean;
isDeleting: boolean;
@ -548,7 +545,6 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
renderDataSourceConfigForm = (sections: any) => {
const {
canCreateDatasourceActions,
canDeleteDatasource,
canManageDatasource,
datasource,
@ -601,7 +597,6 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
<>
{!hiddenHeader && (
<DSFormHeader
canCreateDatasourceActions={canCreateDatasourceActions || false}
canDeleteDatasource={canDeleteDatasource || false}
canManageDatasource={canManageDatasource || false}
datasource={datasource}
@ -775,13 +770,6 @@ const mapStateToProps = (state: AppState, props: any) => {
datasourcePermissions,
);
const pagePermissions = getPagePermissions(state);
const canCreateDatasourceActions = hasCreateDSActionPermissionInApp(
isFeatureEnabled,
datasourcePermissions,
pagePermissions,
);
const gsheetToken = getGsheetToken(state);
const gsheetProjectID = getGsheetProjectID(state);
const documentationLinks = getPluginDocumentationLinks(state);
@ -858,7 +846,6 @@ const mapStateToProps = (state: AppState, props: any) => {
isDatasourceBeingSavedFromPopup:
state.entities.datasources.isDatasourceBeingSavedFromPopup,
isFormDirty,
canCreateDatasourceActions,
gsheetToken,
gsheetProjectID,
showDebugger,