PromucFlow_constructor/app/client/src/pages/setup/SetupForm.tsx
Dipyaman Biswas 94f5744800
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
2023-05-25 23:51:56 +05:30

237 lines
7.3 KiB
TypeScript

import React, { useRef } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import DetailsForm from "./DetailsForm";
import {
WELCOME_FORM_USECASE_FIELD_NAME,
WELCOME_FORM_EMAIL_FIELD_NAME,
WELCOME_FORM_NAME,
WELCOME_FORM_NAME_FIELD_NAME,
WELCOME_FORM_PASSWORD_FIELD_NAME,
WELCOME_FORM_ROLE_FIELD_NAME,
WELCOME_FORM_ROLE_NAME_FIELD_NAME,
WELCOME_FORM_VERIFY_PASSWORD_FIELD_NAME,
WELCOME_FORM_CUSTOM_USECASE_FIELD_NAME,
} from "@appsmith/constants/forms";
import type { FormErrors, InjectedFormProps } from "redux-form";
import { formValueSelector, getFormSyncErrors, reduxForm } from "redux-form";
import { isEmail, isStrongPassword } from "utils/formhelpers";
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";
const PageWrapper = styled.div`
width: 100%;
display: flex;
justify-content: start;
overflow: auto;
position: relative;
z-index: 100;
`;
const SetupFormContainer = styled.div``;
const SetupStep = styled.div<{ active: boolean }>`
display: ${(props) => (props.active ? "block" : "none")};
`;
const SpaceFiller = styled.div`
height: 100px;
`;
export type DetailsFormValues = {
firstName?: string;
lastName?: string;
email?: string;
password?: string;
verifyPassword?: string;
role?: string;
useCase?: string;
custom_useCase?: string;
role_name?: string;
};
const validate = (values: DetailsFormValues) => {
const errors: DetailsFormValues = {};
if (!values.firstName) {
errors.firstName = "This field is required.";
}
if (!values.email || !isEmail(values.email)) {
errors.email = "Enter a valid email address.";
}
if (!values.password || !isStrongPassword(values.password)) {
errors.password = "Please enter a strong password.";
}
if (!values.verifyPassword || values.password != values.verifyPassword) {
errors.verifyPassword = "Passwords don't match.";
}
if (!values.role) {
errors.role = "Please select a role";
}
if (values.role == "other" && !values.role_name) {
errors.role_name = "Please enter a role";
}
if (!values.useCase) {
errors.useCase = "Please select a use case";
}
if (values.useCase === "other" && !values.custom_useCase)
errors.custom_useCase = "Please enter a use case";
return errors;
};
export type SetupFormProps = DetailsFormValues & {
formSyncErrors?: FormErrors<string, string>;
} & InjectedFormProps<
DetailsFormValues,
{
formSyncErrors?: FormErrors<string, string>;
}
>;
function SetupForm(props: SetupFormProps) {
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";
roleInput.name = "role";
roleInput.style.display = "none";
if (props.role !== "other") {
roleInput.value = props.role as string;
} else {
roleInput.value = props.role_name as string;
}
form.appendChild(roleInput);
const useCaseInput = document.createElement("input");
useCaseInput.type = "text";
useCaseInput.name = "useCase";
useCaseInput.style.display = "none";
if (props.useCase !== "other") {
useCaseInput.value = props.useCase as string;
} else {
useCaseInput.value = props.custom_useCase as string;
}
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;
};
const onKeyDown = (event: React.KeyboardEvent<HTMLFormElement>) => {
if (event.key === "Enter") {
if (props.valid) {
if (showDetailsForm) {
// If we are on the details page we do not want to submit the form
// instead we move the user to the next page
event.preventDefault();
onNext();
}
} else {
// The fields to be marked as touched so that we can display the errors
const toTouch = [];
// We fetch the fields which are invalid
for (const key in props.formSyncErrors) {
props.formSyncErrors.hasOwnProperty(key) && toTouch.push(key);
}
props.touch(...toTouch);
// prevent submitting the form on enter if the values are invalid
event.preventDefault();
}
}
};
const onNext = () => {
setShowDetailsForm(false);
};
return (
<PageWrapper>
<SetupFormContainer>
<form
action={signupURL}
data-testid="super-user-form"
id="super-user-form"
method="POST"
onKeyDown={onKeyDown}
onSubmit={onSubmit}
ref={formRef}
>
<SetupStep active={showDetailsForm}>
<DetailsForm {...props} onNext={onNext} />
</SetupStep>
</form>
<SpaceFiller />
</SetupFormContainer>
</PageWrapper>
);
}
const selector = formValueSelector(WELCOME_FORM_NAME);
export default connect((state: AppState) => {
return {
name: selector(state, WELCOME_FORM_NAME_FIELD_NAME),
email: selector(state, WELCOME_FORM_EMAIL_FIELD_NAME),
password: selector(state, WELCOME_FORM_PASSWORD_FIELD_NAME),
verify_password: selector(state, WELCOME_FORM_VERIFY_PASSWORD_FIELD_NAME),
role: selector(state, WELCOME_FORM_ROLE_FIELD_NAME),
role_name: selector(state, WELCOME_FORM_ROLE_NAME_FIELD_NAME),
useCase: selector(state, WELCOME_FORM_USECASE_FIELD_NAME),
custom_useCase: selector(state, WELCOME_FORM_CUSTOM_USECASE_FIELD_NAME),
formSyncErrors: getFormSyncErrors(WELCOME_FORM_NAME)(state),
};
}, null)(
reduxForm<DetailsFormValues, { formSyncErrors?: FormErrors<string, string> }>(
{
validate,
form: WELCOME_FORM_NAME,
touchOnBlur: true,
},
)(SetupForm),
);