diff --git a/static/gsApp/components/billingDetails/panel.tsx b/static/gsApp/components/billingDetails/panel.tsx index b286eff72db022..ca5c40692a486e 100644 --- a/static/gsApp/components/billingDetails/panel.tsx +++ b/static/gsApp/components/billingDetails/panel.tsx @@ -170,6 +170,7 @@ function BillingDetailsPanel({ background="primary" border="primary" radius="md" + data-test-id="billing-details-panel" > diff --git a/static/gsApp/components/creditCardEdit/panel.tsx b/static/gsApp/components/creditCardEdit/panel.tsx index ce7c1ced48c724..0733251f391717 100644 --- a/static/gsApp/components/creditCardEdit/panel.tsx +++ b/static/gsApp/components/creditCardEdit/panel.tsx @@ -157,6 +157,7 @@ function CreditCardPanel({ background="primary" border="primary" radius="md" + data-test-id="credit-card-panel" > diff --git a/static/gsApp/components/partnerPlanEndingBanner.tsx b/static/gsApp/components/partnerPlanEndingBanner.tsx index 073dbe98d3bf74..945e81276a1f6e 100644 --- a/static/gsApp/components/partnerPlanEndingBanner.tsx +++ b/static/gsApp/components/partnerPlanEndingBanner.tsx @@ -3,6 +3,7 @@ import PartnerPlanEndingBackground from 'getsentry-images/partnership/plan-endin import {Tag} from 'sentry/components/core/badge/tag'; import {LinkButton} from 'sentry/components/core/button/linkButton'; +import {Flex} from 'sentry/components/core/layout'; import {IconClock} from 'sentry/icons'; import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; @@ -52,7 +53,14 @@ function PartnerPlanEndingBanner({ : 'Business'; return ( - + @@ -80,20 +88,10 @@ function PartnerPlanEndingBanner({ - + ); } -const PartnerPlanEndingBannerWrapper = styled('div')` - border: 1px solid ${p => p.theme.border}; - border-radius: ${p => p.theme.borderRadius}; - background: ${p => p.theme.background}; - margin-bottom: ${space(2)}; - display: flex; - justify-content: space-between; - align-items: center; -`; - const PartnerPlanEndingText = styled('div')` padding: ${space(2)}; display: flex; diff --git a/static/gsApp/views/subscriptionPage/billingInformation.spec.tsx b/static/gsApp/views/subscriptionPage/billingInformation.spec.tsx index fef2da4bb06e31..c5f6ee99e7a9e5 100644 --- a/static/gsApp/views/subscriptionPage/billingInformation.spec.tsx +++ b/static/gsApp/views/subscriptionPage/billingInformation.spec.tsx @@ -125,25 +125,47 @@ describe('Subscription > BillingInformation', () => { await screen.findByText('Billing Information'); // panels are collapsed with pre-existing information - expect(screen.getByText(/\*\*\*\*4242/)).toBeInTheDocument(); - expect(screen.getByRole('button', {name: 'Edit payment method'})).toBeInTheDocument(); - expect(screen.getByText('Business address')).toBeInTheDocument(); + const cardPanel = await screen.findByTestId('credit-card-panel'); + expect(within(cardPanel).getByText(/\*\*\*\*4242/)).toBeInTheDocument(); expect( - screen.getByRole('button', {name: 'Edit business address'}) + within(cardPanel).getByRole('button', {name: 'Edit payment method'}) ).toBeInTheDocument(); - expect(screen.queryByText('Address Line 1')).not.toBeInTheDocument(); - expect(screen.queryByRole('button', {name: 'Save Changes'})).not.toBeInTheDocument(); + expect( + within(cardPanel).queryByRole('button', {name: 'Save Changes'}) + ).not.toBeInTheDocument(); + + const billingDetailsPanel = await screen.findByTestId('billing-details-panel'); + expect(within(billingDetailsPanel).getByText('Business address')).toBeInTheDocument(); + expect( + within(billingDetailsPanel).getByRole('button', {name: 'Edit business address'}) + ).toBeInTheDocument(); + expect( + within(billingDetailsPanel).queryByText('Address Line 1') + ).not.toBeInTheDocument(); + expect( + within(billingDetailsPanel).queryByRole('button', {name: 'Save Changes'}) + ).not.toBeInTheDocument(); // can edit both - await userEvent.click(screen.getByRole('button', {name: 'Edit payment method'})); + await userEvent.click( + within(cardPanel).getByRole('button', {name: 'Edit payment method'}) + ); expect( - screen.queryByRole('button', {name: 'Edit payment method'}) + within(cardPanel).queryByRole('button', {name: 'Edit payment method'}) ).not.toBeInTheDocument(); - await userEvent.click(screen.getByRole('button', {name: 'Edit business address'})); expect( - screen.queryByRole('button', {name: 'Edit business address'}) + within(cardPanel).getByRole('button', {name: 'Save Changes'}) + ).toBeInTheDocument(); + + await userEvent.click( + within(billingDetailsPanel).getByRole('button', {name: 'Edit business address'}) + ); + expect( + within(billingDetailsPanel).queryByRole('button', {name: 'Edit business address'}) ).not.toBeInTheDocument(); - expect(screen.getAllByRole('button', {name: 'Save Changes'})).toHaveLength(2); + expect( + within(billingDetailsPanel).getByRole('button', {name: 'Save Changes'}) + ).toBeInTheDocument(); }); it('renders with no pre-existing information for new billing UI', async () => { @@ -162,16 +184,23 @@ describe('Subscription > BillingInformation', () => { await screen.findByText('Billing Information'); // panels are expanded with no pre-existing information - await waitFor(() => { - // wait for both panels to be expanded - expect(screen.getAllByRole('button', {name: 'Save Changes'})).toHaveLength(2); - }); + const cardPanel = await screen.findByTestId('credit-card-panel'); + expect(cardPanel).toBeInTheDocument(); expect( - screen.queryByRole('button', {name: 'Edit payment method'}) + within(cardPanel).queryByRole('button', {name: 'Edit payment method'}) ).not.toBeInTheDocument(); expect( - screen.queryByRole('button', {name: 'Edit business address'}) + within(cardPanel).getByRole('button', {name: 'Save Changes'}) + ).toBeInTheDocument(); + + const billingDetailsPanel = await screen.findByTestId('billing-details-panel'); + expect(billingDetailsPanel).toBeInTheDocument(); + expect( + within(billingDetailsPanel).queryByRole('button', {name: 'Edit business address'}) ).not.toBeInTheDocument(); + expect( + within(billingDetailsPanel).getByRole('button', {name: 'Save Changes'}) + ).toBeInTheDocument(); }); it('opens credit card form with billing failure query for new billing UI', async () => { diff --git a/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.spec.tsx b/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.spec.tsx new file mode 100644 index 00000000000000..ce7642de984ac4 --- /dev/null +++ b/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.spec.tsx @@ -0,0 +1,67 @@ +import {OrganizationFixture} from 'sentry-fixture/organization'; + +import {BillingDetailsFixture} from 'getsentry-test/fixtures/billingDetails'; +import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription'; +import {render, screen} from 'sentry-test/reactTestingLibrary'; + +import BillingInfoCard from 'getsentry/views/subscriptionPage/headerCards/billingInfoCard'; + +describe('BillingInfoCard', () => { + const organization = OrganizationFixture({access: ['org:billing']}); + + beforeEach(() => { + MockApiClient.clearMockResponses(); + MockApiClient.addMockResponse({ + url: `/customers/${organization.slug}/billing-details/`, + method: 'GET', + }); + }); + + it('renders with pre-existing info', async () => { + MockApiClient.addMockResponse({ + url: `/customers/${organization.slug}/billing-details/`, + method: 'GET', + body: BillingDetailsFixture(), + }); + const subscription = SubscriptionFixture({organization}); + render(); + + expect(screen.getByText('Billing information')).toBeInTheDocument(); + await screen.findByText('Test company'); + expect(screen.getByText('Card ending in 4242')).toBeInTheDocument(); + }); + + it('renders without pre-existing info', async () => { + const subscription = SubscriptionFixture({organization, paymentSource: null}); + render(); + + expect(screen.getByText('Billing information')).toBeInTheDocument(); + await screen.findByText('No billing details on file'); + expect(screen.getByText('No payment method on file')).toBeInTheDocument(); + }); + + it('does not render for self-serve partner customers', () => { + const subscription = SubscriptionFixture({organization, isSelfServePartner: true}); + render(); + + expect(screen.queryByText('Billing information')).not.toBeInTheDocument(); + }); + + it('does not render for managed customers', () => { + const subscription = SubscriptionFixture({organization, canSelfServe: false}); + render(); + + expect(screen.queryByText('Billing information')).not.toBeInTheDocument(); + }); + + it('renders for managed customers with legacy invoiced OD', () => { + const subscription = SubscriptionFixture({ + organization, + canSelfServe: false, + onDemandInvoiced: true, + }); + render(); + + expect(screen.getByText('Billing information')).toBeInTheDocument(); + }); +}); diff --git a/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.tsx b/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.tsx new file mode 100644 index 00000000000000..6fdf29d5e91029 --- /dev/null +++ b/static/gsApp/views/subscriptionPage/headerCards/billingInfoCard.tsx @@ -0,0 +1,114 @@ +import moment from 'moment-timezone'; + +import {Container, Flex} from 'sentry/components/core/layout'; +import {Text} from 'sentry/components/core/text'; +import Placeholder from 'sentry/components/placeholder'; +import {IconSettings, IconUser} from 'sentry/icons'; +import {t, tct} from 'sentry/locale'; +import type {Organization} from 'sentry/types/organization'; + +import {useBillingDetails} from 'getsentry/hooks/useBillingDetails'; +import type {Subscription} from 'getsentry/types'; +import {hasSomeBillingDetails} from 'getsentry/utils/billing'; +import SubscriptionHeaderCard from 'getsentry/views/subscriptionPage/headerCards/subscriptionHeaderCard'; + +function BillingInfoCard({ + subscription, + organization, +}: { + organization: Organization; + subscription: Subscription; +}) { + if ( + subscription.isSelfServePartner || + (!subscription.canSelfServe && !subscription.onDemandInvoiced) + ) { + return null; + } + + return ( + } + sections={[ + , + , + ]} + button={{ + ariaLabel: t('Edit billing information'), + label: t('Edit billing information'), + linkTo: `/settings/${organization.slug}/billing/details/`, + icon: , + priority: 'default', + }} + /> + ); +} + +function BillingDetailsInfo() { + const {data: billingDetails, isLoading} = useBillingDetails(); + + if (isLoading) { + return ( + + + + + + + ); + } + + if (!billingDetails || !hasSomeBillingDetails(billingDetails)) { + return ( + + {t('No billing details on file')} + + ); + } + + return ( + + {billingDetails.companyName && ( + + {billingDetails.companyName} + + )} + {billingDetails.billingEmail && ( + + {billingDetails.billingEmail} + + )} + {billingDetails.displayAddress && ( + + {billingDetails.displayAddress} + + )} + + ); +} + +function PaymentSourceInfo({subscription}: {subscription: Subscription}) { + const {paymentSource} = subscription; + const paymentSourceExpiryDate = paymentSource + ? moment(new Date(paymentSource.expYear, paymentSource.expMonth - 1)) + : null; + + if (!paymentSource) { + return {t('No payment method on file')}; + } + + return ( + + {tct('Card ending in [last4]', {last4: paymentSource.last4})} + + {tct('Expires [expMonth]/[expYear]', { + expMonth: paymentSourceExpiryDate?.format('MM'), + expYear: paymentSourceExpiryDate?.format('YY'), + })} + + + ); +} + +export default BillingInfoCard; diff --git a/static/gsApp/views/subscriptionPage/headerCards/headerCards.tsx b/static/gsApp/views/subscriptionPage/headerCards/headerCards.tsx index b745d9dde13702..4bcb18bc993954 100644 --- a/static/gsApp/views/subscriptionPage/headerCards/headerCards.tsx +++ b/static/gsApp/views/subscriptionPage/headerCards/headerCards.tsx @@ -1,13 +1,10 @@ -import {css} from '@emotion/react'; -import styled from '@emotion/styled'; - +import {Container, Grid} from 'sentry/components/core/layout'; import ErrorBoundary from 'sentry/components/errorBoundary'; -import Panel from 'sentry/components/panels/panel'; -import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import type {Subscription} from 'getsentry/types'; import {hasNewBillingUI} from 'getsentry/utils/billing'; +import BillingInfoCard from 'getsentry/views/subscriptionPage/headerCards/billingInfoCard'; import SeerAutomationAlert from 'getsentry/views/subscriptionPage/seerAutomationAlert'; import {SubscriptionCard} from './subscriptionCard'; @@ -18,29 +15,61 @@ interface HeaderCardsProps { subscription: Subscription; } -export function HeaderCards({organization, subscription}: HeaderCardsProps) { +function getCards(organization: Organization, subscription: Subscription) { + const cards: React.ReactNode[] = []; + + cards.push( + + + + ); + + if (subscription.canSelfServe || subscription.onDemandInvoiced) { + cards.push( + + ); + } + + return cards; +} + +function HeaderCards({organization, subscription}: HeaderCardsProps) { + const isNewBillingUI = hasNewBillingUI(organization); + + const cards = getCards(organization, subscription); + return ( - - - - + {isNewBillingUI ? ( + + {cards} + + ) : ( + + + + + )} ); } -// TODO(checkout v3): update this with the real layout -const HeaderCardWrapper = styled(Panel)<{hasNewCheckout: boolean}>` - display: grid; - margin-bottom: ${space(2)}; - - ${p => - !p.hasNewCheckout && - css` - @media (min-width: ${p.theme.breakpoints.lg}) { - grid-template-columns: auto minmax(0, 600px); - gap: ${space(2)}; - } - `} -`; +export default HeaderCards; diff --git a/static/gsApp/views/subscriptionPage/headerCards/subscriptionCard.tsx b/static/gsApp/views/subscriptionPage/headerCards/subscriptionCard.tsx index eb04a90a95117f..092be263feb107 100644 --- a/static/gsApp/views/subscriptionPage/headerCards/subscriptionCard.tsx +++ b/static/gsApp/views/subscriptionPage/headerCards/subscriptionCard.tsx @@ -7,21 +7,14 @@ import moment from 'moment-timezone'; import {Tag} from 'sentry/components/core/badge/tag'; import {LinkButton} from 'sentry/components/core/button/linkButton'; -import {IconEdit, IconGrid} from 'sentry/icons'; -import {t, tct} from 'sentry/locale'; +import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import {ANNUAL} from 'getsentry/constants'; import type {Subscription} from 'getsentry/types'; -import { - hasNewBillingUI, - isDeveloperPlan, - isEnterprise, - isTeamPlan, -} from 'getsentry/utils/billing'; +import {isDeveloperPlan, isEnterprise, isTeamPlan} from 'getsentry/utils/billing'; import formatCurrency from 'getsentry/utils/formatCurrency'; -import SubscriptionHeaderCard from 'getsentry/views/subscriptionPage/headerCards/subscriptionHeaderCard'; import {shouldSeeSpendVisibility} from 'getsentry/views/subscriptionPage/utils'; interface SubscriptionCardProps { @@ -88,31 +81,6 @@ export function SubscriptionCard({subscription, organization}: SubscriptionCardP const hasBillingPerms = organization.access?.includes('org:billing'); - const hasNewCheckout = hasNewBillingUI(organization); - - if (hasNewCheckout) { - // TODO(checkout v3): update this with the real layout, this is just a placeholder for knip - return ( - } - subtitle={tct('[startDate] - [endDate]', { - startDate: moment(subscription.contractPeriodStart).format('MMM D, YYYY'), - endDate: moment(subscription.contractPeriodEnd).format('MMM D, YYYY'), - })} - sections={[ - {t('%s Plan', subscription.planDetails?.name)}, - ]} - button={{ - ariaLabel: t('Edit plan'), - label: t('Edit plan'), - linkTo: `/settings/${organization.slug}/billing/checkout/?referrer=edit_plan`, - icon: , - }} - /> - ); - } - return ( diff --git a/static/gsApp/views/subscriptionPage/headerCards/subscriptionHeaderCard.tsx b/static/gsApp/views/subscriptionPage/headerCards/subscriptionHeaderCard.tsx index d21b1d7cb3e1bd..c2b3e9df46e067 100644 --- a/static/gsApp/views/subscriptionPage/headerCards/subscriptionHeaderCard.tsx +++ b/static/gsApp/views/subscriptionPage/headerCards/subscriptionHeaderCard.tsx @@ -10,6 +10,7 @@ import type {SVGIconProps} from 'sentry/icons/svgIcon'; interface ButtonInfo { ariaLabel: string; label: React.ReactNode; + priority: 'primary' | 'default'; icon?: React.ReactNode; linkTo?: string; onClick?: () => void; @@ -42,9 +43,7 @@ function SubscriptionHeaderCard({ {isValidElement(button.icon) ? cloneElement(button.icon, {size: 'sm'} as SVGIconProps) : null} - - {button.label} - + {button.label} ); }; @@ -68,7 +67,7 @@ function SubscriptionHeaderCard({ )} {subtitle && {subtitle}} - + {sections.map((section, index) => { const isLast = index === sections.length - 1; return ( @@ -81,11 +80,19 @@ function SubscriptionHeaderCard({ {button && (button.linkTo ? ( - + {getButtonContent()} ) : ( - + {getButtonContent()} ))} diff --git a/static/gsApp/views/subscriptionPage/managedNote.tsx b/static/gsApp/views/subscriptionPage/managedNote.tsx index 94b6669b981196..2b60cc340e90cc 100644 --- a/static/gsApp/views/subscriptionPage/managedNote.tsx +++ b/static/gsApp/views/subscriptionPage/managedNote.tsx @@ -1,4 +1,4 @@ -import Panel from 'sentry/components/panels/panel'; +import {Container} from 'sentry/components/core/layout'; import PanelBody from 'sentry/components/panels/panelBody'; import {tct} from 'sentry/locale'; import TextBlock from 'sentry/views/settings/components/text/textBlock'; @@ -83,7 +83,12 @@ function ManagedNote({subscription}: Props) { (subscription.customPrice !== null && subscription.customPrice > 0); return ( - + {isSalesAccount @@ -96,7 +101,7 @@ function ManagedNote({subscription}: Props) { DEFAULT_MESSAGE)} - + ); } diff --git a/static/gsApp/views/subscriptionPage/pendingChanges.tsx b/static/gsApp/views/subscriptionPage/pendingChanges.tsx index 1b451b3632f2c8..93713fcb19f767 100644 --- a/static/gsApp/views/subscriptionPage/pendingChanges.tsx +++ b/static/gsApp/views/subscriptionPage/pendingChanges.tsx @@ -397,26 +397,24 @@ class PendingChanges extends Component { } return ( - - - - {Object.entries(changes).map(([effectiveDate, items]) => ( - - {tct('The following changes will take effect on [date]:', { - date: {moment(effectiveDate).format('ll')}, - })} - - {items.map((item, itemIdx) => ( - - {item} - - ))} - - - ))} - - - + + + {Object.entries(changes).map(([effectiveDate, items]) => ( + + {tct('The following changes will take effect on [date]:', { + date: {moment(effectiveDate).format('ll')}, + })} + + {items.map((item, itemIdx) => ( + + {item} + + ))} + + + ))} + + ); } } diff --git a/static/gsApp/views/subscriptionPage/subscriptionHeader.spec.tsx b/static/gsApp/views/subscriptionPage/subscriptionHeader.spec.tsx index 55000bba30f145..5945ae4aa5c441 100644 --- a/static/gsApp/views/subscriptionPage/subscriptionHeader.spec.tsx +++ b/static/gsApp/views/subscriptionPage/subscriptionHeader.spec.tsx @@ -19,6 +19,10 @@ describe('SubscriptionHeader', () => { method: 'GET', body: BillingConfigFixture(PlanTier.AM1), }); + MockApiClient.addMockResponse({ + url: `/customers/org-slug/billing-details/`, + method: 'GET', + }); MockApiClient.addMockResponse({ url: `/subscriptions/org-slug/`, method: 'GET', @@ -40,6 +44,107 @@ describe('SubscriptionHeader', () => { }); }); + async function assertNewHeaderCards({ + hasBillingInfoCard, + }: { + hasBillingInfoCard: boolean; + }) { + await screen.findByRole('heading', {name: 'Subscription'}); + + if (hasBillingInfoCard) { + await screen.findByText('Billing information'); + screen.getByRole('button', {name: 'Edit billing information'}); + } else { + expect(screen.queryByText('Billing information')).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', {name: 'Edit billing information'}) + ).not.toBeInTheDocument(); + } + } + + it('renders new header cards for self-serve customers', () => { + const organization = OrganizationFixture({ + features: ['subscriptions-v3'], + access: ['org:billing'], + }); + const subscription = SubscriptionFixture({ + organization, + plan: 'am3_f', + }); + SubscriptionStore.set(organization.slug, subscription); + render( + + ); + assertNewHeaderCards({hasBillingInfoCard: true}); + }); + + it('renders new header cards for self-serve partner customers', () => { + const organization = OrganizationFixture({ + features: ['subscriptions-v3'], + access: ['org:billing'], + }); + const subscription = SubscriptionFixture({ + organization, + plan: 'am3_f', + isSelfServePartner: true, + }); + SubscriptionStore.set(organization.slug, subscription); + render( + + ); + assertNewHeaderCards({hasBillingInfoCard: false}); + }); + + it('renders new header cards for managed customers', () => { + const organization = OrganizationFixture({ + features: ['subscriptions-v3'], + access: ['org:billing'], + }); + const subscription = SubscriptionFixture({ + organization, + plan: 'am3_f', + canSelfServe: false, + }); + SubscriptionStore.set(organization.slug, subscription); + render( + + ); + assertNewHeaderCards({hasBillingInfoCard: false}); + }); + + it('renders new header cards for managed customers with legacy invoiced OD', () => { + const organization = OrganizationFixture({ + features: ['subscriptions-v3'], + access: ['org:billing'], + }); + const subscription = SubscriptionFixture({ + organization, + plan: 'am3_f', + canSelfServe: false, + onDemandInvoiced: true, + }); + SubscriptionStore.set(organization.slug, subscription); + render( + + ); + assertNewHeaderCards({hasBillingInfoCard: true}); + }); + + it('renders new header cards for self-serve customers and user without billing perms', () => { + const organization = OrganizationFixture({ + features: ['subscriptions-v3'], + }); + const subscription = SubscriptionFixture({ + organization, + plan: 'am3_f', + }); + SubscriptionStore.set(organization.slug, subscription); + render( + + ); + assertNewHeaderCards({hasBillingInfoCard: false}); + }); + it('does not render editable sections for YY partnership', async () => { const organization = OrganizationFixture({ features: ['usage-log'], diff --git a/static/gsApp/views/subscriptionPage/subscriptionHeader.tsx b/static/gsApp/views/subscriptionPage/subscriptionHeader.tsx index e71a40b63b2c77..3a193d67f28896 100644 --- a/static/gsApp/views/subscriptionPage/subscriptionHeader.tsx +++ b/static/gsApp/views/subscriptionPage/subscriptionHeader.tsx @@ -4,6 +4,7 @@ import styled from '@emotion/styled'; import {Button} from 'sentry/components/core/button'; import {LinkButton} from 'sentry/components/core/button/linkButton'; +import {Flex} from 'sentry/components/core/layout'; import {TabList, Tabs} from 'sentry/components/core/tabs'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {IconCodecov} from 'sentry/icons'; @@ -25,7 +26,7 @@ import { import {isDisabledByPartner} from 'getsentry/utils/partnerships'; import PartnershipNote from 'getsentry/views/subscriptionPage/partnershipNote'; -import {HeaderCards} from './headerCards/headerCards'; +import HeaderCards from './headerCards/headerCards'; import DecidePendingChanges from './decidePendingChanges'; import ManagedNote from './managedNote'; import {SubscriptionUpsellBanner} from './subscriptionUpsellBanner'; @@ -179,7 +180,7 @@ const TabsContainer = styled('div')` */ function BodyWithBillingPerms({organization, subscription}: any) { return ( - + {subscription.pendingChanges ? ( ) : null} @@ -192,7 +193,7 @@ function BodyWithBillingPerms({organization, subscription}: any) { )} - + ); } diff --git a/static/gsApp/views/subscriptionPage/trialAlert.tsx b/static/gsApp/views/subscriptionPage/trialAlert.tsx index c0db8f84502d33..361c26c58432e0 100644 --- a/static/gsApp/views/subscriptionPage/trialAlert.tsx +++ b/static/gsApp/views/subscriptionPage/trialAlert.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import {Button} from 'sentry/components/core/button'; -import Panel from 'sentry/components/panels/panel'; +import {Container} from 'sentry/components/core/layout'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; @@ -41,7 +41,12 @@ function TrialAlert({organization, subscription}: Props) { : 'business plan'; return ( - + @@ -67,7 +72,7 @@ function TrialAlert({organization, subscription}: Props) { )} - + ); }