import {Component, Fragment} from 'react'; import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; import Access from 'sentry/components/acl/access'; import Alert from 'sentry/components/alert'; import Button from 'sentry/components/button'; import CircleIndicator from 'sentry/components/circleIndicator'; import Confirm from 'sentry/components/confirm'; import Tooltip from 'sentry/components/tooltip'; import {IconDelete, IconSettings, IconWarning} from 'sentry/icons'; import {t} from 'sentry/locale'; import space from 'sentry/styles/space'; import {Integration, IntegrationProvider, ObjectStatus, Organization} from 'sentry/types'; import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrations'; import {AddIntegrationButton} from './addIntegrationButton'; import IntegrationItem from './integrationItem'; type Props = { integration: Integration; onDisable: (integration: Integration) => void; onRemove: (integration: Integration) => void; organization: Organization; provider: IntegrationProvider; trackIntegrationAnalytics: (eventKey: IntegrationAnalyticsKey) => void; // analytics callback requiresUpgrade?: boolean; }; export default class InstalledIntegration extends Component { handleUninstallClick = () => { this.props.trackIntegrationAnalytics('integrations.uninstall_clicked'); }; getRemovalBodyAndText(aspects: Integration['provider']['aspects']) { if (aspects && aspects.removal_dialog) { return { body: aspects.removal_dialog.body, actionText: aspects.removal_dialog.actionText, }; } return { body: t( 'Deleting this integration will remove any project associated data. This action cannot be undone. Are you sure you want to delete this integration?' ), actionText: t('Delete'), }; } handleRemove(integration: Integration) { this.props.onRemove(integration); this.props.trackIntegrationAnalytics('integrations.uninstall_completed'); } get integrationStatus() { const {integration} = this.props; // there are multiple status fields for an integration we consider const statusList = [integration.status, integration.organizationIntegrationStatus]; const firstNotActive = statusList.find(s => s !== 'active'); // Active if everything is active, otherwise the first inactive status return firstNotActive ?? 'active'; } get removeConfirmProps() { const {integration} = this.props; const {body, actionText} = this.getRemovalBodyAndText(integration.provider.aspects); const message = ( {t('Deleting this integration has consequences!')} {body} ); return { message, confirmText: actionText, onConfirm: () => this.handleRemove(integration), }; } get disableConfirmProps() { const {integration} = this.props; const {body, actionText} = integration.provider.aspects.disable_dialog || {}; const message = ( {t('This integration cannot be removed in Sentry')} {body} ); return { message, confirmText: actionText, onConfirm: () => this.props.onDisable(integration), }; } render() { const {integration, organization, provider, requiresUpgrade} = this.props; const removeConfirmProps = this.integrationStatus === 'active' && integration.provider.canDisable ? this.disableConfirmProps : this.removeConfirmProps; const allowMemberConfiguration = ['github', 'gitlab'].includes( this.props.provider.key ); return ( {({hasAccess}) => { const disableAction = !(hasAccess && this.integrationStatus === 'active'); return (
{requiresUpgrade && ( } onAddIntegration={() => {}} organization={organization} provider={provider} priority="primary" size="sm" /> )} } disabled={!allowMemberConfiguration && disableAction} to={`/settings/${organization.slug}/integrations/${provider.key}/${integration.id}/`} data-test-id="integration-configure-button" > {t('Configure')}
} data-test-id="integration-remove-button" > {t('Uninstall')}
); }}
); } } const StyledButton = styled(Button)` color: ${p => p.theme.gray300}; `; const IntegrationItemBox = styled('div')` flex: 1; `; const IntegrationStatus = ( props: React.HTMLAttributes & { status: ObjectStatus; hideTooltip?: boolean; } ) => { const theme = useTheme(); const {status, hideTooltip, ...p} = props; const color = status === 'active' ? theme.success : theme.gray300; const inner = (
{`${ status === 'active' ? t('enabled') : status === 'disabled' ? t('disabled') : t('pending deletion') }`}
); return hideTooltip ? ( inner ) : ( {inner} ); }; const StyledIntegrationStatus = styled(IntegrationStatus)` display: flex; align-items: center; color: ${p => p.theme.gray300}; font-weight: light; text-transform: capitalize; &:before { content: '|'; color: ${p => p.theme.gray200}; margin-right: ${space(1)}; font-weight: normal; } `; const IntegrationStatusText = styled('div')` margin: 0 ${space(0.75)} 0 ${space(0.5)}; `;