setupMessagingIntegrationButton.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import styled from '@emotion/styled';
  2. import {openModal} from 'sentry/actionCreators/modal';
  3. import {Button} from 'sentry/components/button';
  4. import {t} from 'sentry/locale';
  5. import PluginIcon from 'sentry/plugins/components/pluginIcon';
  6. import {space} from 'sentry/styles/space';
  7. import type {
  8. IntegrationProvider,
  9. OrganizationIntegration,
  10. } from 'sentry/types/integrations';
  11. import {trackAnalytics} from 'sentry/utils/analytics';
  12. import {getIntegrationFeatureGate} from 'sentry/utils/integrationUtil';
  13. import {useApiQueries, useApiQuery} from 'sentry/utils/queryClient';
  14. import useOrganization from 'sentry/utils/useOrganization';
  15. import MessagingIntegrationModal from 'sentry/views/alerts/rules/issue/messagingIntegrationModal';
  16. export enum MessagingIntegrationAnalyticsView {
  17. ALERT_RULE_CREATION = 'alert_rule_creation',
  18. PROJECT_CREATION = 'project_creation',
  19. }
  20. type Props = {
  21. analyticsParams?: {
  22. view: MessagingIntegrationAnalyticsView;
  23. };
  24. projectId?: string;
  25. refetchConfigs?: () => void;
  26. };
  27. function SetupMessagingIntegrationButton({
  28. refetchConfigs,
  29. analyticsParams,
  30. projectId,
  31. }: Props) {
  32. const providerKeys = ['slack', 'discord', 'msteams'];
  33. const organization = useOrganization();
  34. const onAddIntegration = () => {
  35. messagingIntegrationsQuery.refetch();
  36. if (refetchConfigs) {
  37. refetchConfigs();
  38. }
  39. };
  40. const messagingIntegrationsQuery = useApiQuery<OrganizationIntegration[]>(
  41. [`/organizations/${organization.slug}/integrations/?integrationType=messaging`],
  42. {staleTime: Infinity}
  43. );
  44. const integrationProvidersQuery = useApiQueries<{providers: IntegrationProvider[]}>(
  45. providerKeys.map((providerKey: string) => [
  46. `/organizations/${organization.slug}/config/integrations/?provider_key=${providerKey}`,
  47. ]),
  48. {staleTime: Infinity}
  49. );
  50. const {IntegrationFeatures} = getIntegrationFeatureGate();
  51. const shouldRenderSetupButton = messagingIntegrationsQuery.data?.every(
  52. integration => integration.status !== 'active'
  53. );
  54. if (
  55. messagingIntegrationsQuery.isPending ||
  56. messagingIntegrationsQuery.isError ||
  57. integrationProvidersQuery.some(({isPending}) => isPending) ||
  58. integrationProvidersQuery.some(({isError}) => isError) ||
  59. integrationProvidersQuery[0].data == null
  60. ) {
  61. return null;
  62. }
  63. if (!shouldRenderSetupButton) {
  64. return null;
  65. }
  66. return (
  67. <IntegrationFeatures
  68. organization={organization}
  69. features={integrationProvidersQuery[0].data.providers[0]?.metadata?.features}
  70. >
  71. {({disabled, disabledReason}) => (
  72. <div>
  73. <Button
  74. size="sm"
  75. icon={
  76. <IconWrapper>
  77. {providerKeys.map((value: string) => {
  78. return <PluginIcon key={value} pluginId={value} size={16} />;
  79. })}
  80. </IconWrapper>
  81. }
  82. disabled={disabled}
  83. title={
  84. disabled
  85. ? disabledReason
  86. : t('Send alerts to your messaging service. Install the integration now.')
  87. }
  88. onClick={() => {
  89. openModal(
  90. deps => (
  91. <MessagingIntegrationModal
  92. {...deps}
  93. headerContent={t('Connect with a messaging tool')}
  94. bodyContent={t('Receive alerts and digests right where you work.')}
  95. providers={integrationProvidersQuery
  96. .map(result => result.data?.providers[0])
  97. .filter(
  98. (provider): provider is IntegrationProvider =>
  99. provider !== undefined
  100. )}
  101. onAddIntegration={onAddIntegration}
  102. {...(projectId && {modalParams: {projectId: projectId}})}
  103. />
  104. ),
  105. {
  106. closeEvents: 'escape-key',
  107. }
  108. );
  109. trackAnalytics('onboarding.messaging_integration_modal_rendered', {
  110. organization,
  111. ...analyticsParams,
  112. });
  113. }}
  114. >
  115. {t('Connect to messaging')}
  116. </Button>
  117. </div>
  118. )}
  119. </IntegrationFeatures>
  120. );
  121. }
  122. const IconWrapper = styled('div')`
  123. display: flex;
  124. gap: ${space(1)};
  125. `;
  126. export default SetupMessagingIntegrationButton;