- {_.map(section.children, (propertyControlOrSection: ControlProps) => {
- if ("children" in propertyControlOrSection) {
- return this.renderEachConfig(propertyControlOrSection);
- } else {
- try {
- const {
- controlType,
- isRequired,
- configProperty,
- } = propertyControlOrSection;
- const config = { ...propertyControlOrSection };
- const multipleConfig = keyValueItems;
-
- this.configDetails[configProperty] = controlType;
-
- if (isRequired) {
- this.requiredFields[
- configProperty
- ] = propertyControlOrSection;
- }
-
- if (
- controlType === "KEYVALUE_ARRAY" &&
- keyValueItems.length < 2
- ) {
- keyValueItems.push(config);
-
- if (keyValueItems.length < 2) return undefined;
- }
-
- return (
-
-
-
- );
- } catch (e) {
- console.log(e);
- }
- }
- })}
+ renderSingleConfig = (
+ config: ControlProps,
+ multipleConfig?: ControlProps[],
+ ) => {
+ multipleConfig = multipleConfig || [];
+ try {
+ this.setupConfig(config);
+ return (
+
+
+ );
+ } catch (e) {
+ log.error(e);
+ }
+ };
+
+ setupConfig = (config: ControlProps) => {
+ const { controlType, isRequired, configProperty } = config;
+ this.configDetails[configProperty] = controlType;
+
+ if (isRequired) {
+ this.requiredFields[configProperty] = config;
+ }
+ };
+
+ isKVArray = (children: Array
) => {
+ if (!Array.isArray(children) || children.length < 2) return false;
+ return (
+ children[0].controlType && children[0].controlType === "KEYVALUE_ARRAY"
+ );
+ };
+
+ renderKVArray = (children: Array) => {
+ try {
+ // setup config for each child
+ children.forEach((c) => this.setupConfig(c));
+ // We pass last child for legacy reasons, to keep the logic here exactly same as before.
+ return this.renderSingleConfig(children[children.length - 1], children);
+ } catch (e) {
+ log.error(e);
+ }
+ };
+
+ renderEachConfig = (section: any) => {
+ return (
+
+ {_.map(section.children, (propertyControlOrSection: ControlProps) => {
+ // If the section is hidden, skip rendering
+ if (isHidden(this.props.formData, section.hidden)) return null;
+ if ("children" in propertyControlOrSection) {
+ const { children } = propertyControlOrSection;
+ if (this.isKVArray(children)) {
+ return this.renderKVArray(children);
+ }
+ return this.renderEachConfig(propertyControlOrSection);
+ } else {
+ return this.renderSingleConfig(propertyControlOrSection);
+ }
+ })}
);
- }
+ };
}
export default reduxForm({
diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
index e8f7c6db6e..0f3dc8a558 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
@@ -3,6 +3,8 @@ import React from "react";
import { map, get } from "lodash";
import { Colors } from "constants/Colors";
import styled from "styled-components";
+import { isHidden } from "components/formControls/utils";
+import log from "loglevel";
const Key = styled.div`
color: ${Colors.DOVE_GRAY};
@@ -14,7 +16,6 @@ const Value = styled.div`
font-size: 14px;
font-weight: 500;
display: inline-block;
- text-transform: uppercase;
margin-left: 5px;
`;
@@ -34,6 +35,7 @@ export const renderDatasourceSection = (
return (
{map(config.children, (section) => {
+ if (isHidden(datasource, section.hidden)) return null;
if ("children" in section) {
return renderDatasourceSection(section, datasource);
} else {
@@ -85,12 +87,25 @@ export const renderDatasourceSection = (
);
}
+ if (controlType === "DROP_DOWN") {
+ if (Array.isArray(section.options)) {
+ const option = section.options.find(
+ (el: any) => el.value === value,
+ );
+ if (option && option.label) {
+ value = option.label;
+ }
+ }
+ }
+
return (
{label}: {value}
);
- } catch (e) {}
+ } catch (e) {
+ log.error(e);
+ }
}
})}
diff --git a/app/client/src/pages/Editor/DataSourceEditor/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx
index 6f47d53974..89616cf9d0 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/index.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/index.tsx
@@ -21,6 +21,7 @@ import DatasourceHome from "./DatasourceHome";
import DataSourceEditorForm from "./DBForm";
import { Datasource } from "entities/Datasource";
import { RouteComponentProps } from "react-router";
+import EntityNotFoundPane from "pages/Editor/EntityNotFoundPane";
interface ReduxStateProps {
formData: Datasource;
@@ -88,7 +89,9 @@ class DataSourceEditor extends React.Component {
setDatasourceEditorMode,
pluginType,
} = this.props;
-
+ if (!pluginId && datasourceId) {
+ return ;
+ }
return (
{datasourceId ? (
diff --git a/app/client/src/pages/Editor/EntityNotFoundPane.tsx b/app/client/src/pages/Editor/EntityNotFoundPane.tsx
new file mode 100644
index 0000000000..94703ee6d6
--- /dev/null
+++ b/app/client/src/pages/Editor/EntityNotFoundPane.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+import styled from "styled-components";
+import Button, { Size, Category } from "components/ads/Button";
+import PageUnavailableImage from "assets/images/invalid-page.png";
+import { PAGE_NOT_FOUND_ERROR, INVALID_URL_ERROR } from "constants/messages";
+import { useHistory } from "react-router-dom";
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ align-items: center;
+ justify-content: flex-start;
+ padding-top: 15%;
+ background: #fcfcfc;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ .page-details {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 450px;
+ }
+ .bold-text {
+ font-weight: ${(props) => props.theme.fontWeights[3]};
+ font-size: 24px;
+ margin-top: 20px;
+ }
+ .page-message {
+ margin-top: 14px;
+ color: #716e6e;
+ font-size: 14px;
+ line-height: 17px;
+ letter-spacing: 0.733333px;
+ }
+ .page-unavailable-img {
+ width: 72px;
+ }
+ .button-position {
+ margin-top: 14px;
+ }
+`;
+
+const EntityNotFoundPane = () => {
+ const history = useHistory();
+ return (
+
+
+
+
{INVALID_URL_ERROR}
+
{PAGE_NOT_FOUND_ERROR}
+
+
+
+ );
+};
+
+export default EntityNotFoundPane;
diff --git a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx
index d6825b0192..ac21089eb0 100644
--- a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx
+++ b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx
@@ -62,7 +62,7 @@ export const EntityProperties = memo(
actionProperty = actionProperty + "()";
}
if (actionProperty === "data") {
- value = entity.data;
+ value = entity.data?.body;
}
return {
propertyName: actionProperty,
diff --git a/app/client/src/pages/Editor/Explorer/Entity/index.tsx b/app/client/src/pages/Editor/Explorer/Entity/index.tsx
index b2f18dbba3..5f48fe0640 100644
--- a/app/client/src/pages/Editor/Explorer/Entity/index.tsx
+++ b/app/client/src/pages/Editor/Explorer/Entity/index.tsx
@@ -37,7 +37,8 @@ export const EntityItem = styled.div<{
}>`
position: relative;
font-size: 12px;
- padding-left: ${(props) => props.step * props.theme.spaces[2]}px;
+ padding-left: ${(props) =>
+ props.step * props.theme.spaces[2] + props.theme.spaces[2]}px;
background: ${(props) => (props.active ? Colors.TUNDORA : "none")};
height: 30px;
width: 100%;
diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx
index 9fa198df11..fc874ddfbe 100644
--- a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx
+++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx
@@ -9,7 +9,6 @@ import {
} from "./hooks";
import Search from "./ExplorerSearch";
import ExplorerPageGroup from "./Pages/PageGroup";
-import { scrollbarDark } from "constants/DefaultTheme";
import { NonIdealState, Classes, IPanelProps } from "@blueprintjs/core";
import WidgetSidebar from "../WidgetSidebar";
import { BUILDER_PAGE_URL } from "constants/routes";
@@ -22,11 +21,17 @@ import PerformanceTracker, {
} from "utils/PerformanceTracker";
import { useSelector } from "react-redux";
import { getPlugins } from "selectors/entitiesSelector";
+import ScrollIndicator from "components/designSystems/appsmith/ScrollIndicator";
const Wrapper = styled.div`
height: 100%;
- overflow-y: scroll;
- ${scrollbarDark};
+ overflow-y: auto;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+ &::-webkit-scrollbar {
+ width: 0px;
+ -webkit-appearance: none;
+ }
`;
const NoResult = styled(NonIdealState)`
@@ -41,6 +46,7 @@ const StyledDivider = styled(Divider)`
const EntityExplorer = (props: IPanelProps) => {
const { applicationId } = useParams();
+
const searchInputRef: MutableRefObject = useRef(
null,
);
@@ -99,6 +105,7 @@ const EntityExplorer = (props: IPanelProps) => {
)}
+
);
};
diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx
index fe50cabc6c..0d1c433557 100644
--- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx
+++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx
@@ -17,7 +17,6 @@ import { useWidgetSelection } from "utils/hooks/dragResizeHooks";
import { AppState } from "reducers";
import { getWidgetIcon } from "../ExplorerIcons";
-import { noop } from "lodash";
import WidgetContextMenu from "./WidgetContextMenu";
import { updateWidgetName } from "actions/propertyPaneActions";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
@@ -133,7 +132,7 @@ export const WidgetEntity = memo((props: WidgetEntityProps) => {
name={props.widgetName}
entityId={props.widgetId}
step={props.step}
- updateEntityName={props.pageId === pageId ? updateWidgetName : noop}
+ updateEntityName={props.pageId === pageId ? updateWidgetName : undefined}
searchKeyword={props.searchKeyword}
isDefaultExpanded={
shouldExpand ||
diff --git a/app/client/src/pages/UserAuth/FooterLinks.tsx b/app/client/src/pages/UserAuth/FooterLinks.tsx
new file mode 100644
index 0000000000..7cf506fb68
--- /dev/null
+++ b/app/client/src/pages/UserAuth/FooterLinks.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+import styled from "styled-components";
+
+const FooterLink = styled.a`
+ cursor: pointer;
+ text-decoration: none;
+ :hover {
+ text-decoration: underline;
+ color: ${(props) => props.theme.colors.text.normal};
+ }
+ font-weight: ${(props) => props.theme.typography.releaseList.fontWeight};
+ font-size: ${(props) => props.theme.typography.releaseList.fontSize}px;
+ line-height: ${(props) => props.theme.typography.releaseList.lineHeight}px;
+ letter-spacing: ${(props) =>
+ props.theme.typography.releaseList.letterSpacing}px;
+ color: ${(props) => props.theme.colors.text.normal};
+`;
+
+const FooterLinksContainer = styled.div`
+ padding: ${(props) => props.theme.spaces[9]}px;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ width: 100%;
+ max-width: ${(props) => props.theme.authCard.width}px;
+`;
+
+const FooterLinks = () => (
+
+
+ Privacy Policy
+
+
+ Terms and conditions
+
+
+);
+
+export default FooterLinks;
diff --git a/app/client/src/pages/UserAuth/ForgotPassword.tsx b/app/client/src/pages/UserAuth/ForgotPassword.tsx
index e090f5e603..daa119e10f 100644
--- a/app/client/src/pages/UserAuth/ForgotPassword.tsx
+++ b/app/client/src/pages/UserAuth/ForgotPassword.tsx
@@ -4,17 +4,17 @@ import { withRouter, RouteComponentProps } from "react-router-dom";
import { reduxForm, InjectedFormProps, formValueSelector } from "redux-form";
import StyledForm from "components/editorComponents/Form";
import {
- AuthCardContainer,
AuthCardHeader,
- AuthCardBody,
FormActions,
AuthCardNavLink,
+ FormMessagesContainer,
} from "./StyledComponents";
+import { withTheme } from "styled-components";
+import { Theme } from "constants/DefaultTheme";
import {
FORGOT_PASSWORD_PAGE_EMAIL_INPUT_LABEL,
FORGOT_PASSWORD_PAGE_EMAIL_INPUT_PLACEHOLDER,
FORGOT_PASSWORD_PAGE_SUBMIT_BUTTON_TEXT,
- FORGOT_PASSWORD_PAGE_SUBTITLE,
FORGOT_PASSWORD_PAGE_TITLE,
FORM_VALIDATION_EMPTY_EMAIL,
FORM_VALIDATION_INVALID_EMAIL,
@@ -22,11 +22,12 @@ import {
FORGOT_PASSWORD_PAGE_LOGIN_LINK,
} from "constants/messages";
import { AUTH_LOGIN_URL } from "constants/routes";
-import FormMessage from "components/editorComponents/form/FormMessage";
+import FormMessage from "components/ads/formFields/FormMessage";
import { FORGOT_PASSWORD_FORM_NAME } from "constants/forms";
-import FormGroup from "components/editorComponents/form/FormGroup";
-import Button from "components/editorComponents/Button";
-import FormTextField from "components/editorComponents/form/FormTextField";
+import FormGroup from "components/ads/formFields/FormGroup";
+import Button, { Size } from "components/ads/Button";
+import FormTextField from "components/ads/formFields/TextField";
+import { Icon } from "@blueprintjs/core";
import { isEmail, isEmptyString } from "utils/formhelpers";
import {
ForgotPasswordFormValues,
@@ -52,46 +53,56 @@ type ForgotPasswordProps = InjectedFormProps<
> &
RouteComponentProps<{ email: string }> & { emailValue: string };
-export const ForgotPassword = (props: ForgotPasswordProps) => {
- const {
- error,
- handleSubmit,
- submitting,
- submitFailed,
- submitSucceeded,
- } = props;
+export const ForgotPassword = withTheme(
+ (props: ForgotPasswordProps & { theme: Theme }) => {
+ const {
+ error,
+ handleSubmit,
+ submitting,
+ submitFailed,
+ submitSucceeded,
+ } = props;
- return (
-
- {submitSucceeded && (
-
- )}
- {!mailEnabled && (
-
- )}
- {submitFailed && error && (
-
- )}
-
- {FORGOT_PASSWORD_PAGE_TITLE}
- {FORGOT_PASSWORD_PAGE_SUBTITLE}
-
-
+ return (
+ <>
+
+ {FORGOT_PASSWORD_PAGE_TITLE}
+
+
+
+
+ {FORGOT_PASSWORD_PAGE_LOGIN_LINK}
+
+
+
+ {submitSucceeded && (
+
+ )}
+ {!mailEnabled && (
+
+ )}
+ {submitFailed && error && (
+
+ )}
+
{
-
-
- {FORGOT_PASSWORD_PAGE_LOGIN_LINK}
-
-
- );
-};
+ >
+ );
+ },
+);
const selector = formValueSelector(FORGOT_PASSWORD_FORM_NAME);
diff --git a/app/client/src/pages/UserAuth/Login.tsx b/app/client/src/pages/UserAuth/Login.tsx
index 8850421fce..e8ce8f4e0b 100644
--- a/app/client/src/pages/UserAuth/Login.tsx
+++ b/app/client/src/pages/UserAuth/Login.tsx
@@ -9,13 +9,11 @@ import {
} from "constants/forms";
import { FORGOT_PASSWORD_URL, SIGN_UP_URL } from "constants/routes";
import {
- LOGIN_PAGE_SUBTITLE,
LOGIN_PAGE_TITLE,
LOGIN_PAGE_EMAIL_INPUT_LABEL,
LOGIN_PAGE_PASSWORD_INPUT_LABEL,
LOGIN_PAGE_PASSWORD_INPUT_PLACEHOLDER,
LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER,
- FORM_VALIDATION_EMPTY_EMAIL,
FORM_VALIDATION_EMPTY_PASSWORD,
FORM_VALIDATION_INVALID_EMAIL,
FORM_VALIDATION_INVALID_PASSWORD,
@@ -24,29 +22,28 @@ import {
LOGIN_PAGE_SIGN_UP_LINK_TEXT,
LOGIN_PAGE_INVALID_CREDS_ERROR,
LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK,
- FORM_VALIDATION_PASSWORD_RULE,
+ NEW_TO_APPSMITH,
} from "constants/messages";
-import Divider from "components/editorComponents/Divider";
-import FormMessage from "components/editorComponents/form/FormMessage";
-import FormGroup from "components/editorComponents/form/FormGroup";
-import FormTextField from "components/editorComponents/form/FormTextField";
-import Button from "components/editorComponents/Button";
+import FormMessage from "components/ads/formFields/FormMessage";
+import FormGroup from "components/ads/formFields/FormGroup";
+import FormTextField from "components/ads/formFields/TextField";
+import Button, { Size } from "components/ads/Button";
import ThirdPartyAuth, { SocialLoginTypes } from "./ThirdPartyAuth";
import { isEmail, isStrongPassword, isEmptyString } from "utils/formhelpers";
import { LoginFormValues } from "./helpers";
+import { withTheme } from "styled-components";
+import { Theme } from "constants/DefaultTheme";
import {
- AuthCardContainer,
SpacedSubmitForm,
FormActions,
AuthCardHeader,
- AuthCardFooter,
AuthCardNavLink,
- AuthCardBody,
+ SignUpLinkSection,
+ ForgotPasswordLink,
} from "./StyledComponents";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { getAppsmithConfigs } from "configs";
-import { TncPPLinks } from "./SignUp";
import { LOGIN_SUBMIT_PATH } from "constants/ApiConstants";
import PerformanceTracker, {
PerformanceTransactionName,
@@ -55,16 +52,14 @@ const { enableGithubOAuth, enableGoogleOAuth } = getAppsmithConfigs();
const validate = (values: LoginFormValues) => {
const errors: LoginFormValues = {};
- const email = values[LOGIN_FORM_EMAIL_FIELD_NAME];
+ const email = values[LOGIN_FORM_EMAIL_FIELD_NAME] || "";
const password = values[LOGIN_FORM_PASSWORD_FIELD_NAME];
if (!password || isEmptyString(password)) {
errors[LOGIN_FORM_PASSWORD_FIELD_NAME] = FORM_VALIDATION_EMPTY_PASSWORD;
} else if (!isStrongPassword(password)) {
errors[LOGIN_FORM_PASSWORD_FIELD_NAME] = FORM_VALIDATION_INVALID_PASSWORD;
}
- if (!email || isEmptyString(email)) {
- errors[LOGIN_FORM_EMAIL_FIELD_NAME] = FORM_VALIDATION_EMPTY_EMAIL;
- } else if (!isEmail(email)) {
+ if (!isEmptyString(email) && !isEmail(email)) {
errors[LOGIN_FORM_EMAIL_FIELD_NAME] = FORM_VALIDATION_INVALID_EMAIL;
}
@@ -74,14 +69,17 @@ const validate = (values: LoginFormValues) => {
type LoginFormProps = { emailValue: string } & InjectedFormProps<
LoginFormValues,
{ emailValue: string }
->;
+> & {
+ theme: Theme;
+ };
const SocialLoginList: string[] = [];
-if (enableGithubOAuth) SocialLoginList.push(SocialLoginTypes.GITHUB);
if (enableGoogleOAuth) SocialLoginList.push(SocialLoginTypes.GOOGLE);
+if (enableGithubOAuth) SocialLoginList.push(SocialLoginTypes.GITHUB);
export const Login = (props: LoginFormProps) => {
- const { error, valid } = props;
+ const { error, valid, emailValue: email } = props;
+ const isFormValid = valid && email && !isEmptyString(email);
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
@@ -103,7 +101,19 @@ export const Login = (props: LoginFormProps) => {
}
return (
-
+ <>
+
+ {LOGIN_PAGE_TITLE}
+
+
+ {NEW_TO_APPSMITH}
+
+ {LOGIN_PAGE_SIGN_UP_LINK_TEXT}
+
+
{showError && (
{
]}
/>
)}
-
- {LOGIN_PAGE_TITLE}
- {LOGIN_PAGE_SUBTITLE}
-
-
-
-
-
-
-
-
-
- {LOGIN_PAGE_FORGOT_PASSWORD_TEXT}
-
-
-
- {SocialLoginList.length > 0 && (
- <>
-
-
- >
- )}
-
-
- {LOGIN_PAGE_SIGN_UP_LINK_TEXT}
-
-
-
-
-
+ {SocialLoginList.length > 0 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ {LOGIN_PAGE_FORGOT_PASSWORD_TEXT}
+
+ >
);
};
@@ -190,5 +188,5 @@ export default connect((state) => ({
validate,
touchOnBlur: true,
form: LOGIN_FORM_NAME,
- })(Login),
+ })(withTheme(Login)),
);
diff --git a/app/client/src/pages/UserAuth/ResetPassword.tsx b/app/client/src/pages/UserAuth/ResetPassword.tsx
index 9051027b3c..02e7e1a12c 100644
--- a/app/client/src/pages/UserAuth/ResetPassword.tsx
+++ b/app/client/src/pages/UserAuth/ResetPassword.tsx
@@ -7,33 +7,32 @@ import { RESET_PASSWORD_FORM_NAME } from "constants/forms";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { getIsTokenValid, getIsValidatingToken } from "selectors/authSelectors";
import { Icon } from "@blueprintjs/core";
-import FormTextField from "components/editorComponents/form/fields/TextField";
+import FormGroup from "components/ads/formFields/FormGroup";
+import FormTextField from "components/ads/formFields/TextField";
import FormMessage, {
- FormMessageProps,
MessageAction,
-} from "components/editorComponents/form/FormMessage";
+ FormMessageProps,
+} from "components/ads/formFields/FormMessage";
import Spinner from "components/editorComponents/Spinner";
-import Button from "components/editorComponents/Button";
-import FormGroup from "components/editorComponents/form/FormGroup";
+import Button, { Size } from "components/ads/Button";
+
import StyledForm from "components/editorComponents/Form";
import { isEmptyString, isStrongPassword } from "utils/formhelpers";
import { ResetPasswordFormValues, resetPasswordSubmitHandler } from "./helpers";
import {
AuthCardHeader,
- AuthCardFooter,
- AuthCardContainer,
- AuthCardBody,
AuthCardNavLink,
FormActions,
} from "./StyledComponents";
import { AUTH_LOGIN_URL, FORGOT_PASSWORD_URL } from "constants/routes";
+import { withTheme } from "styled-components";
+import { Theme } from "constants/DefaultTheme";
import {
RESET_PASSWORD_PAGE_PASSWORD_INPUT_LABEL,
RESET_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER,
RESET_PASSWORD_LOGIN_LINK_TEXT,
RESET_PASSWORD_SUBMIT_BUTTON_TEXT,
- RESET_PASSWORD_PAGE_SUBTITLE,
RESET_PASSWORD_PAGE_TITLE,
FORM_VALIDATION_INVALID_PASSWORD,
FORM_VALIDATION_EMPTY_PASSWORD,
@@ -43,7 +42,6 @@ import {
RESET_PASSWORD_RESET_SUCCESS,
RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK,
} from "constants/messages";
-import { TncPPLinks } from "./SignUp";
const validate = (values: ResetPasswordFormValues) => {
const errors: ResetPasswordFormValues = {};
@@ -66,6 +64,7 @@ type ResetPasswordProps = InjectedFormProps<
verifyToken: (token: string, email: string) => void;
isTokenValid: boolean;
validatingToken: boolean;
+ theme: Theme;
} & RouteComponentProps<{ email: string; token: string }>;
export const ResetPassword = (props: ResetPasswordProps) => {
@@ -154,50 +153,49 @@ export const ResetPassword = (props: ResetPasswordProps) => {
return ;
}
return (
-
+ <>
+
+ {RESET_PASSWORD_PAGE_TITLE}
+
+
+
+
+ {RESET_PASSWORD_LOGIN_LINK_TEXT}
+
+
{(showSuccessMessage || showFailureMessage) && (
)}
-
- {RESET_PASSWORD_PAGE_TITLE}
- {RESET_PASSWORD_PAGE_SUBTITLE}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {RESET_PASSWORD_LOGIN_LINK_TEXT}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ >
);
};
@@ -232,5 +230,5 @@ export default connect(
validate,
form: RESET_PASSWORD_FORM_NAME,
touchOnBlur: true,
- })(withRouter(ResetPassword)),
+ })(withRouter(withTheme(ResetPassword))),
);
diff --git a/app/client/src/pages/UserAuth/SignUp.tsx b/app/client/src/pages/UserAuth/SignUp.tsx
index 364fd9a986..11f3dab7b4 100644
--- a/app/client/src/pages/UserAuth/SignUp.tsx
+++ b/app/client/src/pages/UserAuth/SignUp.tsx
@@ -1,45 +1,33 @@
import React from "react";
-import { reduxForm, InjectedFormProps } from "redux-form";
+import { reduxForm, InjectedFormProps, formValueSelector } from "redux-form";
import { AUTH_LOGIN_URL } from "constants/routes";
import { SIGNUP_FORM_NAME } from "constants/forms";
-import {
- Link,
- RouteComponentProps,
- useLocation,
- withRouter,
-} from "react-router-dom";
-import Divider from "components/editorComponents/Divider";
+import { RouteComponentProps, useLocation, withRouter } from "react-router-dom";
import {
AuthCardHeader,
- AuthCardBody,
- AuthCardFooter,
AuthCardNavLink,
SpacedSubmitForm,
FormActions,
- AuthCardContainer,
+ SignUpLinkSection,
} from "./StyledComponents";
import {
SIGNUP_PAGE_TITLE,
- SIGNUP_PAGE_SUBTITLE,
SIGNUP_PAGE_EMAIL_INPUT_LABEL,
SIGNUP_PAGE_EMAIL_INPUT_PLACEHOLDER,
SIGNUP_PAGE_PASSWORD_INPUT_LABEL,
SIGNUP_PAGE_PASSWORD_INPUT_PLACEHOLDER,
SIGNUP_PAGE_LOGIN_LINK_TEXT,
- FORM_VALIDATION_EMPTY_EMAIL,
FORM_VALIDATION_EMPTY_PASSWORD,
FORM_VALIDATION_INVALID_EMAIL,
FORM_VALIDATION_INVALID_PASSWORD,
SIGNUP_PAGE_SUBMIT_BUTTON_TEXT,
- PRIVACY_POLICY_LINK,
- TERMS_AND_CONDITIONS_LINK,
- FORM_VALIDATION_PASSWORD_RULE,
+ ALREADY_HAVE_AN_ACCOUNT,
} from "constants/messages";
-import FormMessage from "components/editorComponents/form/FormMessage";
-import FormGroup from "components/editorComponents/form/FormGroup";
-import FormTextField from "components/editorComponents/form/FormTextField";
+import FormMessage from "components/ads/formFields/FormMessage";
+import FormGroup from "components/ads/formFields/FormGroup";
+import FormTextField from "components/ads/formFields/TextField";
import ThirdPartyAuth, { SocialLoginTypes } from "./ThirdPartyAuth";
-import Button from "components/editorComponents/Button";
+import Button, { Size } from "components/ads/Button";
import { isEmail, isStrongPassword, isEmptyString } from "utils/formhelpers";
@@ -54,28 +42,16 @@ import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
import { setOnboardingState } from "utils/storage";
-const {
- enableGithubOAuth,
- enableGoogleOAuth,
- enableTNCPP,
-} = getAppsmithConfigs();
+
+import { SIGNUP_FORM_EMAIL_FIELD_NAME } from "constants/forms";
+
+const { enableGithubOAuth, enableGoogleOAuth } = getAppsmithConfigs();
const SocialLoginList: string[] = [];
if (enableGithubOAuth) SocialLoginList.push(SocialLoginTypes.GITHUB);
if (enableGoogleOAuth) SocialLoginList.push(SocialLoginTypes.GOOGLE);
-export const TncPPLinks = () => {
- if (!enableTNCPP) return null;
- return (
- <>
-
- {PRIVACY_POLICY_LINK}
-
-
- {TERMS_AND_CONDITIONS_LINK}
-
- >
- );
-};
+import { withTheme } from "styled-components";
+import { Theme } from "constants/DefaultTheme";
const validate = (values: SignupFormValues) => {
const errors: SignupFormValues = {};
@@ -84,19 +60,24 @@ const validate = (values: SignupFormValues) => {
} else if (!isStrongPassword(values.password)) {
errors.password = FORM_VALIDATION_INVALID_PASSWORD;
}
- if (!values.email || isEmptyString(values.email)) {
- errors.email = FORM_VALIDATION_EMPTY_EMAIL;
- } else if (!isEmail(values.email)) {
+
+ const email = values.email || "";
+ if (!isEmptyString(email) && !isEmail(email)) {
errors.email = FORM_VALIDATION_INVALID_EMAIL;
}
return errors;
};
-type SignUpFormProps = InjectedFormProps &
- RouteComponentProps<{ email: string }>;
+type SignUpFormProps = InjectedFormProps<
+ SignupFormValues,
+ { emailValue: string }
+> &
+ RouteComponentProps<{ email: string }> & { theme: Theme; emailValue: string };
export const SignUp = (props: SignUpFormProps) => {
- const { error, submitting, pristine, valid } = props;
+ const { error, submitting, pristine, valid, emailValue: email } = props;
+ const isFormValid = valid && email && !isEmptyString(email);
+
const location = useLocation();
let showError = false;
@@ -115,85 +96,84 @@ export const SignUp = (props: SignUpFormProps) => {
}
return (
-
+ <>
{showError && }
{SIGNUP_PAGE_TITLE}
- {SIGNUP_PAGE_SUBTITLE}
-
-
-
-
-
-
-
-
-
-
-
- {SocialLoginList.length > 0 && (
- <>
-
-
- >
- )}
-
-
-
-
-
- {SIGNUP_PAGE_LOGIN_LINK_TEXT}
-
-
+
+ {ALREADY_HAVE_AN_ACCOUNT}
+
+ {SIGNUP_PAGE_LOGIN_LINK_TEXT}
+
+
+ {SocialLoginList.length > 0 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ >
);
};
+const selector = formValueSelector(SIGNUP_FORM_NAME);
export default connect((state: AppState, props: SignUpFormProps) => {
const queryParams = new URLSearchParams(props.location.search);
return {
initialValues: {
email: queryParams.get("email"),
},
+ emailValue: selector(state, SIGNUP_FORM_EMAIL_FIELD_NAME),
};
}, null)(
- reduxForm({
+ reduxForm({
validate,
form: SIGNUP_FORM_NAME,
touchOnBlur: true,
- })(withRouter(SignUp)),
+ })(withRouter(withTheme(SignUp))),
);
diff --git a/app/client/src/pages/UserAuth/StyledComponents.tsx b/app/client/src/pages/UserAuth/StyledComponents.tsx
index 84fee636fb..a1a0818a96 100644
--- a/app/client/src/pages/UserAuth/StyledComponents.tsx
+++ b/app/client/src/pages/UserAuth/StyledComponents.tsx
@@ -1,38 +1,69 @@
-import styled, { css } from "styled-components";
+import styled from "styled-components";
import { Link } from "react-router-dom";
import Form from "components/editorComponents/Form";
import { Card } from "@blueprintjs/core";
+import { getTypographyByKey } from "constants/DefaultTheme";
+import { Classes } from "@blueprintjs/core";
export const AuthContainer = styled.section`
position: absolute;
width: 100%;
- height: 100vh;
- will-change: transform, opacity;
-`;
+ height: ${(props) => `calc(100vh - ${props.theme.headerHeight})`};
+ background-color: ${(props) => props.theme.colors.auth.background};
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow: auto;
-export const AuthCard = styled(Card)`
- && {
- width: ${(props) => props.theme.authCard.width}px;
- background: ${(props) => props.theme.authCard.background};
- border-radius: ${(props) => props.theme.authCard.borderRadius}px;
- padding: ${(props) => props.theme.authCard.padding}px;
- box-shadow: ${(props) => props.theme.authCard.shadow};
- border: none;
- & h1,
- h5 {
- padding: 0;
- margin: 0;
- font-weight: ${(props) => props.theme.fontWeights[1]};
- }
+ & .${Classes.FORM_GROUP} {
+ margin: 0 0 ${(props) => props.theme.spaces[2]}px;
}
`;
-export const AuthCardContainer = styled.div``;
+export const AuthCardContainer = styled.div`
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ justify-content: center;
+ padding: ${(props) => props.theme.authCard.padding}px 0;
+`;
+
+export const AuthCard = styled(Card)`
+ display: flex;
+ flex-direction: column;
+ background-color: ${(props) => props.theme.colors.auth.cardBackground};
+ padding: ${(props) => props.theme.spaces[15]}px 64px;
+ width: ${(props) => props.theme.authCard.width}px;
+ border: none;
+ h1 {
+ text-align: center;
+ padding: 0;
+ margin: 0;
+ ${(props) => getTypographyByKey(props, "authCardHeader")}
+ color: ${(props) => props.theme.colors.auth.headingText};
+ }
+ & .form-message-container {
+ width: ${(props) => props.theme.authCard.formMessageWidth}px;
+ align-self: center;
+ text-align: center;
+ }
+ .form-message-container ~ .form-message-container {
+ margin-top: ${(props) => props.theme.spaces[4]}px;
+ }
+ & > div {
+ margin-bottom: ${(props) => props.theme.spaces[14]}px;
+ }
+ & > div:last-child,
+ & > div:empty {
+ margin-bottom: 0;
+ }
+`;
export const AuthCardHeader = styled.header`
& {
h1 {
font-size: ${(props) => props.theme.fontSizes[6]}px;
+ white-space: nowrap;
}
h5 {
font-size: ${(props) => props.theme.fontSizes[4]}px;
@@ -42,12 +73,10 @@ export const AuthCardHeader = styled.header`
`;
export const AuthCardNavLink = styled(Link)`
- text-align: center;
- margin: 0 auto;
- display: block;
- margin-top: ${(props) => props.theme.spaces[12]}px;
- & span {
- margin-left: ${(props) => props.theme.spaces[4]}px;
+ border-bottom: 1px solid transparent;
+ &:hover {
+ border-bottom: 1px solid ${(props) => props.theme.colors.auth.link};
+ text-decoration: none;
}
`;
@@ -60,26 +89,15 @@ export const AuthCardFooter = styled.footer`
`;
export const AuthCardBody = styled.div`
- display: flex;
- justify-content: flex-start;
- align-items: stretch;
& a {
margin-top: ${(props) => props.theme.spaces[8]}px;
font-size: ${(props) => props.theme.fontSizes[2]}px;
}
`;
-const formSpacing = css`
- flex-grow: 1;
- margin-right: ${(props) => props.theme.authCard.dividerSpacing}px;
-`;
-
-export const SpacedForm = styled(Form)`
- ${formSpacing}
-`;
+export const SpacedForm = styled(Form)``;
export const SpacedSubmitForm = styled.form`
- ${formSpacing}
& a {
font-size: ${(props) => props.theme.fontSizes[3]}px;
}
@@ -95,8 +113,29 @@ export const FormActions = styled.div`
}
justify-content: space-between;
align-items: baseline;
- margin-top: ${(props) => props.theme.spaces[2]}px;
+ margin-top: ${(props) => props.theme.spaces[5]}px;
& > label {
margin-right: ${(props) => props.theme.spaces[11]}px;
}
`;
+
+export const SignUpLinkSection = styled.div`
+ ${(props) => getTypographyByKey(props, "authCardSubheader")}
+ color: ${(props) => props.theme.colors.auth.text};
+ text-align: center;
+`;
+
+export const ForgotPasswordLink = styled.div`
+ ${(props) => getTypographyByKey(props, "authCardSubheader")}
+ color: ${(props) => props.theme.colors.auth.text};
+ text-align: center;
+ margin-top: ${(props) => props.theme.spaces[11]}px;
+ & a {
+ color: ${(props) => props.theme.colors.auth.text};
+ }
+`;
+
+export const FormMessagesContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
diff --git a/app/client/src/pages/UserAuth/ThirdPartyAuth.tsx b/app/client/src/pages/UserAuth/ThirdPartyAuth.tsx
index 30cbfb01db..a608d9e778 100644
--- a/app/client/src/pages/UserAuth/ThirdPartyAuth.tsx
+++ b/app/client/src/pages/UserAuth/ThirdPartyAuth.tsx
@@ -4,7 +4,7 @@ import {
getSocialLoginButtonProps,
SocialLoginType,
} from "constants/SocialLogin";
-import { IntentColors, getBorderCSSShorthand } from "constants/DefaultTheme";
+import { getTypographyByKey } from "constants/DefaultTheme";
import AnalyticsUtil, { EventName } from "utils/AnalyticsUtil";
import { useLocation } from "react-router-dom";
import PerformanceTracker, {
@@ -15,52 +15,42 @@ import { setOnboardingState } from "utils/storage";
const ThirdPartyAuthWrapper = styled.div`
display: flex;
flex-direction: column;
- justify-content: flex-start;
- align-items: flex-end;
- margin-left: ${(props) => props.theme.authCard.dividerSpacing}px;
`;
//TODO(abhinav): Port this to use themes.
const StyledSocialLoginButton = styled.a`
- width: 200px;
display: flex;
align-items: center;
- border: ${(props) => getBorderCSSShorthand(props.theme.borders[2])};
- padding: 8px;
- color: ${(props) => props.theme.colors.textDefault};
- border-radius: ${(props) => props.theme.radii[1]}px;
- position: relative;
- height: 42px;
+ justify-content: center;
+ border: solid 1px ${(props) => props.theme.colors.auth.socialBtnBorder};
+ padding: ${(props) => props.theme.spaces[2]}px;
+
+ &:first-child {
+ margin-bottom: ${(props) => props.theme.spaces[4]}px;
+ }
+
+ &:only-child {
+ margin-bottom: 0;
+ }
&:hover {
text-decoration: none;
- background: ${IntentColors.success};
- color: ${(props) => props.theme.colors.textOnDarkBG};
+ background-color: ${(props) => props.theme.colors.auth.socialBtnHighlight};
}
- & > div {
- width: 36px;
- height: 36px;
- padding: ${(props) => props.theme.radii[1]}px;
- position: absolute;
- left: 2px;
- top: 2px;
- background: white;
- display: flex;
- justify-content: center;
- align-items: center;
- & img {
- width: 80%;
- height: 80%;
- }
- }
- & p {
- display: block;
- margin: 0 0 0 36px;
- font-size: ${(props) => props.theme.fontSizes[3]}px;
- font-weight: ${(props) => props.theme.fontWeights[3]};
+
+ & .login-method {
+ ${(props) => getTypographyByKey(props, "btnLarge")}
+ color: ${(props) => props.theme.colors.auth.socialBtnText};
+ text-transform: uppercase;
}
`;
+const ButtonLogo = styled.img`
+ margin: ${(props) => props.theme.spaces[2]}px;
+ width: 14px;
+ height: 14px;
+`;
+
export const SocialLoginTypes: Record = {
GOOGLE: "google",
GITHUB: "github",
@@ -102,10 +92,8 @@ const SocialLoginButton = (props: {
});
}}
>
-
-

-
- {`Sign in with ${props.name}`}
+
+ {`continue with ${props.name}`}
);
};
diff --git a/app/client/src/pages/UserAuth/index.tsx b/app/client/src/pages/UserAuth/index.tsx
index a2ea945f90..2a9aa79df2 100644
--- a/app/client/src/pages/UserAuth/index.tsx
+++ b/app/client/src/pages/UserAuth/index.tsx
@@ -1,52 +1,43 @@
import React from "react";
import { Switch, useRouteMatch, useLocation, Route } from "react-router-dom";
import Login from "./Login";
-import Centered from "components/designSystems/appsmith/CenteredWrapper";
-import { animated, useTransition } from "react-spring";
-import { AuthContainer, AuthCard } from "./StyledComponents";
+import { AuthContainer, AuthCard, AuthCardContainer } from "./StyledComponents";
import SignUp from "./SignUp";
import ForgotPassword from "./ForgotPassword";
import ResetPassword from "./ResetPassword";
import PageNotFound from "pages/common/PageNotFound";
+import FooterLinks from "./FooterLinks";
import * as Sentry from "@sentry/react";
const SentryRoute = Sentry.withSentryRouting(Route);
-const AnimatedAuthCard = animated(AuthContainer);
export const UserAuth = () => {
const { path } = useRouteMatch();
const location = useLocation();
- const transitions = useTransition(location, (location) => location.pathname, {
- from: { opacity: 0, transform: "translate3d(50%,0,0)" },
- enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
- leave: { opacity: 0, transform: "translate3d(-50%,0,0)" },
- reset: true,
- });
- const renderTransitions = transitions.map(
- ({ item: location, props, key }) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
- ),
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
- return {renderTransitions};
};
export default UserAuth;
diff --git a/app/client/src/pages/common/LoginHeader.tsx b/app/client/src/pages/common/LoginHeader.tsx
index 3e3e38d87a..36ac879ded 100644
--- a/app/client/src/pages/common/LoginHeader.tsx
+++ b/app/client/src/pages/common/LoginHeader.tsx
@@ -14,7 +14,7 @@ const StyledPageHeader = styled(StyledHeader)`
height: 48px;
background: ${Colors.BALTIC_SEA};
display: flex;
- justify-content: space-between;
+ justify-content: center;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.05);
padding: 0px ${(props) => props.theme.spaces[12]};
`;
diff --git a/app/client/src/pages/organization/Members.tsx b/app/client/src/pages/organization/Members.tsx
index abdc1c20ed..80303eec0f 100644
--- a/app/client/src/pages/organization/Members.tsx
+++ b/app/client/src/pages/organization/Members.tsx
@@ -128,6 +128,7 @@ export default function MemberSettings(props: PageProps) {
{
Header: "Delete",
accessor: "delete",
+ disableSortBy: true,
Cell: function DeleteCell(cellProps: any) {
if (
cellProps.cell.row.values.username ===
diff --git a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
index fa01e3eb7f..ed42e46d6d 100644
--- a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
+++ b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
@@ -6,6 +6,7 @@ import {
} from "constants/ReduxActionConstants";
import { WidgetProps } from "widgets/BaseWidget";
import { UpdateWidgetPropertyPayload } from "actions/controlActions";
+import _ from "lodash";
const initialState: CanvasWidgetsReduxState = {};
@@ -14,8 +15,7 @@ export type FlattenedWidgetProps = WidgetProps & {
};
const canvasWidgetsReducer = createImmerReducer(initialState, {
- // TODO Rename to INIT_LAYOUT
- [ReduxActionTypes.UPDATE_CANVAS]: (
+ [ReduxActionTypes.INIT_CANVAS_LAYOUT]: (
state: CanvasWidgetsReduxState,
action: ReduxAction,
) => {
@@ -31,8 +31,13 @@ const canvasWidgetsReducer = createImmerReducer(initialState, {
state: CanvasWidgetsReduxState,
action: ReduxAction,
) => {
- state[action.payload.widgetId][action.payload.propertyName] =
- action.payload.propertyValue;
+ // We loop over all updates
+ Object.entries(action.payload.updates).forEach(
+ ([propertyPath, propertyValue]) => {
+ // since property paths could be nested, we use lodash set method
+ _.set(state[action.payload.widgetId], propertyPath, propertyValue);
+ },
+ );
},
});
diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx
index 0c5550b771..f5ade60ca1 100644
--- a/app/client/src/reducers/uiReducers/editorReducer.tsx
+++ b/app/client/src/reducers/uiReducers/editorReducer.tsx
@@ -93,7 +93,7 @@ const editorReducer = createReducer(initialState, {
state.loadingStates.savingError = true;
return { ...state };
},
- [ReduxActionTypes.UPDATE_CANVAS]: (
+ [ReduxActionTypes.INIT_CANVAS_LAYOUT]: (
state: EditorReduxState,
action: ReduxAction,
) => {
diff --git a/app/client/src/sagas/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecutionSagas.ts
index 05c6e0ee78..cc9436c698 100644
--- a/app/client/src/sagas/ActionExecutionSagas.ts
+++ b/app/client/src/sagas/ActionExecutionSagas.ts
@@ -38,6 +38,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
import history from "utils/history";
import {
BUILDER_PAGE_URL,
+ convertToQueryParams,
getApplicationViewerPageURL,
} from "constants/routes";
import {
@@ -86,42 +87,61 @@ import {
} from "./EvaluationsSaga";
import copy from "copy-to-clipboard";
+export enum NavigationTargetType {
+ SAME_WINDOW = "SAME_WINDOW",
+ NEW_WINDOW = "NEW_WINDOW",
+}
+
function* navigateActionSaga(
- action: { pageNameOrUrl: string; params: Record },
+ action: {
+ pageNameOrUrl: string;
+ params: Record;
+ target?: NavigationTargetType;
+ },
event: ExecuteActionPayloadEvent,
) {
const pageList = yield select(getPageList);
const applicationId = yield select(getCurrentApplicationId);
+ const {
+ pageNameOrUrl,
+ params,
+ target = NavigationTargetType.SAME_WINDOW,
+ } = action;
const page = _.find(
pageList,
- (page: Page) => page.pageName === action.pageNameOrUrl,
+ (page: Page) => page.pageName === pageNameOrUrl,
);
if (page) {
AnalyticsUtil.logEvent("NAVIGATE", {
- pageName: action.pageNameOrUrl,
- pageParams: action.params,
+ pageName: pageNameOrUrl,
+ pageParams: params,
});
- // TODO need to make this check via RENDER_MODE;
+ const appMode = yield select(getAppMode);
const path =
- history.location.pathname.indexOf("/edit") !== -1
- ? BUILDER_PAGE_URL(applicationId, page.pageId, action.params)
- : getApplicationViewerPageURL(
- applicationId,
- page.pageId,
- action.params,
- );
- history.push(path);
+ appMode === APP_MODE.EDIT
+ ? BUILDER_PAGE_URL(applicationId, page.pageId, params)
+ : getApplicationViewerPageURL(applicationId, page.pageId, params);
+ if (target === NavigationTargetType.SAME_WINDOW) {
+ history.push(path);
+ } else if (target === NavigationTargetType.NEW_WINDOW) {
+ window.open(path, "_blank");
+ }
if (event.callback) event.callback({ success: true });
} else {
AnalyticsUtil.logEvent("NAVIGATE", {
- navUrl: action.pageNameOrUrl,
+ navUrl: pageNameOrUrl,
});
// Add a default protocol if it doesn't exist.
- let url = action.pageNameOrUrl;
+ let url = pageNameOrUrl + convertToQueryParams(params);
if (url.indexOf("://") === -1) {
url = "https://" + url;
}
- window.location.assign(url);
+ if (target === NavigationTargetType.SAME_WINDOW) {
+ window.location.assign(url);
+ } else if (target === NavigationTargetType.NEW_WINDOW) {
+ window.open(url, "_blank");
+ }
+ if (event.callback) event.callback({ success: true });
}
}
diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts
index c1a3b5e18d..5bc35771f8 100644
--- a/app/client/src/sagas/ActionSagas.ts
+++ b/app/client/src/sagas/ActionSagas.ts
@@ -44,7 +44,7 @@ import {
} from "selectors/editorSelectors";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
-import { Action } from "entities/Action";
+import { Action, ActionViewMode } from "entities/Action";
import { ActionData } from "reducers/entityReducers/actionsReducer";
import {
getAction,
@@ -145,14 +145,22 @@ export function* fetchActionsForViewModeSaga(
{ mode: "VIEWER", appId: applicationId },
);
try {
- const response: GenericApiResponse = yield ActionAPI.fetchActionsForViewMode(
+ const response: GenericApiResponse = yield ActionAPI.fetchActionsForViewMode(
applicationId,
);
+ const correctFormatResponse = response.data.map((action) => {
+ return {
+ ...action,
+ actionConfiguration: {
+ timeoutInMillisecond: action.timeoutInMillisecond,
+ },
+ };
+ });
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
- payload: response.data,
+ payload: correctFormatResponse,
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
diff --git a/app/client/src/sagas/DatasourcesSagas.ts b/app/client/src/sagas/DatasourcesSagas.ts
index bf087f866d..7de41029cf 100644
--- a/app/client/src/sagas/DatasourcesSagas.ts
+++ b/app/client/src/sagas/DatasourcesSagas.ts
@@ -390,7 +390,9 @@ function* switchDatasourceSaga(action: ReduxAction<{ datasourceId: string }>) {
(datasource: Datasource) => datasource.id === datasourceId,
),
);
- yield put(changeDatasource(datasource));
+ if (datasource) {
+ yield put(changeDatasource(datasource));
+ }
}
function* formValueChangeSaga(
diff --git a/app/client/src/sagas/OnboardingSagas.ts b/app/client/src/sagas/OnboardingSagas.ts
index b65aa962bb..015497b38f 100644
--- a/app/client/src/sagas/OnboardingSagas.ts
+++ b/app/client/src/sagas/OnboardingSagas.ts
@@ -43,6 +43,7 @@ import {
} from "constants/OnboardingConstants";
import AnalyticsUtil from "../utils/AnalyticsUtil";
import { get } from "lodash";
+import { updateWidgetProperty } from "../actions/controlActions";
export const getCurrentStep = (state: AppState) =>
state.ui.onBoarding.currentStep;
@@ -85,14 +86,9 @@ function* listenForWidgetAdditions() {
canvasWidgets[selectedWidget.widgetId]
) {
if (selectedWidget.tableData === initialTableData) {
- yield put({
- type: "UPDATE_WIDGET_PROPERTY",
- payload: {
- widgetId: selectedWidget.widgetId,
- propertyName: "tableData",
- propertyValue: [],
- },
- });
+ yield put(
+ updateWidgetProperty(selectedWidget.widgetId, { tableData: [] }),
+ );
}
AnalyticsUtil.logEvent("ONBOARDING_ADD_WIDGET");
@@ -107,11 +103,11 @@ function* listenForWidgetAdditions() {
}
}
-function* listenForSuccessfullBinding() {
+function* listenForSuccessfulBinding() {
while (true) {
yield take();
- let bindSuccessfull = true;
+ let bindSuccessful = true;
const selectedWidget = yield select(getSelectedWidget);
if (selectedWidget && selectedWidget.type === "TABLE_WIDGET") {
const dataTree = yield select(getDataTree);
@@ -124,19 +120,19 @@ function* listenForSuccessfullBinding() {
const hasBinding =
dynamicBindingPathList && !!dynamicBindingPathList.length;
- bindSuccessfull =
- bindSuccessfull && hasBinding && tableHasData && tableHasData.length;
+ bindSuccessful =
+ bindSuccessful && hasBinding && tableHasData && tableHasData.length;
if (widgetProperties.invalidProps) {
- bindSuccessfull =
- bindSuccessfull &&
+ bindSuccessful =
+ bindSuccessful &&
!(
"tableData" in widgetProperties.invalidProps &&
widgetProperties.invalidProps.tableData
);
}
- if (bindSuccessfull) {
+ if (bindSuccessful) {
yield put(showTooltip(OnboardingStep.NONE));
AnalyticsUtil.logEvent("ONBOARDING_SUCCESSFUL_BINDING");
yield put(setCurrentStep(OnboardingStep.SUCCESSFUL_BINDING));
@@ -320,7 +316,7 @@ export default function* onboardingSagas() {
takeEvery(ReduxActionTypes.LISTEN_FOR_ADD_WIDGET, listenForWidgetAdditions),
takeEvery(
ReduxActionTypes.LISTEN_FOR_TABLE_WIDGET_BINDING,
- listenForSuccessfullBinding,
+ listenForSuccessfulBinding,
),
takeEvery(ReduxActionTypes.SET_CURRENT_STEP, setupOnboardingStep),
takeEvery(ReduxActionTypes.LISTEN_FOR_DEPLOY, listenForDeploySaga),
diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx
index cf0a194798..36a2c4a686 100644
--- a/app/client/src/sagas/PageSagas.tsx
+++ b/app/client/src/sagas/PageSagas.tsx
@@ -16,7 +16,7 @@ import {
fetchPublishedPageSuccess,
savePageSuccess,
setUrlData,
- updateCanvas,
+ initCanvasLayout,
updateCurrentPage,
updateWidgetNameSuccess,
} from "actions/pageActions";
@@ -179,7 +179,7 @@ export function* fetchPageSaga(
// Get Canvas payload
const canvasWidgetsPayload = getCanvasWidgetsPayload(fetchPageResponse);
// Update the canvas
- yield put(updateCanvas(canvasWidgetsPayload));
+ yield put(initCanvasLayout(canvasWidgetsPayload));
// set current page
yield put(updateCurrentPage(id));
// dispatch fetch page success
@@ -242,7 +242,7 @@ export function* fetchPublishedPageSaga(
// Get Canvas payload
const canvasWidgetsPayload = getCanvasWidgetsPayload(response);
// Update the canvas
- yield put(updateCanvas(canvasWidgetsPayload));
+ yield put(initCanvasLayout(canvasWidgetsPayload));
// set current page
yield put(updateCurrentPage(pageId));
// dispatch fetch page success
@@ -613,7 +613,7 @@ export function* updateCanvasWithDSL(
pageActions: data.layoutOnLoadActions,
widgets: normalizedWidgets.entities.canvasWidgets,
};
- yield put(updateCanvas(canvasWidgetsPayload));
+ yield put(initCanvasLayout(canvasWidgetsPayload));
yield put(fetchActionsForPage(pageId));
}
diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx
index 043ec44edb..320ed8072d 100644
--- a/app/client/src/sagas/WidgetOperationSagas.tsx
+++ b/app/client/src/sagas/WidgetOperationSagas.tsx
@@ -1,42 +1,44 @@
import {
- ReduxActionTypes,
- ReduxActionErrorTypes,
ReduxAction,
+ ReduxActionErrorTypes,
+ ReduxActionTypes,
} from "constants/ReduxActionConstants";
import {
- WidgetAddChild,
- WidgetResize,
- WidgetMove,
- WidgetDelete,
updateAndSaveLayout,
+ WidgetAddChild,
WidgetAddChildren,
+ WidgetDelete,
+ WidgetMove,
+ WidgetResize,
} from "actions/pageActions";
import {
- FlattenedWidgetProps,
CanvasWidgetsReduxState,
+ FlattenedWidgetProps,
} from "reducers/entityReducers/canvasWidgetsReducer";
import {
- getWidgets,
- getWidget,
getSelectedWidget,
+ getWidget,
getWidgetMetaProps,
+ getWidgets,
} from "./selectors";
import {
generateWidgetProps,
updateWidgetPosition,
} from "utils/WidgetPropsUtils";
import {
+ all,
call,
put,
select,
takeEvery,
takeLatest,
- all,
} from "redux-saga/effects";
import { convertToString, getNextEntityName } from "utils/AppsmithUtils";
import {
+ DeleteWidgetPropertyPayload,
SetWidgetDynamicPropertyPayload,
updateWidgetProperty,
+ UpdateWidgetPropertyPayload,
UpdateWidgetPropertyRequestPayload,
} from "actions/controlActions";
import {
@@ -44,12 +46,13 @@ import {
getEntityDynamicBindingPathList,
getWidgetDynamicPropertyPathList,
getWidgetDynamicTriggerPathList,
+ isChildPropertyPath,
isDynamicValue,
isPathADynamicBinding,
isPathADynamicTrigger,
} from "utils/DynamicBindingUtils";
import { WidgetProps } from "widgets/BaseWidget";
-import _ from "lodash";
+import _, { cloneDeep } from "lodash";
import WidgetFactory from "utils/WidgetFactory";
import {
buildWidgetBlueprint,
@@ -58,24 +61,23 @@ import {
import { resetWidgetMetaProperty } from "actions/metaActions";
import {
GridDefaults,
- WidgetTypes,
MAIN_CONTAINER_WIDGET_ID,
- WIDGET_DELETE_UNDO_TIMEOUT,
RenderModes,
+ WIDGET_DELETE_UNDO_TIMEOUT,
WidgetType,
+ WidgetTypes,
} from "constants/WidgetConstants";
import WidgetConfigResponse from "mockResponses/WidgetConfigResponse";
import {
+ flushDeletedWidgets,
+ getCopiedWidgets,
+ getDeletedWidgets,
saveCopiedWidgets,
saveDeletedWidgets,
- flushDeletedWidgets,
- getDeletedWidgets,
- getCopiedWidgets,
} from "utils/storage";
import { generateReactKey } from "utils/generators";
import { flashElementById } from "utils/helpers";
import AnalyticsUtil from "utils/AnalyticsUtil";
-import { cloneDeep } from "lodash";
import log from "loglevel";
import { navigateToCanvas } from "pages/Editor/Explorer/Widgets/WidgetEntity";
import {
@@ -86,8 +88,8 @@ import { forceOpenPropertyPane } from "actions/widgetActions";
import { getDataTree } from "selectors/dataTreeSelectors";
import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
import {
- validateProperty,
clearEvalPropertyCacheOfWidget,
+ validateProperty,
} from "./EvaluationsSaga";
import { WidgetBlueprint } from "reducers/entityReducers/widgetConfigReducer";
import { Toaster } from "components/ads/Toast";
@@ -221,7 +223,7 @@ function* generateChildWidgets(
// Add the parentId prop to this widget
widget.parentId = parent.widgetId;
// Remove the blueprint from the widget (if any)
- // as blueprints are not useful beyont this point.
+ // as blueprints are not useful beyond this point.
delete widget.blueprint;
return { widgetId: widget.widgetId, widgets };
}
@@ -461,21 +463,21 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
const deletedWidgets: FlattenedWidgetProps[] = yield getDeletedWidgets(
action.payload.widgetId,
);
- // Find the parent in the list of deleted widgets
- const deletedWidget = deletedWidgets.find(
- (widget) => widget.widgetId === action.payload.widgetId,
- );
+ if (deletedWidgets && Array.isArray(deletedWidgets)) {
+ // Find the parent in the list of deleted widgets
+ const deletedWidget = deletedWidgets.find(
+ (widget) => widget.widgetId === action.payload.widgetId,
+ );
- // If the deleted widget is infact available.
- if (deletedWidget) {
- // Log an undo event
- AnalyticsUtil.logEvent("WIDGET_DELETE_UNDO", {
- widgetName: deletedWidget.widgetName,
- widgetType: deletedWidget.type,
- });
- }
+ // If the deleted widget is in fact available.
+ if (deletedWidget) {
+ // Log an undo event
+ AnalyticsUtil.logEvent("WIDGET_DELETE_UNDO", {
+ widgetName: deletedWidget.widgetName,
+ widgetType: deletedWidget.type,
+ });
+ }
- if (deletedWidgets) {
// Get the current list of widgets from reducer
const stateWidgets = yield select(getWidgets);
let widgets = { ...stateWidgets };
@@ -522,7 +524,7 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
}
let newChildren = [widget.widgetId];
if (widgets[widget.parentId].children) {
- // Concatenate the list of paren't children with the current widgetId
+ // Concatenate the list of parents children with the current widgetId
newChildren = newChildren.concat(widgets[widget.parentId].children);
}
widgets = {
@@ -636,79 +638,86 @@ export function* resizeSaga(resizeAction: ReduxAction) {
}
}
-function* updateDynamicTriggers(
+enum DynamicPathUpdateEffectEnum {
+ ADD = "ADD",
+ REMOVE = "REMOVE",
+ NOOP = "NOOP",
+}
+
+type DynamicPathUpdate = {
+ propertyPath: string;
+ effect: DynamicPathUpdateEffectEnum;
+};
+
+function getDynamicTriggerPathListUpdate(
widget: WidgetProps,
propertyPath: string,
propertyValue: string,
-) {
- // TODO WIDGETFACTORY
- const triggerProperties = WidgetFactory.getWidgetTriggerPropertiesMap(
- widget.type,
- );
- if (propertyPath in triggerProperties) {
- let dynamicTriggerPathList: DynamicPath[] = getWidgetDynamicTriggerPathList(
- widget,
- );
- if (propertyValue && !isPathADynamicTrigger(widget, propertyPath)) {
- dynamicTriggerPathList.push({
- key: propertyPath,
- });
- }
- if (!propertyValue && !isPathADynamicTrigger(widget, propertyPath)) {
- dynamicTriggerPathList = _.reject(dynamicTriggerPathList, {
- key: propertyValue,
- });
- }
- yield put(
- updateWidgetProperty(
- widget.widgetId,
- "dynamicTriggerPathList",
- dynamicTriggerPathList,
- ),
- );
- return true;
+): DynamicPathUpdate {
+ if (propertyValue && !isPathADynamicTrigger(widget, propertyPath)) {
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.ADD,
+ };
+ } else if (!propertyValue && !isPathADynamicTrigger(widget, propertyPath)) {
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.REMOVE,
+ };
}
- return false;
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.NOOP,
+ };
}
-function* updateDynamicBindings(
+function getDynamicBindingPathListUpdate(
widget: WidgetProps,
- propertyName: string,
+ propertyPath: string,
propertyValue: any,
-) {
+): DynamicPathUpdate {
let stringProp = propertyValue;
if (_.isObject(propertyValue)) {
// Stringify this because composite controls may have bindings in the sub controls
stringProp = JSON.stringify(propertyValue);
}
const isDynamic = isDynamicValue(stringProp);
- let dynamicBindingPathList: DynamicPath[] = getEntityDynamicBindingPathList(
- widget,
- );
- if (!isDynamic && isPathADynamicBinding(widget, propertyName)) {
- dynamicBindingPathList = _.reject(dynamicBindingPathList, {
- key: propertyName,
- });
+ if (!isDynamic && isPathADynamicBinding(widget, propertyPath)) {
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.REMOVE,
+ };
+ } else if (isDynamic && !isPathADynamicBinding(widget, propertyPath)) {
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.ADD,
+ };
}
- if (isDynamic && !isPathADynamicBinding(widget, propertyName)) {
- dynamicBindingPathList.push({
- key: propertyName,
+ return {
+ propertyPath,
+ effect: DynamicPathUpdateEffectEnum.NOOP,
+ };
+}
+
+function applyDynamicPathUpdates(
+ currentList: DynamicPath[],
+ update: DynamicPathUpdate,
+): DynamicPath[] {
+ if (update.effect === DynamicPathUpdateEffectEnum.ADD) {
+ currentList.push({
+ key: update.propertyPath,
});
+ } else if (update.effect === DynamicPathUpdateEffectEnum.REMOVE) {
+ _.reject(currentList, { key: update.propertyPath });
}
- yield put(
- updateWidgetProperty(
- widget.widgetId,
- "dynamicBindingPathList",
- dynamicBindingPathList,
- ),
- );
+ return currentList;
}
function* updateWidgetPropertySaga(
updateAction: ReduxAction,
) {
const {
- payload: { propertyValue, propertyName, widgetId },
+ payload: { propertyValue, propertyPath, widgetId },
} = updateAction;
if (!widgetId) {
// Handling the case where sometimes widget id is not passed through here
@@ -717,55 +726,190 @@ function* updateWidgetPropertySaga(
const stateWidget: WidgetProps = yield select(getWidget, widgetId);
const widget = { ...stateWidget };
- const dynamicTriggersUpdated = yield updateDynamicTriggers(
- widget,
- propertyName,
- propertyValue,
+ // Holder object to collect all updates
+ const updates: Record = {
+ [propertyPath]: propertyValue,
+ };
+
+ // Check if the path is a of a dynamic trigger property
+ const triggerProperties = WidgetFactory.getWidgetTriggerPropertiesMap(
+ widget.type,
);
- if (!dynamicTriggersUpdated) {
- yield updateDynamicBindings(widget, propertyName, propertyValue);
+ const isTriggerProperty = propertyPath in triggerProperties;
+ // If it is a trigger property, it will go in a different list than the general
+ // dynamicBindingPathList.
+ if (isTriggerProperty) {
+ const currentDynamicTriggerPathList: DynamicPath[] = getWidgetDynamicTriggerPathList(
+ widget,
+ );
+ const effect = getDynamicTriggerPathListUpdate(
+ widget,
+ propertyPath,
+ propertyValue,
+ );
+ updates.dynamicTriggerPathList = applyDynamicPathUpdates(
+ currentDynamicTriggerPathList,
+ effect,
+ );
+ } else {
+ const currentDynamicBindingPathList: DynamicPath[] = getEntityDynamicBindingPathList(
+ widget,
+ );
+ const effect = getDynamicBindingPathListUpdate(
+ widget,
+ propertyPath,
+ propertyValue,
+ );
+ updates.dynamicBindingPathList = applyDynamicPathUpdates(
+ currentDynamicBindingPathList,
+ effect,
+ );
}
- yield put(updateWidgetProperty(widgetId, propertyName, propertyValue));
+ // Send the updates
+ yield put(updateWidgetProperty(widgetId, updates));
+
const stateWidgets = yield select(getWidgets);
const widgets = { ...stateWidgets, [widgetId]: widget };
+
+ // Save the layout
yield put(updateAndSaveLayout(widgets));
}
function* setWidgetDynamicPropertySaga(
action: ReduxAction,
) {
- const { isDynamic, propertyName, widgetId } = action.payload;
+ const { isDynamic, propertyPath, widgetId } = action.payload;
const widget: WidgetProps = yield select(getWidget, widgetId);
- // const tree = yield select(evaluateDataTree);
- const propertyValue = _.get(widget, propertyName);
+ const propertyValue = _.get(widget, propertyPath);
let dynamicPropertyPathList = getWidgetDynamicPropertyPathList(widget);
+ const propertyUpdates: Record = {};
if (isDynamic) {
dynamicPropertyPathList.push({
- key: propertyName,
+ key: propertyPath,
});
- const value = convertToString(propertyValue);
- yield put(updateWidgetProperty(widgetId, propertyName, value));
+ propertyUpdates[propertyPath] = convertToString(propertyValue);
} else {
dynamicPropertyPathList = _.reject(dynamicPropertyPathList, {
- key: propertyName,
+ key: propertyPath,
});
const { parsed } = yield call(
validateProperty,
widget.type,
- propertyName,
+ propertyPath,
propertyValue,
widget,
);
- yield put(updateWidgetProperty(widgetId, propertyName, parsed));
+ propertyUpdates[propertyPath] = parsed;
}
- yield put(
- updateWidgetProperty(
- widgetId,
- "dynamicPropertyPathList",
- dynamicPropertyPathList,
- ),
+ propertyUpdates.dynamicPropertyPathList = dynamicPropertyPathList;
+
+ yield put(updateWidgetProperty(widgetId, propertyUpdates));
+
+ const stateWidgets = yield select(getWidgets);
+ const widgets = { ...stateWidgets, [widgetId]: widget };
+
+ // Save the layout
+ yield put(updateAndSaveLayout(widgets));
+}
+
+function* batchUpdateWidgetPropertySaga(
+ action: ReduxAction,
+) {
+ const { updates, widgetId } = action.payload;
+ if (!widgetId) {
+ // Handling the case where sometimes widget id is not passed through here
+ return;
+ }
+ const widget: WidgetProps = yield select(getWidget, widgetId);
+ const triggerProperties = WidgetFactory.getWidgetTriggerPropertiesMap(
+ widget.type,
);
+ const propertyUpdates: Record = {};
+ const currentDynamicTriggerPathList: DynamicPath[] = getWidgetDynamicTriggerPathList(
+ widget,
+ );
+ const currentDynamicBindingPathList: DynamicPath[] = getEntityDynamicBindingPathList(
+ widget,
+ );
+ const dynamicTriggerPathListUpdates: DynamicPathUpdate[] = [];
+ const dynamicBindingPathListUpdates: DynamicPathUpdate[] = [];
+ Object.entries(updates).forEach(([propertyPath, propertyValue]) => {
+ // Set the actual property update
+ propertyUpdates[propertyPath] = propertyValue;
+
+ // Check if the path is a of a dynamic trigger property
+ const isTriggerProperty = propertyPath in triggerProperties;
+ // If it is a trigger property, it will go in a different list than the general
+ // dynamicBindingPathList.
+ if (isTriggerProperty && _.isString(propertyValue)) {
+ dynamicTriggerPathListUpdates.push(
+ getDynamicTriggerPathListUpdate(widget, propertyPath, propertyValue),
+ );
+ } else {
+ dynamicBindingPathListUpdates.push(
+ getDynamicBindingPathListUpdate(widget, propertyPath, propertyValue),
+ );
+ }
+ });
+
+ propertyUpdates.dynamicTriggerPathList = dynamicTriggerPathListUpdates.reduce(
+ applyDynamicPathUpdates,
+ currentDynamicTriggerPathList,
+ );
+ propertyUpdates.dynamicBindingPathList = dynamicBindingPathListUpdates.reduce(
+ applyDynamicPathUpdates,
+ currentDynamicBindingPathList,
+ );
+
+ // Send the updates
+ yield put(updateWidgetProperty(widgetId, updates));
+
+ const stateWidgets = yield select(getWidgets);
+ const widgets = { ...stateWidgets, [widgetId]: widget };
+
+ // Save the layout
+ yield put(updateAndSaveLayout(widgets));
+}
+
+function* deleteWidgetPropertySaga(
+ action: ReduxAction,
+) {
+ const { widgetId, propertyPath } = action.payload;
+ if (!widgetId) {
+ // Handling the case where sometimes widget id is not passed through here
+ return;
+ }
+ const stateWidget: WidgetProps = yield select(getWidget, widgetId);
+ const dynamicTriggerPathList: DynamicPath[] = getWidgetDynamicTriggerPathList(
+ stateWidget,
+ );
+ const dynamicBindingPathList: DynamicPath[] = getEntityDynamicBindingPathList(
+ stateWidget,
+ );
+
+ dynamicTriggerPathList.filter((dynamicPath) => {
+ return !isChildPropertyPath(propertyPath, dynamicPath.key);
+ });
+
+ dynamicBindingPathList.forEach((dynamicPath) => {
+ return !isChildPropertyPath(propertyPath, dynamicPath.key);
+ });
+
+ yield put(
+ updateWidgetProperty(widgetId, {
+ dynamicTriggerPathList,
+ dynamicBindingPathList,
+ }),
+ );
+
+ const stateWidgets = yield select(getWidgets);
+ const widget = { ...stateWidget };
+ _.unset(widget, propertyPath);
+ const widgets = { ...stateWidgets, [widgetId]: widget };
+
+ // Save the layout
+ yield put(updateAndSaveLayout(widgets));
}
function* getWidgetChildren(widgetId: string): any {
@@ -774,11 +918,13 @@ function* getWidgetChildren(widgetId: string): any {
const { children } = widget;
if (children && children.length) {
for (const childIndex in children) {
- const child = children[childIndex];
- childrenIds.push(child);
- const grandChildren = yield call(getWidgetChildren, child);
- if (grandChildren.length) {
- childrenIds.push(...grandChildren);
+ if (children.hasOwnProperty(childIndex)) {
+ const child = children[childIndex];
+ childrenIds.push(child);
+ const grandChildren = yield call(getWidgetChildren, child);
+ if (grandChildren.length) {
+ childrenIds.push(...grandChildren);
+ }
}
}
}
@@ -803,6 +949,10 @@ function* resetEvaluatedWidgetMetaProperties(widgetIds: string[]) {
for (const index in widgetIds) {
const widgetId = widgetIds[index];
const widget = _.find(evaluatedDataTree, { widgetId }) as DataTreeWidget;
+
+ // the widget was not found in the data tree, so don't do anything
+ if (!widget) continue;
+
const widgetToUpdate = { ...widget };
const metaPropsMap = WidgetFactory.getWidgetMetaPropertiesMap(widget.type);
const defaultPropertiesMap = WidgetFactory.getWidgetDefaultPropertiesMap(
@@ -843,7 +993,9 @@ function* updateCanvasSize(
// TODO(abhinav): This considers that the topRow will always be zero
// Check this out when non canvas widgets are updating snapRows
// erstwhile: Math.round((rows * props.snapRowSpace) / props.parentRowSpace),
- yield put(updateWidgetProperty(canvasWidgetId, "bottomRow", newBottomRow));
+ yield put(
+ updateWidgetProperty(canvasWidgetId, { bottomRow: newBottomRow }),
+ );
}
}
@@ -852,11 +1004,9 @@ function* createWidgetCopy() {
if (!selectedWidget) return;
const widgets = yield select(getWidgets);
const widgetsToStore = getAllWidgetsInTree(selectedWidget.widgetId, widgets);
- const saveResult = yield saveCopiedWidgets(
+ return yield saveCopiedWidgets(
JSON.stringify({ widgetId: selectedWidget.widgetId, list: widgetsToStore }),
);
-
- return saveResult;
}
function* copyWidgetSaga(action: ReduxAction<{ isShortcut: boolean }>) {
@@ -1232,7 +1382,7 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction) {
// The following is computed to be used in the entity explorer
// Every time a widget is selected, we need to expand widget entities
// in the entity explorer so that the selected widget is visible
-function* selectedWidgetAncestorySaga(
+function* selectedWidgetAncestrySaga(
action: ReduxAction<{ widgetId: string }>,
) {
try {
@@ -1259,7 +1409,7 @@ function* selectedWidgetAncestorySaga(
payload: widgetIdsExpandList,
});
} catch (error) {
- log.debug("Could not compute selected widget's ancestory", error);
+ log.debug("Could not compute selected widget's ancestry", error);
}
}
@@ -1285,12 +1435,20 @@ export default function* widgetOperationSagas() {
ReduxActionTypes.RESET_CHILDREN_WIDGET_META,
resetChildrenMetaSaga,
),
+ takeEvery(
+ ReduxActionTypes.BATCH_UPDATE_WIDGET_PROPERTY,
+ batchUpdateWidgetPropertySaga,
+ ),
+ takeEvery(
+ ReduxActionTypes.DELETE_WIDGET_PROPERTY,
+ deleteWidgetPropertySaga,
+ ),
takeLatest(ReduxActionTypes.UPDATE_CANVAS_SIZE, updateCanvasSize),
takeLatest(ReduxActionTypes.COPY_SELECTED_WIDGET_INIT, copyWidgetSaga),
takeEvery(ReduxActionTypes.PASTE_COPIED_WIDGET_INIT, pasteWidgetSaga),
takeEvery(ReduxActionTypes.UNDO_DELETE_WIDGET, undoDeleteSaga),
takeEvery(ReduxActionTypes.CUT_SELECTED_WIDGET, cutWidgetSaga),
takeEvery(ReduxActionTypes.WIDGET_ADD_CHILDREN, addChildrenSaga),
- takeLatest(ReduxActionTypes.SELECT_WIDGET, selectedWidgetAncestorySaga),
+ takeLatest(ReduxActionTypes.SELECT_WIDGET, selectedWidgetAncestrySaga),
]);
}
diff --git a/app/client/src/selectors/themeSelectors.tsx b/app/client/src/selectors/themeSelectors.tsx
index f42a41cdcc..6f7c8b7596 100644
--- a/app/client/src/selectors/themeSelectors.tsx
+++ b/app/client/src/selectors/themeSelectors.tsx
@@ -1,6 +1,33 @@
import { AppState } from "reducers";
+import {
+ AUTH_LOGIN_URL,
+ SIGN_UP_URL,
+ RESET_PASSWORD_URL,
+ FORGOT_PASSWORD_URL,
+} from "constants/routes";
+import { theme, dark } from "constants/DefaultTheme";
+
+const enforceDarkThemeRoutes = [
+ AUTH_LOGIN_URL,
+ SIGN_UP_URL,
+ RESET_PASSWORD_URL,
+ FORGOT_PASSWORD_URL,
+];
+const getShouldEnforceDarkTheme = () => {
+ const currentPath = window.location.pathname;
+ return enforceDarkThemeRoutes.some(
+ (path: string) => currentPath.indexOf(path) !== -1,
+ );
+};
export const getThemeDetails = (state: AppState) => {
+ if (getShouldEnforceDarkTheme()) {
+ return {
+ mode: state.ui.theme.mode,
+ theme: { ...theme, colors: { ...theme.colors, ...dark } },
+ };
+ }
+
return {
theme: state.ui.theme.theme,
mode: state.ui.theme.mode,
diff --git a/app/client/src/utils/DynamicBindingUtils.ts b/app/client/src/utils/DynamicBindingUtils.ts
index a7e87c2096..705fbbb9a7 100644
--- a/app/client/src/utils/DynamicBindingUtils.ts
+++ b/app/client/src/utils/DynamicBindingUtils.ts
@@ -249,3 +249,12 @@ export const unsafeFunctionForEval = [
"setInterval",
"Promise",
];
+export const isChildPropertyPath = (
+ parentPropertyPath: string,
+ childPropertyPath: string,
+): boolean => {
+ const regexTest = new RegExp(
+ `^${parentPropertyPath.replace(".", "\\.")}(\\.\\S+)?$`,
+ );
+ return regexTest.test(childPropertyPath);
+};
diff --git a/app/client/src/utils/DynamicBindingsUtil.test.ts b/app/client/src/utils/DynamicBindingsUtil.test.ts
index ef0bea5619..b319780940 100644
--- a/app/client/src/utils/DynamicBindingsUtil.test.ts
+++ b/app/client/src/utils/DynamicBindingsUtil.test.ts
@@ -1,4 +1,7 @@
-import { getDynamicStringSegments } from "./DynamicBindingUtils";
+import {
+ getDynamicStringSegments,
+ isChildPropertyPath,
+} from "./DynamicBindingUtils";
describe.each([
["{{A}}", ["{{A}}"]],
@@ -30,3 +33,20 @@ describe.each([
);
});
});
+
+describe("isChildPropertyPath function", () => {
+ it("works", () => {
+ const cases: Array<[string, string, boolean]> = [
+ ["Table1.selectedRow", "Table1.selectedRow", true],
+ ["Table1.selectedRow", "Table1.selectedRows", false],
+ ["Table1.selectedRow", "Table1.selectedRow.email", true],
+ ["Table1.selectedRow", "1Table1.selectedRow", false],
+ ["Table1.selectedRow", "Table11selectedRow", false],
+ ["Table1.selectedRow", "Table1.selectedRow", true],
+ ];
+ cases.forEach((testCase) => {
+ const result = isChildPropertyPath(testCase[0], testCase[1]);
+ expect(result).toBe(testCase[2]);
+ });
+ });
+});
diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts
index a0cecf10e9..01428c5270 100644
--- a/app/client/src/utils/autocomplete/EntityDefinitions.ts
+++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts
@@ -231,7 +231,7 @@ export const GLOBAL_DEFS = {
export const GLOBAL_FUNCTIONS = {
navigateTo: {
"!doc": "Action to navigate the user to another page or url",
- "!type": "fn(pageNameOrUrl: string, params: {}) -> void",
+ "!type": "fn(pageNameOrUrl: string, params: {}, target?: string) -> void",
},
showAlert: {
"!doc": "Show a temporary notification style message to the user",
diff --git a/app/client/src/widgets/InputWidget.tsx b/app/client/src/widgets/InputWidget.tsx
index f186283381..90427ccd2e 100644
--- a/app/client/src/widgets/InputWidget.tsx
+++ b/app/client/src/widgets/InputWidget.tsx
@@ -44,6 +44,7 @@ class InputWidget extends BaseWidget {
static getTriggerPropertyMap(): TriggerPropertiesMap {
return {
onTextChanged: true,
+ onSubmit: true,
};
}
@@ -136,6 +137,22 @@ class InputWidget extends BaseWidget {
this.props.updateWidgetMetaProperty("isFocused", focusState);
};
+ handleKeyDown = (
+ e:
+ | React.KeyboardEvent
+ | React.KeyboardEvent,
+ ) => {
+ const isEnterKey = e.key === "Enter" || e.keyCode === 13;
+ if (isEnterKey && this.props.onSubmit) {
+ super.executeAction({
+ dynamicString: this.props.onSubmit,
+ event: {
+ type: EventType.ON_SUBMIT,
+ },
+ });
+ }
+ };
+
getPageView() {
const value = this.props.text || "";
const isInvalid =
@@ -149,6 +166,7 @@ class InputWidget extends BaseWidget {
if (this.props.maxChars) conditionalProps.maxChars = this.props.maxChars;
if (this.props.maxNum) conditionalProps.maxNum = this.props.maxNum;
if (this.props.minNum) conditionalProps.minNum = this.props.minNum;
+
return (
{
stepSize={1}
onFocusChange={this.handleFocusChange}
showError={!!this.props.isFocused}
+ disableNewLineOnPressEnterKey={!!this.props.onSubmit}
+ onKeyDown={this.handleKeyDown}
{...conditionalProps}
/>
);
@@ -215,6 +235,7 @@ export interface InputWidgetProps extends WidgetProps, WithMeta {
isRequired?: boolean;
isFocused?: boolean;
isDirty?: boolean;
+ onSubmit?: string;
}
export default InputWidget;
diff --git a/app/client/src/widgets/MapWidget.tsx b/app/client/src/widgets/MapWidget.tsx
index ba35a190a5..ddc72df2e0 100644
--- a/app/client/src/widgets/MapWidget.tsx
+++ b/app/client/src/widgets/MapWidget.tsx
@@ -132,6 +132,16 @@ class MapWidget extends BaseWidget {
return this.props.center || this.props.mapCenter || DefaultCenter;
}
+ componentDidUpdate(prevProps: MapWidgetProps) {
+ //remove selectedMarker when map initial location is updated
+ if (
+ JSON.stringify(prevProps.center) !== JSON.stringify(this.props.center) &&
+ this.props.selectedMarker
+ ) {
+ this.unselectMarker();
+ }
+ }
+
getPageView() {
return (
<>
diff --git a/app/client/src/widgets/TableWidget.tsx b/app/client/src/widgets/TableWidget.tsx
index dd86f8f347..026ed95bf5 100644
--- a/app/client/src/widgets/TableWidget.tsx
+++ b/app/client/src/widgets/TableWidget.tsx
@@ -90,13 +90,13 @@ class TableWidget extends BaseWidget {
searchText: VALIDATION_TYPES.TEXT,
defaultSearchText: VALIDATION_TYPES.TEXT,
defaultSelectedRow: VALIDATION_TYPES.DEFAULT_SELECTED_ROW,
+ pageSize: VALIDATION_TYPES.NUMBER,
};
}
static getMetaPropertiesMap(): Record {
return {
pageNo: 1,
- pageSize: undefined,
selectedRowIndex: undefined,
selectedRowIndices: undefined,
searchText: undefined,
@@ -120,10 +120,54 @@ class TableWidget extends BaseWidget {
onRowSelected: true,
onPageChange: true,
onSearchTextChanged: true,
+ onPageSizeChange: true,
columnActions: true,
};
}
+ static getDerivedPropertiesMap() {
+ return {
+ pageSize: `{{function() {
+ const TABLE_SIZES = {
+ ${CompactModeTypes.DEFAULT}: {
+ COLUMN_HEADER_HEIGHT: 38,
+ TABLE_HEADER_HEIGHT: 42,
+ ROW_HEIGHT: 40,
+ ROW_FONT_SIZE: 14,
+ },
+ ${CompactModeTypes.SHORT}: {
+ COLUMN_HEADER_HEIGHT: 38,
+ TABLE_HEADER_HEIGHT: 42,
+ ROW_HEIGHT: 20,
+ ROW_FONT_SIZE: 12,
+ },
+ ${CompactModeTypes.TALL}: {
+ COLUMN_HEADER_HEIGHT: 38,
+ TABLE_HEADER_HEIGHT: 42,
+ ROW_HEIGHT: 60,
+ ROW_FONT_SIZE: 18,
+ },
+ };
+ const compactMode = this.compactMode || "${CompactModeTypes.DEFAULT}";
+ const componentHeight = (this.bottomRow - this.topRow) * this.parentRowSpace;
+ const tableSizes = TABLE_SIZES[compactMode];
+ let pageSize= Math.floor((componentHeight - tableSizes.TABLE_HEADER_HEIGHT - tableSizes.COLUMN_HEADER_HEIGHT) / tableSizes.ROW_HEIGHT);
+ if (
+ componentHeight -
+ (tableSizes.TABLE_HEADER_HEIGHT +
+ tableSizes.COLUMN_HEADER_HEIGHT +
+ tableSizes.ROW_HEIGHT * pageSize) >
+ 0
+ ) {
+ pageSize += 1;
+ }
+ return pageSize;
+ }()
+ }}`,
+ triggerRowSelection: "{{!!this.onRowSelected}}",
+ };
+ }
+
getTableColumns = (tableData: Array>) => {
let columns: ReactTableColumnProps[] = [];
const hiddenColumns: ReactTableColumnProps[] = [];
@@ -135,6 +179,7 @@ class TableWidget extends BaseWidget {
} = this.props;
if (tableData.length) {
const columnKeys: string[] = getAllTableColumnKeys(tableData);
+ const { componentWidth } = this.getComponentDimensions();
const sortedColumn = this.props.sortedColumn;
for (let index = 0; index < columnKeys.length; index++) {
const i = columnKeys[index];
@@ -170,7 +215,12 @@ class TableWidget extends BaseWidget {
inputFormat: columnType.inputFormat,
},
Cell: (props: any) => {
- return renderCell(props.cell.value, columnType.type, isHidden);
+ return renderCell(
+ props.cell.value,
+ columnType.type,
+ isHidden,
+ componentWidth,
+ );
},
};
if (isHidden) {
@@ -361,7 +411,7 @@ class TableWidget extends BaseWidget {
const columnKeys: string[] = getAllTableColumnKeys(this.props.tableData);
const selectedRow: { [key: string]: any } = {};
for (let i = 0; i < columnKeys.length; i++) {
- selectedRow[columnKeys[i]] = undefined;
+ selectedRow[columnKeys[i]] = "";
}
return selectedRow;
}
@@ -493,6 +543,14 @@ class TableWidget extends BaseWidget {
);
}
}
+ if (this.props.pageSize !== prevProps.pageSize) {
+ super.executeAction({
+ dynamicString: this.props.onPageSizeChange,
+ event: {
+ type: EventType.ON_PAGE_SIZE_CHANGE,
+ },
+ });
+ }
}
getSelectedRowIndexes = (selectedRowIndices: string) => {
@@ -502,7 +560,12 @@ class TableWidget extends BaseWidget {
};
getPageView() {
- const { tableData, hiddenColumns, filteredTableData } = this.props;
+ const {
+ tableData,
+ hiddenColumns,
+ filteredTableData,
+ pageSize,
+ } = this.props;
const computedSelectedRowIndices = Array.isArray(
this.props.selectedRowIndices,
)
@@ -524,28 +587,6 @@ class TableWidget extends BaseWidget {
this.props.updateWidgetMetaProperty("pageNo", pageNo);
}
const { componentWidth, componentHeight } = this.getComponentDimensions();
- const tableSizes =
- TABLE_SIZES[this.props.compactMode || CompactModeTypes.DEFAULT];
- let pageSize = Math.floor(
- (componentHeight -
- tableSizes.TABLE_HEADER_HEIGHT -
- tableSizes.COLUMN_HEADER_HEIGHT) /
- tableSizes.ROW_HEIGHT,
- );
-
- if (
- componentHeight -
- (tableSizes.TABLE_HEADER_HEIGHT +
- tableSizes.COLUMN_HEADER_HEIGHT +
- tableSizes.ROW_HEIGHT * pageSize) >
- 0
- )
- pageSize += 1;
-
- if (pageSize !== this.props.pageSize) {
- this.props.updateWidgetMetaProperty("pageSize", pageSize);
- }
-
return (
}>
{
columnNameMap={this.props.columnNameMap}
columnTypeMap={this.props.columnTypeMap}
columnOrder={this.props.columnOrder}
+ triggerRowSelection={this.props.triggerRowSelection}
pageSize={Math.max(1, pageSize)}
onCommandClick={this.onCommandClick}
selectedRowIndex={
@@ -590,7 +632,14 @@ class TableWidget extends BaseWidget {
super.updateWidgetProperty("columnNameMap", columnNameMap);
}}
handleResizeColumn={(columnSizeMap: { [key: string]: number }) => {
- super.updateWidgetProperty("columnSizeMap", columnSizeMap);
+ if (this.props.renderMode === RenderModes.CANVAS) {
+ super.updateWidgetProperty("columnSizeMap", columnSizeMap);
+ } else {
+ this.props.updateWidgetMetaProperty(
+ "columnSizeMap",
+ columnSizeMap,
+ );
+ }
}}
handleReorderColumn={(columnOrder: string[]) => {
super.updateWidgetProperty("columnOrder", columnOrder);
@@ -607,11 +656,7 @@ class TableWidget extends BaseWidget {
}}
compactMode={this.props.compactMode || CompactModeTypes.DEFAULT}
updateCompactMode={(compactMode: CompactMode) => {
- if (this.props.renderMode === RenderModes.CANVAS) {
- this.props.updateWidgetMetaProperty("compactMode", compactMode);
- } else {
- this.props.updateWidgetMetaProperty("compactMode", compactMode);
- }
+ this.props.updateWidgetMetaProperty("compactMode", compactMode);
}}
sortTableColumn={this.handleColumnSorting}
/>
diff --git a/app/client/src/workers/evaluation.worker.ts b/app/client/src/workers/evaluation.worker.ts
index 78c036fe17..43ffa95e42 100644
--- a/app/client/src/workers/evaluation.worker.ts
+++ b/app/client/src/workers/evaluation.worker.ts
@@ -15,6 +15,7 @@ import {
extraLibraries,
getDynamicBindings,
getEntityDynamicBindingPathList,
+ isChildPropertyPath,
isPathADynamicBinding,
isPathADynamicTrigger,
unsafeFunctionForEval,
@@ -33,7 +34,6 @@ import {
CrashingError,
DataTreeDiffEvent,
getValidatedTree,
- isChildPropertyPath,
makeParentsDependOnChildren,
removeFunctions,
removeFunctionsFromDataTree,
@@ -148,9 +148,10 @@ ctx.addEventListener(
true,
callbackData,
);
+ const cleanTriggers = removeFunctions(triggers);
const errors = dataTreeEvaluator.errors;
dataTreeEvaluator.clearErrors();
- return { triggers, errors };
+ return { triggers: cleanTriggers, errors };
}
case EVAL_WORKER_ACTIONS.CLEAR_CACHE: {
dataTreeEvaluator = undefined;
@@ -180,12 +181,14 @@ ctx.addEventListener(
value,
props,
} = requestData;
- return validateWidgetProperty(
- widgetTypeConfigMap,
- widgetType,
- property,
- value,
- props,
+ return removeFunctions(
+ validateWidgetProperty(
+ widgetTypeConfigMap,
+ widgetType,
+ property,
+ value,
+ props,
+ ),
);
}
default: {
@@ -1373,10 +1376,11 @@ const addFunctions = (dataTree: Readonly): DataTree => {
withFunction.navigateTo = function(
pageNameOrUrl: string,
params: Record,
+ target?: string,
) {
return {
type: "NAVIGATE_TO",
- payload: { pageNameOrUrl, params },
+ payload: { pageNameOrUrl, params, target },
};
};
withFunction.actionPaths.push("navigateTo");
diff --git a/app/client/src/workers/evaluationUtils.test.ts b/app/client/src/workers/evaluationUtils.test.ts
deleted file mode 100644
index 0419fb5ada..0000000000
--- a/app/client/src/workers/evaluationUtils.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { isChildPropertyPath } from "./evaluationUtils";
-
-describe("isChildPropertyPath function", () => {
- it("works", () => {
- const cases: Array<[string, string, boolean]> = [
- ["Table1.selectedRow", "Table1.selectedRows", false],
- ["Table1.selectedRow", "Table1.selectedRow.email", true],
- ["Table1.selectedRow", "1Table1.selectedRow", false],
- ["Table1.selectedRow", "Table11selectedRow", false],
- ["Table1.selectedRow", "Table1.selectedRow", true],
- ];
- cases.forEach((testCase) => {
- const result = isChildPropertyPath(testCase[0], testCase[1]);
- expect(result).toBe(testCase[2]);
- });
- });
-});
diff --git a/app/client/src/workers/evaluationUtils.ts b/app/client/src/workers/evaluationUtils.ts
index 3a94668c83..6f05837929 100644
--- a/app/client/src/workers/evaluationUtils.ts
+++ b/app/client/src/workers/evaluationUtils.ts
@@ -169,7 +169,7 @@ export function isAction(entity: DataTreeEntity): boolean {
export const removeFunctions = (value: any) => {
if (_.isFunction(value)) {
return "Function call";
- } else if (_.isObject(value) && _.some(value, _.isFunction)) {
+ } else if (_.isObject(value)) {
return JSON.parse(JSON.stringify(value));
} else {
return value;
@@ -297,13 +297,3 @@ export function getValidatedTree(
return { ...tree, [entityKey]: parsedEntity };
}, tree);
}
-
-export const isChildPropertyPath = (
- parentPropertyPath: string,
- childPropertyPath: string,
-): boolean => {
- const regexTest = new RegExp(
- `^${parentPropertyPath.replace(".", "\\.")}(\\.\\S+)?$`,
- );
- return regexTest.test(childPropertyPath);
-};
diff --git a/app/client/start-https.sh b/app/client/start-https.sh
index 626bafb919..f8fa9d0c06 100755
--- a/app/client/start-https.sh
+++ b/app/client/start-https.sh
@@ -63,24 +63,18 @@ client_proxy_pass="${default_client_proxy}"
network_mode="bridge"
case "${uname_out}" in
Linux*) machine=Linux
-
- proc_version="$(cat /proc/version)"
- case "$proc_version" in
- *icrosoft*)
+
+ source ../util/is_wsl.sh
+ if [ $IS_WSL ]; then
# ignore to continue using host.docker.internal
- ;;
- *WSL*)
- # ignore to continue using host.docker.internal
- ;;
- *)
+ else
network_mode="host"
client_proxy_pass=$default_linux_client_proxy
# if no server was passed
if [[ -z $1 ]]; then
server_proxy_pass=$default_linux_server_proxy
fi
- ;;
- esac
+ fi
echo "
Starting nginx for Linux...
"
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/pluginExceptions/AppsmithPluginError.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/pluginExceptions/AppsmithPluginError.java
index 49d0f0fc3a..b3a7b2b360 100644
--- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/pluginExceptions/AppsmithPluginError.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/pluginExceptions/AppsmithPluginError.java
@@ -7,9 +7,9 @@ import java.text.MessageFormat;
@Getter
public enum AppsmithPluginError {
- PLUGIN_ERROR(500, 5000, "PluginExecution failed with error {0}"),
- PLUGIN_STRUCTURE_ERROR(500, 5001, "Plugin failed to get structure with error {0}"),
- PLUGIN_TIMEOUT_ERROR(504, 5002, "Plugin execution for query \"{0}\" timed out in {1}ms. Please increase timeout duration in your action settings or check your backend action endpoint."),
+ PLUGIN_ERROR(500, 5000, "{0}"),
+ PLUGIN_STRUCTURE_ERROR(500, 5001, "Failed to get database structure with error {0}"),
+ PLUGIN_TIMEOUT_ERROR(504, 5002, "{0} timed out in {1} milliseconds. Please increase timeout. This can be found in Settings tab of {0}."),
;
private final Integer httpErrorCode;
diff --git a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java
index 76e0ebe7f5..0d931ff897 100644
--- a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java
+++ b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java
@@ -359,6 +359,10 @@ public class FirestorePlugin extends BasePlugin {
}
private Object resultToMap(Object objResult) throws AppsmithPluginException {
+ return resultToMap(objResult, true);
+ }
+
+ private Object resultToMap(Object objResult, boolean isRoot) throws AppsmithPluginException {
if (objResult instanceof WriteResult) {
WriteResult writeResult = (WriteResult) objResult;
Map resultMap = new HashMap<>();
@@ -377,7 +381,11 @@ public class FirestorePlugin extends BasePlugin {
QuerySnapshot querySnapshot = (QuerySnapshot) objResult;
List