Skip to content

Commit

Permalink
🪟 🎉 Free connector program enrollment modal (#21396)
Browse files Browse the repository at this point in the history
  • Loading branch information
josephkmh authored Jan 17, 2023
1 parent 9b7535e commit b58a951
Show file tree
Hide file tree
Showing 13 changed files with 682 additions and 2 deletions.
2 changes: 1 addition & 1 deletion airbyte-webapp/src/components/ui/Button/types.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";

type ButtonSize = "xs" | "sm" | "lg";
type ButtonVariant = "primary" | "secondary" | "danger" | "light" | "clear" | "dark";
export type ButtonVariant = "primary" | "secondary" | "danger" | "light" | "clear" | "dark";

export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
clickable?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export interface Experiments {
"connection.autoDetectSchemaChanges": boolean;
"connection.columnSelection": boolean;
"connection.newTableDesign": boolean;
"workspace.freeConnectorsProgram.visible": boolean;
}
9 changes: 8 additions & 1 deletion airbyte-webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -743,5 +743,12 @@

"jobs.noAttemptsFailure": "Failed to start job.",

"cloudApi.loginCallbackUrlError": "There was an error connecting to the developer portal. Please try again."
"cloudApi.loginCallbackUrlError": "There was an error connecting to the developer portal. Please try again.",

"freeConnectorProgram.enrollmentModal.title": "Free connector program",
"freeConnectorProgram.enrollmentModal.free": "<p1>Alpha and Beta Connectors are free while you're in the program.</p1><p2>The whole Connection is free until both Connectors have move into General Availability (GA)</p2>",
"freeConnectorProgram.enrollmentModal.emailNotification": "We will let you know through email before a Connector you use moves to GA",
"freeConnectorProgram.enrollmentModal.cardOnFile": "When both Connectors are in GA, the Connection will no longer be free. You'll need to have a credit card on file to enroll so Airbyte can handle a Connection's transition to paid service.",
"freeConnectorProgram.enrollmentModal.cancelButtonText": "Cancel",
"freeConnectorProgram.enrollmentModal.enrollButtonText": "Enroll now!"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@use "scss/colors";
@use "scss/variables";

.header {
overflow: hidden;
position: relative;
background: colors.$blue-100;
height: 120px;
border-top-left-radius: variables.$border-radius-lg;
border-top-right-radius: variables.$border-radius-lg;
}

.headerBackgroundImageContainer {
position: absolute;
left: 50%;
transform: translateX(-50%);
}

.pill {
z-index: 2;
}

.contentWrapper {
width: variables.$width-modal-sm;
padding: 0 variables.$spacing-xl variables.$spacing-2xl;
}

.contentHeader {
margin: variables.$spacing-xl 0;
}

.iconContainer {
flex: 0 0 82px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React, { useEffect, useRef, useState } from "react";
import { FormattedMessage } from "react-intl";

import { Button } from "components/ui/Button";
import { FlexContainer, FlexItem } from "components/ui/Flex";
import { Heading } from "components/ui/Heading";
import { ModalFooter } from "components/ui/Modal/ModalFooter";
import { Text } from "components/ui/Text";

import { StripeCheckoutSessionCreate, StripeCheckoutSessionRead } from "packages/cloud/lib/domain/stripe";

import { ReactComponent as CardSVG } from "./cards.svg";
import { ReactComponent as ConnectorGridSvg } from "./connectorGrid.svg";
import styles from "./EnrollmentModal.module.scss";
import { ReactComponent as FreeSVG } from "./free.svg";
import { ReactComponent as MailSVG } from "./mail.svg";

const STRIPE_SUCCESS_QUERY = "stripeCheckoutSuccess";

interface EnrollmentModalContentProps {
closeModal: () => void;
createCheckout: (p: StripeCheckoutSessionCreate) => Promise<StripeCheckoutSessionRead>;
workspaceId: string;
}

export const EnrollmentModalContent: React.FC<EnrollmentModalContentProps> = ({
closeModal,
createCheckout,
workspaceId,
}) => {
const isMountedRef = useRef(false);
const [isLoading, setIsLoading] = useState(false);

const startStripeCheckout = async () => {
setIsLoading(true);
// Use the current URL as a success URL but attach the STRIPE_SUCCESS_QUERY to it
const successUrl = new URL(window.location.href);
successUrl.searchParams.set(STRIPE_SUCCESS_QUERY, "true");
const { stripeUrl } = await createCheckout({
workspaceId,
successUrl: successUrl.href,
cancelUrl: window.location.href,
stripeMode: "setup",
});

// Forward to stripe as soon as we created a checkout session successfully
if (isMountedRef.current) {
window.location.assign(stripeUrl);
}
};

// If the user closes the modal while the request is processing, we don't want to redirect them
useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);

return (
<>
<FlexContainer alignItems="center" justifyContent="center" className={styles.header}>
<div className={styles.headerBackgroundImageContainer}>
<ConnectorGridSvg />
</div>
<div className={styles.pill}>Pill #1</div>
<div className={styles.pill}>Pill #2</div>
</FlexContainer>
<div className={styles.contentWrapper}>
<Heading size="lg" as="h2" className={styles.contentHeader}>
<FormattedMessage id="freeConnectorProgram.enrollmentModal.title" />
</Heading>
<FlexContainer direction="column" gap="xl">
<FlexContainer>
<FlexContainer justifyContent="center" className={styles.iconContainer}>
<FreeSVG />
</FlexContainer>
<FlexContainer direction="column" gap="lg">
<FormattedMessage
id="freeConnectorProgram.enrollmentModal.free"
values={{
p1: (content: React.ReactNode) => <Text size="lg">{content}</Text>,
p2: (content: React.ReactNode) => <Text size="lg">{content}</Text>,
}}
/>
</FlexContainer>
</FlexContainer>
<FlexContainer>
<FlexContainer justifyContent="center" className={styles.iconContainer}>
<MailSVG />
</FlexContainer>
<Text size="lg">
<FormattedMessage id="freeConnectorProgram.enrollmentModal.emailNotification" />
</Text>
</FlexContainer>
<FlexContainer>
<FlexContainer justifyContent="center" className={styles.iconContainer}>
<CardSVG />
</FlexContainer>
<Text size="lg">
<FormattedMessage id="freeConnectorProgram.enrollmentModal.cardOnFile" />
</Text>
</FlexContainer>
</FlexContainer>
</div>

<ModalFooter>
<FlexContainer justifyContent="flex-end" gap="lg">
<FlexItem>
<Button variant="secondary" onClick={closeModal}>
<FormattedMessage id="freeConnectorProgram.enrollmentModal.cancelButtonText" />
</Button>
</FlexItem>
<FlexItem>
<Button isLoading={isLoading} onClick={startStripeCheckout}>
<FormattedMessage id="freeConnectorProgram.enrollmentModal.enrollButtonText" />
</Button>
</FlexItem>
</FlexContainer>
</ModalFooter>
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit b58a951

Please sign in to comment.