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;