Browse Source

feat(growth) : Adds Sign Up Modal to Sandbox (#27839)

This PR adds a modal that asks Sandbox users to Sign up (or keep exploring) after 2 minutes. Previously, there was no sign up modal for the Sandbox.

GRW-162
Richard Roggenkemper 3 years ago
parent
commit
ebd6723f4c

+ 7 - 0
static/app/actionCreators/modal.tsx

@@ -244,3 +244,10 @@ export async function openReprocessEventModal({
 
   openModal(deps => <Modal {...deps} {...options} />, {onClose});
 }
+
+export async function demoSignupModal(options: ModalOptions = {}) {
+  const mod = await import('app/components/modals/demoSignUp');
+  const {default: Modal, modalCss} = mod;
+
+  openModal(deps => <Modal {...deps} {...options} />, {modalCss});
+}

+ 9 - 11
static/app/components/demo/demoHeader.tsx

@@ -9,6 +9,7 @@ import {t} from 'app/locale';
 import PreferencesStore from 'app/stores/preferencesStore';
 import space from 'app/styles/space';
 import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
+import {emailQueryParameter, extraQueryParameter} from 'app/utils/demoMode';
 import getCookie from 'app/utils/getCookie';
 
 type Preferences = typeof PreferencesStore.prefs;
@@ -16,18 +17,15 @@ type Preferences = typeof PreferencesStore.prefs;
 export default function DemoHeader() {
   // if the user came from a SaaS org, we should send them back to upgrade when they leave the sandbox
   const saasOrgSlug = getCookie('saas_org_slug');
-  const extraQueryString = getCookie('extra_query_string');
-  // cookies that have = sign are quotes so extra quotes need to be removed
-  const extraQuery = extraQueryString ? extraQueryString.replaceAll('"', '') : '';
-  const email = localStorage.getItem('email');
-  const queryParameter = email ? `?email=${email}` : '';
-  const emailSeparator = email ? '&' : '?';
-  const getStartedSeparator = extraQueryString ? emailSeparator : '';
-  const extraQueryParameter = extraQueryString ? `?${extraQuery}` : '';
+
+  const queryParameter = emailQueryParameter();
+  const getStartedExtraParameter = extraQueryParameter(true);
+  const extraParameter = extraQueryParameter(false);
+
   const getStartedText = saasOrgSlug ? t('Upgrade Now') : t('Sign Up for Free');
   const getStartedUrl = saasOrgSlug
     ? `https://sentry.io/settings/${saasOrgSlug}/billing/checkout/`
-    : `https://sentry.io/signup/${queryParameter}${getStartedSeparator}${extraQuery}`;
+    : `https://sentry.io/signup/${queryParameter}${getStartedExtraParameter}`;
 
   const [collapsed, setCollapsed] = useState(PreferencesStore.prefs.collapsed);
 
@@ -55,7 +53,7 @@ export default function DemoHeader() {
       <ButtonBar gap={4}>
         <StyledExternalLink
           onClick={() => trackAdvancedAnalyticsEvent('growth.demo_click_docs', {}, null)}
-          href={`https://docs.sentry.io/${extraQueryParameter}`}
+          href={`https://docs.sentry.io/${extraParameter}`}
         >
           {t('Documentation')}
         </StyledExternalLink>
@@ -64,7 +62,7 @@ export default function DemoHeader() {
           onClick={() =>
             trackAdvancedAnalyticsEvent('growth.demo_click_request_demo', {}, null)
           }
-          href={`https://sentry.io/_/demo/${extraQueryParameter}`}
+          href={`https://sentry.io/_/demo/${extraParameter}`}
         >
           {t('Request a Demo')}
         </BaseButton>

+ 46 - 0
static/app/components/highlightModalContainer.tsx

@@ -0,0 +1,46 @@
+import {Fragment} from 'react';
+import styled from '@emotion/styled';
+
+import BottomLeft from 'sentry-images/pattern/highlight-bottom-left.svg';
+import TopRight from 'sentry-images/pattern/highlight-top-right.svg';
+
+type Props = {
+  topWidth: string;
+  bottomWidth: string;
+  children: React.ReactNode;
+};
+
+export default function HighlightModalContainer({
+  topWidth,
+  bottomWidth,
+  children,
+}: Props) {
+  return (
+    <Fragment>
+      <PositionTopRight src={TopRight} width={topWidth} />
+      {children}
+      <PositionBottomLeft src={BottomLeft} width={bottomWidth} />
+    </Fragment>
+  );
+}
+
+const PositionTopRight = styled('img')<{width: string}>`
+  position: absolute;
+  width: ${p => p.width};
+  right: 0;
+  top: 0;
+  pointer-events: none;
+`;
+
+const PositionBottomLeft = styled('img')<{width: string}>`
+  position: absolute;
+  width: ${p => p.width};
+  bottom: 0;
+  left: 0;
+  pointer-events: none;
+`;
+
+HighlightModalContainer.defaultProps = {
+  topWidth: '400px',
+  bottomWidth: '200px',
+};

+ 91 - 0
static/app/components/modals/demoSignUp.tsx

@@ -0,0 +1,91 @@
+import {css} from '@emotion/react';
+import styled from '@emotion/styled';
+
+import {ModalRenderProps} from 'app/actionCreators/modal';
+import Button from 'app/components/button';
+import ButtonBar from 'app/components/buttonBar';
+import HighlightModalContainer from 'app/components/highlightModalContainer';
+import {t} from 'app/locale';
+import space from 'app/styles/space';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
+import {emailQueryParameter, extraQueryParameter} from 'app/utils/demoMode';
+
+type Props = ModalRenderProps;
+
+const DemoSignUpModal = ({closeModal}: Props) => {
+  const queryParameter = emailQueryParameter();
+  const getStartedExtraParameter = extraQueryParameter(true);
+  const signupUrl = `https://sentry.io/signup/${queryParameter}${getStartedExtraParameter}`;
+
+  return (
+    <HighlightModalContainer>
+      <div>
+        <TrialCheckInfo>
+          <Subheader>{t('Sandbox Signup')}</Subheader>
+          <h2>{t('Hey, love what you see?')}</h2>
+          <p>
+            {t(
+              'Sign up now to setup your own project to see problems within your code and learn how to quickly improve your project.'
+            )}
+          </p>
+        </TrialCheckInfo>
+        <StyledButtonBar gap={2}>
+          <Button
+            priority="primary"
+            href={signupUrl}
+            onClick={() =>
+              trackAdvancedAnalyticsEvent('growth.demo_modal_clicked_signup', {}, null)
+            }
+          >
+            {t('Sign up now')}
+          </Button>
+          <Button
+            priority="default"
+            onClick={() => {
+              trackAdvancedAnalyticsEvent('growth.demo_modal_clicked_continue', {}, null);
+              closeModal();
+            }}
+          >
+            {t('Keep Exploring')}
+          </Button>
+        </StyledButtonBar>
+      </div>
+    </HighlightModalContainer>
+  );
+};
+
+const TrialCheckInfo = styled('div')`
+  padding: ${space(3)} 0;
+  p {
+    font-size: ${p => p.theme.fontSizeMedium};
+    margin: 0;
+  }
+  h2 {
+    font-size: 1.5em;
+  }
+`;
+
+export const modalCss = css`
+  width: 100%;
+  max-width: 730px;
+  [role='document'] {
+    position: relative;
+    padding: 70px 80px;
+    overflow: hidden;
+  }
+`;
+
+const Subheader = styled('h4')`
+  margin-bottom: ${space(2)};
+  text-transform: uppercase;
+  font-weight: bold;
+  color: ${p => p.theme.purple300};
+  font-size: ${p => p.theme.fontSizeExtraSmall};
+`;
+
+const StyledButtonBar = styled(ButtonBar)`
+  margin-top: ${space(2)};
+  max-width: fit-content;
+`;
+
+export default DemoSignUpModal;

+ 24 - 0
static/app/utils/demoMode.tsx

@@ -0,0 +1,24 @@
+import getCookie from 'app/utils/getCookie';
+
+// return email query parameter
+export function emailQueryParameter(): string {
+  const email = localStorage.getItem('email');
+  const queryParameter = email ? `?email=${email}` : '';
+  return queryParameter;
+}
+
+// return extra query depending, depending on if used in getStartedUrl
+export function extraQueryParameter(getStarted: boolean): string {
+  const email = localStorage.getItem('email');
+  const extraQueryString = getCookie('extra_query_string');
+  // cookies that have = sign are quotes so extra quotes need to be removed
+  const extraQuery = extraQueryString ? extraQueryString.replaceAll('"', '') : '';
+
+  if (getStarted) {
+    const emailSeparator = email ? '&' : '?';
+    const getStartedSeparator = extraQueryString ? emailSeparator : '';
+    return getStartedSeparator + extraQuery;
+  }
+  const extraSeparator = extraQueryString ? `?` : '';
+  return extraSeparator + extraQuery;
+}

+ 4 - 0
static/app/utils/growthAnalyticsEvents.tsx

@@ -61,6 +61,8 @@ export type GrowthEventParameters = {
   'growth.onboarding_view_sample_event': SampleEventParam;
   'invite_request.approved': InviteRequestParam;
   'invite_request.denied': InviteRequestParam;
+  'growth.demo_modal_clicked_signup': {};
+  'growth.demo_modal_clicked_continue': {};
 };
 
 type GrowthAnalyticsKey = keyof GrowthEventParameters;
@@ -91,4 +93,6 @@ export const growthEventMap: Record<GrowthAnalyticsKey, string> = {
   'growth.onboarding_view_sample_event': 'Growth: Onboarding View Sample Event',
   'invite_request.approved': 'Invite Request Approved',
   'invite_request.denied': 'Invite Request Denied',
+  'growth.demo_modal_clicked_signup': 'Growth: Demo Modal Clicked Signup',
+  'growth.demo_modal_clicked_continue': 'Growth: Demo Modal Clicked Continue',
 };