diff --git a/app/client/cypress/locators/welcomePage.json b/app/client/cypress/locators/welcomePage.json index bfb3cea92e..c46795fa08 100644 --- a/app/client/cypress/locators/welcomePage.json +++ b/app/client/cypress/locators/welcomePage.json @@ -1,18 +1,18 @@ { - "getStarted": ".t--welcome-form-get-started", - "fullName": ".t--welcome-form-full-name input", - "email": ".t--welcome-form-email input", - "password": ".t--welcome-form-password input", - "verifyPassword": ".t--welcome-form-verify-password input", + "firstName":".t--welcome-form-first-name input", + "lastName":".t--welcome-form-last-name input", + "email":".t--welcome-form-email input", + "password":".t--welcome-form-password input", + "verifyPassword":".t--welcome-form-verify-password input", "roleDropdown": ".t--welcome-form-role-dropdown .setup-dropdown", "roleDropdownOption": ".rc-select-item-option", "roleInput": ".t--welcome-form-role-input input", "useCaseDropdown": ".t--welcome-form-role-usecase", "useCaseDropdownOption": ".rc-select-item-option-content", - "nextButton": ".t--welcome-form-next-button", - "dataCollection": ".ads-v2-switch__label:last", - "newsLetter": ".ads-v2-switch__label:first", + "submitButton": ".t--welcome-form-submit-button", + "dataCollection": ".t--welcome-form-datacollection", + "newsLetter": ".t--welcome-form-newsletter", "createButton": ".t--welcome-form-create-button", "createSuperUser": "#super-user-form", "superUserForm": "[data-testid='super-user-form']" -} +} \ No newline at end of file diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index b300978d0c..055a0736e6 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -267,7 +267,7 @@ Cypress.Commands.add("Signup", (uname, pword) => { cy.get(signupPage.dropdownOption).click(); cy.get(signupPage.useCaseDropdown).click(); cy.get(signupPage.dropdownOption).click(); - cy.get(signupPage.roleUsecaseSubmit).click(); + cy.get(signupPage.roleUsecaseSubmit).click({ force: true }); cy.wait("@getMe"); cy.wait(3000); @@ -1275,73 +1275,36 @@ Cypress.Commands.add("createJSObject", (JSCode) => { Cypress.Commands.add("createSuperUser", () => { cy.wait(1000); - cy.get(welcomePage.getStarted).should("be.visible"); - cy.get(welcomePage.getStarted).should("not.be.disabled"); - cy.get(welcomePage.getStarted).click(); - cy.get(welcomePage.fullName).should("be.visible"); + cy.get(welcomePage.firstName).should("be.visible"); + cy.get(welcomePage.lastName).should("be.visible"); cy.get(welcomePage.email).should("be.visible"); cy.get(welcomePage.password).should("be.visible"); cy.get(welcomePage.verifyPassword).should("be.visible"); - cy.get(welcomePage.roleDropdown).should("be.visible"); - cy.get(welcomePage.useCaseDropdown).should("be.visible"); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.submitButton).should("be.disabled"); - cy.get(welcomePage.fullName).type(Cypress.env("USERNAME")); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.firstName).type(Cypress.env("USERNAME")); + cy.get(welcomePage.submitButton).should("be.disabled"); cy.get(welcomePage.email).type(Cypress.env("USERNAME")); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.submitButton).should("be.disabled"); cy.get(welcomePage.password).type(Cypress.env("PASSWORD")); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.submitButton).should("be.disabled"); cy.get(welcomePage.verifyPassword).type(Cypress.env("PASSWORD")); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.submitButton).should("not.be.disabled"); + cy.get(welcomePage.submitButton).click(); + cy.get(welcomePage.roleDropdown).click(); cy.get(welcomePage.roleDropdownOption).eq(1).click(); - cy.get(welcomePage.nextButton).should("be.disabled"); + cy.get(welcomePage.submitButton).should("be.disabled"); cy.get(welcomePage.useCaseDropdown).click(); cy.get(welcomePage.useCaseDropdownOption).eq(1).click(); - cy.get(welcomePage.nextButton).should("not.be.disabled"); - cy.get(welcomePage.nextButton).click(); - if (Cypress.env("AIRGAPPED")) { - cy.get(welcomePage.newsLetter).should("not.exist"); - cy.get(welcomePage.dataCollection).should("not.exist"); - cy.get(welcomePage.createButton).should("not.exist"); - } else { - cy.get(welcomePage.superUserForm).should("be.visible"); - cy.get(welcomePage.newsLetter).should("be.visible"); - cy.get(welcomePage.dataCollection).should("be.visible"); - cy.get(welcomePage.createButton).should("be.visible"); - cy.get(welcomePage.createButton).trigger("mouseover").click(); - cy.wait("@createSuperUser").then((interception) => { - expect(interception.request.body).contains( - "allowCollectingAnonymousData=true", - ); - expect(interception.request.body).contains("signupForNewsletter=true"); - }); - } - //cy.get(welcomePage.newsLetter).trigger("mouseover").click(); - //cy.get(welcomePage.newsLetter).find("input").uncheck();//not working - //cy.get(welcomePage.dataCollection).trigger("mouseover").click(); - //cy.wait(1000); //for toggles to settle - - //Seeing issue with above also, trying multiple click as below - //cy.get(welcomePage.createButton).click({ multiple: true }); - //cy.get(welcomePage.createButton).trigger("click"); - - //Submit also not working - //cy.get(welcomePage.createSuperUser).submit(); - //cy.wait(5000); //waiting a bit before attempting logout - - // cy.get("body").then(($ele) => { - // if ($ele.find(locator._spanButton("Next").length) > 0) { - // agHelper.GetNClick(locator._spanButton("Next")); - // } else agHelper.GetNClick(locator._spanButton("Make your first App")); - // }); - - //trying jquery way - also not working - // cy.get(welcomePage.createButton).then(($createBtn) => { - // const $jQueryButton = Cypress.$($createBtn); // wrap the button element in jQuery - // $jQueryButton.trigger("click"); // click on the button using jQuery - // }); + cy.get(welcomePage.submitButton).should("not.be.disabled"); + cy.get(welcomePage.submitButton).click(); + cy.wait("@createSuperUser").then((interception) => { + expect(interception.request.body).contains( + "allowCollectingAnonymousData=true", + ); + expect(interception.request.body).contains("signupForNewsletter=true"); + }); cy.LogOut(); cy.wait(2000); diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 22a07c4b75..ccd6a1ca39 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -999,6 +999,9 @@ export const ONBOARDING_CHECKLIST_DEPLOY_APPLICATIONS = { export const ONBOARDING_CHECKLIST_FOOTER = () => "Not sure where to start? Take the welcome tour"; +export const ONBOARDING_TELEMETRY_POPUP = () => + "We only collect usage data to make Appsmith better for everyone. Visit admin settings to toggle this off."; + //Introduction modal export const HOW_APPSMITH_WORKS = () => "Here’s a quick overview of how Appsmith works. "; @@ -1062,9 +1065,8 @@ export const USE_SNIPPET = () => "Snippet"; export const SNIPPET_TOOLTIP = () => "Search code snippets"; //Welcome page -export const WELCOME_HEADER = () => "Welcome!"; -export const WELCOME_BODY = () => - "Let us setup your account so you can make awesome applications!"; +export const WELCOME_HEADER = () => "Almost there"; +export const WELCOME_BODY = () => "Let's setup your account first"; export const WELCOME_ACTION = () => "Get started"; // API Editor @@ -1081,10 +1083,11 @@ export const ACTION_EXECUTION_MESSAGE = (actionType: string) => export const ACTION_EXECUTION_CANCEL = () => "Cancel request"; export const WELCOME_FORM_HEADER = () => "Let us get to know you better!"; -export const WELCOME_FORM_FULL_NAME = () => "Full Name"; -export const WELCOME_FORM_EMAIL_ID = () => "Email Id"; -export const WELCOME_FORM_CREATE_PASSWORD = () => "Create Password"; -export const WELCOME_FORM_VERIFY_PASSWORD = () => "Verify Password"; +export const WELCOME_FORM_FIRST_NAME = () => "First name"; +export const WELCOME_FORM_LAST_NAME = () => "Last name"; +export const WELCOME_FORM_EMAIL_ID = () => "Email"; +export const WELCOME_FORM_CREATE_PASSWORD = () => "Enter password"; +export const WELCOME_FORM_VERIFY_PASSWORD = () => "Verify password"; export const WELCOME_FORM_ROLE_DROPDOWN = () => "Tell us about your primary skillset"; export const WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER = () => diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/AnonymousDataPopup.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/AnonymousDataPopup.tsx new file mode 100644 index 0000000000..6ed56b2f68 --- /dev/null +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/AnonymousDataPopup.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { Callout } from "design-system"; +import { + ADMIN_SETTINGS, + LEARN_MORE, + ONBOARDING_TELEMETRY_POPUP, + createMessage, +} from "@appsmith/constants/messages"; +import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes"; +import { TELEMETRY_DOCS_PAGE_URL } from "./constants"; + +export default function AnonymousDataPopup(props: { + onCloseCallout: () => void; +}) { + return ( +
+ { + props.onCloseCallout(); + }} + > + {createMessage(ONBOARDING_TELEMETRY_POPUP)} + +
+ ); +} diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Tasks.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Tasks.tsx index c20483114f..f615e03735 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Tasks.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Tasks.tsx @@ -21,7 +21,7 @@ import { import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { INTEGRATION_TABS } from "constants/routes"; import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { @@ -33,14 +33,25 @@ import { getDatasources, getPageActions, } from "selectors/entitiesSelector"; -import { getFirstTimeUserOnboardingModal } from "selectors/onboardingSelectors"; import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; import history from "utils/history"; -import IntroductionModal from "./IntroductionModal"; import { integrationEditorURL } from "RouteBuilder"; -import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; +import { getAssetUrl, isAirgapped } from "@appsmith/utils/airgapHelpers"; +import AnonymousDataPopup from "./AnonymousDataPopup"; +import { + getFirstTimeUserOnboardingComplete, + getFirstTimeUserOnboardingModal, + getIsFirstTimeUserOnboardingEnabled, +} from "selectors/onboardingSelectors"; +import { getCurrentUser } from "selectors/usersSelectors"; +import { + getFirstTimeUserOnboardingTelemetryCalloutIsAlreadyShown, + setFirstTimeUserOnboardingTelemetryCalloutVisibility, +} from "utils/storage"; +import { ANONYMOUS_DATA_POPOP_TIMEOUT } from "./constants"; import { DatasourceCreateEntryPoints } from "constants/Datasource"; +import IntroductionModal from "./IntroductionModal"; const Wrapper = styled.div` width: 100%; @@ -92,6 +103,12 @@ const getOnboardingQueryImg = () => `${ASSETS_CDN_URL}/onboarding-query.svg`; const getOnboardingWidgetImg = () => `${ASSETS_CDN_URL}/onboarding-widget.svg`; export default function OnboardingTasks() { + const [isAnonymousDataPopupOpen, setisAnonymousDataPopupOpen] = + useState(false); + + const isFirstTimeUserOnboardingEnabled = useSelector( + getIsFirstTimeUserOnboardingEnabled, + ); const applicationId = useSelector(getCurrentApplicationId); const pageId = useSelector(getCurrentPageId); let content; @@ -99,7 +116,44 @@ export default function OnboardingTasks() { const actions = useSelector(getPageActions(pageId)); const widgets = useSelector(getCanvasWidgets); const dispatch = useDispatch(); + const user = useSelector(getCurrentUser); + const isAdmin = user?.isSuperUser || false; + const isOnboardingCompleted = useSelector(getFirstTimeUserOnboardingComplete); const showModal = useSelector(getFirstTimeUserOnboardingModal); + + const hideAnonymousDataPopup = () => { + setisAnonymousDataPopupOpen(false); + setFirstTimeUserOnboardingTelemetryCalloutVisibility(true); + }; + + const showShowAnonymousDataPopup = async () => { + const shouldPopupShow = + !isAirgapped() && + isFirstTimeUserOnboardingEnabled && + isAdmin && + !isOnboardingCompleted; + if (shouldPopupShow) { + const isAnonymousDataPopupAlreadyOpen = + await getFirstTimeUserOnboardingTelemetryCalloutIsAlreadyShown(); + //true if the modal was already shown else show the modal and set to already shown, also hide the modal after 10 secs + if (isAnonymousDataPopupAlreadyOpen) { + setisAnonymousDataPopupOpen(false); + } else { + setisAnonymousDataPopupOpen(true); + setTimeout(() => { + hideAnonymousDataPopup(); + }, ANONYMOUS_DATA_POPOP_TIMEOUT); + await setFirstTimeUserOnboardingTelemetryCalloutVisibility(true); + } + } else { + setisAnonymousDataPopupOpen(shouldPopupShow); + } + }; + + useEffect(() => { + showShowAnonymousDataPopup(); + }, []); + if (!datasources.length && !actions.length) { content = ( @@ -272,7 +326,10 @@ export default function OnboardingTasks() { return ( {content} - {showModal && ( + {isAnonymousDataPopupOpen && ( + + )} + {!isAdmin && showModal && ( { dispatch({ diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/constants.ts b/app/client/src/pages/Editor/FirstTimeUserOnboarding/constants.ts new file mode 100644 index 0000000000..22c1f646a7 --- /dev/null +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/constants.ts @@ -0,0 +1,6 @@ +//Hide Anonymous Data Popup after 15 seconds +export const ANONYMOUS_DATA_POPOP_TIMEOUT = 15000; + +//Telemetry Docs Page +export const TELEMETRY_DOCS_PAGE_URL = + "https://docs.appsmith.com/product/telemetry"; diff --git a/app/client/src/pages/setup/DataCollectionForm.tsx b/app/client/src/pages/setup/DataCollectionForm.tsx deleted file mode 100644 index a9aab0efc9..0000000000 --- a/app/client/src/pages/setup/DataCollectionForm.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { memo, useState } from "react"; -import styled from "styled-components"; -import { Switch, Link } from "design-system"; -import { ControlWrapper } from "components/propertyControls/StyledControls"; -import { - AllowToggle, - AllowToggleWrapper, - FormBodyWrapper, - FormHeaderIndex, - FormHeaderLabel, - FormHeaderSubtext, -} from "./common"; -import { TELEMETRY_URL } from "constants/ThirdPartyConstants"; -import { - createMessage, - WELCOME_FORM_DATA_COLLECTION_BODY, - WELCOME_FORM_DATA_COLLECTION_HEADER, - WELCOME_FORM_DATA_COLLECTION_LABEL_ENABLE, - WELCOME_FORM_DATA_COLLECTION_LINK, -} from "@appsmith/constants/messages"; - -const DataCollectionFormWrapper = styled.div` - width: 100%; - position: relative; - padding-left: ${(props) => props.theme.spaces[17] * 2}px; -`; - -const StyledLink = styled(Link)` - display: inline-block; - margin-top: 8px; -`; - -export default memo(function DataCollectionForm() { - const [allowCollection, setAllowCollection] = useState(true); - return ( - -
- 2. - - {createMessage(WELCOME_FORM_DATA_COLLECTION_HEADER)} - - - {createMessage(WELCOME_FORM_DATA_COLLECTION_BODY)} -
- - {createMessage(WELCOME_FORM_DATA_COLLECTION_LINK)} - -
-
- - - - - setAllowCollection(value)} - value={allowCollection.toString()} - > - {createMessage(WELCOME_FORM_DATA_COLLECTION_LABEL_ENABLE)} - - - - - -
- ); -}); diff --git a/app/client/src/pages/setup/DetailsForm.tsx b/app/client/src/pages/setup/DetailsForm.tsx index 84b8676f93..51a09a3706 100644 --- a/app/client/src/pages/setup/DetailsForm.tsx +++ b/app/client/src/pages/setup/DetailsForm.tsx @@ -1,44 +1,51 @@ -import React from "react"; +import React, { useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import { Field } from "redux-form"; -import { - DropdownWrapper, - FormBodyWrapper, - FormHeaderIndex, - FormHeaderLabel, - withDropdown, -} from "./common"; +import { DropdownWrapper, FormBodyWrapper, withDropdown } from "./common"; import { createMessage, WELCOME_FORM_EMAIL_ID, - WELCOME_FORM_FULL_NAME, + WELCOME_FORM_FIRST_NAME, + WELCOME_FORM_LAST_NAME, WELCOME_FORM_CREATE_PASSWORD, WELCOME_FORM_VERIFY_PASSWORD, WELCOME_FORM_ROLE_DROPDOWN, WELCOME_FORM_ROLE, WELCOME_FORM_USE_CASE, WELCOME_FORM_CUSTOM_USE_CASE, - WELCOME_FORM_HEADER, WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER, WELCOME_FORM_USE_CASE_PLACEHOLDER, + CONTINUE, + ONBOARDING_STATUS_GET_STARTED, } from "@appsmith/constants/messages"; import FormTextField from "components/utils/ReduxFormTextField"; import type { SetupFormProps } from "./SetupForm"; import { ButtonWrapper } from "pages/Applications/ForkModalStyles"; import { FormGroup } from "design-system-old"; -import { Button } from "design-system"; +import { Button, Checkbox } from "design-system"; import { roleOptions, useCaseOptions } from "./constants"; import { isAirgapped } from "@appsmith/utils/airgapHelpers"; +import { setFirstTimeUserOnboardingTelemetryCalloutVisibility } from "utils/storage"; const DetailsFormWrapper = styled.div` width: 100%; position: relative; - padding-left: ${(props) => props.theme.spaces[17] * 2}px; - padding-right: ${(props) => props.theme.spaces[4]}px; `; -const StyledFormBodyWrapper = styled(FormBodyWrapper)` - width: 260px; +const StyledFormBodyWrapper = styled(FormBodyWrapper)``; + +const StyledTabIndicatorWrapper = styled.div` + display: flex; +`; + +const StyledTabIndicator = styled.div<{ isFirstPage?: boolean }>` + width: 48px; + height: 3px; + margin: 0 6px 0 0; + background-color: ${(props) => + props.isFirstPage + ? `var(--ads-v2-color-bg-emphasis);` + : `var(--ads-v2-color-bg-brand);`}; `; const StyledFormGroup = styled(FormGroup)` @@ -52,107 +59,143 @@ export default function DetailsForm( ) { const ref = React.createRef(); - const isAirgappedInstance = isAirgapped(); + const [formState, setFormState] = useState(0); + + const isFirstPage = useMemo(() => formState === 0, [formState]); + + useEffect(() => { + const setTelemetryVisibleFalse = async () => { + await setFirstTimeUserOnboardingTelemetryCalloutVisibility(false); + }; + setTelemetryVisibleFalse(); + }, []); return ( -
- 1. - {createMessage(WELCOME_FORM_HEADER)} -
+ + + + - - - - - - - - - - - - - - - - {props.role == "other" && ( - - +
+
+ + + + + + + +
+ + - )} - - - - {props.useCase == "other" && ( - - + + + + + +
+ + {!isFirstPage && ( +
+ + + + {props.role == "other" && ( + + + + )} + + + + {props.useCase == "other" && ( + + + + )} + + {!isAirgapped() && ( + + I want security and product updates. + + )} +
)}
diff --git a/app/client/src/pages/setup/GetStarted.tsx b/app/client/src/pages/setup/GetStarted.tsx index 8746fbc056..18acea0d1b 100644 --- a/app/client/src/pages/setup/GetStarted.tsx +++ b/app/client/src/pages/setup/GetStarted.tsx @@ -22,6 +22,7 @@ import { Field, formValueSelector, reduxForm } from "redux-form"; import styled from "styled-components"; import { DropdownWrapper, withDropdown } from "./common"; import { roleOptions, useCaseOptions } from "./constants"; +import SetupForm from "./SetupForm"; const ActionContainer = styled.div` margin-top: ${(props) => props.theme.spaces[15]}px; @@ -41,16 +42,10 @@ type NonSuperUserFormData = { role_name?: string; }; -export function SuperUserForm(props: UserFormProps) { +export function SuperUserForm() { return ( - props.onGetStarted && props.onGetStarted()} - size="md" - > - {createMessage(WELCOME_ACTION)} - + ); } @@ -124,15 +119,9 @@ function NonSuperUser( - !props.invalid && // temp fix - design system needs to be fixed for disabling click - props.onGetStarted && - props.onGetStarted( - props.role !== "other" ? props.role : props.role_name, - props.useCase, - ) - } - size="md" + kind="primary" + renderAs="button" + type="submit" > {createMessage(WELCOME_ACTION)} diff --git a/app/client/src/pages/setup/NewsletterForm.tsx b/app/client/src/pages/setup/NewsletterForm.tsx deleted file mode 100644 index 8058c2c1b3..0000000000 --- a/app/client/src/pages/setup/NewsletterForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { noop } from "lodash"; -import React from "react"; -import styled from "styled-components"; -import { Button, Switch } from "design-system"; -import { - AllowToggle, - AllowToggleWrapper, - ButtonWrapper, - FormBodyWrapper, - FormHeaderIndex, - FormHeaderLabel, -} from "./common"; -import { memo } from "react"; -import { - createMessage, - WELCOME_FORM_NEWLETTER_HEADER, - WELCOME_FORM_NEWLETTER_LABEL, - WELCOME_FORM_SUBMIT_LABEL, -} from "@appsmith/constants/messages"; - -const NewsletterContainer = styled.div` - width: 100%; - position: relative; - padding-left: ${(props) => props.theme.spaces[17] * 2}px; - margin-top: ${(props) => props.theme.spaces[12] * 2}px; -`; - -export default memo(function NewsletterForm() { - return ( - -
- 3. - - {createMessage(WELCOME_FORM_NEWLETTER_HEADER)} - -
- - - - noop} - value={"true"} - > - {createMessage(WELCOME_FORM_NEWLETTER_LABEL)} - - - - - - - -
- ); -}); diff --git a/app/client/src/pages/setup/SetupForm.tsx b/app/client/src/pages/setup/SetupForm.tsx index 79594906ff..ad28087370 100644 --- a/app/client/src/pages/setup/SetupForm.tsx +++ b/app/client/src/pages/setup/SetupForm.tsx @@ -1,10 +1,7 @@ import React, { useRef } from "react"; import styled from "styled-components"; import { connect } from "react-redux"; -import DataCollectionForm from "./DataCollectionForm"; import DetailsForm from "./DetailsForm"; -import NewsletterForm from "./NewsletterForm"; -import AppsmithLogo from "assets/images/appsmith_logo.png"; import { WELCOME_FORM_USECASE_FIELD_NAME, WELCOME_FORM_EMAIL_FIELD_NAME, @@ -23,48 +20,29 @@ import type { AppState } from "@appsmith/reducers"; import { SUPER_USER_SUBMIT_PATH } from "@appsmith/constants/ApiConstants"; import { useState } from "react"; import { isAirgapped } from "@appsmith/utils/airgapHelpers"; -import { noop } from "utils/AppsmithUtils"; const PageWrapper = styled.div` width: 100%; display: flex; - justify-content: center; - height: 100vh; + justify-content: start; overflow: auto; position: relative; z-index: 100; `; -const SetupFormContainer = styled.div` - padding: 120px 42px 0px 0px; -`; +const SetupFormContainer = styled.div``; const SetupStep = styled.div<{ active: boolean }>` display: ${(props) => (props.active ? "block" : "none")}; `; -const LogoContainer = styled.div` - padding-left: ${(props) => props.theme.spaces[17] * 2}px; - padding-top: ${(props) => props.theme.spaces[12] * 2}px; - transform: translate(-11px, 0); - background-color: ${(props) => props.theme.colors.homepageBackground}; - position: fixed; - width: 566px; - height: 112px; - z-index: 1; - top: 0; -`; - -const AppsmithLogoImg = styled.img` - max-width: 170px; -`; - const SpaceFiller = styled.div` height: 100px; `; export type DetailsFormValues = { - name?: string; + firstName?: string; + lastName?: string; email?: string; password?: string; verifyPassword?: string; @@ -76,20 +54,20 @@ export type DetailsFormValues = { const validate = (values: DetailsFormValues) => { const errors: DetailsFormValues = {}; - if (!values.name) { - errors.name = "Please enter a valid Full Name"; + if (!values.firstName) { + errors.firstName = "This field is required."; } if (!values.email || !isEmail(values.email)) { - errors.email = "Please enter a valid Email address"; + errors.email = "Enter a valid email address."; } if (!values.password || !isStrongPassword(values.password)) { - errors.password = "Please enter a strong password"; + errors.password = "Please enter a strong password."; } if (!values.verifyPassword || values.password != values.verifyPassword) { - errors.verifyPassword = "Please reenter the password"; + errors.verifyPassword = "Passwords don't match."; } if (!values.role) { @@ -120,15 +98,35 @@ export type SetupFormProps = DetailsFormValues & { >; function SetupForm(props: SetupFormProps) { - const isAirgappedInstance = isAirgapped(); const signupURL = `/api/v1/${SUPER_USER_SUBMIT_PATH}`; const [showDetailsForm, setShowDetailsForm] = useState(true); const formRef = useRef(null); + const isAirgappedFlag = isAirgapped(); + const onSubmit = () => { const form: HTMLFormElement = formRef.current as HTMLFormElement; const verifyPassword: HTMLInputElement = document.querySelector( `[name="verifyPassword"]`, ) as HTMLInputElement; + verifyPassword.removeAttribute("name"); + + const firstName: HTMLInputElement = document.querySelector( + `[name="firstName"]`, + ) as HTMLInputElement; + + const lastName: HTMLInputElement = document.querySelector( + `[name="lastName"]`, + ) as HTMLInputElement; + + if (firstName && lastName) { + const fullName = document.createElement("input"); + fullName.type = "text"; + fullName.name = "name"; + fullName.style.display = "none"; + fullName.value = `${firstName.value} ${lastName.value}`; + form.appendChild(fullName); + } + const roleInput = document.createElement("input"); verifyPassword.removeAttribute("name"); roleInput.type = "text"; @@ -138,10 +136,6 @@ function SetupForm(props: SetupFormProps) { roleInput.value = props.role as string; } else { roleInput.value = props.role_name as string; - const roleNameInput: HTMLInputElement = document.querySelector( - `[name="role_name"]`, - ) as HTMLInputElement; - if (roleNameInput) roleNameInput.remove(); } form.appendChild(roleInput); const useCaseInput = document.createElement("input"); @@ -152,12 +146,20 @@ function SetupForm(props: SetupFormProps) { useCaseInput.value = props.useCase as string; } else { useCaseInput.value = props.custom_useCase as string; - const customUseCaseInput: HTMLInputElement = document.querySelector( - `[name="custom_useCase"]`, - ) as HTMLInputElement; - if (customUseCaseInput) customUseCaseInput.remove(); } form.appendChild(useCaseInput); + const anonymousDataInput = document.createElement("input"); + anonymousDataInput.type = "checkbox"; + anonymousDataInput.value = isAirgappedFlag ? "false" : "true"; + anonymousDataInput.checked = isAirgappedFlag ? false : true; + anonymousDataInput.name = "allowCollectingAnonymousData"; + anonymousDataInput.style.display = "none"; + form.appendChild(anonymousDataInput); + const signupForNewsletter: HTMLInputElement = document.querySelector( + `[name="signupForNewsletter"]`, + ) as HTMLInputElement; + if (signupForNewsletter) + signupForNewsletter.value = signupForNewsletter.checked.toString(); return true; }; @@ -191,9 +193,6 @@ function SetupForm(props: SetupFormProps) { return ( - - -
- noop} - /> + - {!isAirgappedInstance && ( - - - - - )}
diff --git a/app/client/src/pages/setup/Welcome.tsx b/app/client/src/pages/setup/Welcome.tsx index 0aceeb8466..56d857b86e 100644 --- a/app/client/src/pages/setup/Welcome.tsx +++ b/app/client/src/pages/setup/Welcome.tsx @@ -1,4 +1,4 @@ -import React, { memo, useState } from "react"; +import React, { memo } from "react"; import styled from "styled-components"; import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; import { useEffect } from "react"; @@ -11,49 +11,64 @@ import { import NonSuperUserForm, { SuperUserForm } from "./GetStarted"; import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; -const LandingPageWrapper = styled.div<{ hide: boolean }>` - width: ${(props) => props.theme.pageContentWidth}px; +const LandingPageWrapper = styled.div` + width: 100%; height: 100vh; display: flex; justify-content: center; - align-items: center; + align-items: flex-start; margin: 0 auto; - opacity: ${(props) => (props.hide ? 0 : 1)}; `; const LandingPageContent = styled.div` width: 100%; + height: 100%; display: flex; - align-items: center; + align-items: start; + justify-content: center; position: relative; z-index: 100; `; const StyledTextBanner = styled.div` - min-width: ${(props) => props.theme.pageContentWidth * 0.55}px; - padding-left: 64px; + width: 60%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: 6%; `; -const StyledBannerHeader = styled.h1` - font-family: "Paytone One", sans-serif; +const StyledBannerHeader = styled.div` font-size: 72px; margin: 0px 0px; - color: var(--ads-v2-color-fg-emphasis-plus); + font-weight: 600; + margin-right: 1rem; + width: 100%; + text-align: center; `; -const StyledBannerBody = styled.p` - font-family: "Montserrat", sans-serif; +const StyledBannerBody = styled.div` font-size: 24px; margin: ${(props) => props.theme.spaces[7]}px 0px; - width: 400px; + font-weight: 500; + margin-right: 8rem; + width: 100%; + text-align: center; color: var(--ads-v2-color-fg); `; const StyledImageBanner = styled.div` - min-width: ${(props) => props.theme.pageContentWidth * 0.45}px; + width: 40%; + display: flex; + justify-content: center; + height: 100%; + flex-direction: column; + align-items: end; `; -const getWelcomeImage = () => `${ASSETS_CDN_URL}/welcome-banner.svg`; +const getWelcomeImage = () => `${ASSETS_CDN_URL}/welcome-banner-v2.svg`; +const getAppsmithLogo = () => `${ASSETS_CDN_URL}/appsmith-logo.svg`; type LandingPageProps = { onGetStarted?: (role?: string, useCase?: string) => void; @@ -62,25 +77,6 @@ type LandingPageProps = { const WELCOME_PAGE_ANIMATION_CONTAINER = "welcome-page-animation-container"; -const includeFonts = () => { - const preconnectGoogleapis = document.createElement("link"); - preconnectGoogleapis.rel = "preconnect"; - preconnectGoogleapis.href = "https://fonts.googleapis.com"; - document.head.appendChild(preconnectGoogleapis); - - const preconnectGstatic = document.createElement("link") as any; - preconnectGstatic.rel = "preconnect"; - preconnectGstatic.href = "https://fonts.gstatic.com"; - preconnectGstatic.crossorigin = "crossorigin"; - document.head.appendChild(preconnectGstatic); - - const fonts = document.createElement("link"); - fonts.rel = "stylesheet"; - fonts.href = - "https://fonts.googleapis.com/css2?family=Montserrat&family=Paytone+One&display=swap"; - document.head.appendChild(fonts); -}; - function Banner() { return ( <> @@ -91,32 +87,30 @@ function Banner() { } export default memo(function LandingPage(props: LandingPageProps) { - const [fontsInjected, setFontsInjected] = useState(false); useEffect(() => { - includeFonts(); playWelcomeAnimation(`#${WELCOME_PAGE_ANIMATION_CONTAINER}`); - //wait for the fonts to be loaded - setTimeout(() => { - setFontsInjected(true); - }, 100); }, []); return ( {props.forSuperUser ? ( - + ) : ( )} - +
+ +
+
+ +
diff --git a/app/client/src/pages/setup/common.tsx b/app/client/src/pages/setup/common.tsx index a1d1e311f8..dd5ad75309 100644 --- a/app/client/src/pages/setup/common.tsx +++ b/app/client/src/pages/setup/common.tsx @@ -60,9 +60,6 @@ export const StyledLink = styled.a` const DROPDOWN_CLASSNAME = "setup-dropdown"; export const DropdownWrapper = styled(StyledFormGroup)` - && { - margin-bottom: 33px; - } && .cs-text { width: 100%; } diff --git a/app/client/src/utils/storage.ts b/app/client/src/utils/storage.ts index 436193d859..8cdc6f44f4 100644 --- a/app/client/src/utils/storage.ts +++ b/app/client/src/utils/storage.ts @@ -20,6 +20,8 @@ export const STORAGE_KEYS: { "FIRST_TIME_USER_ONBOARDING_INTRO_MODAL_VISIBILITY", HIDE_CONCURRENT_EDITOR_WARNING_TOAST: "HIDE_CONCURRENT_EDITOR_WARNING_TOAST", APP_THEMING_BETA_SHOWN: "APP_THEMING_BETA_SHOWN", + FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY: + "FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY", }; const store = localforage.createInstance({ @@ -384,3 +386,34 @@ export const setTemplateNotificationSeen = async (flag: boolean) => { return false; } }; +export const getFirstTimeUserOnboardingTelemetryCalloutIsAlreadyShown = + async () => { + try { + const flag = await store.getItem( + STORAGE_KEYS.FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY, + ); + return flag; + } catch (error) { + log.error( + "An error occurred while fetching FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY", + ); + log.error(error); + } + }; + +export const setFirstTimeUserOnboardingTelemetryCalloutVisibility = async ( + flag: boolean, +) => { + try { + await store.setItem( + STORAGE_KEYS.FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY, + flag, + ); + return true; + } catch (error) { + log.error( + "An error occurred while fetching FIRST_TIME_USER_ONBOARDING_TELEMETRY_CALLOUT_VISIBILITY", + ); + log.error(error); + } +};