demoEndModal.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import {useCallback} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {fetchGuides} from 'sentry/actionCreators/guides';
  5. import {ModalRenderProps} from 'sentry/actionCreators/modal';
  6. import {Button} from 'sentry/components/button';
  7. import ModalTask from 'sentry/components/onboardingWizard/modalTask';
  8. import {SidebarPanelKey} from 'sentry/components/sidebar/types';
  9. import {IconClose} from 'sentry/icons/iconClose';
  10. import {t} from 'sentry/locale';
  11. import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
  12. import {Organization} from 'sentry/types';
  13. import {trackAnalytics} from 'sentry/utils/analytics';
  14. import useApi from 'sentry/utils/useApi';
  15. import {useNavigate} from 'sentry/utils/useNavigate';
  16. // tour is a string that tells which tour the user is completing in the walkthrough
  17. type Props = ModalRenderProps & {orgSlug: Organization['slug'] | null; tour: string};
  18. export default function DemoEndingModal({tour, closeModal, CloseButton, orgSlug}: Props) {
  19. const api = useApi();
  20. const navigate = useNavigate();
  21. let cardTitle = '',
  22. body = '',
  23. guides = [''],
  24. path = '';
  25. switch (tour) {
  26. case 'issues':
  27. cardTitle = t('Issues Tour');
  28. body = t(
  29. 'Thank you for completing the Issues tour. Learn about other Sentry features by starting another tour.'
  30. );
  31. guides = ['issues_v3', 'issue_stream_v3'];
  32. path = `/organizations/${orgSlug}/issues/`;
  33. break;
  34. case 'performance':
  35. cardTitle = t('Performance Tour');
  36. body = t(
  37. 'Thank you for completing the Performance tour. Learn about other Sentry features by starting another tour.'
  38. );
  39. guides = ['performance', 'transaction_summary', 'transaction_details_v2'];
  40. path = `/organizations/${orgSlug}/performance/`;
  41. break;
  42. case 'releases':
  43. cardTitle = t('Releases Tour');
  44. body = t(
  45. 'Thank you for completing the Releases tour. Learn about other Sentry features by starting another tour.'
  46. );
  47. guides = ['releases_v2', 'react-native-release', 'release-details_v2'];
  48. path = `/organizations/${orgSlug}/releases/`;
  49. break;
  50. case 'tabs':
  51. cardTitle = t('Check out the different tabs');
  52. body = t(
  53. 'Thank you for checking out the different tabs. Learn about other Sentry features by starting another tour.'
  54. );
  55. guides = ['sidebar_v2'];
  56. path = `/organizations/${orgSlug}/projects/`;
  57. break;
  58. default:
  59. }
  60. const sandboxData = window.SandboxData;
  61. const url = sandboxData?.cta?.url || 'https://sentry.io/signup/';
  62. const navigation = useCallback(() => {
  63. navigate(path);
  64. }, [path, navigate]);
  65. async function handleRestart() {
  66. await Promise.all(
  67. guides.map(guide =>
  68. api.requestPromise('/assistant/', {
  69. method: 'PUT',
  70. data: {guide, status: 'restart'},
  71. })
  72. )
  73. );
  74. trackAnalytics('growth.end_modal_restart_tours', {
  75. organization: null,
  76. });
  77. closeModal?.();
  78. fetchGuides();
  79. navigation();
  80. }
  81. const handleMoreTours = () => {
  82. closeModal?.();
  83. SidebarPanelStore.togglePanel(SidebarPanelKey.OnboardingWizard);
  84. trackAnalytics('growth.end_modal_more_tours', {
  85. organization: null,
  86. });
  87. };
  88. return (
  89. <EndModal>
  90. <CloseButton
  91. size="zero"
  92. onClick={() => {
  93. trackAnalytics('growth.end_modal_close', {
  94. organization: null,
  95. });
  96. if (closeModal) {
  97. closeModal();
  98. }
  99. }}
  100. icon={<IconClose size="xs" />}
  101. />
  102. <ModalHeader>
  103. <h2> {t('Tour Complete')} </h2>
  104. </ModalHeader>
  105. <ModalTask title={cardTitle} />
  106. <ModalHeader>{body}</ModalHeader>
  107. <ButtonContainer>
  108. <SignUpButton
  109. external
  110. href={url}
  111. onClick={() => {
  112. trackAnalytics('growth.end_modal_signup', {
  113. organization: null,
  114. });
  115. }}
  116. >
  117. {sandboxData?.cta?.title || t('Sign up for Sentry')}
  118. </SignUpButton>
  119. <ButtonBar>
  120. <Button onClick={handleMoreTours}>{t('More Tours')} </Button>
  121. <Button onClick={handleRestart}>{t('Restart Tour')}</Button>
  122. </ButtonBar>
  123. </ButtonContainer>
  124. </EndModal>
  125. );
  126. }
  127. export const modalCss = css`
  128. width: 100%;
  129. max-width: 500px;
  130. [role='document'] {
  131. position: relative;
  132. padding: 50px 60px;
  133. }
  134. `;
  135. const EndModal = styled('div')`
  136. display: flex;
  137. flex-direction: column;
  138. gap: 20px;
  139. align-items: center;
  140. `;
  141. const ModalHeader = styled('div')`
  142. p {
  143. font-size: 16px;
  144. text-align: center;
  145. margin: 0;
  146. }
  147. h2 {
  148. font-size: 2em;
  149. margin: 0;
  150. }
  151. `;
  152. const SignUpButton = styled(Button)`
  153. background-color: ${p => p.theme.purple300};
  154. border: none;
  155. color: ${p => p.theme.white};
  156. width: 100%;
  157. `;
  158. const ButtonBar = styled('div')`
  159. display: flex;
  160. flex-direction: row;
  161. gap: 5px;
  162. justify-content: center;
  163. `;
  164. const ButtonContainer = styled('div')`
  165. display: flex;
  166. flex-direction: column;
  167. gap: 10px;
  168. `;