Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions static/gsApp/components/partnerPlanEndingBanner.tsx
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for this, managedNote, pendingChanges, and trialAlert, I removed the bottom margins from here so the new parent component can handle the spacing

Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -52,7 +53,14 @@ function PartnerPlanEndingBanner({
: 'Business';

return (
<PartnerPlanEndingBannerWrapper data-test-id="partner-plan-ending-banner">
<Flex
data-test-id="partner-plan-ending-banner"
background="primary"
border="primary"
radius="md"
justify="between"
align="center"
>
<div>
<PartnerPlanEndingText>
<PartnerPlanEndingBannerTitle>
Expand Down Expand Up @@ -80,20 +88,10 @@ function PartnerPlanEndingBanner({
</PartnerPlanEndingText>
</div>
<IllustrationContainer src={PartnerPlanEndingBackground} />
</PartnerPlanEndingBannerWrapper>
</Flex>
);
}

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;
Expand Down
111 changes: 88 additions & 23 deletions static/gsApp/views/subscriptionPage/headerCards/headerCards.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import {css} from '@emotion/react';
import styled from '@emotion/styled';
import moment from 'moment-timezone';

import {Flex, Grid} from 'sentry/components/core/layout';
import {Text} from 'sentry/components/core/text';
import ErrorBoundary from 'sentry/components/errorBoundary';
import Panel from 'sentry/components/panels/panel';
import {space} from 'sentry/styles/space';
import {IconGrid, IconUpgrade} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import {toTitleCase} from 'sentry/utils/string/toTitleCase';

import type {Subscription} from 'getsentry/types';
import {hasNewBillingUI} from 'getsentry/utils/billing';
import {getPlanIcon, getProductIcon, hasNewBillingUI} from 'getsentry/utils/billing';
import SubscriptionHeaderCard from 'getsentry/views/subscriptionPage/headerCards/subscriptionHeaderCard';
import SeerAutomationAlert from 'getsentry/views/subscriptionPage/seerAutomationAlert';

import {SubscriptionCard} from './subscriptionCard';
Expand All @@ -19,28 +22,90 @@ interface HeaderCardsProps {
}

export function HeaderCards({organization, subscription}: HeaderCardsProps) {
const isNewBillingUI = hasNewBillingUI(organization);

const cards = [
<PlanCard key="plan" subscription={subscription} organization={organization} />,
].filter(card => card !== null);

return (
<ErrorBoundary mini>
<SeerAutomationAlert organization={organization} />
<HeaderCardWrapper hasNewCheckout={hasNewBillingUI(organization)}>
<SubscriptionCard organization={organization} subscription={subscription} />
<UsageCard organization={organization} subscription={subscription} />
</HeaderCardWrapper>
{isNewBillingUI ? (
<Grid
columns={{
xs: '1fr',
sm: `repeat(${Math.min(cards.length, 2)}, 1fr)`,
md: `repeat(${cards.length}, 1fr)`,
}}
>
{cards}
</Grid>
) : (
<Grid
background="primary"
border="primary"
radius="md"
columns={{lg: 'auto minmax(0, 600px)'}}
gap={{lg: 'xl'}}
>
<SubscriptionCard organization={organization} subscription={subscription} />
<UsageCard organization={organization} subscription={subscription} />
</Grid>
)}
</ErrorBoundary>
);
}

// 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)};
}
`}
`;
function PlanCard({
subscription,
organization,
}: {
organization: Organization;
subscription: Subscription;
}) {
const button = subscription.canSelfServe
? subscription.isFree
? {
ariaLabel: t('Upgrade plan'),
label: t('Upgrade plan'),
linkTo: `/settings/${organization.slug}/billing/checkout/?referrer=upgrade_plan`,
icon: <IconUpgrade />,
priority: 'primary' as const,
}
: {
ariaLabel: t('Edit plan'),
label: t('Edit plan'),
linkTo: `/settings/${organization.slug}/billing/checkout/?referrer=edit_plan`,
icon: <IconUpgrade />,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Inconsistent Icons for Plan Actions

The PlanCard component's "Edit plan" button uses IconUpgrade, matching the "Upgrade plan" button. This is inconsistent with the previous UI, where "Edit plan" used IconEdit, and could confuse users.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentional change

priority: 'default' as const,
}
: undefined;
return (
<SubscriptionHeaderCard
title={t('Current plan')}
icon={<IconGrid />}
subtitle={tct('[startDate] - [endDate]', {
startDate: moment(subscription.contractPeriodStart).format('MMM D, YYYY'),
endDate: moment(subscription.contractPeriodEnd).format('MMM D, YYYY'),
})}
sections={[
<Flex key="plan" gap="sm" align="center">
{getPlanIcon(subscription.planDetails)}
<Text bold>{t('%s Plan', subscription.planDetails.name)}</Text>
</Flex>,
...Object.values(subscription.addOns ?? {})
.filter(addOn => addOn.enabled)
.map(addOn => (
<Flex key={addOn.apiName} gap="sm" align="center">
{getProductIcon(addOn.apiName)}
<Text bold>
{toTitleCase(addOn.productName, {allowInnerUpperCase: true})}
</Text>
</Flex>
)),
]}
button={button}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 (
<SubscriptionHeaderCard
title={t('Current plan')}
icon={<IconGrid />}
subtitle={tct('[startDate] - [endDate]', {
startDate: moment(subscription.contractPeriodStart).format('MMM D, YYYY'),
endDate: moment(subscription.contractPeriodEnd).format('MMM D, YYYY'),
})}
sections={[
<strong key="plan">{t('%s Plan', subscription.planDetails?.name)}</strong>,
]}
button={{
ariaLabel: t('Edit plan'),
label: t('Edit plan'),
linkTo: `/settings/${organization.slug}/billing/checkout/?referrer=edit_plan`,
icon: <IconEdit />,
}}
/>
);
}

return (
<SubscriptionCardBody data-test-id="subscription-card">
<PlanHeaderWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,9 +43,7 @@ function SubscriptionHeaderCard({
{isValidElement(button.icon)
? cloneElement(button.icon, {size: 'sm'} as SVGIconProps)
: null}
<Text bold size="sm">
{button.label}
</Text>
{button.label}
</Flex>
);
};
Expand All @@ -68,7 +67,7 @@ function SubscriptionHeaderCard({
</Flex>
)}
{subtitle && <Text variant="muted">{subtitle}</Text>}
<Flex direction="column" gap="md" padding="xl 0">
<Flex direction="column" gap="md" padding={button ? 'xl 0' : 'xl 0 0'}>
{sections.map((section, index) => {
const isLast = index === sections.length - 1;
return (
Expand All @@ -81,11 +80,19 @@ function SubscriptionHeaderCard({
</Flex>
{button &&
(button.linkTo ? (
<LinkButton aria-label={button.ariaLabel} to={button.linkTo}>
<LinkButton
priority={button.priority}
aria-label={button.ariaLabel}
to={button.linkTo}
>
{getButtonContent()}
</LinkButton>
) : (
<Button onClick={button.onClick} aria-label={button.ariaLabel}>
<Button
onClick={button.onClick}
aria-label={button.ariaLabel}
priority={button.priority}
>
{getButtonContent()}
</Button>
))}
Expand Down
11 changes: 8 additions & 3 deletions static/gsApp/views/subscriptionPage/managedNote.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -83,7 +83,12 @@ function ManagedNote({subscription}: Props) {
(subscription.customPrice !== null && subscription.customPrice > 0);

return (
<Panel data-test-id="managed-note">
<Container
data-test-id="managed-note"
background="primary"
border="primary"
radius="md"
>
<PanelBody withPadding>
<TextBlock noMargin>
{isSalesAccount
Expand All @@ -96,7 +101,7 @@ function ManagedNote({subscription}: Props) {
DEFAULT_MESSAGE)}
</TextBlock>
</PanelBody>
</Panel>
</Container>
);
}

Expand Down
38 changes: 18 additions & 20 deletions static/gsApp/views/subscriptionPage/pendingChanges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,26 +397,24 @@ class PendingChanges extends Component<Props> {
}

return (
<Alert.Container>
<Alert type="info">
<PendingLists>
{Object.entries(changes).map(([effectiveDate, items]) => (
<div key={effectiveDate} data-test-id="pending-list">
{tct('The following changes will take effect on [date]:', {
date: <strong>{moment(effectiveDate).format('ll')}</strong>,
})}
<ItemList>
{items.map((item, itemIdx) => (
<li key={itemIdx} data-test-id="pending-item">
{item}
</li>
))}
</ItemList>
</div>
))}
</PendingLists>
</Alert>
</Alert.Container>
<Alert type="info">
<PendingLists>
{Object.entries(changes).map(([effectiveDate, items]) => (
<div key={effectiveDate} data-test-id="pending-list">
{tct('The following changes will take effect on [date]:', {
date: <strong>{moment(effectiveDate).format('ll')}</strong>,
})}
<ItemList>
{items.map((item, itemIdx) => (
<li key={itemIdx} data-test-id="pending-item">
{item}
</li>
))}
</ItemList>
</div>
))}
</PendingLists>
</Alert>
);
}
}
Expand Down
Loading
Loading