cronsLandingPanel.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import {useState} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import onboardingImg from 'sentry-images/spot/onboarding-preview.svg';
  5. import {Button, LinkButton} from 'sentry/components/button';
  6. import ButtonBar from 'sentry/components/buttonBar';
  7. import OnboardingPanel from 'sentry/components/onboardingPanel';
  8. import Panel from 'sentry/components/panels/panel';
  9. import PanelBody from 'sentry/components/panels/panelBody';
  10. import {TabList, TabPanels, Tabs} from 'sentry/components/tabs';
  11. import {IconChevron} from 'sentry/icons';
  12. import {t} from 'sentry/locale';
  13. import {space} from 'sentry/styles/space';
  14. import type {PlatformKey} from 'sentry/types';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  17. import MonitorForm from 'sentry/views/monitors/components/monitorForm';
  18. import {Monitor} from 'sentry/views/monitors/types';
  19. import {NewMonitorButton} from './newMonitorButton';
  20. import {
  21. CRON_SDK_PLATFORMS,
  22. PlatformPickerPanel,
  23. SupportedPlatform,
  24. } from './platformPickerPanel';
  25. import {
  26. CeleryBeatAutoDiscovery,
  27. GoUpsertPlatformGuide,
  28. LaravelUpsertPlatformGuide,
  29. NodeJsUpsertPlatformGuide,
  30. PHPUpsertPlatformGuide,
  31. QuickStartProps,
  32. } from './quickStartEntries';
  33. interface PlatformGuide {
  34. Guide: React.ComponentType<QuickStartProps>;
  35. title: string;
  36. }
  37. const platformGuides: Record<SupportedPlatform, PlatformGuide[]> = {
  38. 'python-celery': [
  39. {
  40. Guide: CeleryBeatAutoDiscovery,
  41. title: 'Beat Auto Discovery',
  42. },
  43. ],
  44. php: [
  45. {
  46. Guide: PHPUpsertPlatformGuide,
  47. title: 'Upsert',
  48. },
  49. ],
  50. 'php-laravel': [
  51. {
  52. Guide: LaravelUpsertPlatformGuide,
  53. title: 'Upsert',
  54. },
  55. ],
  56. python: [],
  57. node: [
  58. {
  59. Guide: NodeJsUpsertPlatformGuide,
  60. title: 'Upsert',
  61. },
  62. ],
  63. go: [
  64. {
  65. Guide: GoUpsertPlatformGuide,
  66. title: 'Upsert',
  67. },
  68. ],
  69. };
  70. export function CronsLandingPanel() {
  71. const organization = useOrganization();
  72. const [platform, setPlatform] = useState<PlatformKey | null>(null);
  73. if (!platform) {
  74. return <PlatformPickerPanel onSelect={setPlatform} />;
  75. }
  76. const platformText = CRON_SDK_PLATFORMS.find(
  77. ({platform: sdkPlatform}) => sdkPlatform === platform
  78. )?.label;
  79. const guides = platformGuides[platform];
  80. function onCreateMonitor(data: Monitor) {
  81. const url = normalizeUrl(`/organizations/${organization.slug}/crons/${data.slug}/`);
  82. browserHistory.push(url);
  83. }
  84. return (
  85. <Panel>
  86. <BackButton
  87. icon={<IconChevron size="sm" direction="left" />}
  88. onClick={() => setPlatform(null)}
  89. borderless
  90. >
  91. {t('Back to Platforms')}
  92. </BackButton>
  93. <PanelBody withPadding>
  94. <h3>{t('Get Started with %s', platformText)}</h3>
  95. <Tabs>
  96. <TabList>
  97. {[
  98. ...guides.map(({title}) => (
  99. <TabList.Item key={title}>{title}</TabList.Item>
  100. )),
  101. <TabList.Item key="manual">{t('Manual')}</TabList.Item>,
  102. ]}
  103. </TabList>
  104. <TabPanels>
  105. {[
  106. ...guides.map(({title, Guide}) => (
  107. <TabPanels.Item key={title}>
  108. <GuideContainer>
  109. <Guide />
  110. </GuideContainer>
  111. </TabPanels.Item>
  112. )),
  113. <TabPanels.Item key="manual">
  114. <GuideContainer>
  115. <MonitorForm
  116. apiMethod="POST"
  117. apiEndpoint={`/organizations/${organization.slug}/monitors/`}
  118. onSubmitSuccess={onCreateMonitor}
  119. submitLabel={t('Next')}
  120. />
  121. </GuideContainer>
  122. </TabPanels.Item>,
  123. ]}
  124. </TabPanels>
  125. </Tabs>
  126. </PanelBody>
  127. </Panel>
  128. );
  129. }
  130. const BackButton = styled(Button)`
  131. font-weight: normal;
  132. color: ${p => p.theme.subText};
  133. margin: ${space(1)} 0 0 ${space(1)};
  134. padding-left: ${space(0.5)};
  135. padding-right: ${space(0.5)};
  136. `;
  137. const GuideContainer = styled('div')`
  138. display: flex;
  139. flex-direction: column;
  140. gap: ${space(2)};
  141. padding-top: ${space(2)};
  142. `;
  143. export function OldCronsLandingPanel() {
  144. return (
  145. <OnboardingPanel image={<img src={onboardingImg} />}>
  146. <h3>{t('Let Sentry monitor your recurring jobs')}</h3>
  147. <p>
  148. {t(
  149. "We'll tell you if your recurring jobs are running on schedule, failing, or succeeding."
  150. )}
  151. </p>
  152. <OnboardingActions gap={1}>
  153. <NewMonitorButton>{t('Set up first cron monitor')}</NewMonitorButton>
  154. <LinkButton href="https://docs.sentry.io/product/crons" external>
  155. {t('Read docs')}
  156. </LinkButton>
  157. </OnboardingActions>
  158. </OnboardingPanel>
  159. );
  160. }
  161. const OnboardingActions = styled(ButtonBar)`
  162. grid-template-columns: repeat(auto-fit, minmax(130px, max-content));
  163. `;