createNewIntegrationModal.tsx 5.4 KB

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