feat: onboarding flow revamp for first time admin users: ce (#23581)
Onboarding flow revamp for first time admin users. Uses the new ADS2.0 Components. Shows telemetry popup. zeplin: https://app.zeplin.io/project/642f9f61af65ec6928659130/screen/64476316d729f744cb695232 #### PR fixes following issue(s) Fixes #https://github.com/appsmithorg/cloud-services/issues/673 #### Type of change > Please delete options that are not relevant. - Breaking change (fix or feature that would cause existing functionality to not work as expected) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] Jest - [x] Cypress > > #### Test Plan > > https://github.com/appsmithorg/TestSmith/issues/2401 > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
parent
55056dacda
commit
94f5744800
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"getStarted": ".t--welcome-form-get-started",
|
"firstName":".t--welcome-form-first-name input",
|
||||||
"fullName": ".t--welcome-form-full-name input",
|
"lastName":".t--welcome-form-last-name input",
|
||||||
"email":".t--welcome-form-email input",
|
"email":".t--welcome-form-email input",
|
||||||
"password":".t--welcome-form-password input",
|
"password":".t--welcome-form-password input",
|
||||||
"verifyPassword":".t--welcome-form-verify-password input",
|
"verifyPassword":".t--welcome-form-verify-password input",
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
"roleInput": ".t--welcome-form-role-input input",
|
"roleInput": ".t--welcome-form-role-input input",
|
||||||
"useCaseDropdown": ".t--welcome-form-role-usecase",
|
"useCaseDropdown": ".t--welcome-form-role-usecase",
|
||||||
"useCaseDropdownOption": ".rc-select-item-option-content",
|
"useCaseDropdownOption": ".rc-select-item-option-content",
|
||||||
"nextButton": ".t--welcome-form-next-button",
|
"submitButton": ".t--welcome-form-submit-button",
|
||||||
"dataCollection": ".ads-v2-switch__label:last",
|
"dataCollection": ".t--welcome-form-datacollection",
|
||||||
"newsLetter": ".ads-v2-switch__label:first",
|
"newsLetter": ".t--welcome-form-newsletter",
|
||||||
"createButton": ".t--welcome-form-create-button",
|
"createButton": ".t--welcome-form-create-button",
|
||||||
"createSuperUser": "#super-user-form",
|
"createSuperUser": "#super-user-form",
|
||||||
"superUserForm": "[data-testid='super-user-form']"
|
"superUserForm": "[data-testid='super-user-form']"
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ Cypress.Commands.add("Signup", (uname, pword) => {
|
||||||
cy.get(signupPage.dropdownOption).click();
|
cy.get(signupPage.dropdownOption).click();
|
||||||
cy.get(signupPage.useCaseDropdown).click();
|
cy.get(signupPage.useCaseDropdown).click();
|
||||||
cy.get(signupPage.dropdownOption).click();
|
cy.get(signupPage.dropdownOption).click();
|
||||||
cy.get(signupPage.roleUsecaseSubmit).click();
|
cy.get(signupPage.roleUsecaseSubmit).click({ force: true });
|
||||||
|
|
||||||
cy.wait("@getMe");
|
cy.wait("@getMe");
|
||||||
cy.wait(3000);
|
cy.wait(3000);
|
||||||
|
|
@ -1275,73 +1275,36 @@ Cypress.Commands.add("createJSObject", (JSCode) => {
|
||||||
|
|
||||||
Cypress.Commands.add("createSuperUser", () => {
|
Cypress.Commands.add("createSuperUser", () => {
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
cy.get(welcomePage.getStarted).should("be.visible");
|
cy.get(welcomePage.firstName).should("be.visible");
|
||||||
cy.get(welcomePage.getStarted).should("not.be.disabled");
|
cy.get(welcomePage.lastName).should("be.visible");
|
||||||
cy.get(welcomePage.getStarted).click();
|
|
||||||
cy.get(welcomePage.fullName).should("be.visible");
|
|
||||||
cy.get(welcomePage.email).should("be.visible");
|
cy.get(welcomePage.email).should("be.visible");
|
||||||
cy.get(welcomePage.password).should("be.visible");
|
cy.get(welcomePage.password).should("be.visible");
|
||||||
cy.get(welcomePage.verifyPassword).should("be.visible");
|
cy.get(welcomePage.verifyPassword).should("be.visible");
|
||||||
cy.get(welcomePage.roleDropdown).should("be.visible");
|
cy.get(welcomePage.submitButton).should("be.disabled");
|
||||||
cy.get(welcomePage.useCaseDropdown).should("be.visible");
|
|
||||||
cy.get(welcomePage.nextButton).should("be.disabled");
|
|
||||||
|
|
||||||
cy.get(welcomePage.fullName).type(Cypress.env("USERNAME"));
|
cy.get(welcomePage.firstName).type(Cypress.env("USERNAME"));
|
||||||
cy.get(welcomePage.nextButton).should("be.disabled");
|
cy.get(welcomePage.submitButton).should("be.disabled");
|
||||||
cy.get(welcomePage.email).type(Cypress.env("USERNAME"));
|
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.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.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.roleDropdown).click();
|
||||||
cy.get(welcomePage.roleDropdownOption).eq(1).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.useCaseDropdown).click();
|
||||||
cy.get(welcomePage.useCaseDropdownOption).eq(1).click();
|
cy.get(welcomePage.useCaseDropdownOption).eq(1).click();
|
||||||
cy.get(welcomePage.nextButton).should("not.be.disabled");
|
cy.get(welcomePage.submitButton).should("not.be.disabled");
|
||||||
cy.get(welcomePage.nextButton).click();
|
cy.get(welcomePage.submitButton).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) => {
|
cy.wait("@createSuperUser").then((interception) => {
|
||||||
expect(interception.request.body).contains(
|
expect(interception.request.body).contains(
|
||||||
"allowCollectingAnonymousData=true",
|
"allowCollectingAnonymousData=true",
|
||||||
);
|
);
|
||||||
expect(interception.request.body).contains("signupForNewsletter=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.LogOut();
|
cy.LogOut();
|
||||||
cy.wait(2000);
|
cy.wait(2000);
|
||||||
|
|
|
||||||
|
|
@ -999,6 +999,9 @@ export const ONBOARDING_CHECKLIST_DEPLOY_APPLICATIONS = {
|
||||||
export const ONBOARDING_CHECKLIST_FOOTER = () =>
|
export const ONBOARDING_CHECKLIST_FOOTER = () =>
|
||||||
"Not sure where to start? Take the welcome tour";
|
"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
|
//Introduction modal
|
||||||
export const HOW_APPSMITH_WORKS = () =>
|
export const HOW_APPSMITH_WORKS = () =>
|
||||||
"Here’s a quick overview of 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";
|
export const SNIPPET_TOOLTIP = () => "Search code snippets";
|
||||||
|
|
||||||
//Welcome page
|
//Welcome page
|
||||||
export const WELCOME_HEADER = () => "Welcome!";
|
export const WELCOME_HEADER = () => "Almost there";
|
||||||
export const WELCOME_BODY = () =>
|
export const WELCOME_BODY = () => "Let's setup your account first";
|
||||||
"Let us setup your account so you can make awesome applications!";
|
|
||||||
export const WELCOME_ACTION = () => "Get started";
|
export const WELCOME_ACTION = () => "Get started";
|
||||||
|
|
||||||
// API Editor
|
// API Editor
|
||||||
|
|
@ -1081,10 +1083,11 @@ export const ACTION_EXECUTION_MESSAGE = (actionType: string) =>
|
||||||
export const ACTION_EXECUTION_CANCEL = () => "Cancel request";
|
export const ACTION_EXECUTION_CANCEL = () => "Cancel request";
|
||||||
|
|
||||||
export const WELCOME_FORM_HEADER = () => "Let us get to know you better!";
|
export const WELCOME_FORM_HEADER = () => "Let us get to know you better!";
|
||||||
export const WELCOME_FORM_FULL_NAME = () => "Full Name";
|
export const WELCOME_FORM_FIRST_NAME = () => "First name";
|
||||||
export const WELCOME_FORM_EMAIL_ID = () => "Email Id";
|
export const WELCOME_FORM_LAST_NAME = () => "Last name";
|
||||||
export const WELCOME_FORM_CREATE_PASSWORD = () => "Create Password";
|
export const WELCOME_FORM_EMAIL_ID = () => "Email";
|
||||||
export const WELCOME_FORM_VERIFY_PASSWORD = () => "Verify Password";
|
export const WELCOME_FORM_CREATE_PASSWORD = () => "Enter password";
|
||||||
|
export const WELCOME_FORM_VERIFY_PASSWORD = () => "Verify password";
|
||||||
export const WELCOME_FORM_ROLE_DROPDOWN = () =>
|
export const WELCOME_FORM_ROLE_DROPDOWN = () =>
|
||||||
"Tell us about your primary skillset";
|
"Tell us about your primary skillset";
|
||||||
export const WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER = () =>
|
export const WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER = () =>
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<div className="absolute top-5">
|
||||||
|
<Callout
|
||||||
|
isClosable
|
||||||
|
kind="info"
|
||||||
|
links={[
|
||||||
|
{
|
||||||
|
children: createMessage(ADMIN_SETTINGS),
|
||||||
|
to: ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
children: createMessage(LEARN_MORE),
|
||||||
|
to: TELEMETRY_DOCS_PAGE_URL,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onClose={() => {
|
||||||
|
props.onCloseCallout();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{createMessage(ONBOARDING_TELEMETRY_POPUP)}
|
||||||
|
</Callout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ import {
|
||||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
import { INTEGRATION_TABS } from "constants/routes";
|
import { INTEGRATION_TABS } from "constants/routes";
|
||||||
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -33,14 +33,25 @@ import {
|
||||||
getDatasources,
|
getDatasources,
|
||||||
getPageActions,
|
getPageActions,
|
||||||
} from "selectors/entitiesSelector";
|
} from "selectors/entitiesSelector";
|
||||||
import { getFirstTimeUserOnboardingModal } from "selectors/onboardingSelectors";
|
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import IntroductionModal from "./IntroductionModal";
|
|
||||||
import { integrationEditorURL } from "RouteBuilder";
|
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 { DatasourceCreateEntryPoints } from "constants/Datasource";
|
||||||
|
import IntroductionModal from "./IntroductionModal";
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -92,6 +103,12 @@ const getOnboardingQueryImg = () => `${ASSETS_CDN_URL}/onboarding-query.svg`;
|
||||||
const getOnboardingWidgetImg = () => `${ASSETS_CDN_URL}/onboarding-widget.svg`;
|
const getOnboardingWidgetImg = () => `${ASSETS_CDN_URL}/onboarding-widget.svg`;
|
||||||
|
|
||||||
export default function OnboardingTasks() {
|
export default function OnboardingTasks() {
|
||||||
|
const [isAnonymousDataPopupOpen, setisAnonymousDataPopupOpen] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
|
const isFirstTimeUserOnboardingEnabled = useSelector(
|
||||||
|
getIsFirstTimeUserOnboardingEnabled,
|
||||||
|
);
|
||||||
const applicationId = useSelector(getCurrentApplicationId);
|
const applicationId = useSelector(getCurrentApplicationId);
|
||||||
const pageId = useSelector(getCurrentPageId);
|
const pageId = useSelector(getCurrentPageId);
|
||||||
let content;
|
let content;
|
||||||
|
|
@ -99,7 +116,44 @@ export default function OnboardingTasks() {
|
||||||
const actions = useSelector(getPageActions(pageId));
|
const actions = useSelector(getPageActions(pageId));
|
||||||
const widgets = useSelector(getCanvasWidgets);
|
const widgets = useSelector(getCanvasWidgets);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const user = useSelector(getCurrentUser);
|
||||||
|
const isAdmin = user?.isSuperUser || false;
|
||||||
|
const isOnboardingCompleted = useSelector(getFirstTimeUserOnboardingComplete);
|
||||||
const showModal = useSelector(getFirstTimeUserOnboardingModal);
|
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) {
|
if (!datasources.length && !actions.length) {
|
||||||
content = (
|
content = (
|
||||||
<CenteredContainer>
|
<CenteredContainer>
|
||||||
|
|
@ -272,7 +326,10 @@ export default function OnboardingTasks() {
|
||||||
return (
|
return (
|
||||||
<Wrapper data-testid="onboarding-tasks-wrapper">
|
<Wrapper data-testid="onboarding-tasks-wrapper">
|
||||||
{content}
|
{content}
|
||||||
{showModal && (
|
{isAnonymousDataPopupOpen && (
|
||||||
|
<AnonymousDataPopup onCloseCallout={hideAnonymousDataPopup} />
|
||||||
|
)}
|
||||||
|
{!isAdmin && showModal && (
|
||||||
<IntroductionModal
|
<IntroductionModal
|
||||||
close={() => {
|
close={() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -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 (
|
|
||||||
<DataCollectionFormWrapper>
|
|
||||||
<div className="relative flex flex-col items-start">
|
|
||||||
<FormHeaderIndex className="absolute -left-6">2.</FormHeaderIndex>
|
|
||||||
<FormHeaderLabel>
|
|
||||||
{createMessage(WELCOME_FORM_DATA_COLLECTION_HEADER)}
|
|
||||||
</FormHeaderLabel>
|
|
||||||
<FormHeaderSubtext>
|
|
||||||
{createMessage(WELCOME_FORM_DATA_COLLECTION_BODY)}
|
|
||||||
<br />
|
|
||||||
<StyledLink kind="primary" target="_blank" to={TELEMETRY_URL}>
|
|
||||||
{createMessage(WELCOME_FORM_DATA_COLLECTION_LINK)}
|
|
||||||
</StyledLink>
|
|
||||||
</FormHeaderSubtext>
|
|
||||||
</div>
|
|
||||||
<FormBodyWrapper>
|
|
||||||
<ControlWrapper>
|
|
||||||
<AllowToggleWrapper>
|
|
||||||
<AllowToggle>
|
|
||||||
<Switch
|
|
||||||
className="t--welcome-form-datacollection"
|
|
||||||
isSelected={allowCollection}
|
|
||||||
name="allowCollectingAnonymousData"
|
|
||||||
onChange={(value: boolean) => setAllowCollection(value)}
|
|
||||||
value={allowCollection.toString()}
|
|
||||||
>
|
|
||||||
{createMessage(WELCOME_FORM_DATA_COLLECTION_LABEL_ENABLE)}
|
|
||||||
</Switch>
|
|
||||||
</AllowToggle>
|
|
||||||
</AllowToggleWrapper>
|
|
||||||
</ControlWrapper>
|
|
||||||
</FormBodyWrapper>
|
|
||||||
</DataCollectionFormWrapper>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -1,44 +1,51 @@
|
||||||
import React from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Field } from "redux-form";
|
import { Field } from "redux-form";
|
||||||
import {
|
import { DropdownWrapper, FormBodyWrapper, withDropdown } from "./common";
|
||||||
DropdownWrapper,
|
|
||||||
FormBodyWrapper,
|
|
||||||
FormHeaderIndex,
|
|
||||||
FormHeaderLabel,
|
|
||||||
withDropdown,
|
|
||||||
} from "./common";
|
|
||||||
import {
|
import {
|
||||||
createMessage,
|
createMessage,
|
||||||
WELCOME_FORM_EMAIL_ID,
|
WELCOME_FORM_EMAIL_ID,
|
||||||
WELCOME_FORM_FULL_NAME,
|
WELCOME_FORM_FIRST_NAME,
|
||||||
|
WELCOME_FORM_LAST_NAME,
|
||||||
WELCOME_FORM_CREATE_PASSWORD,
|
WELCOME_FORM_CREATE_PASSWORD,
|
||||||
WELCOME_FORM_VERIFY_PASSWORD,
|
WELCOME_FORM_VERIFY_PASSWORD,
|
||||||
WELCOME_FORM_ROLE_DROPDOWN,
|
WELCOME_FORM_ROLE_DROPDOWN,
|
||||||
WELCOME_FORM_ROLE,
|
WELCOME_FORM_ROLE,
|
||||||
WELCOME_FORM_USE_CASE,
|
WELCOME_FORM_USE_CASE,
|
||||||
WELCOME_FORM_CUSTOM_USE_CASE,
|
WELCOME_FORM_CUSTOM_USE_CASE,
|
||||||
WELCOME_FORM_HEADER,
|
|
||||||
WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER,
|
WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER,
|
||||||
WELCOME_FORM_USE_CASE_PLACEHOLDER,
|
WELCOME_FORM_USE_CASE_PLACEHOLDER,
|
||||||
|
CONTINUE,
|
||||||
|
ONBOARDING_STATUS_GET_STARTED,
|
||||||
} from "@appsmith/constants/messages";
|
} from "@appsmith/constants/messages";
|
||||||
import FormTextField from "components/utils/ReduxFormTextField";
|
import FormTextField from "components/utils/ReduxFormTextField";
|
||||||
import type { SetupFormProps } from "./SetupForm";
|
import type { SetupFormProps } from "./SetupForm";
|
||||||
import { ButtonWrapper } from "pages/Applications/ForkModalStyles";
|
import { ButtonWrapper } from "pages/Applications/ForkModalStyles";
|
||||||
import { FormGroup } from "design-system-old";
|
import { FormGroup } from "design-system-old";
|
||||||
import { Button } from "design-system";
|
import { Button, Checkbox } from "design-system";
|
||||||
import { roleOptions, useCaseOptions } from "./constants";
|
import { roleOptions, useCaseOptions } from "./constants";
|
||||||
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
||||||
|
import { setFirstTimeUserOnboardingTelemetryCalloutVisibility } from "utils/storage";
|
||||||
|
|
||||||
const DetailsFormWrapper = styled.div`
|
const DetailsFormWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: ${(props) => props.theme.spaces[17] * 2}px;
|
|
||||||
padding-right: ${(props) => props.theme.spaces[4]}px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledFormBodyWrapper = styled(FormBodyWrapper)`
|
const StyledFormBodyWrapper = styled(FormBodyWrapper)``;
|
||||||
width: 260px;
|
|
||||||
|
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)`
|
const StyledFormGroup = styled(FormGroup)`
|
||||||
|
|
@ -52,57 +59,74 @@ export default function DetailsForm(
|
||||||
) {
|
) {
|
||||||
const ref = React.createRef<HTMLDivElement>();
|
const ref = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
const isAirgappedInstance = isAirgapped();
|
const [formState, setFormState] = useState(0);
|
||||||
|
|
||||||
|
const isFirstPage = useMemo(() => formState === 0, [formState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const setTelemetryVisibleFalse = async () => {
|
||||||
|
await setFirstTimeUserOnboardingTelemetryCalloutVisibility(false);
|
||||||
|
};
|
||||||
|
setTelemetryVisibleFalse();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DetailsFormWrapper ref={ref}>
|
<DetailsFormWrapper ref={ref}>
|
||||||
<div className="relative flex-col items-start">
|
<StyledTabIndicatorWrapper>
|
||||||
<FormHeaderIndex className="absolute -left-6">1.</FormHeaderIndex>
|
<StyledTabIndicator />
|
||||||
<FormHeaderLabel>{createMessage(WELCOME_FORM_HEADER)}</FormHeaderLabel>
|
<StyledTabIndicator isFirstPage={isFirstPage} />
|
||||||
</div>
|
</StyledTabIndicatorWrapper>
|
||||||
<StyledFormBodyWrapper>
|
<StyledFormBodyWrapper>
|
||||||
<StyledFormGroup
|
<div className={isFirstPage ? "block" : "hidden"}>
|
||||||
className="t--welcome-form-full-name"
|
<div className="flex flex-row justify-between w-100">
|
||||||
label={createMessage(WELCOME_FORM_FULL_NAME)}
|
<StyledFormGroup className="!w-52 t--welcome-form-first-name">
|
||||||
>
|
|
||||||
<FormTextField
|
<FormTextField
|
||||||
autoFocus
|
autoFocus
|
||||||
name="name"
|
label={createMessage(WELCOME_FORM_FIRST_NAME)}
|
||||||
placeholder="John Doe"
|
name="firstName"
|
||||||
|
placeholder="John"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
<StyledFormGroup
|
|
||||||
className="t--welcome-form-email"
|
<StyledFormGroup className="!w-52 t--welcome-form-last-name">
|
||||||
label={createMessage(WELCOME_FORM_EMAIL_ID)}
|
|
||||||
>
|
|
||||||
<FormTextField
|
<FormTextField
|
||||||
|
label={createMessage(WELCOME_FORM_LAST_NAME)}
|
||||||
|
name="lastName"
|
||||||
|
placeholder="Doe"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</StyledFormGroup>
|
||||||
|
</div>
|
||||||
|
<StyledFormGroup className="t--welcome-form-email">
|
||||||
|
<FormTextField
|
||||||
|
label={createMessage(WELCOME_FORM_EMAIL_ID)}
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="How can we reach you?"
|
placeholder="How can we reach you?"
|
||||||
type="email"
|
type="email"
|
||||||
/>
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
<StyledFormGroup
|
<StyledFormGroup className="t--welcome-form-password">
|
||||||
className="t--welcome-form-password"
|
|
||||||
label={createMessage(WELCOME_FORM_CREATE_PASSWORD)}
|
|
||||||
>
|
|
||||||
<FormTextField
|
<FormTextField
|
||||||
|
label={createMessage(WELCOME_FORM_CREATE_PASSWORD)}
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Make it strong!"
|
placeholder="Make it strong!"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
<StyledFormGroup
|
<StyledFormGroup className="t--welcome-form-verify-password">
|
||||||
className="t--welcome-form-verify-password"
|
|
||||||
label={createMessage(WELCOME_FORM_VERIFY_PASSWORD)}
|
|
||||||
>
|
|
||||||
<FormTextField
|
<FormTextField
|
||||||
data-testid="verifyPassword"
|
data-testid="verifyPassword"
|
||||||
|
label={createMessage(WELCOME_FORM_VERIFY_PASSWORD)}
|
||||||
name="verifyPassword"
|
name="verifyPassword"
|
||||||
placeholder="Re-enter password"
|
placeholder="Type correctly"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isFirstPage && (
|
||||||
|
<div>
|
||||||
<DropdownWrapper
|
<DropdownWrapper
|
||||||
className="t--welcome-form-role-dropdown"
|
className="t--welcome-form-role-dropdown"
|
||||||
label={createMessage(WELCOME_FORM_ROLE_DROPDOWN)}
|
label={createMessage(WELCOME_FORM_ROLE_DROPDOWN)}
|
||||||
|
|
@ -111,16 +135,21 @@ export default function DetailsForm(
|
||||||
asyncControl
|
asyncControl
|
||||||
component={withDropdown(roleOptions)}
|
component={withDropdown(roleOptions)}
|
||||||
name="role"
|
name="role"
|
||||||
placeholder={createMessage(WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER)}
|
placeholder={createMessage(
|
||||||
|
WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER,
|
||||||
|
)}
|
||||||
|
size="md"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</DropdownWrapper>
|
</DropdownWrapper>
|
||||||
{props.role == "other" && (
|
{props.role == "other" && (
|
||||||
<StyledFormGroup
|
<StyledFormGroup className="t--welcome-form-role-input">
|
||||||
className="t--welcome-form-role-input"
|
<FormTextField
|
||||||
label={createMessage(WELCOME_FORM_ROLE)}
|
label={createMessage(WELCOME_FORM_ROLE)}
|
||||||
>
|
name="role_name"
|
||||||
<FormTextField name="role_name" placeholder="" type="text" />
|
placeholder=""
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
)}
|
)}
|
||||||
<DropdownWrapper
|
<DropdownWrapper
|
||||||
|
|
@ -136,23 +165,37 @@ export default function DetailsForm(
|
||||||
/>
|
/>
|
||||||
</DropdownWrapper>
|
</DropdownWrapper>
|
||||||
{props.useCase == "other" && (
|
{props.useCase == "other" && (
|
||||||
<StyledFormGroup
|
<StyledFormGroup className="t--welcome-form-use-case-input">
|
||||||
className="t--welcome-form-use-case-input"
|
<FormTextField
|
||||||
label={createMessage(WELCOME_FORM_CUSTOM_USE_CASE)}
|
label={createMessage(WELCOME_FORM_CUSTOM_USE_CASE)}
|
||||||
>
|
name="custom_useCase"
|
||||||
<FormTextField name="custom_useCase" placeholder="" type="text" />
|
placeholder=""
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</StyledFormGroup>
|
</StyledFormGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isAirgapped() && (
|
||||||
|
<Checkbox defaultSelected name="signupForNewsletter" value="true">
|
||||||
|
I want security and product updates.
|
||||||
|
</Checkbox>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
<Button
|
<Button
|
||||||
className="t--welcome-form-next-button"
|
className="t--welcome-form-submit-button w-100"
|
||||||
isDisabled={props.invalid}
|
isDisabled={props.invalid}
|
||||||
kind="secondary"
|
kind="primary"
|
||||||
onClick={!isAirgappedInstance ? props.onNext : undefined}
|
onClick={() => {
|
||||||
|
if (isFirstPage) setFormState(1);
|
||||||
|
}}
|
||||||
size="md"
|
size="md"
|
||||||
type={!isAirgappedInstance ? "button" : "submit"}
|
type={isFirstPage ? "button" : "submit"}
|
||||||
>
|
>
|
||||||
Next
|
{isFirstPage
|
||||||
|
? createMessage(CONTINUE)
|
||||||
|
: createMessage(ONBOARDING_STATUS_GET_STARTED)}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonWrapper>
|
</ButtonWrapper>
|
||||||
</StyledFormBodyWrapper>
|
</StyledFormBodyWrapper>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { Field, formValueSelector, reduxForm } from "redux-form";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { DropdownWrapper, withDropdown } from "./common";
|
import { DropdownWrapper, withDropdown } from "./common";
|
||||||
import { roleOptions, useCaseOptions } from "./constants";
|
import { roleOptions, useCaseOptions } from "./constants";
|
||||||
|
import SetupForm from "./SetupForm";
|
||||||
|
|
||||||
const ActionContainer = styled.div`
|
const ActionContainer = styled.div`
|
||||||
margin-top: ${(props) => props.theme.spaces[15]}px;
|
margin-top: ${(props) => props.theme.spaces[15]}px;
|
||||||
|
|
@ -41,16 +42,10 @@ type NonSuperUserFormData = {
|
||||||
role_name?: string;
|
role_name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SuperUserForm(props: UserFormProps) {
|
export function SuperUserForm() {
|
||||||
return (
|
return (
|
||||||
<ActionContainer>
|
<ActionContainer>
|
||||||
<StyledButton
|
<SetupForm />
|
||||||
className="t--welcome-form-get-started"
|
|
||||||
onClick={() => props.onGetStarted && props.onGetStarted()}
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
{createMessage(WELCOME_ACTION)}
|
|
||||||
</StyledButton>
|
|
||||||
</ActionContainer>
|
</ActionContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -124,15 +119,9 @@ function NonSuperUser(
|
||||||
<StyledButton
|
<StyledButton
|
||||||
className="t--get-started-button"
|
className="t--get-started-button"
|
||||||
isDisabled={props.invalid}
|
isDisabled={props.invalid}
|
||||||
onClick={() =>
|
kind="primary"
|
||||||
!props.invalid && // temp fix - design system needs to be fixed for disabling click
|
renderAs="button"
|
||||||
props.onGetStarted &&
|
type="submit"
|
||||||
props.onGetStarted(
|
|
||||||
props.role !== "other" ? props.role : props.role_name,
|
|
||||||
props.useCase,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
size="md"
|
|
||||||
>
|
>
|
||||||
{createMessage(WELCOME_ACTION)}
|
{createMessage(WELCOME_ACTION)}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
|
||||||
<NewsletterContainer>
|
|
||||||
<div className="relative flex-col items-start">
|
|
||||||
<FormHeaderIndex className="absolute -left-6">3.</FormHeaderIndex>
|
|
||||||
<FormHeaderLabel>
|
|
||||||
{createMessage(WELCOME_FORM_NEWLETTER_HEADER)}
|
|
||||||
</FormHeaderLabel>
|
|
||||||
</div>
|
|
||||||
<FormBodyWrapper>
|
|
||||||
<AllowToggleWrapper>
|
|
||||||
<AllowToggle>
|
|
||||||
<Switch
|
|
||||||
className="t--welcome-form-newsletter"
|
|
||||||
defaultSelected
|
|
||||||
name="signupForNewsletter"
|
|
||||||
onChange={() => noop}
|
|
||||||
value={"true"}
|
|
||||||
>
|
|
||||||
{createMessage(WELCOME_FORM_NEWLETTER_LABEL)}
|
|
||||||
</Switch>
|
|
||||||
</AllowToggle>
|
|
||||||
</AllowToggleWrapper>
|
|
||||||
<ButtonWrapper>
|
|
||||||
<Button
|
|
||||||
className="t--welcome-form-create-button"
|
|
||||||
size="md"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{createMessage(WELCOME_FORM_SUBMIT_LABEL)}
|
|
||||||
</Button>
|
|
||||||
</ButtonWrapper>
|
|
||||||
</FormBodyWrapper>
|
|
||||||
</NewsletterContainer>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import React, { useRef } from "react";
|
import React, { useRef } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import DataCollectionForm from "./DataCollectionForm";
|
|
||||||
import DetailsForm from "./DetailsForm";
|
import DetailsForm from "./DetailsForm";
|
||||||
import NewsletterForm from "./NewsletterForm";
|
|
||||||
import AppsmithLogo from "assets/images/appsmith_logo.png";
|
|
||||||
import {
|
import {
|
||||||
WELCOME_FORM_USECASE_FIELD_NAME,
|
WELCOME_FORM_USECASE_FIELD_NAME,
|
||||||
WELCOME_FORM_EMAIL_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 { SUPER_USER_SUBMIT_PATH } from "@appsmith/constants/ApiConstants";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
|
||||||
import { noop } from "utils/AppsmithUtils";
|
|
||||||
|
|
||||||
const PageWrapper = styled.div`
|
const PageWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: start;
|
||||||
height: 100vh;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SetupFormContainer = styled.div`
|
const SetupFormContainer = styled.div``;
|
||||||
padding: 120px 42px 0px 0px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SetupStep = styled.div<{ active: boolean }>`
|
const SetupStep = styled.div<{ active: boolean }>`
|
||||||
display: ${(props) => (props.active ? "block" : "none")};
|
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`
|
const SpaceFiller = styled.div`
|
||||||
height: 100px;
|
height: 100px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export type DetailsFormValues = {
|
export type DetailsFormValues = {
|
||||||
name?: string;
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
verifyPassword?: string;
|
verifyPassword?: string;
|
||||||
|
|
@ -76,20 +54,20 @@ export type DetailsFormValues = {
|
||||||
|
|
||||||
const validate = (values: DetailsFormValues) => {
|
const validate = (values: DetailsFormValues) => {
|
||||||
const errors: DetailsFormValues = {};
|
const errors: DetailsFormValues = {};
|
||||||
if (!values.name) {
|
if (!values.firstName) {
|
||||||
errors.name = "Please enter a valid Full Name";
|
errors.firstName = "This field is required.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!values.email || !isEmail(values.email)) {
|
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)) {
|
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) {
|
if (!values.verifyPassword || values.password != values.verifyPassword) {
|
||||||
errors.verifyPassword = "Please reenter the password";
|
errors.verifyPassword = "Passwords don't match.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!values.role) {
|
if (!values.role) {
|
||||||
|
|
@ -120,15 +98,35 @@ export type SetupFormProps = DetailsFormValues & {
|
||||||
>;
|
>;
|
||||||
|
|
||||||
function SetupForm(props: SetupFormProps) {
|
function SetupForm(props: SetupFormProps) {
|
||||||
const isAirgappedInstance = isAirgapped();
|
|
||||||
const signupURL = `/api/v1/${SUPER_USER_SUBMIT_PATH}`;
|
const signupURL = `/api/v1/${SUPER_USER_SUBMIT_PATH}`;
|
||||||
const [showDetailsForm, setShowDetailsForm] = useState(true);
|
const [showDetailsForm, setShowDetailsForm] = useState(true);
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
const isAirgappedFlag = isAirgapped();
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
const form: HTMLFormElement = formRef.current as HTMLFormElement;
|
const form: HTMLFormElement = formRef.current as HTMLFormElement;
|
||||||
const verifyPassword: HTMLInputElement = document.querySelector(
|
const verifyPassword: HTMLInputElement = document.querySelector(
|
||||||
`[name="verifyPassword"]`,
|
`[name="verifyPassword"]`,
|
||||||
) as HTMLInputElement;
|
) 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");
|
const roleInput = document.createElement("input");
|
||||||
verifyPassword.removeAttribute("name");
|
verifyPassword.removeAttribute("name");
|
||||||
roleInput.type = "text";
|
roleInput.type = "text";
|
||||||
|
|
@ -138,10 +136,6 @@ function SetupForm(props: SetupFormProps) {
|
||||||
roleInput.value = props.role as string;
|
roleInput.value = props.role as string;
|
||||||
} else {
|
} else {
|
||||||
roleInput.value = props.role_name as string;
|
roleInput.value = props.role_name as string;
|
||||||
const roleNameInput: HTMLInputElement = document.querySelector(
|
|
||||||
`[name="role_name"]`,
|
|
||||||
) as HTMLInputElement;
|
|
||||||
if (roleNameInput) roleNameInput.remove();
|
|
||||||
}
|
}
|
||||||
form.appendChild(roleInput);
|
form.appendChild(roleInput);
|
||||||
const useCaseInput = document.createElement("input");
|
const useCaseInput = document.createElement("input");
|
||||||
|
|
@ -152,12 +146,20 @@ function SetupForm(props: SetupFormProps) {
|
||||||
useCaseInput.value = props.useCase as string;
|
useCaseInput.value = props.useCase as string;
|
||||||
} else {
|
} else {
|
||||||
useCaseInput.value = props.custom_useCase as string;
|
useCaseInput.value = props.custom_useCase as string;
|
||||||
const customUseCaseInput: HTMLInputElement = document.querySelector(
|
|
||||||
`[name="custom_useCase"]`,
|
|
||||||
) as HTMLInputElement;
|
|
||||||
if (customUseCaseInput) customUseCaseInput.remove();
|
|
||||||
}
|
}
|
||||||
form.appendChild(useCaseInput);
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -191,9 +193,6 @@ function SetupForm(props: SetupFormProps) {
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<SetupFormContainer>
|
<SetupFormContainer>
|
||||||
<LogoContainer>
|
|
||||||
<AppsmithLogoImg alt="Appsmith logo" src={AppsmithLogo} />
|
|
||||||
</LogoContainer>
|
|
||||||
<form
|
<form
|
||||||
action={signupURL}
|
action={signupURL}
|
||||||
data-testid="super-user-form"
|
data-testid="super-user-form"
|
||||||
|
|
@ -204,17 +203,8 @@ function SetupForm(props: SetupFormProps) {
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
>
|
>
|
||||||
<SetupStep active={showDetailsForm}>
|
<SetupStep active={showDetailsForm}>
|
||||||
<DetailsForm
|
<DetailsForm {...props} onNext={onNext} />
|
||||||
{...props}
|
|
||||||
onNext={!isAirgappedInstance ? onNext : () => noop}
|
|
||||||
/>
|
|
||||||
</SetupStep>
|
</SetupStep>
|
||||||
{!isAirgappedInstance && (
|
|
||||||
<SetupStep active={!showDetailsForm}>
|
|
||||||
<DataCollectionForm />
|
|
||||||
<NewsletterForm />
|
|
||||||
</SetupStep>
|
|
||||||
)}
|
|
||||||
</form>
|
</form>
|
||||||
<SpaceFiller />
|
<SpaceFiller />
|
||||||
</SetupFormContainer>
|
</SetupFormContainer>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { memo, useState } from "react";
|
import React, { memo } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
@ -11,49 +11,64 @@ import {
|
||||||
import NonSuperUserForm, { SuperUserForm } from "./GetStarted";
|
import NonSuperUserForm, { SuperUserForm } from "./GetStarted";
|
||||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||||
|
|
||||||
const LandingPageWrapper = styled.div<{ hide: boolean }>`
|
const LandingPageWrapper = styled.div`
|
||||||
width: ${(props) => props.theme.pageContentWidth}px;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
opacity: ${(props) => (props.hide ? 0 : 1)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LandingPageContent = styled.div`
|
const LandingPageContent = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: start;
|
||||||
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledTextBanner = styled.div`
|
const StyledTextBanner = styled.div`
|
||||||
min-width: ${(props) => props.theme.pageContentWidth * 0.55}px;
|
width: 60%;
|
||||||
padding-left: 64px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 6%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledBannerHeader = styled.h1`
|
const StyledBannerHeader = styled.div`
|
||||||
font-family: "Paytone One", sans-serif;
|
|
||||||
font-size: 72px;
|
font-size: 72px;
|
||||||
margin: 0px 0px;
|
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`
|
const StyledBannerBody = styled.div`
|
||||||
font-family: "Montserrat", sans-serif;
|
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin: ${(props) => props.theme.spaces[7]}px 0px;
|
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);
|
color: var(--ads-v2-color-fg);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledImageBanner = styled.div`
|
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 = {
|
type LandingPageProps = {
|
||||||
onGetStarted?: (role?: string, useCase?: string) => void;
|
onGetStarted?: (role?: string, useCase?: string) => void;
|
||||||
|
|
@ -62,25 +77,6 @@ type LandingPageProps = {
|
||||||
|
|
||||||
const WELCOME_PAGE_ANIMATION_CONTAINER = "welcome-page-animation-container";
|
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() {
|
function Banner() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -91,32 +87,30 @@ function Banner() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(function LandingPage(props: LandingPageProps) {
|
export default memo(function LandingPage(props: LandingPageProps) {
|
||||||
const [fontsInjected, setFontsInjected] = useState(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
includeFonts();
|
|
||||||
playWelcomeAnimation(`#${WELCOME_PAGE_ANIMATION_CONTAINER}`);
|
playWelcomeAnimation(`#${WELCOME_PAGE_ANIMATION_CONTAINER}`);
|
||||||
//wait for the fonts to be loaded
|
|
||||||
setTimeout(() => {
|
|
||||||
setFontsInjected(true);
|
|
||||||
}, 100);
|
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<LandingPageWrapper
|
<LandingPageWrapper
|
||||||
data-testid={"welcome-page"}
|
data-testid={"welcome-page"}
|
||||||
hide={!fontsInjected}
|
|
||||||
id={WELCOME_PAGE_ANIMATION_CONTAINER}
|
id={WELCOME_PAGE_ANIMATION_CONTAINER}
|
||||||
>
|
>
|
||||||
<LandingPageContent>
|
<LandingPageContent>
|
||||||
<StyledTextBanner>
|
<StyledTextBanner>
|
||||||
<Banner />
|
<Banner />
|
||||||
{props.forSuperUser ? (
|
{props.forSuperUser ? (
|
||||||
<SuperUserForm onGetStarted={props.onGetStarted} />
|
<SuperUserForm />
|
||||||
) : (
|
) : (
|
||||||
<NonSuperUserForm onGetStarted={props.onGetStarted} />
|
<NonSuperUserForm onGetStarted={props.onGetStarted} />
|
||||||
)}
|
)}
|
||||||
</StyledTextBanner>
|
</StyledTextBanner>
|
||||||
<StyledImageBanner>
|
<StyledImageBanner>
|
||||||
<img src={getAssetUrl(getWelcomeImage())} />
|
<div className="flex self-start w-2/6 h-16 ml-56">
|
||||||
|
<img src={getAssetUrl(getAppsmithLogo())} />
|
||||||
|
</div>
|
||||||
|
<div className="flex w-5/6 my-1 h-4/6">
|
||||||
|
<img className="w-full" src={getAssetUrl(getWelcomeImage())} />
|
||||||
|
</div>
|
||||||
</StyledImageBanner>
|
</StyledImageBanner>
|
||||||
</LandingPageContent>
|
</LandingPageContent>
|
||||||
</LandingPageWrapper>
|
</LandingPageWrapper>
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,6 @@ export const StyledLink = styled.a`
|
||||||
|
|
||||||
const DROPDOWN_CLASSNAME = "setup-dropdown";
|
const DROPDOWN_CLASSNAME = "setup-dropdown";
|
||||||
export const DropdownWrapper = styled(StyledFormGroup)`
|
export const DropdownWrapper = styled(StyledFormGroup)`
|
||||||
&& {
|
|
||||||
margin-bottom: 33px;
|
|
||||||
}
|
|
||||||
&& .cs-text {
|
&& .cs-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ export const STORAGE_KEYS: {
|
||||||
"FIRST_TIME_USER_ONBOARDING_INTRO_MODAL_VISIBILITY",
|
"FIRST_TIME_USER_ONBOARDING_INTRO_MODAL_VISIBILITY",
|
||||||
HIDE_CONCURRENT_EDITOR_WARNING_TOAST: "HIDE_CONCURRENT_EDITOR_WARNING_TOAST",
|
HIDE_CONCURRENT_EDITOR_WARNING_TOAST: "HIDE_CONCURRENT_EDITOR_WARNING_TOAST",
|
||||||
APP_THEMING_BETA_SHOWN: "APP_THEMING_BETA_SHOWN",
|
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({
|
const store = localforage.createInstance({
|
||||||
|
|
@ -384,3 +386,34 @@ export const setTemplateNotificationSeen = async (flag: boolean) => {
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user