Files
2024-02-15 17:01:50 +00:00

224 lines
6.4 KiB
TypeScript

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;