123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- import {Fragment, useCallback, useEffect, useState} from 'react';
- import styled from '@emotion/styled';
- 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 {t, tct} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import type {IntegrationProvider, Project} from 'sentry/types';
- 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 {AddIntegrationButton} from 'sentry/views/settings/organizationIntegrations/addIntegrationButton';
- import AddInstallationInstructions from './components/integrations/addInstallationInstructions';
- import PostInstallCodeSnippet from './components/integrations/postInstallCodeSnippet';
- import SetupIntroduction from './components/setupIntroduction';
- type Props = {
- integrationSlug: string;
- project: Project | null;
- onClickManualSetup?: () => void;
- };
- function IntegrationSetup(props: Props) {
- const [hasError, setHasError] = useState(false);
- const [installed, setInstalled] = useState(false);
- const [provider, setProvider] = useState<IntegrationProvider | null>(null);
- const organization = useOrganization();
- const {project, integrationSlug} = props;
- const api = useApi();
- const fetchData = useCallback(() => {
- if (!integrationSlug) {
- return Promise.resolve();
- }
- const endpoint = `/organizations/${organization.slug}/config/integrations/?provider_key=${integrationSlug}`;
- return api
- .requestPromise(endpoint)
- .then(integrations => {
- setProvider(integrations.providers[0]);
- setHasError(false);
- })
- .catch(error => {
- setHasError(true);
- throw error;
- });
- }, [integrationSlug, api, organization.slug]);
- useEffect(() => {
- fetchData();
- }, [fetchData]);
- const loadingError = (
- <LoadingError
- message={t(
- 'Failed to load the integration for the %s platform.',
- project?.platform ?? 'other'
- )}
- onRetry={fetchData}
- />
- );
- const testOnlyAlert = (
- <Alert type="warning">
- Platform documentation is not rendered in for tests in CI
- </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) {
- return null;
- }
- return (
- <Fragment>
- {renderSetupInstructions()}
- <motion.p
- variants={{
- initial: {opacity: 0},
- animate: {opacity: 1},
- exit: {opacity: 0},
- }}
- >
- {tct(
- "Don't have have permissions to create a Cloudformation stack? [link:Invite your team instead].",
- {
- link: (
- <Button
- priority="link"
- onClick={() => {
- openInviteMembersModal();
- }}
- aria-label={t('Invite your team instead')}
- />
- ),
- }
- )}
- </motion.p>
- <motion.div
- variants={{
- initial: {opacity: 0},
- animate: {opacity: 1},
- exit: {opacity: 0},
- }}
- >
- <AddInstallationInstructions />
- </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"
- to={{
- pathname: window.location.pathname,
- query: {manual: '1'},
- }}
- onClick={() => {
- props.onClickManualSetup?.();
- trackIntegrationAnalytics('integrations.switch_manual_sdk_setup', {
- integration_type: 'first_party',
- integration: integrationSlug,
- view: 'onboarding',
- organization,
- });
- }}
- >
- {t('Manual Setup')}
- </Button>
- </StyledButtonBar>
- </DocsWrapper>
- </Fragment>
- );
- };
- const renderPostInstallInstructions = () => {
- if (!project || !provider) {
- return null;
- }
- return (
- <Fragment>
- {renderSetupInstructions()}
- <PostInstallCodeSnippet
- provider={provider}
- platform={project.platform}
- isOnboarding
- />
- <ExternalLink
- onClick={() => {
- trackAnalytics('growth.onboarding_view_full_docs', {
- organization,
- });
- }}
- href="https://docs.sentry.io/product/integrations/cloud-monitoring/aws-lambda/"
- >
- {t('View Full Documentation')}
- </ExternalLink>
- </Fragment>
- );
- };
- return (
- <Fragment>
- {installed ? renderPostInstallInstructions() : renderIntegrationInstructions()}
- {getDynamicText({
- value: !hasError ? null : loadingError,
- fixed: testOnlyAlert,
- })}
- </Fragment>
- );
- }
- const DocsWrapper = styled(motion.div)``;
- DocsWrapper.defaultProps = {
- initial: {opacity: 0, y: 40},
- animate: {opacity: 1, y: 0},
- 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;
- }
- `;
- export default IntegrationSetup;
|