legacyPlanToggle.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {useCallback, useEffect, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import * as Sentry from '@sentry/react';
  4. import type {Client} from 'sentry/api';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {Tooltip} from 'sentry/components/tooltip';
  7. import {IconInfo} from 'sentry/icons';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {Organization} from 'sentry/types/organization';
  11. import withApi from 'sentry/utils/withApi';
  12. import withOrganization from 'sentry/utils/withOrganization';
  13. import withSubscription from 'getsentry/components/withSubscription';
  14. import {DEFAULT_TIER, SUPPORTED_TIERS} from 'getsentry/constants';
  15. import type {PlanMigration, Subscription} from 'getsentry/types';
  16. import {PlanTier} from 'getsentry/types';
  17. const PREVIOUS_DESCRIPTION = t(
  18. `You still have access to the previous tier of Sentry plans. However, you'll be missing
  19. out on features only available on the latest plans.`
  20. );
  21. const LATEST_DESCRIPTION = t(
  22. `You are viewing the previous Sentry plans. Click to see the current tier of
  23. Sentry plans.`
  24. );
  25. type Props = {
  26. api: Client;
  27. onClick: () => void;
  28. organization: Organization;
  29. subscription: Subscription;
  30. /**
  31. * The currently selected planTier of the checkout page.
  32. */
  33. checkoutTier?: string;
  34. };
  35. function LegacyPlanToggle({
  36. onClick,
  37. organization,
  38. subscription,
  39. checkoutTier,
  40. api,
  41. }: Props) {
  42. const [isLoading, setIsLoading] = useState(false);
  43. const [error, setError] = useState<undefined | Error | string>(undefined);
  44. const [planMigrations, setPlanMigrations] = useState<PlanMigration[]>([]);
  45. const fetchPlanMigrations = useCallback(async () => {
  46. if (subscription.planTier === DEFAULT_TIER) {
  47. return;
  48. }
  49. setIsLoading(true);
  50. setError(undefined);
  51. try {
  52. const response = await api.requestPromise(
  53. `/customers/${organization.slug}/plan-migrations/?applied=0`
  54. );
  55. setPlanMigrations(response);
  56. setIsLoading(false);
  57. } catch (err) {
  58. setError(err);
  59. Sentry.captureException(err);
  60. setIsLoading(false);
  61. }
  62. }, [api, organization, subscription.planTier]);
  63. useEffect(() => void fetchPlanMigrations(), [fetchPlanMigrations]);
  64. if (error) {
  65. return null;
  66. }
  67. if (isLoading) {
  68. return <LoadingIndicator mini />;
  69. }
  70. const canToggleLegacy = subscription.planTier === PlanTier.AM1;
  71. /**
  72. * Show the legacy plan toggle if:
  73. * 1. The subscription is paid
  74. * 2. The subscription is on the AM1 tier
  75. * 3. The subscription has no active plan migration
  76. */
  77. const showToggle =
  78. canToggleLegacy &&
  79. subscription.planDetails?.basePrice > 0 &&
  80. planMigrations?.length === 0;
  81. if (!showToggle) {
  82. return null;
  83. }
  84. // We only allow AM1 customers to toggle, and only between AM1 and AM2
  85. const onLatestPlans = checkoutTier === SUPPORTED_TIERS[1];
  86. return (
  87. <ToggleWrapper>
  88. <ToggleLink role="button" onClick={onClick} data-test-id="legacy-tier-toggle">
  89. {onLatestPlans ? t('Show previous plans') : t('Show latest plans')}
  90. </ToggleLink>
  91. <Tooltip title={onLatestPlans ? PREVIOUS_DESCRIPTION : LATEST_DESCRIPTION}>
  92. <IconWrapper>
  93. <IconInfo size="xs" />
  94. </IconWrapper>
  95. </Tooltip>
  96. </ToggleWrapper>
  97. );
  98. }
  99. const ToggleWrapper = styled('div')`
  100. display: flex;
  101. align-items: center;
  102. font-weight: normal;
  103. text-transform: none;
  104. `;
  105. const ToggleLink = styled('a')`
  106. margin-right: ${space(0.5)};
  107. color: ${p => p.theme.subText};
  108. font-size: ${p => p.theme.fontSizeMedium};
  109. &:active,
  110. &:focus,
  111. &:hover {
  112. color: ${p => p.theme.subText};
  113. }
  114. `;
  115. const IconWrapper = styled('div')`
  116. display: grid;
  117. align-items: center;
  118. color: ${p => p.theme.subText};
  119. `;
  120. export default withApi(withOrganization(withSubscription(LegacyPlanToggle)));