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,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']"
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 = () =>
|
||||
|
|
|
|||
|
|
@ -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 { 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 = (
|
||||
<CenteredContainer>
|
||||
|
|
@ -272,7 +326,10 @@ export default function OnboardingTasks() {
|
|||
return (
|
||||
<Wrapper data-testid="onboarding-tasks-wrapper">
|
||||
{content}
|
||||
{showModal && (
|
||||
{isAnonymousDataPopupOpen && (
|
||||
<AnonymousDataPopup onCloseCallout={hideAnonymousDataPopup} />
|
||||
)}
|
||||
{!isAdmin && showModal && (
|
||||
<IntroductionModal
|
||||
close={() => {
|
||||
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 { 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<HTMLDivElement>();
|
||||
|
||||
const isAirgappedInstance = isAirgapped();
|
||||
const [formState, setFormState] = useState(0);
|
||||
|
||||
const isFirstPage = useMemo(() => formState === 0, [formState]);
|
||||
|
||||
useEffect(() => {
|
||||
const setTelemetryVisibleFalse = async () => {
|
||||
await setFirstTimeUserOnboardingTelemetryCalloutVisibility(false);
|
||||
};
|
||||
setTelemetryVisibleFalse();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DetailsFormWrapper ref={ref}>
|
||||
<div className="relative flex-col items-start">
|
||||
<FormHeaderIndex className="absolute -left-6">1.</FormHeaderIndex>
|
||||
<FormHeaderLabel>{createMessage(WELCOME_FORM_HEADER)}</FormHeaderLabel>
|
||||
</div>
|
||||
<StyledTabIndicatorWrapper>
|
||||
<StyledTabIndicator />
|
||||
<StyledTabIndicator isFirstPage={isFirstPage} />
|
||||
</StyledTabIndicatorWrapper>
|
||||
<StyledFormBodyWrapper>
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-full-name"
|
||||
label={createMessage(WELCOME_FORM_FULL_NAME)}
|
||||
>
|
||||
<FormTextField
|
||||
autoFocus
|
||||
name="name"
|
||||
placeholder="John Doe"
|
||||
type="text"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-email"
|
||||
label={createMessage(WELCOME_FORM_EMAIL_ID)}
|
||||
>
|
||||
<FormTextField
|
||||
name="email"
|
||||
placeholder="How can we reach you?"
|
||||
type="email"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-password"
|
||||
label={createMessage(WELCOME_FORM_CREATE_PASSWORD)}
|
||||
>
|
||||
<FormTextField
|
||||
name="password"
|
||||
placeholder="Make it strong!"
|
||||
type="password"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-verify-password"
|
||||
label={createMessage(WELCOME_FORM_VERIFY_PASSWORD)}
|
||||
>
|
||||
<FormTextField
|
||||
data-testid="verifyPassword"
|
||||
name="verifyPassword"
|
||||
placeholder="Re-enter password"
|
||||
type="password"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
<DropdownWrapper
|
||||
className="t--welcome-form-role-dropdown"
|
||||
label={createMessage(WELCOME_FORM_ROLE_DROPDOWN)}
|
||||
>
|
||||
<Field
|
||||
asyncControl
|
||||
component={withDropdown(roleOptions)}
|
||||
name="role"
|
||||
placeholder={createMessage(WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER)}
|
||||
type="text"
|
||||
/>
|
||||
</DropdownWrapper>
|
||||
{props.role == "other" && (
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-role-input"
|
||||
label={createMessage(WELCOME_FORM_ROLE)}
|
||||
>
|
||||
<FormTextField name="role_name" placeholder="" type="text" />
|
||||
<div className={isFirstPage ? "block" : "hidden"}>
|
||||
<div className="flex flex-row justify-between w-100">
|
||||
<StyledFormGroup className="!w-52 t--welcome-form-first-name">
|
||||
<FormTextField
|
||||
autoFocus
|
||||
label={createMessage(WELCOME_FORM_FIRST_NAME)}
|
||||
name="firstName"
|
||||
placeholder="John"
|
||||
type="text"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
|
||||
<StyledFormGroup className="!w-52 t--welcome-form-last-name">
|
||||
<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"
|
||||
placeholder="How can we reach you?"
|
||||
type="email"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
)}
|
||||
<DropdownWrapper
|
||||
className="t--welcome-form-role-usecase"
|
||||
label={createMessage(WELCOME_FORM_USE_CASE)}
|
||||
>
|
||||
<Field
|
||||
asyncControl
|
||||
component={withDropdown(useCaseOptions)}
|
||||
name="useCase"
|
||||
placeholder={createMessage(WELCOME_FORM_USE_CASE_PLACEHOLDER)}
|
||||
type="text"
|
||||
/>
|
||||
</DropdownWrapper>
|
||||
{props.useCase == "other" && (
|
||||
<StyledFormGroup
|
||||
className="t--welcome-form-use-case-input"
|
||||
label={createMessage(WELCOME_FORM_CUSTOM_USE_CASE)}
|
||||
>
|
||||
<FormTextField name="custom_useCase" placeholder="" type="text" />
|
||||
<StyledFormGroup className="t--welcome-form-password">
|
||||
<FormTextField
|
||||
label={createMessage(WELCOME_FORM_CREATE_PASSWORD)}
|
||||
name="password"
|
||||
placeholder="Make it strong!"
|
||||
type="password"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
<StyledFormGroup className="t--welcome-form-verify-password">
|
||||
<FormTextField
|
||||
data-testid="verifyPassword"
|
||||
label={createMessage(WELCOME_FORM_VERIFY_PASSWORD)}
|
||||
name="verifyPassword"
|
||||
placeholder="Type correctly"
|
||||
type="password"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
</div>
|
||||
|
||||
{!isFirstPage && (
|
||||
<div>
|
||||
<DropdownWrapper
|
||||
className="t--welcome-form-role-dropdown"
|
||||
label={createMessage(WELCOME_FORM_ROLE_DROPDOWN)}
|
||||
>
|
||||
<Field
|
||||
asyncControl
|
||||
component={withDropdown(roleOptions)}
|
||||
name="role"
|
||||
placeholder={createMessage(
|
||||
WELCOME_FORM_ROLE_DROPDOWN_PLACEHOLDER,
|
||||
)}
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
</DropdownWrapper>
|
||||
{props.role == "other" && (
|
||||
<StyledFormGroup className="t--welcome-form-role-input">
|
||||
<FormTextField
|
||||
label={createMessage(WELCOME_FORM_ROLE)}
|
||||
name="role_name"
|
||||
placeholder=""
|
||||
type="text"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
)}
|
||||
<DropdownWrapper
|
||||
className="t--welcome-form-role-usecase"
|
||||
label={createMessage(WELCOME_FORM_USE_CASE)}
|
||||
>
|
||||
<Field
|
||||
asyncControl
|
||||
component={withDropdown(useCaseOptions)}
|
||||
name="useCase"
|
||||
placeholder={createMessage(WELCOME_FORM_USE_CASE_PLACEHOLDER)}
|
||||
type="text"
|
||||
/>
|
||||
</DropdownWrapper>
|
||||
{props.useCase == "other" && (
|
||||
<StyledFormGroup className="t--welcome-form-use-case-input">
|
||||
<FormTextField
|
||||
label={createMessage(WELCOME_FORM_CUSTOM_USE_CASE)}
|
||||
name="custom_useCase"
|
||||
placeholder=""
|
||||
type="text"
|
||||
/>
|
||||
</StyledFormGroup>
|
||||
)}
|
||||
|
||||
{!isAirgapped() && (
|
||||
<Checkbox defaultSelected name="signupForNewsletter" value="true">
|
||||
I want security and product updates.
|
||||
</Checkbox>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<ButtonWrapper>
|
||||
<Button
|
||||
className="t--welcome-form-next-button"
|
||||
className="t--welcome-form-submit-button w-100"
|
||||
isDisabled={props.invalid}
|
||||
kind="secondary"
|
||||
onClick={!isAirgappedInstance ? props.onNext : undefined}
|
||||
kind="primary"
|
||||
onClick={() => {
|
||||
if (isFirstPage) setFormState(1);
|
||||
}}
|
||||
size="md"
|
||||
type={!isAirgappedInstance ? "button" : "submit"}
|
||||
type={isFirstPage ? "button" : "submit"}
|
||||
>
|
||||
Next
|
||||
{isFirstPage
|
||||
? createMessage(CONTINUE)
|
||||
: createMessage(ONBOARDING_STATUS_GET_STARTED)}
|
||||
</Button>
|
||||
</ButtonWrapper>
|
||||
</StyledFormBodyWrapper>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<ActionContainer>
|
||||
<StyledButton
|
||||
className="t--welcome-form-get-started"
|
||||
onClick={() => props.onGetStarted && props.onGetStarted()}
|
||||
size="md"
|
||||
>
|
||||
{createMessage(WELCOME_ACTION)}
|
||||
</StyledButton>
|
||||
<SetupForm />
|
||||
</ActionContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -124,15 +119,9 @@ function NonSuperUser(
|
|||
<StyledButton
|
||||
className="t--get-started-button"
|
||||
isDisabled={props.invalid}
|
||||
onClick={() =>
|
||||
!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)}
|
||||
</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 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<HTMLFormElement>(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 (
|
||||
<PageWrapper>
|
||||
<SetupFormContainer>
|
||||
<LogoContainer>
|
||||
<AppsmithLogoImg alt="Appsmith logo" src={AppsmithLogo} />
|
||||
</LogoContainer>
|
||||
<form
|
||||
action={signupURL}
|
||||
data-testid="super-user-form"
|
||||
|
|
@ -204,17 +203,8 @@ function SetupForm(props: SetupFormProps) {
|
|||
ref={formRef}
|
||||
>
|
||||
<SetupStep active={showDetailsForm}>
|
||||
<DetailsForm
|
||||
{...props}
|
||||
onNext={!isAirgappedInstance ? onNext : () => noop}
|
||||
/>
|
||||
<DetailsForm {...props} onNext={onNext} />
|
||||
</SetupStep>
|
||||
{!isAirgappedInstance && (
|
||||
<SetupStep active={!showDetailsForm}>
|
||||
<DataCollectionForm />
|
||||
<NewsletterForm />
|
||||
</SetupStep>
|
||||
)}
|
||||
</form>
|
||||
<SpaceFiller />
|
||||
</SetupFormContainer>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<LandingPageWrapper
|
||||
data-testid={"welcome-page"}
|
||||
hide={!fontsInjected}
|
||||
id={WELCOME_PAGE_ANIMATION_CONTAINER}
|
||||
>
|
||||
<LandingPageContent>
|
||||
<StyledTextBanner>
|
||||
<Banner />
|
||||
{props.forSuperUser ? (
|
||||
<SuperUserForm onGetStarted={props.onGetStarted} />
|
||||
<SuperUserForm />
|
||||
) : (
|
||||
<NonSuperUserForm onGetStarted={props.onGetStarted} />
|
||||
)}
|
||||
</StyledTextBanner>
|
||||
<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>
|
||||
</LandingPageContent>
|
||||
</LandingPageWrapper>
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user