setupMessagingIntegrationButton.tsx 4.5 KB

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