Add action settings tab to api and query pane (#434)
* Add action settings tab to api and query pane - Ask for confirmation before running an action * Update property of actions basedon the updateLayout response Prevent confirmation dialog for Action run, until property of action is true Send an API Request when the user toggles the property of an Action * update http method to toggle executeOnLoad for an action to PUT * Fix save layout response type * Remove console.log * If updating executeOnLoad, avoid calling update action API Co-authored-by: Abhinav Jha <abhinav@appsmith.com>
This commit is contained in:
parent
dfabda6009
commit
dbfd986d0e
|
|
@ -55,6 +55,7 @@
|
|||
"fusioncharts": "^3.15.0-sr.1",
|
||||
"history": "^4.10.1",
|
||||
"husky": "^3.0.5",
|
||||
"immer": "^7.0.8",
|
||||
"instantsearch.css": "^7.4.2",
|
||||
"instantsearch.js": "^4.4.1",
|
||||
"interweave": "^12.1.1",
|
||||
|
|
|
|||
|
|
@ -67,6 +67,38 @@ export const runAction = (id: string, paginationField?: PaginationField) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const runActionInit = (
|
||||
id: string,
|
||||
paginationField?: PaginationField,
|
||||
) => {
|
||||
return {
|
||||
type: ReduxActionTypes.RUN_ACTION_INIT,
|
||||
payload: {
|
||||
id,
|
||||
paginationField,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const showRunActionConfirmModal = (show: boolean) => {
|
||||
return {
|
||||
type: ReduxActionTypes.SHOW_RUN_ACTION_CONFIRM_MODAL,
|
||||
payload: show,
|
||||
};
|
||||
};
|
||||
|
||||
export const cancelRunActionConfirmModal = () => {
|
||||
return {
|
||||
type: ReduxActionTypes.CANCEL_RUN_ACTION_CONFIRM_MODAL,
|
||||
};
|
||||
};
|
||||
|
||||
export const acceptRunActionConfirmModal = () => {
|
||||
return {
|
||||
type: ReduxActionTypes.ACCEPT_RUN_ACTION_CONFIRM_MODAL,
|
||||
};
|
||||
};
|
||||
|
||||
export const updateAction = (payload: { id: string }) => {
|
||||
return batchAction({
|
||||
type: ReduxActionTypes.UPDATE_ACTION_INIT,
|
||||
|
|
@ -196,6 +228,13 @@ export const updateActionProperty = (
|
|||
});
|
||||
};
|
||||
|
||||
export const setActionsToExecuteOnPageLoad = (actions: string[]) => {
|
||||
return {
|
||||
type: ReduxActionTypes.SET_ACTION_TO_EXECUTE_ON_PAGELOAD,
|
||||
payload: actions,
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
createAction: createActionRequest,
|
||||
fetchActions,
|
||||
|
|
|
|||
|
|
@ -182,6 +182,12 @@ class ActionAPI extends API {
|
|||
static executeQuery(executeAction: any): AxiosPromise<ActionApiResponse> {
|
||||
return API.post(ActionAPI.url + "/execute", executeAction);
|
||||
}
|
||||
|
||||
static toggleActionExecuteOnLoad(actionId: string, shouldExecute: boolean) {
|
||||
return API.put(ActionAPI.url + `/executeOnLoad/${actionId}`, undefined, {
|
||||
flag: shouldExecute.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ActionAPI;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,11 @@ export type FetchPublishedPageResponse = ApiResponse & {
|
|||
};
|
||||
|
||||
export interface SavePageResponse extends ApiResponse {
|
||||
pageId: string;
|
||||
data: {
|
||||
id: string;
|
||||
layoutOnLoadActions: PageAction[][];
|
||||
dsl: Partial<ContainerWidgetProps<any>>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CreatePageRequest {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
const Wrapper = styled.div`
|
||||
.dynamic-text-field {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d7dd;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
height: calc(100vh / 4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,21 +21,30 @@ const SwitchWrapped = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const Info = styled.div`
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
|
||||
export class SwitchField extends React.Component<Props> {
|
||||
render() {
|
||||
const { label, isRequired, input } = this.props;
|
||||
const { label, isRequired, input, info } = this.props;
|
||||
|
||||
return (
|
||||
<SwitchWrapped>
|
||||
<StyledFormLabel>
|
||||
{label} {isRequired && "*"}
|
||||
</StyledFormLabel>
|
||||
<StyledSwitch
|
||||
checked={input.value}
|
||||
onChange={value => input.onChange(value)}
|
||||
large
|
||||
/>
|
||||
</SwitchWrapped>
|
||||
<div>
|
||||
<SwitchWrapped>
|
||||
<StyledFormLabel>
|
||||
{label} {isRequired && "*"}
|
||||
</StyledFormLabel>
|
||||
<StyledSwitch
|
||||
checked={input.value}
|
||||
onChange={value => input.onChange(value)}
|
||||
large
|
||||
/>
|
||||
</SwitchWrapped>
|
||||
{info && <Info>{info}</Info>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +65,8 @@ class SwitchControl extends BaseControl<SwitchControlProps> {
|
|||
}
|
||||
}
|
||||
|
||||
export type SwitchControlProps = ControlProps;
|
||||
export interface SwitchControlProps extends ControlProps {
|
||||
info?: string;
|
||||
}
|
||||
|
||||
export default SwitchControl;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET",
|
||||
LOAD_API_RESPONSE: "LOAD_API_RESPONSE",
|
||||
LOAD_QUERY_RESPONSE: "LOAD_QUERY_RESPONSE",
|
||||
RUN_ACTION_INIT: "RUN_ACTION_INIT",
|
||||
RUN_ACTION_REQUEST: "RUN_ACTION_REQUEST",
|
||||
RUN_ACTION_SUCCESS: "RUN_ACTION_SUCCESS",
|
||||
INIT_API_PANE: "INIT_API_PANE",
|
||||
|
|
@ -59,6 +60,9 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
UPDATE_ACTION_SUCCESS: "UPDATE_ACTION_SUCCESS",
|
||||
DELETE_ACTION_INIT: "DELETE_ACTION_INIT",
|
||||
DELETE_ACTION_SUCCESS: "DELETE_ACTION_SUCCESS",
|
||||
SHOW_RUN_ACTION_CONFIRM_MODAL: "SHOW_RUN_ACTION_CONFIRM_MODAL",
|
||||
CANCEL_RUN_ACTION_CONFIRM_MODAL: "CANCEL_RUN_ACTION_CONFIRM_MODAL",
|
||||
ACCEPT_RUN_ACTION_CONFIRM_MODAL: "ACCEPT_RUN_ACTION_CONFIRM_MODAL",
|
||||
CREATE_QUERY_INIT: "CREATE_QUERY_INIT",
|
||||
FETCH_DATASOURCES_INIT: "FETCH_DATASOURCES_INIT",
|
||||
FETCH_DATASOURCES_SUCCESS: "FETCH_DATASOURCES_SUCCESS",
|
||||
|
|
@ -254,6 +258,10 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
TOGGLE_PROPERTY_PANE_WIDGET_NAME_EDIT:
|
||||
"TOGGLE_PROPERTY_PANE_WIDGET_NAME_EDIT",
|
||||
UPDATE_APP_STORE: "UPDATE_APP_STORE",
|
||||
SET_ACTION_TO_EXECUTE_ON_PAGELOAD: "SET_ACTION_TO_EXECUTE_ON_PAGELOAD",
|
||||
TOGGLE_ACTION_EXECUTE_ON_LOAD_SUCCESS:
|
||||
"TOGGLE_ACTION_EXECUTE_ON_LOAD_SUCCESS",
|
||||
TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT: "TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT",
|
||||
};
|
||||
|
||||
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
|
||||
|
|
@ -344,6 +352,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
|
|||
SAVE_API_NAME_ERROR: "SAVE_API_NAME_ERROR",
|
||||
POPULATE_PAGEDSLS_ERROR: "POPULATE_PAGEDSLS_ERROR",
|
||||
FETCH_PAGE_DSL_ERROR: "FETCH_PAGE_DSL_ERROR",
|
||||
TOGGLE_ACTION_EXECUTE_ON_LOAD_ERROR: "TOGGLE_ACTION_EXECUTE_ON_LOAD_ERROR",
|
||||
};
|
||||
|
||||
export const ReduxFormActionTypes: { [key: string]: string } = {
|
||||
|
|
|
|||
41
app/client/src/mockResponses/ActionSettings.tsx
Normal file
41
app/client/src/mockResponses/ActionSettings.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
export const queryActionSettingsConfig = [
|
||||
{
|
||||
sectionName: "",
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
label: "Run query on Page load",
|
||||
configProperty: "executeOnLoad",
|
||||
controlType: "SWITCH",
|
||||
info: "Will refresh data everytime page is reloaded",
|
||||
},
|
||||
// {
|
||||
// label: "Request confirmation before running query",
|
||||
// configProperty: "requestConfirmation",
|
||||
// controlType: "SWITCH",
|
||||
// info: "Ask confirmation from the user everytime before refreshing data",
|
||||
// },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const apiActionSettingsConfig = [
|
||||
{
|
||||
sectionName: "",
|
||||
id: 1,
|
||||
children: [
|
||||
{
|
||||
label: "Run api on Page load",
|
||||
configProperty: "executeOnLoad",
|
||||
controlType: "SWITCH",
|
||||
info: "Will refresh data everytime page is reloaded",
|
||||
},
|
||||
// {
|
||||
// label: "Request confirmation before running api",
|
||||
// configProperty: "requestConfirmation",
|
||||
// controlType: "SWITCH",
|
||||
// info: "Ask confirmation from the user everytime before refreshing data",
|
||||
// },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
@ -25,6 +25,8 @@ import EmbeddedDatasourcePathField from "components/editorComponents/form/fields
|
|||
import { AppState } from "reducers";
|
||||
import { getApiName } from "selectors/formSelectors";
|
||||
import ActionNameEditor from "components/editorComponents/ActionNameEditor";
|
||||
import ActionSettings from "pages/Editor/ActionSettings";
|
||||
import { apiActionSettingsConfig } from "mockResponses/ActionSettings";
|
||||
|
||||
const Form = styled.form`
|
||||
display: flex;
|
||||
|
|
@ -111,6 +113,13 @@ const RequestParamsWrapper = styled.div`
|
|||
padding-right: 10px;
|
||||
`;
|
||||
|
||||
const SettingsWrapper = styled.div`
|
||||
padding-left: 15px;
|
||||
${FormLabel} {
|
||||
padding: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
const HeadersSection = styled.div`
|
||||
margin-bottom: 32px;
|
||||
`;
|
||||
|
|
@ -256,6 +265,17 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
|||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
title: "Settings",
|
||||
panelComponent: (
|
||||
<SettingsWrapper>
|
||||
<ActionSettings
|
||||
actionSettingsConfig={apiActionSettingsConfig}
|
||||
/>
|
||||
</SettingsWrapper>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</TabbedViewContainer>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { submit } from "redux-form";
|
|||
import ApiEditorForm from "./Form";
|
||||
import RapidApiEditorForm from "./RapidApiEditorForm";
|
||||
import ApiHomeScreen from "./ApiHomeScreen";
|
||||
import { runAction, deleteAction } from "actions/actionActions";
|
||||
import { deleteAction, runActionInit } from "actions/actionActions";
|
||||
import { PaginationField } from "api/ActionAPI";
|
||||
import { AppState } from "reducers";
|
||||
import { RouteComponentProps } from "react-router";
|
||||
|
|
@ -234,7 +234,7 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
|
|||
const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
|
||||
submitForm: (name: string) => dispatch(submit(name)),
|
||||
runAction: (id: string, paginationField?: PaginationField) =>
|
||||
dispatch(runAction(id, paginationField)),
|
||||
dispatch(runActionInit(id, paginationField)),
|
||||
deleteAction: (id: string, name: string) =>
|
||||
dispatch(deleteAction({ id, name })),
|
||||
changeAPIPage: (actionId: string) => dispatch(changeApi(actionId)),
|
||||
|
|
|
|||
37
app/client/src/pages/Editor/ActionSettings.tsx
Normal file
37
app/client/src/pages/Editor/ActionSettings.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
import FormControlFactory from "utils/FormControlFactory";
|
||||
import { ControlProps } from "components/formControls/BaseControl";
|
||||
|
||||
interface ActionSettingsProps {
|
||||
actionSettingsConfig: any;
|
||||
}
|
||||
|
||||
const ActionSettings = (props: ActionSettingsProps): JSX.Element => {
|
||||
return <>{props.actionSettingsConfig.map(renderEachConfig)}</>;
|
||||
};
|
||||
|
||||
const renderEachConfig = (section: any): any => {
|
||||
return section.children.map((propertyControlOrSection: ControlProps) => {
|
||||
if ("children" in propertyControlOrSection) {
|
||||
return renderEachConfig(propertyControlOrSection);
|
||||
} else {
|
||||
try {
|
||||
const { configProperty } = propertyControlOrSection;
|
||||
return (
|
||||
<div key={configProperty} style={{ marginTop: "18px" }}>
|
||||
{FormControlFactory.createControl(
|
||||
{ ...propertyControlOrSection },
|
||||
{},
|
||||
false,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
export default ActionSettings;
|
||||
61
app/client/src/pages/Editor/ConfirmRunModal.tsx
Normal file
61
app/client/src/pages/Editor/ConfirmRunModal.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
import { Dialog, Classes } from "@blueprintjs/core";
|
||||
import Button from "components/editorComponents/Button";
|
||||
import {
|
||||
showRunActionConfirmModal,
|
||||
cancelRunActionConfirmModal,
|
||||
acceptRunActionConfirmModal,
|
||||
} from "actions/actionActions";
|
||||
|
||||
type Props = {
|
||||
isModalOpen: boolean;
|
||||
dispatch: any;
|
||||
};
|
||||
|
||||
class ConfirmRunModal extends React.Component<Props> {
|
||||
render() {
|
||||
const { dispatch, isModalOpen } = this.props;
|
||||
const handleClose = () => {
|
||||
dispatch(showRunActionConfirmModal(false));
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog title="Confirm run" isOpen={isModalOpen} onClose={handleClose}>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
Are you sure you want to refresh your current data
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button
|
||||
filled
|
||||
text="Cancel"
|
||||
onClick={() => {
|
||||
dispatch(cancelRunActionConfirmModal());
|
||||
|
||||
handleClose();
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
filled
|
||||
text="Confirm and run"
|
||||
intent="primary"
|
||||
onClick={() => {
|
||||
dispatch(acceptRunActionConfirmModal());
|
||||
|
||||
handleClose();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
isModalOpen: state.ui.confirmRunAction.modalOpen,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ConfirmRunModal);
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
import React from "react";
|
||||
import {
|
||||
formValueSelector,
|
||||
InjectedFormProps,
|
||||
reduxForm,
|
||||
Field,
|
||||
} from "redux-form";
|
||||
import { formValueSelector, InjectedFormProps, reduxForm } from "redux-form";
|
||||
import styled, { createGlobalStyle } from "styled-components";
|
||||
import { Icon, Popover, Spinner, Tag } from "@blueprintjs/core";
|
||||
import {
|
||||
|
|
@ -22,6 +17,7 @@ import FormRow from "components/editorComponents/FormRow";
|
|||
import DropdownField from "components/editorComponents/form/fields/DropdownField";
|
||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||
import { Datasource } from "api/DatasourcesApi";
|
||||
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
|
||||
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
||||
import { Colors } from "constants/Colors";
|
||||
import JSONViewer from "./JSONViewer";
|
||||
|
|
@ -39,7 +35,8 @@ import {
|
|||
import FormControlFactory from "utils/FormControlFactory";
|
||||
import { ControlProps } from "components/formControls/BaseControl";
|
||||
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import { SwitchField } from "components/formControls/SwitchControl";
|
||||
import ActionSettings from "pages/Editor/ActionSettings";
|
||||
import { queryActionSettingsConfig } from "mockResponses/ActionSettings";
|
||||
|
||||
const QueryFormContainer = styled.div`
|
||||
padding: 20px 32px;
|
||||
|
|
@ -216,6 +213,22 @@ const LoadingContainer = styled(CenteredWrapper)`
|
|||
height: 50%;
|
||||
`;
|
||||
|
||||
const TabContainerView = styled.div`
|
||||
height: calc(100vh / 3);
|
||||
|
||||
.react-tabs__tab-panel {
|
||||
border: 1px solid #ebeff2;
|
||||
}
|
||||
.react-tabs__tab-list {
|
||||
margin: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
const SettingsWrapper = styled.div`
|
||||
padding-left: 15px;
|
||||
padding-top: 8px;
|
||||
`;
|
||||
|
||||
type QueryFormProps = {
|
||||
onDeleteClick: () => void;
|
||||
onRunClick: () => void;
|
||||
|
|
@ -442,29 +455,44 @@ const QueryEditorForm: React.FC<Props> = (props: Props) => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{editorConfig && editorConfig.length > 0 ? (
|
||||
editorConfig.map(renderEachConfig)
|
||||
) : (
|
||||
<>
|
||||
<ErrorMessage>An unexpected error occurred</ErrorMessage>
|
||||
<Tag
|
||||
round
|
||||
intent="warning"
|
||||
interactive
|
||||
minimal
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Refresh
|
||||
</Tag>
|
||||
</>
|
||||
)}
|
||||
<div className="executeOnLoad">
|
||||
<Field
|
||||
name="executeOnLoad"
|
||||
component={SwitchField}
|
||||
label={"Run on Page Load"}
|
||||
<TabContainerView>
|
||||
<BaseTabbedView
|
||||
tabs={[
|
||||
{
|
||||
key: "query",
|
||||
title: "Query",
|
||||
panelComponent:
|
||||
editorConfig && editorConfig.length > 0 ? (
|
||||
editorConfig.map(renderEachConfig)
|
||||
) : (
|
||||
<>
|
||||
<ErrorMessage>An unexpected error occurred</ErrorMessage>
|
||||
<Tag
|
||||
round
|
||||
intent="warning"
|
||||
interactive
|
||||
minimal
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Refresh
|
||||
</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
title: "Settings",
|
||||
panelComponent: (
|
||||
<SettingsWrapper>
|
||||
<ActionSettings
|
||||
actionSettingsConfig={queryActionSettingsConfig}
|
||||
/>
|
||||
</SettingsWrapper>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</TabContainerView>
|
||||
</form>
|
||||
|
||||
{dataSources.length === 0 && (
|
||||
|
|
|
|||
|
|
@ -6,11 +6,9 @@ import { getPluginTemplates } from "selectors/entitiesSelector";
|
|||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
height: 185px;
|
||||
padding: 16px 24px;
|
||||
flex: 1;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d0d7dd;
|
||||
flex-direction: column;
|
||||
color: #4e5d78;
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import styled from "styled-components";
|
|||
import { QueryEditorRouteParams } from "constants/routes";
|
||||
import QueryEditorForm from "./Form";
|
||||
import QueryHomeScreen from "./QueryHomeScreen";
|
||||
import { deleteAction, runAction } from "actions/actionActions";
|
||||
import { deleteAction, runActionInit } from "actions/actionActions";
|
||||
import { AppState } from "reducers";
|
||||
import { getIsEditorInitialized } from "selectors/editorSelectors";
|
||||
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
||||
|
|
@ -187,7 +187,7 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
|
|||
const mapDispatchToProps = (dispatch: any): ReduxDispatchProps => ({
|
||||
deleteAction: (id: string, name: string) =>
|
||||
dispatch(deleteAction({ id, name })),
|
||||
runAction: (actionId: string) => dispatch(runAction(actionId)),
|
||||
runAction: (actionId: string) => dispatch(runActionInit(actionId)),
|
||||
changeQueryPage: (queryId: string) => {
|
||||
dispatch(changeQuery(queryId));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
|
|||
import { getAppsmithConfigs } from "configs";
|
||||
import { getCurrentUser } from "selectors/usersSelectors";
|
||||
import { User } from "constants/userConstants";
|
||||
import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
|
||||
|
||||
const { cloudHosting, intercomAppID } = getAppsmithConfigs();
|
||||
|
||||
|
|
@ -192,6 +193,7 @@ class Editor extends Component<Props> {
|
|||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
<ConfirmRunModal />
|
||||
</DndProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import { ExecuteErrorPayload } from "constants/ActionConstants";
|
|||
import _ from "lodash";
|
||||
import { RapidApiAction, RestAction } from "entities/Action";
|
||||
import { UpdateActionPropertyActionPayload } from "actions/actionActions";
|
||||
import produce from "immer";
|
||||
|
||||
export interface ActionData {
|
||||
isLoading: boolean;
|
||||
config: RestAction | RapidApiAction;
|
||||
|
|
@ -294,6 +296,18 @@ const actionsReducer = createReducer(initialState, {
|
|||
|
||||
return true;
|
||||
}),
|
||||
[ReduxActionTypes.SET_ACTION_TO_EXECUTE_ON_PAGELOAD]: (
|
||||
state: ActionDataState,
|
||||
actionIds: ReduxAction<string[]>,
|
||||
) => {
|
||||
return produce(state, draft => {
|
||||
draft.forEach((action, index) => {
|
||||
if (actionIds.payload.indexOf(action.config.id) > -1) {
|
||||
draft[index].config.executeOnLoad = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default actionsReducer;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import { HelpReduxState } from "./uiReducers/helpReducer";
|
|||
import { ApiNameReduxState } from "./uiReducers/apiNameReducer";
|
||||
import { ExplorerReduxState } from "./uiReducers/explorerReducer";
|
||||
import { PageDSLsReduxState } from "./uiReducers/pageDSLReducer";
|
||||
import { ConfirmRunActionReduxState } from "./uiReducers/confirmRunActionReducer";
|
||||
import { AppDataState } from "@appsmith/reducers/entityReducers/appReducer";
|
||||
import { DatasourceNameReduxState } from "./uiReducers/datasourceNameReducer";
|
||||
|
||||
|
|
@ -63,6 +64,7 @@ export interface AppState {
|
|||
apiName: ApiNameReduxState;
|
||||
explorer: ExplorerReduxState;
|
||||
pageDSLs: PageDSLsReduxState;
|
||||
confirmRunAction: ConfirmRunActionReduxState;
|
||||
datasourceName: DatasourceNameReduxState;
|
||||
};
|
||||
entities: {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
|
||||
const initialState: ConfirmRunActionReduxState = {
|
||||
modalOpen: false,
|
||||
};
|
||||
|
||||
const confirmRunActionReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.SHOW_RUN_ACTION_CONFIRM_MODAL]: (
|
||||
state: ConfirmRunActionReduxState,
|
||||
action: ReduxAction<boolean>,
|
||||
) => {
|
||||
return { ...state, modalOpen: action.payload };
|
||||
},
|
||||
});
|
||||
|
||||
export interface ConfirmRunActionReduxState {
|
||||
modalOpen: boolean;
|
||||
}
|
||||
|
||||
export default confirmRunActionReducer;
|
||||
|
|
@ -19,6 +19,7 @@ import helpReducer from "./helpReducer";
|
|||
import apiNameReducer from "./apiNameReducer";
|
||||
import explorerReducer from "./explorerReducer";
|
||||
import pageDSLsReducer from "./pageDSLReducer";
|
||||
import confirmRunActionReducer from "./confirmRunActionReducer";
|
||||
import datasourceNameReducer from "./datasourceNameReducer";
|
||||
|
||||
const uiReducer = combineReducers({
|
||||
|
|
@ -43,5 +44,6 @@ const uiReducer = combineReducers({
|
|||
apiName: apiNameReducer,
|
||||
explorer: explorerReducer,
|
||||
pageDSLs: pageDSLsReducer,
|
||||
confirmRunAction: confirmRunActionReducer,
|
||||
});
|
||||
export default uiReducer;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
take,
|
||||
takeEvery,
|
||||
takeLatest,
|
||||
race,
|
||||
} from "redux-saga/effects";
|
||||
import {
|
||||
evaluateDataTreeWithFunctions,
|
||||
|
|
@ -50,6 +51,7 @@ import {
|
|||
executeApiActionRequest,
|
||||
executeApiActionSuccess,
|
||||
updateAction,
|
||||
showRunActionConfirmModal,
|
||||
} from "actions/actionActions";
|
||||
import { Action, RestAction } from "entities/Action";
|
||||
import ActionAPI, {
|
||||
|
|
@ -500,6 +502,35 @@ function* runActionSaga(
|
|||
}
|
||||
}
|
||||
|
||||
function* confirmRunActionSaga(
|
||||
reduxAction: ReduxAction<{
|
||||
id: string;
|
||||
paginationField: PaginationField;
|
||||
}>,
|
||||
) {
|
||||
const action = yield select(getAction, reduxAction.payload.id);
|
||||
if (action.requestConfirmation) {
|
||||
yield put(showRunActionConfirmModal(true));
|
||||
|
||||
const { accept } = yield race({
|
||||
cancel: take(ReduxActionTypes.CANCEL_RUN_ACTION_CONFIRM_MODAL),
|
||||
accept: take(ReduxActionTypes.ACCEPT_RUN_ACTION_CONFIRM_MODAL),
|
||||
});
|
||||
|
||||
if (accept) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.RUN_ACTION_REQUEST,
|
||||
payload: reduxAction.payload,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
yield put({
|
||||
type: ReduxActionTypes.RUN_ACTION_REQUEST,
|
||||
payload: reduxAction.payload,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function* executePageLoadAction(pageAction: PageAction) {
|
||||
yield put(executeApiActionRequest({ id: pageAction.id }));
|
||||
const params: Property[] = yield call(
|
||||
|
|
@ -545,6 +576,7 @@ export function* watchActionExecutionSagas() {
|
|||
yield all([
|
||||
takeEvery(ReduxActionTypes.EXECUTE_ACTION, executeAppAction),
|
||||
takeLatest(ReduxActionTypes.RUN_ACTION_REQUEST, runActionSaga),
|
||||
takeLatest(ReduxActionTypes.RUN_ACTION_INIT, confirmRunActionSaga),
|
||||
takeLatest(
|
||||
ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
|
||||
executePageLoadActionsSaga,
|
||||
|
|
|
|||
|
|
@ -442,6 +442,7 @@ function* setActionPropertySaga(action: ReduxAction<SetActionPropertyPayload>) {
|
|||
const { actionId, value, propertyName } = action.payload;
|
||||
if (!actionId) return;
|
||||
if (propertyName === "name") return;
|
||||
|
||||
const actionObj = yield select(getAction, actionId);
|
||||
const effects: Record<string, any> = {};
|
||||
// Value change effect
|
||||
|
|
@ -457,9 +458,42 @@ function* setActionPropertySaga(action: ReduxAction<SetActionPropertyPayload>) {
|
|||
put(updateActionProperty({ id: actionId, field, value: effects[field] })),
|
||||
),
|
||||
);
|
||||
if (propertyName === "executeOnLoad") {
|
||||
yield put({
|
||||
type: ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT,
|
||||
payload: {
|
||||
actionId,
|
||||
shouldExecute: value,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
yield put(updateAction({ id: actionId }));
|
||||
}
|
||||
|
||||
function* toggleActionExecuteOnLoadSaga(
|
||||
action: ReduxAction<{ actionId: string; shouldExecute: boolean }>,
|
||||
) {
|
||||
try {
|
||||
const response = yield call(
|
||||
ActionAPI.toggleActionExecuteOnLoad,
|
||||
action.payload.actionId,
|
||||
action.payload.shouldExecute,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_SUCCESS,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_ERROR,
|
||||
payload: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function* handleMoveOrCopySaga(actionPayload: ReduxAction<{ id: string }>) {
|
||||
const { id } = actionPayload.payload;
|
||||
const action: Action = yield select(getAction, id);
|
||||
|
|
@ -499,5 +533,9 @@ export function* watchActionSagas() {
|
|||
takeEvery(ReduxActionTypes.COPY_ACTION_SUCCESS, handleMoveOrCopySaga),
|
||||
takeEvery(ReduxActionErrorTypes.MOVE_ACTION_ERROR, handleMoveOrCopySaga),
|
||||
takeEvery(ReduxActionErrorTypes.COPY_ACTION_ERROR, handleMoveOrCopySaga),
|
||||
takeLatest(
|
||||
ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT,
|
||||
toggleActionExecuteOnLoadSaga,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,10 @@ import {
|
|||
getCurrentPageId,
|
||||
getCurrentPageName,
|
||||
} from "selectors/editorSelectors";
|
||||
import { fetchActionsForPage } from "actions/actionActions";
|
||||
import {
|
||||
fetchActionsForPage,
|
||||
setActionsToExecuteOnPageLoad,
|
||||
} from "actions/actionActions";
|
||||
import { clearCaches } from "utils/DynamicBindingUtils";
|
||||
import { UrlDataState } from "reducers/entityReducers/appReducer";
|
||||
import { getQueryParams } from "utils/AppsmithUtils";
|
||||
|
|
@ -245,6 +248,14 @@ function* savePageSaga() {
|
|||
);
|
||||
const isValidResponse = yield validateResponse(savePageResponse);
|
||||
if (isValidResponse) {
|
||||
if (
|
||||
savePageResponse.data.layoutOnLoadActions &&
|
||||
savePageResponse.data.layoutOnLoadActions.length > 0
|
||||
) {
|
||||
for (const actionSet of savePageResponse.data.layoutOnLoadActions) {
|
||||
yield put(setActionsToExecuteOnPageLoad(actionSet.map(a => a.id)));
|
||||
}
|
||||
}
|
||||
yield put(savePageSuccess(savePageResponse));
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -9258,6 +9258,11 @@ immer@1.10.0:
|
|||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
|
||||
immer@^7.0.8:
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.8.tgz#41dcbc5669a76500d017bef3ad0d03ce0a1d7c1e"
|
||||
integrity sha512-XnpIN8PXBBaOD43U8Z17qg6RQiKQYGDGGCIbz1ixmLGwBkSWwmrmx5X7d+hTtXDM8ur7m5OdLE0PiO+y5RB3pw==
|
||||
|
||||
immutable@3.8.2:
|
||||
version "3.8.2"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user