Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
24f4eea
Fix layout breakpoints
itsdangold Sep 8, 2025
a9774a1
Cart styling tweaks
itsdangold Sep 10, 2025
bce6946
Structural styling
itsdangold Sep 10, 2025
b424be4
Typography tweaks
itsdangold Sep 10, 2025
170ca29
Refactor plan select card styling
itsdangold Sep 10, 2025
12afd32
Additional products styling
itsdangold Sep 10, 2025
7efb60a
Billing cycle UI tweaks and refactor
itsdangold Sep 11, 2025
db24604
Update PAYG table styling
itsdangold Sep 11, 2025
9fdc641
Tidy up checkout structure
itsdangold Sep 11, 2025
d490f3c
Squashed commit of the following:
itsdangold Sep 12, 2025
9b0c16f
Increase chonk
itsdangold Sep 12, 2025
6730f08
Add grid to additional products
itsdangold Sep 12, 2025
7b3f4d0
Remove PAYG example
itsdangold Sep 12, 2025
b66fb82
Tidy up plan card features
itsdangold Sep 12, 2025
32b1e96
Improve plan selection styling
itsdangold Sep 12, 2025
7d71470
Reduce plan feature icon size
itsdangold Sep 12, 2025
916d9d6
Resize cart and logo
itsdangold Sep 12, 2025
442c2c4
Reserve card styling
itsdangold Sep 12, 2025
57309b8
Cart styling tweaks
itsdangold Sep 12, 2025
0e3cc21
Checkout spacing
itsdangold Sep 12, 2025
5fb893e
Chonky card interactions
itsdangold Sep 15, 2025
b8750ce
Table adjustments
itsdangold Sep 15, 2025
d582551
Resolve comments
itsdangold Sep 16, 2025
758141c
Merge branch 'master' into dang/new-checkout-ui
isabellaenriquez Sep 17, 2025
71233a3
fix payg price table
isabellaenriquez Sep 17, 2025
eb7cc0d
only show chonky styles for chonk theme
isabellaenriquez Sep 17, 2025
0ffd4da
only show chonky styles for chonk theme pt2
isabellaenriquez Sep 17, 2025
1c3a510
add aria attrs + fix tests
isabellaenriquez Sep 17, 2025
b332c1a
fix test
isabellaenriquez Sep 17, 2025
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
110 changes: 45 additions & 65 deletions static/gsApp/views/amCheckout/billingCycleSelectCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import styled from '@emotion/styled';
import moment from 'moment-timezone';

import {Tag} from 'sentry/components/core/badge/tag';
import {Flex} from 'sentry/components/core/layout';
import {Radio} from 'sentry/components/core/radio';
import {Container, Flex} from 'sentry/components/core/layout';
import {Heading, Text} from 'sentry/components/core/text';
import {t, tct} from 'sentry/locale';

import {ANNUAL} from 'getsentry/constants';
import type {Plan, Subscription} from 'getsentry/types';
import CheckoutOption from 'getsentry/views/amCheckout/checkoutOption';
import type {CheckoutFormData} from 'getsentry/views/amCheckout/types';

type BillingCycleSelectCardProps = {
Expand Down Expand Up @@ -73,77 +74,56 @@ function BillingCycleSelectCard({
: t('Cancel anytime');

return (
<BillingCycleOption
data-test-id={`billing-cycle-option-${plan.contractInterval}`}
<CheckoutOption
dataTestId={`billing-cycle-option-${plan.contractInterval}`}
isSelected={isSelected}
onClick={onCycleSelect}
ariaLabel={t('%s billing cycle', intervalName)}
ariaRole="radio"
>
<div>
<Flex align="center" gap="sm">
<BillingInterval>{intervalName}</BillingInterval>
{isAnnual && <Tag type="success">{t('save 10%')}</Tag>}
<Flex align="start" justify="between" gap="md" padding="xl">
<Container paddingTop="2xs">
<RadioMarker isSelected={isSelected} />
</Container>
<Flex direction="column" gap="sm" width="100%">
<Flex align="center" gap="sm">
<Heading as="h3" variant="primary">
{intervalName}
</Heading>
{isAnnual && <Tag type="promotion">{t('save 10%')}</Tag>}
</Flex>
<Flex align="center" gap="md">
{formattedPriceBeforeDiscount && (
<Text
variant={'muted'}
strikethrough
size="2xl"
>{`$${formattedPriceBeforeDiscount}`}</Text>
)}
<Text
size="2xl"
bold
variant="primary"
>{`$${formattedPriceAfterDiscount}`}</Text>
</Flex>
<Flex direction="column" gap="xs" paddingTop="xs">
<Text variant="muted">{cycleInfo}</Text>
<Text variant="muted">{additionalInfo}</Text>
</Flex>
</Flex>
<Flex align="center" gap="sm">
{formattedPriceBeforeDiscount && (
<PriceBeforeDiscount>{`$${formattedPriceBeforeDiscount}`}</PriceBeforeDiscount>
)}
<Price>{`$${formattedPriceAfterDiscount}`}</Price>
</Flex>
<Description>{cycleInfo}</Description>
<Description>{additionalInfo}</Description>
</div>
<StyledRadio
readOnly
id={plan.contractInterval}
name="billing-cycle"
aria-label={`${intervalName} billing cycle`}
value={plan.contractInterval}
checked={isSelected}
onClick={onCycleSelect}
onKeyDown={e => {
if (e.key === 'Enter') {
onCycleSelect();
}
}}
/>
</BillingCycleOption>
</Flex>
</CheckoutOption>
);
}

export default BillingCycleSelectCard;

const BillingCycleOption = styled('div')<{isSelected: boolean}>`
background: ${p => (p.isSelected ? `${p.theme.active}05` : p.theme.background)};
color: ${p => (p.isSelected ? p.theme.activeText : p.theme.textColor)};
border-radius: ${p => p.theme.borderRadius};
border: 1px solid ${p => (p.isSelected ? p.theme.active : p.theme.border)};
cursor: pointer;

display: grid;
padding: ${p => p.theme.space.xl};
grid-template-columns: 1fr max-content;
`;

const BillingInterval = styled('div')`
font-size: ${p => p.theme.fontSize.lg};
font-weight: ${p => p.theme.fontWeight.bold};
`;

const Price = styled('div')`
font-size: ${p => p.theme.fontSize.xl};
font-weight: ${p => p.theme.fontWeight.bold};
`;

const PriceBeforeDiscount = styled(Price)`
text-decoration: line-through;
color: ${p => p.theme.subText};
`;

const Description = styled('div')`
font-size: ${p => p.theme.fontSize.sm};
color: ${p => p.theme.subText};
`;

const StyledRadio = styled(Radio)`
const RadioMarker = styled('div')<{isSelected?: boolean}>`
width: ${p => p.theme.space.xl};
height: ${p => p.theme.space.xl};
border-radius: ${p => p.theme.space['3xl']};
background: ${p => p.theme.background};
border-color: ${p => (p.isSelected ? p.theme.tokens.border.accent : p.theme.border)};
border-width: ${p => (p.isSelected ? '4px' : '1px')};
border-style: solid;
`;
108 changes: 66 additions & 42 deletions static/gsApp/views/amCheckout/cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Alert} from 'sentry/components/core/alert';
import {Tag} from 'sentry/components/core/badge/tag';
import {Button} from 'sentry/components/core/button';
import {Flex} from 'sentry/components/core/layout';
import {Heading, Text} from 'sentry/components/core/text';
import Panel from 'sentry/components/panels/panel';
import Placeholder from 'sentry/components/placeholder';
import {IconChevron, IconLightning, IconLock} from 'sentry/icons';
Expand Down Expand Up @@ -133,11 +134,11 @@ function ItemsSummary({activePlan, formData}: ItemsSummaryProps) {
<IconContainer>{getPlanIcon(activePlan)}</IconContainer>
<Flex direction="column" gap="xs">
<ItemFlex>
<strong>{tct('[name] Plan', {name: activePlan.name})}</strong>
<div>
<Text bold>{tct('[name] Plan', {name: activePlan.name})}</Text>
<Text>
{utils.displayPrice({cents: activePlan.totalPrice})}
{`/${shortInterval}`}
</div>
</Text>
</ItemFlex>
{activePlan.categories
.filter(
Expand Down Expand Up @@ -166,7 +167,7 @@ function ItemsSummary({activePlan, formData}: ItemsSummaryProps) {
return (
<ItemFlex key={category}>
<div>
{isPaygOnly ? '' : `${formattedReserved} `}
<Text>{isPaygOnly ? '' : `${formattedReserved} `}</Text>
{reserved === 1 && category !== DataCategory.ATTACHMENTS
? getSingularCategoryName({
plan: activePlan,
Expand Down Expand Up @@ -682,29 +683,34 @@ function Cart({
onToggle={setChangesIsOpen}
organization={organization}
/>
<PlanSummaryHeader isOpen={summaryIsOpen} shouldShadow={changesIsOpen}>
<Title>{t('Plan Summary')}</Title>
<Flex gap="xs" align="center">
<OrgSlug>{organization.slug.toUpperCase()}</OrgSlug>
<Button
aria-label={`${summaryIsOpen ? 'Hide' : 'Show'} plan summary`}
onClick={() => setSummaryIsOpen(!summaryIsOpen)}
borderless
icon={<IconChevron direction={summaryIsOpen ? 'up' : 'down'} />}
/>
</Flex>
</PlanSummaryHeader>
{summaryIsOpen && (
<div data-test-id="plan-summary">
<ItemsSummary activePlan={activePlan} formData={formData} />
<SubtotalSummary
activePlan={activePlan}
formData={formData}
previewDataLoading={previewState.isLoading}
renewalDate={previewState.renewalDate}
/>
</div>
)}
<PlanSummary>
<PlanSummaryHeader isOpen={summaryIsOpen}>
<Heading as="h2" textWrap="nowrap">
{t('Plan Summary')}
</Heading>
<Flex gap="xs" align="center">
<OrgSlug>{organization.slug.toUpperCase()}</OrgSlug>
<Button
aria-label={summaryIsOpen ? t('Hide plan summary') : t('Show plan summary')}
onClick={() => setSummaryIsOpen(!summaryIsOpen)}
borderless
size="xs"
icon={<IconChevron direction={summaryIsOpen ? 'up' : 'down'} />}
/>
</Flex>
</PlanSummaryHeader>
{summaryIsOpen && (
<PlanSummaryContents data-test-id="plan-summary">
<ItemsSummary activePlan={activePlan} formData={formData} />
<SubtotalSummary
activePlan={activePlan}
formData={formData}
previewDataLoading={previewState.isLoading}
renewalDate={previewState.renewalDate}
/>
</PlanSummaryContents>
)}
</PlanSummary>
<TotalSummary
activePlan={activePlan}
billedTotal={previewState.billedTotal}
Expand All @@ -729,39 +735,48 @@ export default Cart;
const CartContainer = styled(Panel)`
display: flex;
flex-direction: column;
border-bottom: ${p => (p.theme.isChonk ? '3px' : '1px')} solid ${p => p.theme.border};
padding-bottom: ${p => p.theme.space.xl};

> *:first-child {
border-bottom: 1px solid ${p => p.theme.border};
}
`;

const SummarySection = styled('div')`
display: flex;
flex-direction: column;
padding: 0 ${p => p.theme.space.xl} ${p => p.theme.space['2xl']};
padding: 0 ${p => p.theme.space.xl};

& > *:not(:last-child) {
margin-bottom: ${p => p.theme.space.xl};
}

border-bottom: 1px solid ${p => p.theme.border};

&:not(:first-child) {
padding-top: ${p => p.theme.space['2xl']};
}
`;

const Title = styled('h1')`
font-size: ${p => p.theme.fontSize.xl};
font-weight: ${p => p.theme.fontWeight.bold};
margin: 0;
text-wrap: nowrap;
const PlanSummary = styled('div')`
&:not(:first-child) {
border-top: 1px solid ${p => p.theme.border};
}
`;

const PlanSummaryHeader = styled('div')<{isOpen: boolean; shouldShadow: boolean}>`
const PlanSummaryHeader = styled('div')<{isOpen: boolean}>`
display: flex;
justify-content: space-between;
align-items: center;
padding: ${p => p.theme.space.xl};
border-bottom: ${p => (p.isOpen ? 'none' : `1px solid ${p.theme.border}`)};
box-shadow: ${p => (p.shouldShadow ? '0 -5px 5px #00000010' : 'none')};
gap: ${p => p.theme.space.sm};
padding: ${p => p.theme.space.lg} ${p => p.theme.space.xl};
`;

const PlanSummaryContents = styled('div')`
> *:last-child {
margin-top: ${p => p.theme.space.lg};
padding: ${p => p.theme.space.xl} ${p => p.theme.space.xl};
border-top: 1px solid ${p => p.theme.border};
}
`;

const OrgSlug = styled('div')`
Expand All @@ -788,11 +803,21 @@ const ItemFlex = styled('div')`
justify-content: space-between;
align-items: center;
gap: ${p => p.theme.space['3xl']};
line-height: 100%;

> * {
margin-bottom: ${p => p.theme.space.xs};
}

&:not(:first-child) {
color: ${p => p.theme.subText};
}
`;

const IconContainer = styled('div')`
display: flex;
align-items: center;
margin-top: 1px;
`;

const RenewalDate = styled('div')`
Expand All @@ -806,12 +831,12 @@ const DueToday = styled('div')`
`;

const DueTodayPrice = styled('div')`
font-size: ${p => p.theme.fontSize.lg};
font-size: ${p => p.theme.fontSize.md};
`;

const DueTodayAmount = styled('span')`
font-weight: ${p => p.theme.fontWeight.bold};
font-size: ${p => p.theme.fontSize.xl};
font-size: ${p => p.theme.fontSize['2xl']};
`;

const DueTodayAmountBeforeDiscount = styled(DueTodayAmount)`
Expand All @@ -837,7 +862,6 @@ const ButtonContainer = styled('div')`
`;

const Subtext = styled('div')`
margin-top: ${p => p.theme.space['2xl']};
font-size: ${p => p.theme.fontSize.sm};
color: ${p => p.theme.subText};
text-align: center;
Expand Down
Loading
Loading