onboarding.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import {browserHistory} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import * as Sentry from '@sentry/react';
  4. import emptyStateImg from 'sentry-images/spot/performance-empty-state.svg';
  5. import tourAlert from 'sentry-images/spot/performance-tour-alert.svg';
  6. import tourCorrelate from 'sentry-images/spot/performance-tour-correlate.svg';
  7. import tourMetrics from 'sentry-images/spot/performance-tour-metrics.svg';
  8. import tourTrace from 'sentry-images/spot/performance-tour-trace.svg';
  9. import {
  10. addErrorMessage,
  11. addLoadingMessage,
  12. clearIndicators,
  13. } from 'app/actionCreators/indicator';
  14. import {Client} from 'app/api';
  15. import Button from 'app/components/button';
  16. import ButtonBar from 'app/components/buttonBar';
  17. import FeatureTourModal, {
  18. TourImage,
  19. TourStep,
  20. TourText,
  21. } from 'app/components/modals/featureTourModal';
  22. import OnboardingPanel from 'app/components/onboardingPanel';
  23. import {t} from 'app/locale';
  24. import {Organization, Project} from 'app/types';
  25. import {trackAdvancedAnalyticsEvent} from 'app/utils/advancedAnalytics';
  26. import withApi from 'app/utils/withApi';
  27. const performanceSetupUrl =
  28. 'https://docs.sentry.io/performance-monitoring/getting-started/';
  29. const docsLink = (
  30. <Button external href={performanceSetupUrl}>
  31. {t('Setup')}
  32. </Button>
  33. );
  34. export const PERFORMANCE_TOUR_STEPS: TourStep[] = [
  35. {
  36. title: t('Track Application Metrics'),
  37. image: <TourImage src={tourMetrics} />,
  38. body: (
  39. <TourText>
  40. {t(
  41. 'Monitor your slowest pageloads and APIs to see which users are having the worst time.'
  42. )}
  43. </TourText>
  44. ),
  45. actions: docsLink,
  46. },
  47. {
  48. title: t('Correlate Errors and Performance'),
  49. image: <TourImage src={tourCorrelate} />,
  50. body: (
  51. <TourText>
  52. {t(
  53. 'See what errors occurred within a transaction and the impact of those errors.'
  54. )}
  55. </TourText>
  56. ),
  57. actions: docsLink,
  58. },
  59. {
  60. title: t('Watch and Alert'),
  61. image: <TourImage src={tourAlert} />,
  62. body: (
  63. <TourText>
  64. {t(
  65. 'Highlight mission-critical pages and APIs and set latency alerts to notify you before things go wrong.'
  66. )}
  67. </TourText>
  68. ),
  69. actions: docsLink,
  70. },
  71. {
  72. title: t('Trace Across Systems'),
  73. image: <TourImage src={tourTrace} />,
  74. body: (
  75. <TourText>
  76. {t(
  77. "Follow a trace from a user's session and drill down to identify any bottlenecks that occur."
  78. )}
  79. </TourText>
  80. ),
  81. },
  82. ];
  83. type Props = {
  84. organization: Organization;
  85. api: Client;
  86. project: Project;
  87. };
  88. function Onboarding({organization, project, api}: Props) {
  89. function handleAdvance(step: number, duration: number) {
  90. trackAdvancedAnalyticsEvent(
  91. 'performance_views.tour.advance',
  92. {step, duration},
  93. organization
  94. );
  95. }
  96. function handleClose(step: number, duration: number) {
  97. trackAdvancedAnalyticsEvent(
  98. 'performance_views.tour.close',
  99. {step, duration},
  100. organization
  101. );
  102. }
  103. const showSampleTransactionBtn = organization.features.includes(
  104. 'performance-create-sample-transaction'
  105. );
  106. const featureTourBtn = (
  107. <FeatureTourModal
  108. steps={PERFORMANCE_TOUR_STEPS}
  109. onAdvance={handleAdvance}
  110. onCloseModal={handleClose}
  111. doneUrl={performanceSetupUrl}
  112. doneText={t('Start Setup')}
  113. >
  114. {({showModal}) => (
  115. <Button
  116. priority={showSampleTransactionBtn ? 'link' : 'default'}
  117. onClick={() => {
  118. trackAdvancedAnalyticsEvent('performance_views.tour.start', {}, organization);
  119. showModal();
  120. }}
  121. >
  122. {t('Take a Tour')}
  123. </Button>
  124. )}
  125. </FeatureTourModal>
  126. );
  127. const secondaryBtn = showSampleTransactionBtn ? (
  128. <Button
  129. data-test-id="create-sample-transaction-btn"
  130. onClick={async () => {
  131. trackAdvancedAnalyticsEvent(
  132. 'performance_views.create_sample_transaction',
  133. {platform: project.platform},
  134. organization
  135. );
  136. addLoadingMessage(t('Processing sample event...'), {
  137. duration: 15000,
  138. });
  139. const url = `/projects/${organization.slug}/${project.slug}/create-sample-transaction/`;
  140. try {
  141. const eventData = await api.requestPromise(url, {method: 'POST'});
  142. browserHistory.push(
  143. `/organizations/${organization.slug}/performance/${project.slug}:${eventData.eventID}/`
  144. );
  145. clearIndicators();
  146. } catch (error) {
  147. Sentry.withScope(scope => {
  148. scope.setExtra('error', error);
  149. Sentry.captureException(new Error('Failed to create sample event'));
  150. });
  151. clearIndicators();
  152. addErrorMessage(t('Failed to create a new sample event'));
  153. return;
  154. }
  155. }}
  156. >
  157. {t('Create Sample Transaction')}
  158. </Button>
  159. ) : (
  160. featureTourBtn
  161. );
  162. return (
  163. <OnboardingPanel image={<PerfImage src={emptyStateImg} />}>
  164. <h3>{t('Pinpoint problems')}</h3>
  165. <p>
  166. {t(
  167. 'Something seem slow? Track down transactions to connect the dots between 10-second page loads and poor-performing API calls or slow database queries.'
  168. )}
  169. </p>
  170. <ButtonList gap={1}>
  171. <Button
  172. priority="primary"
  173. target="_blank"
  174. href="https://docs.sentry.io/performance-monitoring/getting-started/"
  175. >
  176. {t('Start Setup')}
  177. </Button>
  178. {secondaryBtn}
  179. </ButtonList>
  180. {showSampleTransactionBtn && featureTourBtn}
  181. </OnboardingPanel>
  182. );
  183. }
  184. const PerfImage = styled('img')`
  185. @media (min-width: ${p => p.theme.breakpoints[0]}) {
  186. max-width: unset;
  187. user-select: none;
  188. position: absolute;
  189. top: 75px;
  190. bottom: 0;
  191. width: 450px;
  192. margin-top: auto;
  193. margin-bottom: auto;
  194. }
  195. @media (min-width: ${p => p.theme.breakpoints[1]}) {
  196. width: 480px;
  197. }
  198. @media (min-width: ${p => p.theme.breakpoints[2]}) {
  199. width: 600px;
  200. }
  201. `;
  202. const ButtonList = styled(ButtonBar)`
  203. grid-template-columns: repeat(auto-fit, minmax(130px, max-content));
  204. margin-bottom: 16px;
  205. `;
  206. export default withApi(Onboarding);