RequestIntegrationModal.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import {Fragment, useState} from 'react';
  2. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  3. import type {ModalRenderProps} from 'sentry/actionCreators/modal';
  4. import {Button} from 'sentry/components/button';
  5. import TextareaField from 'sentry/components/forms/fields/textareaField';
  6. import {t} from 'sentry/locale';
  7. import type {IntegrationType} from 'sentry/types/integrations';
  8. import {trackIntegrationAnalytics} from 'sentry/utils/integrationUtil';
  9. import {useMutation} from 'sentry/utils/queryClient';
  10. import useApi from 'sentry/utils/useApi';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  13. type Props = {
  14. name: string;
  15. onSuccess: () => void;
  16. slug: string;
  17. type: IntegrationType;
  18. } & ModalRenderProps;
  19. /**
  20. * This modal serves as a non-owner's confirmation step before sending
  21. * organization owners an email requesting a new organization integration. It
  22. * lets the user attach an optional message to be included in the email.
  23. */
  24. export default function RequestIntegrationModal(props: Props) {
  25. const [isSending, setIsSending] = useState<boolean>(false);
  26. const [message, setMessage] = useState<string>('');
  27. const organization = useOrganization();
  28. const api = useApi({persistInFlight: true});
  29. const {Header, Body, Footer, name, slug, type, closeModal, onSuccess} = props;
  30. const endpoint = `/organizations/${organization.slug}/integration-requests/`;
  31. const sendRequestMutation = useMutation({
  32. mutationFn: () => {
  33. return api.requestPromise(endpoint, {
  34. method: 'POST',
  35. data: {
  36. providerSlug: slug,
  37. providerType: type,
  38. message,
  39. },
  40. });
  41. },
  42. onMutate: () => {
  43. setIsSending(true);
  44. trackIntegrationAnalytics('integrations.request_install', {
  45. integration_type: type,
  46. integration: slug,
  47. organization,
  48. });
  49. },
  50. onSuccess: () => {
  51. addSuccessMessage(t('Request successfully sent.'));
  52. setIsSending(false);
  53. onSuccess();
  54. closeModal();
  55. },
  56. onError: () => {
  57. addErrorMessage('Error sending the request');
  58. setIsSending(false);
  59. },
  60. });
  61. const buttonText = isSending ? t('Sending Request') : t('Send Request');
  62. return (
  63. <Fragment>
  64. <Header>
  65. <h4>{t('Request %s Installation', name)}</h4>
  66. </Header>
  67. <Body>
  68. <TextBlock>
  69. {t(
  70. 'Looks like your organization owner, manager, or admin needs to install %s. Want to send them a request?',
  71. name
  72. )}
  73. </TextBlock>
  74. <TextBlock>
  75. {t(
  76. '(Optional) You’ve got good reasons for installing the %s Integration. Share them with your organization owner.',
  77. name
  78. )}
  79. </TextBlock>
  80. <TextareaField
  81. inline={false}
  82. flexibleControlStateSize
  83. stacked
  84. name="message"
  85. type="string"
  86. onChange={setMessage}
  87. placeholder={t('Optional message…')}
  88. />
  89. <TextBlock>
  90. {t(
  91. 'When you click “Send Request”, we’ll email your request to your organization’s owners. So just keep that in mind.'
  92. )}
  93. </TextBlock>
  94. </Body>
  95. <Footer>
  96. <Button onClick={() => sendRequestMutation.mutate()}>{buttonText}</Button>
  97. </Footer>
  98. </Fragment>
  99. );
  100. }