Browse Source

ref(onboarding): Consolidate Manual button in integrations (#77795)

Priscila Oliveira 5 months ago
parent
commit
22760e5f50

+ 24 - 5
static/app/gettingStartedDocs/node/awslambda.tsx

@@ -12,9 +12,15 @@ import {
 } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
 import {getJSServerMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
 import {t, tct} from 'sentry/locale';
+import {trackAnalytics} from 'sentry/utils/analytics';
 import {getInstallConfig, getSdkInitSnippet} from 'sentry/utils/gettingStartedDocs/node';
+import {
+  InstallationMode,
+  platformOptions,
+} from 'sentry/views/onboarding/integrationSetup';
 
-type Params = DocsParams;
+type PlatformOptions = typeof platformOptions;
+type Params = DocsParams<PlatformOptions>;
 
 const getSdkSetupSnippet = (params: Params) => `
 // IMPORTANT: Make sure to import and initialize Sentry at the top of your file.
@@ -39,7 +45,7 @@ Sentry.init({
   // },
 });`;
 
-const onboarding: OnboardingConfig = {
+const onboarding: OnboardingConfig<PlatformOptions> = {
   install: params => [
     {
       type: StepType.INSTALL,
@@ -87,9 +93,21 @@ const onboarding: OnboardingConfig = {
       ],
     },
   ],
+  onPlatformOptionsChange(params) {
+    return option => {
+      if (option.installationMode === InstallationMode.MANUAL) {
+        trackAnalytics('integrations.switch_manual_sdk_setup', {
+          integration_type: 'first_party',
+          integration: 'aws_lambda',
+          view: 'onboarding',
+          organization: params.organization,
+        });
+      }
+    };
+  },
 };
 
-const customMetricsOnboarding: OnboardingConfig = {
+const customMetricsOnboarding: OnboardingConfig<PlatformOptions> = {
   install: params => [
     {
       type: StepType.INSTALL,
@@ -122,7 +140,7 @@ const customMetricsOnboarding: OnboardingConfig = {
   verify: getJSServerMetricsOnboarding().verify,
 };
 
-const crashReportOnboarding: OnboardingConfig = {
+const crashReportOnboarding: OnboardingConfig<PlatformOptions> = {
   introduction: () => getCrashReportModalIntroduction(),
   install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
   configure: () => [
@@ -137,10 +155,11 @@ const crashReportOnboarding: OnboardingConfig = {
   nextSteps: () => [],
 };
 
-const docs: Docs = {
+const docs: Docs<PlatformOptions> = {
   onboarding,
   customMetricsOnboarding,
   crashReportOnboarding,
+  platformOptions,
 };
 
 export default docs;

+ 22 - 3
static/app/gettingStartedDocs/python/awslambda.tsx

@@ -13,8 +13,14 @@ import {getPythonMetricsOnboarding} from 'sentry/components/onboarding/gettingSt
 import {crashReportOnboardingPython} from 'sentry/gettingStartedDocs/python/python';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
+import {trackAnalytics} from 'sentry/utils/analytics';
+import {
+  InstallationMode,
+  platformOptions,
+} from 'sentry/views/onboarding/integrationSetup';
 
-type Params = DocsParams;
+type PlatformOptions = typeof platformOptions;
+type Params = DocsParams<PlatformOptions>;
 
 const getInstallSnippet = () => `pip install --upgrade sentry-sdk`;
 
@@ -53,7 +59,7 @@ sentry_sdk.init(
   ],
 )`;
 
-const onboarding: OnboardingConfig = {
+const onboarding: OnboardingConfig<PlatformOptions> = {
   introduction: () =>
     tct(
       'Create a deployment package on your local machine and install the required dependencies in the deployment package. For more information, see [link:AWS Lambda deployment package in Python].',
@@ -146,14 +152,27 @@ const onboarding: OnboardingConfig = {
     },
   ],
   verify: () => [],
+  onPlatformOptionsChange(params) {
+    return option => {
+      if (option.installationMode === InstallationMode.MANUAL) {
+        trackAnalytics('integrations.switch_manual_sdk_setup', {
+          integration_type: 'first_party',
+          integration: 'aws_lambda',
+          view: 'onboarding',
+          organization: params.organization,
+        });
+      }
+    };
+  },
 };
 
-const docs: Docs = {
+const docs: Docs<PlatformOptions> = {
   onboarding,
   customMetricsOnboarding: getPythonMetricsOnboarding({
     installSnippet: getInstallSnippet(),
   }),
   crashReportOnboarding: crashReportOnboardingPython,
+  platformOptions,
 };
 
 export default docs;

+ 2 - 2
static/app/views/onboarding/components/integrations/addInstallationInstructions.tsx

@@ -27,8 +27,8 @@ export default function AddInstallationInstructions() {
       </p>
       <p>
         {tct(
-          'If you don’t want to add CloudFormation stack to your AWS environment, press the [manualSetup] button instead.',
-          {manualSetup: <strong>{t('Manual Setup')}</strong>}
+          'If you don’t want to add CloudFormation stack to your AWS environment, press the [manual] button above instead.',
+          {manual: <strong>{t('Manual')}</strong>}
         )}
       </p>
     </Fragment>

+ 116 - 63
static/app/views/onboarding/integrationSetup.tsx

@@ -5,39 +5,83 @@ import {motion} from 'framer-motion';
 import {openInviteMembersModal} from 'sentry/actionCreators/modal';
 import {Alert} from 'sentry/components/alert';
 import {Button} from 'sentry/components/button';
-import ButtonBar from 'sentry/components/buttonBar';
 import ExternalLink from 'sentry/components/links/externalLink';
 import LoadingError from 'sentry/components/loadingError';
-import platforms from 'sentry/data/platforms';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
+import type {
+  BasePlatformOptions,
+  DocsParams,
+} from 'sentry/components/onboarding/gettingStartedDoc/types';
+import {useLoadGettingStarted} from 'sentry/components/onboarding/gettingStartedDoc/utils/useLoadGettingStarted';
+import {
+  PlatformOptionsControl,
+  useUrlPlatformOptions,
+} from 'sentry/components/onboarding/platformOptionsControl';
 import {t, tct} from 'sentry/locale';
+import ConfigStore from 'sentry/stores/configStore';
+import {useLegacyStore} from 'sentry/stores/useLegacyStore';
 import {space} from 'sentry/styles/space';
 import type {IntegrationProvider} from 'sentry/types/integrations';
-import type {Project} from 'sentry/types/project';
+import type {PlatformIntegration, Project} from 'sentry/types/project';
 import {trackAnalytics} from 'sentry/utils/analytics';
 import getDynamicText from 'sentry/utils/getDynamicText';
-import {trackIntegrationAnalytics} from 'sentry/utils/integrationUtil';
 import useApi from 'sentry/utils/useApi';
 import useOrganization from 'sentry/utils/useOrganization';
+import SetupIntroduction from 'sentry/views/onboarding/components/setupIntroduction';
 import {AddIntegrationButton} from 'sentry/views/settings/organizationIntegrations/addIntegrationButton';
 
 import AddInstallationInstructions from './components/integrations/addInstallationInstructions';
 import PostInstallCodeSnippet from './components/integrations/postInstallCodeSnippet';
-import SetupIntroduction from './components/setupIntroduction';
+
+export enum InstallationMode {
+  AUTO = 'auto',
+  MANUAL = 'manual',
+}
+
+export const platformOptions = {
+  installationMode: {
+    label: t('Installation Mode'),
+    items: [
+      {
+        label: t('Auto'),
+        value: InstallationMode.AUTO,
+      },
+      {
+        label: t('Manual'),
+        value: InstallationMode.MANUAL,
+      },
+    ],
+    defaultValue: InstallationMode.AUTO,
+  },
+} satisfies BasePlatformOptions;
 
 type Props = {
   integrationSlug: string;
-  project: Project | null;
-  onClickManualSetup?: () => void;
+  platform: PlatformIntegration;
+  project: Project;
 };
 
-function IntegrationSetup(props: Props) {
+function IntegrationSetup({project, integrationSlug, platform}: Props) {
   const [hasError, setHasError] = useState(false);
   const [installed, setInstalled] = useState(false);
   const [provider, setProvider] = useState<IntegrationProvider | null>(null);
 
   const organization = useOrganization();
+  const {isSelfHosted, urlPrefix} = useLegacyStore(ConfigStore);
+
+  const {
+    isLoading,
+    docs: docsConfig,
+    dsn,
+    projectKeyId,
+    refetch,
+  } = useLoadGettingStarted({
+    orgSlug: organization.slug,
+    projSlug: project.slug,
+    platform,
+  });
 
-  const {project, integrationSlug} = props;
+  const selectedPlatformOptions = useUrlPlatformOptions(docsConfig?.platformOptions);
 
   const api = useApi();
   const fetchData = useCallback(() => {
@@ -64,10 +108,7 @@ function IntegrationSetup(props: Props) {
 
   const loadingError = (
     <LoadingError
-      message={t(
-        'Failed to load the integration for the %s platform.',
-        project?.platform ?? 'other'
-      )}
+      message={t('Failed to load the integration for the %s platform.', platform.name)}
       onRetry={fetchData}
     />
   );
@@ -78,26 +119,13 @@ function IntegrationSetup(props: Props) {
     </Alert>
   );
 
-  const renderSetupInstructions = () => {
-    const currentPlatform = project?.platform ?? 'other';
-    return (
-      <SetupIntroduction
-        stepHeaderText={t(
-          'Automatically instrument %s',
-          platforms.find(p => p.id === currentPlatform)?.name ?? ''
-        )}
-        platform={currentPlatform}
-      />
-    );
-  };
   const renderIntegrationInstructions = () => {
-    if (!provider || !project) {
+    if (!provider) {
       return null;
     }
 
     return (
       <Fragment>
-        {renderSetupInstructions()}
         <motion.p
           variants={{
             initial: {opacity: 0},
@@ -131,43 +159,26 @@ function IntegrationSetup(props: Props) {
         </motion.div>
 
         <DocsWrapper>
-          <StyledButtonBar gap={1}>
-            <AddIntegrationButton
-              provider={provider}
-              onAddIntegration={() => setInstalled(true)}
-              organization={organization}
-              priority="primary"
-              size="sm"
-              analyticsParams={{view: 'onboarding', already_installed: false}}
-              modalParams={{projectId: project.id}}
-            />
-            <Button
-              size="sm"
-              onClick={() => {
-                props.onClickManualSetup?.();
-                trackIntegrationAnalytics('integrations.switch_manual_sdk_setup', {
-                  integration_type: 'first_party',
-                  integration: integrationSlug,
-                  view: 'onboarding',
-                  organization,
-                });
-              }}
-            >
-              {t('Manual Setup')}
-            </Button>
-          </StyledButtonBar>
+          <AddIntegrationButton
+            provider={provider}
+            onAddIntegration={() => setInstalled(true)}
+            organization={organization}
+            priority="primary"
+            size="sm"
+            analyticsParams={{view: 'onboarding', already_installed: false}}
+            modalParams={{projectId: project.id}}
+          />
         </DocsWrapper>
       </Fragment>
     );
   };
 
   const renderPostInstallInstructions = () => {
-    if (!project || !provider) {
+    if (!provider) {
       return null;
     }
     return (
       <Fragment>
-        {renderSetupInstructions()}
         <PostInstallCodeSnippet
           provider={provider}
           platform={project.platform}
@@ -187,8 +198,53 @@ function IntegrationSetup(props: Props) {
     );
   };
 
+  if (isLoading) {
+    return <LoadingIndicator />;
+  }
+
+  if (!docsConfig || !dsn || !projectKeyId) {
+    return (
+      <LoadingError
+        message={t(
+          'The getting started documentation for this platform is currently unavailable.'
+        )}
+        onRetry={refetch}
+      />
+    );
+  }
+
+  const docParams: DocsParams<any> = {
+    api,
+    projectKeyId,
+    dsn,
+    organization,
+    platformKey: platform.id,
+    projectId: project.id,
+    projectSlug: project.slug,
+    isFeedbackSelected: false,
+    isPerformanceSelected: false,
+    isProfilingSelected: false,
+    isReplaySelected: false,
+    isSelfHosted,
+    platformOptions: selectedPlatformOptions,
+    sourcePackageRegistries: {
+      isLoading: false,
+      data: undefined,
+    },
+    urlPrefix,
+  };
+
   return (
     <Fragment>
+      <SetupIntroduction
+        stepHeaderText={t('Automatically instrument %s SDK', platform.name)}
+        platform={platform.id}
+      />
+      <PlatformOptionsControl
+        platformOptions={platformOptions}
+        onChange={docsConfig.onboarding.onPlatformOptionsChange?.(docParams)}
+      />
+      <Divider />
       {installed ? renderPostInstallInstructions() : renderIntegrationInstructions()}
       {getDynamicText({
         value: !hasError ? null : loadingError,
@@ -206,15 +262,12 @@ DocsWrapper.defaultProps = {
   exit: {opacity: 0},
 };
 
-const StyledButtonBar = styled(ButtonBar)`
-  margin-top: ${space(3)};
-  width: max-content;
-
-  @media (max-width: ${p => p.theme.breakpoints.small}) {
-    width: auto;
-    grid-row-gap: ${space(1)};
-    grid-auto-flow: row;
-  }
+const Divider = styled('hr')`
+  height: 1px;
+  width: 100%;
+  background: ${p => p.theme.border};
+  border: none;
+  margin-bottom: ${space(3)};
 `;
 
 export default IntegrationSetup;

+ 4 - 8
static/app/views/onboarding/setupDocs.tsx

@@ -13,11 +13,10 @@ import {platformToIntegrationMap} from 'sentry/utils/integrationUtil';
 import {decodeList} from 'sentry/utils/queryString';
 import useOrganization from 'sentry/utils/useOrganization';
 import SetupIntroduction from 'sentry/views/onboarding/components/setupIntroduction';
-import {useOnboardingQueryParams} from 'sentry/views/onboarding/components/useOnboardingQueryParams';
 import {OtherPlatformsInfo} from 'sentry/views/projectInstall/otherPlatformsInfo';
 
 import FirstEventFooter from './components/firstEventFooter';
-import IntegrationSetup from './integrationSetup';
+import IntegrationSetup, {InstallationMode} from './integrationSetup';
 import type {StepProps} from './types';
 
 function SetupDocs({location, recentCreatedProject: project}: StepProps) {
@@ -32,15 +31,14 @@ function SetupDocs({location, recentCreatedProject: project}: StepProps) {
   const currentPlatform =
     platforms.find(p => p.id === currentPlatformKey) ?? otherPlatform;
 
-  const [params, setParams] = useOnboardingQueryParams();
-
   if (!project || !currentPlatform) {
     return null;
   }
 
   const platformName = currentPlatform.name;
   const integrationSlug = project.platform && platformToIntegrationMap[project.platform];
-  const showIntegrationOnboarding = integrationSlug && !params.showManualSetup;
+  const showIntegrationOnboarding =
+    integrationSlug && location.query.installationMode !== InstallationMode.MANUAL;
 
   return (
     <Fragment>
@@ -50,9 +48,7 @@ function SetupDocs({location, recentCreatedProject: project}: StepProps) {
             <IntegrationSetup
               integrationSlug={integrationSlug}
               project={project}
-              onClickManualSetup={() => {
-                setParams({showManualSetup: true});
-              }}
+              platform={currentPlatform}
             />
           ) : (
             <Fragment>