actionButtons.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import {useCallback} from 'react';
  2. import styled from '@emotion/styled';
  3. import * as Sentry from '@sentry/react';
  4. import {addSuccessMessage} from 'sentry/actionCreators/indicator';
  5. import {closeModal} from 'sentry/actionCreators/modal';
  6. import {Button} from 'sentry/components/button';
  7. import {SidebarPanelKey} from 'sentry/components/sidebar/types';
  8. import {t} from 'sentry/locale';
  9. import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
  10. import {space} from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types/organization';
  12. import useApi from 'sentry/utils/useApi';
  13. import {sendReplayOnboardRequest} from 'getsentry/actionCreators/upsell';
  14. import SubscriptionStore from 'getsentry/stores/subscriptionStore';
  15. import type {Plan, PreviewData, Subscription} from 'getsentry/types';
  16. import {PlanTier} from 'getsentry/types';
  17. import type {AM2UpdateSurfaces} from 'getsentry/utils/trackGetsentryAnalytics';
  18. import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics';
  19. import type {Reservations} from './types';
  20. import {redirectToManage} from './utils';
  21. type Props = {
  22. organization: Organization;
  23. plan: Plan;
  24. previewData: PreviewData;
  25. reservations: Reservations;
  26. subscription: Subscription;
  27. surface: AM2UpdateSurfaces;
  28. isActionDisabled?: boolean;
  29. onComplete?: () => void;
  30. };
  31. function ActionButtons({
  32. isActionDisabled,
  33. onComplete,
  34. organization,
  35. plan,
  36. previewData,
  37. reservations,
  38. subscription,
  39. surface,
  40. }: Props) {
  41. const api = useApi();
  42. const onUpdatePlan = useCallback(async () => {
  43. try {
  44. await api.requestPromise(`/customers/${organization.slug}/subscription/`, {
  45. method: 'PUT',
  46. data: {
  47. ...reservations,
  48. plan: plan?.id,
  49. referrer: 'replay-am2-update-modal',
  50. },
  51. });
  52. SubscriptionStore.loadData(organization.slug, () => {
  53. if (onComplete) {
  54. onComplete();
  55. }
  56. closeModal();
  57. addSuccessMessage(t('Subscription Updated!'));
  58. window.location.hash = 'replay-sidequest';
  59. SidebarPanelStore.activatePanel(SidebarPanelKey.REPLAYS_ONBOARDING);
  60. trackGetsentryAnalytics('upgrade_now.modal.update_now', {
  61. organization,
  62. planTier: subscription.planTier,
  63. canSelfServe: subscription.canSelfServe,
  64. channel: subscription.channel,
  65. has_billing_scope: organization.access?.includes('org:billing'),
  66. surface,
  67. has_price_change: previewData.billedAmount !== 0,
  68. });
  69. });
  70. } catch (err) {
  71. Sentry.captureException(err);
  72. redirectToManage(organization);
  73. }
  74. }, [
  75. api,
  76. onComplete,
  77. organization,
  78. plan,
  79. previewData.billedAmount,
  80. reservations,
  81. subscription,
  82. surface,
  83. ]);
  84. const onEmailOwner = useCallback(async () => {
  85. const currentPlanName =
  86. subscription.planTier === PlanTier.AM2 ? 'am2-non-beta' : 'am1-non-beta';
  87. await sendReplayOnboardRequest({
  88. api,
  89. orgSlug: organization.slug,
  90. currentPlan: currentPlanName,
  91. onSuccess: () => {
  92. onComplete?.();
  93. closeModal();
  94. trackGetsentryAnalytics('upgrade_now.modal.sent_email', {
  95. organization,
  96. surface,
  97. planTier: subscription.planTier,
  98. canSelfServe: subscription.canSelfServe,
  99. channel: subscription.channel,
  100. has_billing_scope: organization.access?.includes('org:billing'),
  101. });
  102. },
  103. onError: () => {
  104. redirectToManage(organization);
  105. },
  106. });
  107. }, [api, organization, subscription, surface, onComplete]);
  108. const onClickManageSubscription = useCallback(() => {
  109. trackGetsentryAnalytics('upgrade_now.modal.manage_sub', {
  110. organization,
  111. surface,
  112. planTier: subscription.planTier,
  113. canSelfServe: subscription.canSelfServe,
  114. channel: subscription.channel,
  115. has_billing_scope: organization.access?.includes('org:billing'),
  116. });
  117. }, [organization, subscription, surface]);
  118. const hasBillingAccess = organization.access?.includes('org:billing');
  119. return hasBillingAccess ? (
  120. <ButtonRow>
  121. <Button
  122. priority="primary"
  123. onClick={onUpdatePlan}
  124. disabled={isActionDisabled === true}
  125. >
  126. {t('Update Now')}
  127. </Button>
  128. <Button
  129. to={`/settings/${organization.slug}/billing/checkout/?referrer=replay_onboard_modal-owner-modal`}
  130. onClick={onClickManageSubscription}
  131. >
  132. {t('Manage Subscription')}
  133. </Button>
  134. </ButtonRow>
  135. ) : (
  136. <ButtonRow>
  137. <Button
  138. priority="primary"
  139. title={t('Notify an owner by email to update to the latest version of your plan')}
  140. onClick={onEmailOwner}
  141. disabled={isActionDisabled === true}
  142. >
  143. {t('Request to Update Plan')}
  144. </Button>
  145. <Button
  146. disabled
  147. title={t(
  148. 'Only members with the role “Owner” or “Billing” can manage subscriptions'
  149. )}
  150. >
  151. {t('Manage Subscription')}
  152. </Button>
  153. </ButtonRow>
  154. );
  155. }
  156. const ButtonRow = styled('p')`
  157. display: flex;
  158. gap: ${space(1.5)};
  159. margin-top: ${space(3)};
  160. margin-bottom: ${space(2)};
  161. `;
  162. export default ActionButtons;