Browse Source

feat(growth): Adds more analytics to Onboarding (#27169)

This PR adds analytic tracking to the onboarding process. Previously a lot of the steps of the onboarding process were not being tracked (such as entering the onboarding page, which platform you selected, etc.), but now they will be.

GRW-174
roggenkemper 3 years ago
parent
commit
57b40a8810

+ 26 - 8
static/app/components/platformPicker.tsx

@@ -14,8 +14,8 @@ import {IconClose, IconProject, IconSearch} from 'app/icons';
 import {t, tct} from 'app/locale';
 import {inputStyles} from 'app/styles/input';
 import space from 'app/styles/space';
-import {PlatformIntegration} from 'app/types';
-import {analytics} from 'app/utils/analytics';
+import {Organization, PlatformIntegration} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import EmptyMessage from 'app/views/settings/components/emptyMessage';
 
 const PLATFORM_CATEGORIES = [...categoryList, {id: 'all', name: t('All')}] as const;
@@ -37,6 +37,8 @@ type Props = {
   listProps?: React.ComponentProps<typeof PlatformList>;
   noAutoFilter?: boolean;
   defaultCategory?: Category;
+  organization?: Organization;
+  source?: string;
 };
 
 type State = {
@@ -78,10 +80,15 @@ class PlatformPicker extends React.Component<Props, State> {
 
   logSearch = debounce(() => {
     if (this.state.filter) {
-      analytics('platformpicker.search', {
-        query: this.state.filter.toLowerCase(),
-        num_results: this.platformList.length,
-      });
+      trackAdvancedAnalyticsEvent(
+        'growth.platformpicker_search',
+        {
+          search: this.state.filter.toLowerCase(),
+          num_results: this.platformList.length,
+          source: this.props.source,
+        },
+        this.props.organization ?? null
+      );
     }
   }, 300);
 
@@ -108,7 +115,11 @@ class PlatformPicker extends React.Component<Props, State> {
               <ListLink
                 key={id}
                 onClick={(e: React.MouseEvent) => {
-                  analytics('platformpicker.select_tab', {category: id});
+                  trackAdvancedAnalyticsEvent(
+                    'growth.platformpicker_category',
+                    {category: id, source: this.props.source},
+                    this.props.organization ?? null
+                  );
                   this.setState({category: id, filter: ''});
                   e.preventDefault();
                 }}
@@ -142,7 +153,14 @@ class PlatformPicker extends React.Component<Props, State> {
                 e.stopPropagation();
               }}
               onClick={() => {
-                analytics('platformpicker.select_platform', {platform: platform.id});
+                trackAdvancedAnalyticsEvent(
+                  'growth.select_platform',
+                  {
+                    platform_id: platform.id,
+                    source: this.props.source,
+                  },
+                  this.props.organization ?? null
+                );
                 setPlatform(platform.id as PlatformKey);
               }}
             />

+ 0 - 1
static/app/utils/advancedAnalytics.tsx

@@ -86,7 +86,6 @@ export function trackAdvancedAnalyticsEvent<T extends AnalyticsKey>(
       custom_referrer,
       ...analyticsParams,
     };
-
     if (mapValuesFn) {
       params = mapValuesFn(params) as any;
     }

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

@@ -1,3 +1,5 @@
+import {PlatformKey} from 'app/data/platformCategories';
+
 type MobilePromptBannerParams = {
   matchedUserAgentString: string;
 };
@@ -8,6 +10,30 @@ type ShowParams = MobilePromptBannerParams & {
   mobileEventClientOsName: string;
 };
 
+type PlatformParam = {
+  platform: PlatformKey;
+};
+
+type PlatformCategory = {
+  category: string;
+  source?: string;
+};
+
+type PlatformPickerParam = {
+  platform_id: string;
+  source?: string;
+};
+
+type PlatformSearchParam = {
+  search: string;
+  num_results: number;
+  source?: string;
+};
+
+type SampleEventParam = {
+  platform?: PlatformKey;
+};
+
 // define the event key to payload mappings
 export type GrowthEventParameters = {
   'growth.show_mobile_prompt_banner': ShowParams;
@@ -19,6 +45,15 @@ export type GrowthEventParameters = {
   'growth.demo_click_get_started': {};
   'growth.demo_click_docs': {};
   'growth.demo_click_request_demo': {};
+  'growth.onboarding_load_choose_platform': {};
+  'growth.onboarding_set_up_your_project': PlatformParam;
+  'growth.select_platform': PlatformPickerParam;
+  'growth.platformpicker_category': PlatformCategory;
+  'growth.platformpicker_search': PlatformSearchParam;
+  'growth.onboarding_start_onboarding': {};
+  'growth.onboarding_take_to_error': {};
+  'growth.onboarding_view_full_docs': {};
+  'growth.onboarding_view_sample_event': SampleEventParam;
 };
 
 type GrowthAnalyticsKey = keyof GrowthEventParameters;
@@ -37,4 +72,14 @@ export const growthEventMap: Record<GrowthAnalyticsKey, string> = {
   'growth.demo_click_get_started': 'Growth: Demo Click Get Started',
   'growth.demo_click_docs': 'Growth: Demo Click Docs',
   'growth.demo_click_request_demo': 'Growth: Demo Click Request Demo',
+  'growth.onboarding_load_choose_platform':
+    'Growth: Onboarding Load Choose Platform Page',
+  'growth.onboarding_set_up_your_project': 'Growth: Onboarding Click Set Up Your Project',
+  'growth.select_platform': 'Growth: Onboarding Choose Platform',
+  'growth.platformpicker_category': 'Growth: Onboarding Platform Category',
+  'growth.platformpicker_search': 'Growth: Onboarding Platform Search',
+  'growth.onboarding_start_onboarding': 'Growth: Onboarding Start Onboarding',
+  'growth.onboarding_take_to_error': 'Growth: Onboarding Take to Error',
+  'growth.onboarding_view_full_docs': 'Growth: Onboarding View Full Docs',
+  'growth.onboarding_view_sample_event': 'Growth: Onboarding View Sample Event',
 };

+ 8 - 0
static/app/views/onboarding/components/firstEventIndicator.tsx

@@ -8,6 +8,7 @@ import {t} from 'app/locale';
 import pulsingIndicatorStyles from 'app/styles/pulsingIndicator';
 import space from 'app/styles/space';
 import {Group} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import EventWaiter from 'app/utils/eventWaiter';
 import testableTransition from 'app/utils/testableTransition';
 
@@ -34,6 +35,13 @@ const FirstEventIndicator = ({children, ...props}: Props) => (
             tooltipProps={{disabled: !!firstIssue}}
             disabled={!firstIssue}
             priority="primary"
+            onClick={() =>
+              trackAdvancedAnalyticsEvent(
+                'growth.onboarding_take_to_error',
+                {},
+                props.organization
+              )
+            }
             to={`/organizations/${props.organization.slug}/issues/${
               firstIssue !== true && firstIssue !== null ? `${firstIssue.id}/` : ''
             }`}

+ 7 - 0
static/app/views/onboarding/createSampleEventButton.tsx

@@ -11,6 +11,7 @@ import {Client} from 'app/api';
 import Button from 'app/components/button';
 import {t} from 'app/locale';
 import {Organization, Project} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import {trackAdhocEvent, trackAnalyticsEvent} from 'app/utils/analytics';
 import withApi from 'app/utils/withApi';
 import withOrganization from 'app/utils/withOrganization';
@@ -102,6 +103,12 @@ class CreateSampleEventButton extends React.Component<Props, State> {
       return;
     }
 
+    trackAdvancedAnalyticsEvent(
+      'growth.onboarding_view_sample_event',
+      {platform: project.platform},
+      organization
+    );
+
     addLoadingMessage(t('Processing sample event...'), {
       duration: EVENT_POLL_RETRIES * EVENT_POLL_INTERVAL,
     });

+ 4 - 17
static/app/views/onboarding/documentationSetup.tsx

@@ -15,8 +15,8 @@ import platforms from 'app/data/platforms';
 import {IconInfo} from 'app/icons';
 import {t, tct} from 'app/locale';
 import space from 'app/styles/space';
-import {Organization, Project} from 'app/types';
-import {analytics} from 'app/utils/analytics';
+import {Organization} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import getDynamicText from 'app/utils/getDynamicText';
 import {Theme} from 'app/utils/theme';
 import withApi from 'app/utils/withApi';
@@ -32,19 +32,6 @@ import {StepProps} from './types';
  */
 const INCOMPLETE_DOC_FLAG = 'TODO-ADD-VERIFICATION-EXAMPLE';
 
-type AnalyticsOpts = {
-  organization: Organization;
-  project: Project | null;
-  platform: PlatformKey | null;
-};
-
-const recordAnalyticsDocsClicked = ({organization, project, platform}: AnalyticsOpts) =>
-  analytics('onboarding_v2.full_docs_clicked', {
-    org_id: organization.id,
-    project: project?.slug,
-    platform,
-  });
-
 type Props = StepProps & {
   api: Client;
   organization: Organization;
@@ -93,8 +80,8 @@ class DocumentationSetup extends React.Component<Props, State> {
   };
 
   handleFullDocsClick = () => {
-    const {organization, project, platform} = this.props;
-    recordAnalyticsDocsClicked({organization, project, platform});
+    const {organization} = this.props;
+    trackAdvancedAnalyticsEvent('growth.onboarding_view_full_docs', {}, organization);
   };
 
   /**

+ 4 - 17
static/app/views/onboarding/integrationSetup.tsx

@@ -14,8 +14,8 @@ import {PlatformKey} from 'app/data/platformCategories';
 import platforms from 'app/data/platforms';
 import {t, tct} from 'app/locale';
 import space from 'app/styles/space';
-import {IntegrationProvider, Organization, Project} from 'app/types';
-import {analytics} from 'app/utils/analytics';
+import {IntegrationProvider, Organization} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import getDynamicText from 'app/utils/getDynamicText';
 import {trackIntegrationEvent} from 'app/utils/integrationUtil';
 import withApi from 'app/utils/withApi';
@@ -28,19 +28,6 @@ import PostInstallCodeSnippet from './components/integrations/postInstallCodeSni
 import SetupIntroduction from './components/setupIntroduction';
 import {StepProps} from './types';
 
-type AnalyticsOpts = {
-  organization: Organization;
-  project: Project | null;
-  platform: PlatformKey | null;
-};
-
-const recordAnalyticsDocsClicked = ({organization, project, platform}: AnalyticsOpts) =>
-  analytics('onboarding_v2.full_docs_clicked', {
-    org_id: organization.id,
-    project: project?.slug,
-    platform,
-  });
-
 type Props = StepProps & {
   api: Client;
   organization: Organization;
@@ -107,8 +94,8 @@ class IntegrationSetup extends Component<Props, State> {
   };
 
   handleFullDocsClick = () => {
-    const {organization, project, platform} = this.props;
-    recordAnalyticsDocsClicked({organization, project, platform});
+    const {organization} = this.props;
+    trackAdvancedAnalyticsEvent('growth.onboarding_view_full_docs', {}, organization);
   };
 
   trackSwitchToManual = () => {

+ 1 - 20
static/app/views/onboarding/onboarding.tsx

@@ -11,7 +11,6 @@ import {IconChevron} from 'app/icons';
 import {t} from 'app/locale';
 import space from 'app/styles/space';
 import {Organization, Project} from 'app/types';
-import {analytics} from 'app/utils/analytics';
 import testableTransition from 'app/utils/testableTransition';
 import withOrganization from 'app/utils/withOrganization';
 import withProjects from 'app/utils/withProjects';
@@ -22,19 +21,6 @@ import SdkConfiguration from './sdkConfiguration';
 import {StepData, StepDescriptor} from './types';
 import OnboardingWelcome from './welcome';
 
-type AnalyticsOpts = {
-  organization: Organization;
-  project: Project | null;
-  step: StepDescriptor;
-};
-
-const recordAnalyticStepComplete = ({organization, project, step}: AnalyticsOpts) =>
-  analytics('onboarding_v2.step_compete', {
-    org_id: parseInt(organization.id, 10),
-    project: project ? project.slug : null,
-    step: step.id,
-  });
-
 const ONBOARDING_STEPS: StepDescriptor[] = [
   {
     id: 'welcome',
@@ -127,12 +113,6 @@ class Onboarding extends React.Component<Props, State> {
     const {orgId} = this.props.params;
     const nextStep = this.props.steps[this.activeStepIndex + 1];
 
-    recordAnalyticStepComplete({
-      organization: this.props.organization,
-      project: this.firstProject,
-      step: nextStep,
-    });
-
     browserHistory.push(`/onboarding/${orgId}/${nextStep.id}/`);
   }
 
@@ -169,6 +149,7 @@ class Onboarding extends React.Component<Props, State> {
           platform={this.projectPlatform}
           onComplete={data => this.handleNextStep(step, data)}
           onUpdate={this.handleUpdate}
+          organization={this.props.organization}
         />
       </OnboardingStep>
     );

+ 4 - 18
static/app/views/onboarding/otherSetup.tsx

@@ -8,10 +8,9 @@ import {Client} from 'app/api';
 import Alert from 'app/components/alert';
 import AsyncComponent from 'app/components/asyncComponent';
 import ExternalLink from 'app/components/links/externalLink';
-import {PlatformKey} from 'app/data/platformCategories';
 import {t, tct} from 'app/locale';
-import {Organization, Project} from 'app/types';
-import {analytics} from 'app/utils/analytics';
+import {Organization} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import getDynamicText from 'app/utils/getDynamicText';
 import withApi from 'app/utils/withApi';
 import withOrganization from 'app/utils/withOrganization';
@@ -21,19 +20,6 @@ import FirstEventFooter from './components/firstEventFooter';
 import FullIntroduction from './components/fullIntroduction';
 import {StepProps} from './types';
 
-type AnalyticsOpts = {
-  organization: Organization;
-  project: Project | null;
-  platform: PlatformKey | null;
-};
-
-const recordAnalyticsDocsClicked = ({organization, project, platform}: AnalyticsOpts) =>
-  analytics('onboarding_v2.full_docs_clicked', {
-    org_id: organization.id,
-    project: project?.slug,
-    platform,
-  });
-
 type Props = StepProps & {
   api: Client;
   organization: Organization;
@@ -57,8 +43,8 @@ class OtherSetup extends AsyncComponent<Props, State> {
   }
 
   handleFullDocsClick = () => {
-    const {organization, project, platform} = this.props;
-    recordAnalyticsDocsClicked({organization, project, platform});
+    const {organization} = this.props;
+    trackAdvancedAnalyticsEvent('growth.onboarding_view_full_docs', {}, organization);
   };
 
   render() {

+ 18 - 2
static/app/views/onboarding/platform.tsx

@@ -11,6 +11,7 @@ import PlatformPicker from 'app/components/platformPicker';
 import {PlatformKey} from 'app/data/platformCategories';
 import {t, tct} from 'app/locale';
 import {Team} from 'app/types';
+import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
 import withApi from 'app/utils/withApi';
 import withTeams from 'app/utils/withTeams';
 
@@ -45,6 +46,14 @@ class OnboardingPlatform extends Component<Props, State> {
     progressing: false,
   };
 
+  componentDidMount() {
+    trackAdvancedAnalyticsEvent(
+      'growth.onboarding_load_choose_platform',
+      {},
+      this.props.organization ?? null
+    );
+  }
+
   componentDidUpdate(prevProps: Props) {
     if (prevProps.active && !this.props.active) {
       // eslint-disable-next-line react/no-did-update-set-state
@@ -56,7 +65,7 @@ class OnboardingPlatform extends Component<Props, State> {
     return this.props.project || this.state.firstProjectCreated;
   }
 
-  get contineButtonLabel() {
+  get continueButtonLabel() {
     if (this.state.progressing) {
       return t('Creating Project...');
     }
@@ -102,6 +111,11 @@ class OnboardingPlatform extends Component<Props, State> {
     if (platform === null) {
       return;
     }
+    trackAdvancedAnalyticsEvent(
+      'growth.onboarding_set_up_your_project',
+      {platform},
+      this.props.organization ?? null
+    );
 
     // Create their first project if they don't already have one. This is a
     // no-op if they already have a project.
@@ -138,6 +152,8 @@ class OnboardingPlatform extends Component<Props, State> {
             noAutoFilter
             platform={selectedPlatform}
             setPlatform={this.handleSetPlatform}
+            source="Onboarding"
+            organization={this.props.organization}
           />
           <p>
             {tct(
@@ -165,7 +181,7 @@ class OnboardingPlatform extends Component<Props, State> {
               disabled={continueDisabled}
               onClick={this.handleContinue}
             >
-              {this.contineButtonLabel}
+              {this.continueButtonLabel}
             </Button>
           )}
         </motion.div>

Some files were not shown because too many files changed in this diff