import {Fragment} from 'react'; import styled from '@emotion/styled'; import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator'; import {hasEveryAccess} from 'sentry/components/acl/access'; import {Button} from 'sentry/components/button'; import Confirm from 'sentry/components/confirm'; import type DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent'; import EmptyMessage from 'sentry/components/emptyMessage'; import TextField from 'sentry/components/forms/fields/textField'; import ExternalLink from 'sentry/components/links/externalLink'; import Panel from 'sentry/components/panels/panel'; import PanelBody from 'sentry/components/panels/panelBody'; import PanelHeader from 'sentry/components/panels/panelHeader'; import {Tooltip} from 'sentry/components/tooltip'; import {IconDelete} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {ExternalTeam, Integration} from 'sentry/types/integrations'; import type {RouteComponentProps} from 'sentry/types/legacyReactRouter'; import type {Organization, Team} from 'sentry/types/organization'; import {toTitleCase} from 'sentry/utils/string/toTitleCase'; import withOrganization from 'sentry/utils/withOrganization'; import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView'; import PermissionAlert from 'sentry/views/settings/project/permissionAlert'; type Props = RouteComponentProps<{teamId: string}, {}> & { organization: Organization; team: Team; }; type State = DeprecatedAsyncView['state'] & { integrations: Integration[]; teamDetails: Team; }; const DOCS_LINK = 'https://docs.sentry.io/product/integrations/notification-incidents/slack/#team-notifications'; const NOTIFICATION_PROVIDERS = ['slack']; class TeamNotificationSettings extends DeprecatedAsyncView { getTitle() { return 'Team Notification Settings'; } getEndpoints(): ReturnType { const {organization, team} = this.props; return [ [ 'teamDetails', `/teams/${organization.slug}/${team.slug}/`, {query: {expand: ['externalTeams']}}, ], [ 'integrations', `/organizations/${organization.slug}/integrations/`, {query: {includeConfig: 0}}, ], ]; } handleDelete = async (mapping: ExternalTeam) => { try { const {organization, team} = this.props; const endpoint = `/teams/${organization.slug}/${team.slug}/external-teams/${mapping.id}/`; await this.api.requestPromise(endpoint, { method: 'DELETE', }); addSuccessMessage(t('Deletion successful')); this.fetchData(); } catch { addErrorMessage(t('An error occurred')); } }; renderBody() { const {team} = this.props; return ( {t('Notifications')} {this.renderPanelBody()} ); } renderPanelBody() { const {organization, team} = this.props; const {teamDetails, integrations} = this.state; const notificationIntegrations = integrations.filter(integration => NOTIFICATION_PROVIDERS.includes(integration.provider.key) ); if (!notificationIntegrations.length) { return ( {t('No Notification Integrations have been installed yet.')} ); } const externalTeams = (teamDetails.externalTeams || []).filter(externalTeam => NOTIFICATION_PROVIDERS.includes(externalTeam.provider) ); if (!externalTeams.length) { return (
{t('No teams have been linked yet.')}
{tct('Head over to Slack and type [code] to get started. [link].', { code: /sentry link team, link: {t('Learn more')}, })}
); } const integrationsById = Object.fromEntries( notificationIntegrations.map(integration => [integration.id, integration]) ); const hasWriteAccess = hasEveryAccess(['team:write'], {organization, team}); return externalTeams.map(externalTeam => ( {toTitleCase(externalTeam.provider)}: {integrationsById[externalTeam.integrationId].name} {tct('Unlink this channel in Slack with [code]. [link].', { code: /sentry unlink team, link: {t('Learn more')}, })} } labelText={t('Unlink this channel in slack with `/slack unlink team`')} name="externalName" value={externalTeam.externalName} /> this.handleDelete(externalTeam)} message={t('Are you sure you want to remove this Slack team link?')} >