createNewIntegrationModal.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import type {ReactNode} from 'react';
  2. import {Fragment, useState} from 'react';
  3. import styled from '@emotion/styled';
  4. import type {ModalRenderProps} from 'sentry/actionCreators/modal';
  5. import {Button} from 'sentry/components/button';
  6. import RadioGroup from 'sentry/components/forms/controls/radioGroup';
  7. import ExternalLink from 'sentry/components/links/externalLink';
  8. import {t, tct} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {Organization} from 'sentry/types';
  11. import {
  12. platformEventLinkMap,
  13. PlatformEvents,
  14. } from 'sentry/utils/analytics/integrations/platformAnalyticsEvents';
  15. import {trackIntegrationAnalytics} from 'sentry/utils/integrationUtil';
  16. import withOrganization from 'sentry/utils/withOrganization';
  17. import ExampleIntegrationButton from 'sentry/views/settings/organizationIntegrations/exampleIntegrationButton';
  18. export type CreateNewIntegrationModalOptions = {organization: Organization};
  19. type CreateNewIntegrationModalProps = CreateNewIntegrationModalOptions & ModalRenderProps;
  20. const analyticsView = 'new_integration_modal' as const;
  21. function CreateNewIntegrationModal({
  22. Body,
  23. Header,
  24. Footer,
  25. closeModal,
  26. organization,
  27. }: CreateNewIntegrationModalProps) {
  28. const [option, selectOption] = useState('internal');
  29. const choices = [
  30. [
  31. 'internal',
  32. <RadioChoiceHeader data-test-id="internal-integration" key="header-internal">
  33. {t('Internal Integration')}
  34. </RadioChoiceHeader>,
  35. <RadioChoiceDescription key="description-internal">
  36. {tct(
  37. 'Internal integrations are meant for custom integrations unique to your organization. See more info on [docsLink].',
  38. {
  39. docsLink: (
  40. <ExternalLink
  41. href={platformEventLinkMap[PlatformEvents.INTERNAL_DOCS]}
  42. onClick={() => {
  43. trackIntegrationAnalytics(PlatformEvents.INTERNAL_DOCS, {
  44. organization,
  45. view: analyticsView,
  46. });
  47. }}
  48. >
  49. {t('Internal Integrations')}
  50. </ExternalLink>
  51. ),
  52. }
  53. )}
  54. </RadioChoiceDescription>,
  55. ],
  56. [
  57. 'public',
  58. <RadioChoiceHeader data-test-id="public-integration" key="header-public">
  59. {t('Public Integration')}
  60. </RadioChoiceHeader>,
  61. <RadioChoiceDescription key="description-public">
  62. {tct(
  63. 'A public integration will be available for all Sentry users for installation. See more info on [docsLink].',
  64. {
  65. docsLink: (
  66. <ExternalLink
  67. href={platformEventLinkMap[PlatformEvents.PUBLIC_DOCS]}
  68. onClick={() => {
  69. trackIntegrationAnalytics(PlatformEvents.PUBLIC_DOCS, {
  70. organization,
  71. view: analyticsView,
  72. });
  73. }}
  74. >
  75. {t('Public Integrations')}
  76. </ExternalLink>
  77. ),
  78. }
  79. )}
  80. </RadioChoiceDescription>,
  81. ],
  82. ] as [string, ReactNode, ReactNode][];
  83. if (organization.features.includes('sentry-functions')) {
  84. choices.push([
  85. 'sentry-fx',
  86. <RadioChoiceHeader data-test-id="sentry-function" key="header-sentryfx">
  87. {t('Sentry Function')}
  88. </RadioChoiceHeader>,
  89. <RadioChoiceDescription key="description-sentry-function">
  90. {t(
  91. 'A Sentry Function is a new type of integration leveraging the power of cloud functions.'
  92. )}
  93. </RadioChoiceDescription>,
  94. ]);
  95. }
  96. return (
  97. <Fragment>
  98. <Header>
  99. <HeaderWrapper>
  100. <h3>{t('Choose Integration Type')}</h3>
  101. <ExampleIntegrationButton analyticsView={analyticsView} />
  102. </HeaderWrapper>
  103. </Header>
  104. <Body>
  105. <StyledRadioGroup
  106. choices={choices}
  107. label={t('Avatar Type')}
  108. onChange={value => selectOption(value)}
  109. value={option}
  110. />
  111. </Body>
  112. <Footer>
  113. <Button size="sm" onClick={() => closeModal()} style={{marginRight: space(1)}}>
  114. {t('Cancel')}
  115. </Button>
  116. <Button
  117. priority="primary"
  118. size="sm"
  119. to={
  120. option === 'sentry-fx'
  121. ? `/settings/${organization.slug}/developer-settings/sentry-functions/new/`
  122. : `/settings/${organization.slug}/developer-settings/${
  123. option === 'public' ? 'new-public' : 'new-internal'
  124. }/`
  125. }
  126. onClick={() => {
  127. trackIntegrationAnalytics(
  128. option === 'sentry-fx'
  129. ? PlatformEvents.CHOSE_SENTRY_FX
  130. : option === 'public'
  131. ? PlatformEvents.CHOSE_PUBLIC
  132. : PlatformEvents.CHOSE_INTERNAL,
  133. {
  134. organization,
  135. view: analyticsView,
  136. }
  137. );
  138. }}
  139. >
  140. {t('Next')}
  141. </Button>
  142. </Footer>
  143. </Fragment>
  144. );
  145. }
  146. const StyledRadioGroup = styled(RadioGroup)`
  147. grid-auto-columns: auto;
  148. & > label:not(:last-child) > div:last-child > * {
  149. padding-bottom: ${space(1)};
  150. }
  151. `;
  152. const RadioChoiceHeader = styled('h6')`
  153. margin: 0;
  154. `;
  155. const RadioChoiceDescription = styled('div')`
  156. color: ${p => p.theme.gray400};
  157. font-size: ${p => p.theme.fontSizeMedium};
  158. line-height: 1.6em;
  159. `;
  160. const HeaderWrapper = styled('div')`
  161. display: flex;
  162. align-items: center;
  163. justify-content: space-between;
  164. width: 100%;
  165. `;
  166. export default withOrganization(CreateNewIntegrationModal);