Skip to content

Commit

Permalink
🪟 🧪 [Experiment] Simplify signup left side (#22402)
Browse files Browse the repository at this point in the history
* 🪟 🧪 [Experiment] Simplify signup left side

We want to experiment if simplifying the left side on the sign up page
and dividing the signup methods, increase conversion of corporate emails
on the page. How?
- Making Oauth the default method
- Email/password method is still available but user will see an error
  for personal emails. They can still sign up though.

Airtable details:
https://airtable.com/appIuY0uKPVnk8TWT/tbl2SxXnUwf6fVCWS/viw9frYvld7ks7aNo/recnFzE4HBB8RP1uY?blocks=hide
Demo: https://www.loom.com/share/9b706682d89845b1bf2455a1f3e1520d
  • Loading branch information
letiescanciano authored Feb 9, 2023
1 parent f5e0f80 commit 17c77fc
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface Experiments {
"authPage.signup.hideName": boolean;
"authPage.signup.hideCompanyName": boolean;
"onboarding.speedyConnection": boolean;
"authPage.signup.simplifyLeftSide": boolean;
"connection.onboarding.sources": string;
"connection.onboarding.destinations": string;
"connection.autoDetectSchemaChanges": boolean;
Expand Down
1 change: 1 addition & 0 deletions airbyte-webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"form.yourEmail": "Your email",
"form.email.placeholder": "you@company.com",
"form.email.error": "Enter a valid email",
"form.workEmail.error": "Enter a valid work email",
"form.empty.error": "Required",
"form.selectConnector": "Type to search for a connector",
"form.searchName": "search by name...",
Expand Down
8 changes: 7 additions & 1 deletion airbyte-webapp/src/packages/cloud/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"login.resendEmail": "Didn’t receive the email? Send it again",
"login.yourEmail": "Your work email*",
"login.inviteEmail": "For security, re-enter your invite email*",
"login.yourEmail.placeholder": "work.email@example.com",
"login.yourEmail.placeholder": "name@company.com",
"login.yourEmail.notFound": "User not found",
"login.unknownError": "An unknown error has occurred",
"login.password": "Enter your password*",
Expand Down Expand Up @@ -160,6 +160,12 @@
"firebase.auth.error.default": "Confirmation email cannot be sent. Please try again later.",

"signup.password.minLength": "Password should be at least 12 characters",
"signup.details.noCreditCard": "No credit card required",
"signup.details.instantSetup": "Instant setup",
"signup.details.freeTrial": "14-day free trial",
"signup.title": "Create your Airbyte account",
"signup.method.email": "Sign up using email",
"signup.method.oauth": "Sign up using Google or GitHub",
"email.duplicate": "Email already exists",
"email.notfound": "Email not found",
"email.disabled": "Your account is disabled",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import { FlexContainer } from "components/ui/Flex";
import { Heading } from "components/ui/Heading";

import { PageTrackingCodes, useTrackPage } from "hooks/services/Analytics";
import { useExperiment } from "hooks/services/Experiment";

import { Separator } from "./components/Separator";
import { Disclaimer, SignupForm } from "./components/SignupForm";
import { SimpleLeftSide } from "./components/SimpleLeftSide/SimpleLeftSide";
import SpecialBlock from "./components/SpecialBlock";
import styles from "./SignupPage.module.scss";
import { OAuthLogin } from "../OAuthLogin";
Expand All @@ -19,7 +21,11 @@ interface SignupPageProps {

const SignupPage: React.FC<SignupPageProps> = ({ highlightStyle }) => {
useTrackPage(PageTrackingCodes.SIGNUP);
const isSimpleLeftSide = useExperiment("authPage.signup.simplifyLeftSide", false);

if (isSimpleLeftSide) {
return <SimpleLeftSide />;
}
return (
<FlexContainer direction="column" gap="xl">
<HeadTitle titles={[{ id: "login.signup" }]} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use "../../../../../../scss/colors";
@use "../../../../../../scss/variables";
@use "scss/colors";
@use "scss/variables";

.statusMessage {
margin-top: variables.$spacing-md;
Expand All @@ -9,3 +9,19 @@
.disclaimer {
margin-top: variables.$spacing-xl;
}

.passwordCheckContainer {
margin-top: variables.$spacing-lg;
}

.checkIcon {
color: colors.$dark-blue-100;
}

.error {
color: colors.$red;
}

.valid {
color: colors.$green;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { faCheckCircle, faXmarkCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import classNames from "classnames";
import { Field, FieldProps, Formik, Form } from "formik";
import React, { useMemo } from "react";
import { FormattedMessage, useIntl } from "react-intl";
Expand All @@ -7,10 +10,13 @@ import * as yup from "yup";

import { LabeledInput, Link } from "components";
import { Button } from "components/ui/Button";
import { FlexContainer } from "components/ui/Flex";
import { Text } from "components/ui/Text";

import { useExperiment } from "hooks/services/Experiment";
import { FieldError } from "packages/cloud/lib/errors/FieldError";
import { useAuthService } from "packages/cloud/services/auth/AuthService";
import { FREE_EMAIL_SERVICE_PROVIDERS } from "packages/cloud/services/auth/freeEmailProviders";
import { isGdprCountry } from "utils/dataPrivacy";
import { links } from "utils/links";

Expand Down Expand Up @@ -75,6 +81,18 @@ export const CompanyNameField: React.FC = () => {
export const EmailField: React.FC<{ label?: React.ReactNode }> = ({ label }) => {
const { formatMessage } = useIntl();

const isCorporateEmail = (email?: string) =>
!FREE_EMAIL_SERVICE_PROVIDERS.some((provider) => email?.endsWith(`@${provider}`));

const getMessage = ({ touched, error, value }: { touched: boolean; error?: string; value?: string }) => {
if (touched && error) {
return formatMessage({ id: error });
}
if (touched && !isCorporateEmail(value)) {
return formatMessage({ id: "form.workEmail.error" });
}
return null;
};
return (
<Field name="email">
{({ field, meta }: FieldProps<string>) => (
Expand All @@ -85,8 +103,8 @@ export const EmailField: React.FC<{ label?: React.ReactNode }> = ({ label }) =>
id: "login.yourEmail.placeholder",
})}
type="text"
error={!!meta.error && meta.touched}
message={meta.touched && meta.error && formatMessage({ id: meta.error })}
error={(!!meta.error && meta.touched) || (meta.touched && !isCorporateEmail(field.value))}
message={getMessage({ touched: meta.touched, error: meta.error, value: field.value })}
/>
)}
</Field>
Expand All @@ -99,16 +117,36 @@ export const PasswordField: React.FC<{ label?: React.ReactNode }> = ({ label })
return (
<Field name="password">
{({ field, meta }: FieldProps<string>) => (
<LabeledInput
{...field}
label={label || <FormattedMessage id="login.password" />}
placeholder={formatMessage({
id: "login.password.placeholder",
})}
type="password"
error={!!meta.error && meta.touched}
message={meta.touched && meta.error && formatMessage({ id: meta.error })}
/>
<>
<LabeledInput
{...field}
label={label || <FormattedMessage id="login.password" />}
placeholder={formatMessage({
id: "login.password.placeholder",
})}
type="password"
error={!!meta.error && meta.touched}
/>

<FlexContainer gap="sm" alignItems="center" className={styles.passwordCheckContainer}>
<FontAwesomeIcon
icon={Boolean(meta.error) && meta.touched ? faXmarkCircle : faCheckCircle}
className={classNames(styles.checkIcon, {
[styles.error]: Boolean(meta.error) && meta.touched,
[styles.valid]: meta.touched && !meta.error,
})}
/>

<Text
size="sm"
className={classNames({
[styles.error]: Boolean(meta.error) && meta.touched,
})}
>
<FormattedMessage id="signup.password.minLength" />
</Text>
</FlexContainer>
</>
)}
</Field>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@use "scss/colors";
@use "scss/variables";

.checkIcon {
color: colors.$dark-blue-100;
}

.detailTextContainer {
min-width: fit-content;
color: colors.$dark-blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faCheckCircle, faEnvelope } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useState } from "react";
import { FormattedMessage } from "react-intl";

import { HeadTitle } from "components/common/HeadTitle";
import { Button } from "components/ui/Button";
import { FlexContainer } from "components/ui/Flex";
import { Heading } from "components/ui/Heading";

import styles from "./SimpleLeftSide.module.scss";
import { OAuthLogin } from "../../../OAuthLogin";
import { Disclaimer, SignupForm } from "../SignupForm";

const Detail: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
return (
<FlexContainer gap="sm" alignItems="center" className={styles.detailTextContainer}>
<FontAwesomeIcon icon={faCheckCircle} className={styles.checkIcon} />
{children}
</FlexContainer>
);
};
export const SimpleLeftSide: React.FC = () => {
const [showOauth, setShowOauth] = useState(true);
return (
<FlexContainer direction="column" gap="xl">
<HeadTitle titles={[{ id: "login.signup" }]} />
<Heading as="h1" centered>
<FormattedMessage id="signup.title" />
</Heading>

<FlexContainer justifyContent="center" alignItems="center">
<Detail>
<FormattedMessage id="signup.details.noCreditCard" />
</Detail>
<Detail>
<FormattedMessage id="signup.details.instantSetup" />
</Detail>
<Detail>
<FormattedMessage id="signup.details.freeTrial" />
</Detail>
</FlexContainer>
{showOauth ? <OAuthLogin /> : <SignupForm />}

{showOauth ? (
<Button
onClick={() => setShowOauth(false)}
variant="clear"
size="sm"
icon={<FontAwesomeIcon icon={faEnvelope} />}
>
<FormattedMessage id="signup.method.email" />
</Button>
) : (
<Button onClick={() => setShowOauth(true)} variant="clear" size="sm" icon={<FontAwesomeIcon icon={faGoogle} />}>
<FormattedMessage id="signup.method.oauth" />
</Button>
)}

<Disclaimer />
</FlexContainer>
);
};

0 comments on commit 17c77fc

Please sign in to comment.