-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
chore(seer): Adjust copy + styling in checkout #93778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
chore(seer): Adjust copy + styling in checkout
- Loading branch information
commit fbbba63ba7bed99c9a716835bd5d4f9af77786d5
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,16 +9,19 @@ import mediumStarDark from 'sentry-images/spot/product-select-star-m-dark.svg'; | |||||
import smallStarLight from 'sentry-images/spot/product-select-star-s.svg'; | ||||||
import smallStarDark from 'sentry-images/spot/product-select-star-s-dark.svg'; | ||||||
|
||||||
import {Tag} from 'sentry/components/core/badge/tag'; | ||||||
import {Button} from 'sentry/components/core/button'; | ||||||
import PanelItem from 'sentry/components/panels/panelItem'; | ||||||
import {IconAdd, IconCheckmark, IconSeer} from 'sentry/icons'; | ||||||
import {t, tct} from 'sentry/locale'; | ||||||
import ConfigStore from 'sentry/stores/configStore'; | ||||||
import {useLegacyStore} from 'sentry/stores/useLegacyStore'; | ||||||
import {space} from 'sentry/styles/space'; | ||||||
import {DataCategory} from 'sentry/types/core'; | ||||||
import {toTitleCase} from 'sentry/utils/string/toTitleCase'; | ||||||
import type {Color} from 'sentry/utils/theme'; | ||||||
|
||||||
import {getSingularCategoryName} from 'getsentry/utils/dataCategory'; | ||||||
import formatCurrency from 'getsentry/utils/formatCurrency'; | ||||||
import {SelectableProduct, type StepProps} from 'getsentry/views/amCheckout/types'; | ||||||
import * as utils from 'getsentry/views/amCheckout/utils'; | ||||||
|
@@ -46,12 +49,27 @@ function ProductSelect({ | |||||
color: theme.pink400 as Color, | ||||||
gradientEndColor: theme.pink100 as Color, | ||||||
buttonBorderColor: theme.pink200 as Color, | ||||||
description: t('Detect and fix issues faster with our AI debugging agent.'), | ||||||
features: [ | ||||||
t('Issue scan'), | ||||||
t('Root cause analysis'), | ||||||
t('Solution and code changes'), | ||||||
], | ||||||
description: (includedBudget: string) => | ||||||
tct( | ||||||
'Detect and fix issues faster with [includedBudget]/mo in credits towards our AI agent', | ||||||
{ | ||||||
includedBudget, | ||||||
} | ||||||
), | ||||||
categoryInfo: { | ||||||
[DataCategory.SEER_AUTOFIX]: { | ||||||
description: t( | ||||||
'Uses the latest AI models with Sentry data to find root causes & proposes PRs' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
), | ||||||
maxEventPriceDigits: 2, | ||||||
}, | ||||||
[DataCategory.SEER_SCANNER]: { | ||||||
description: t( | ||||||
'Triages issues as they happen, automatically flagging the most important ones for followup' | ||||||
), | ||||||
maxEventPriceDigits: 3, | ||||||
}, | ||||||
}, | ||||||
}, | ||||||
}; | ||||||
const billingInterval = utils.getShortInterval(activePlan.billingInterval); | ||||||
|
@@ -67,17 +85,18 @@ function ProductSelect({ | |||||
return null; | ||||||
} | ||||||
|
||||||
const cost = formatCurrency( | ||||||
utils.getReservedPriceForReservedBudgetCategory({ | ||||||
plan: activePlan, | ||||||
reservedBudgetCategory: productInfo.apiName, | ||||||
}) | ||||||
); | ||||||
const priceInCents = utils.getReservedPriceForReservedBudgetCategory({ | ||||||
plan: activePlan, | ||||||
reservedBudgetCategory: productInfo.apiName, | ||||||
}); | ||||||
const priceInDollars = utils.formatPrice({ | ||||||
cents: priceInCents, | ||||||
}); | ||||||
|
||||||
// if no default budget, then the included budget is how much the customer is paying for the product | ||||||
const includedBudget = productInfo.defaultBudget | ||||||
? formatCurrency(productInfo.defaultBudget) | ||||||
: cost; | ||||||
const formattedMonthlyBudget = formatCurrency( | ||||||
productInfo.defaultBudget ?? priceInCents | ||||||
); | ||||||
|
||||||
return ( | ||||||
<ProductOption | ||||||
|
@@ -118,17 +137,48 @@ function ProductSelect({ | |||||
})} | ||||||
</ProductName> | ||||||
</ProductLabel> | ||||||
<p>{checkoutInfo.description}</p> | ||||||
</Column> | ||||||
<Column> | ||||||
{checkoutInfo.features.map(feature => ( | ||||||
<Feature key={feature}> | ||||||
<IconCheckmark color={checkoutInfo.color} /> | ||||||
{feature} | ||||||
</Feature> | ||||||
))} | ||||||
<ProductDescription> | ||||||
{checkoutInfo.description(formattedMonthlyBudget)} | ||||||
</ProductDescription> | ||||||
</Column> | ||||||
<PriceContainer> | ||||||
<PriceHeader>{t('Starts At')}</PriceHeader> | ||||||
<Price> | ||||||
<Currency>$</Currency> | ||||||
<Amount>{priceInDollars}</Amount> | ||||||
<BillingInterval>{`/${billingInterval}`}</BillingInterval> | ||||||
</Price> | ||||||
</PriceContainer> | ||||||
</Row> | ||||||
<FeatureRow> | ||||||
{Object.entries(checkoutInfo.categoryInfo).map(([category, info]) => { | ||||||
const pricingInfo = activePlan.planCategories[category as DataCategory]; | ||||||
const eventPrice = pricingInfo ? pricingInfo[1]?.onDemandPrice : null; | ||||||
return ( | ||||||
<Feature key={category}> | ||||||
<IconContainer> | ||||||
<IconCheckmark color={checkoutInfo.color} /> | ||||||
</IconContainer> | ||||||
<FeatureTitle key={category}> | ||||||
<span> | ||||||
{getSingularCategoryName({ | ||||||
plan: activePlan, | ||||||
category: category as DataCategory, | ||||||
hadCustomDynamicSampling: false, | ||||||
title: true, | ||||||
})} | ||||||
</span> | ||||||
|
||||||
{eventPrice && ( | ||||||
<EventPriceTag>{`${utils.displayUnitPrice({cents: eventPrice, minDigits: 2, maxDigits: info.maxEventPriceDigits})} / ${getSingularCategoryName({plan: activePlan, category: category as DataCategory, hadCustomDynamicSampling: false, capitalize: false})}`}</EventPriceTag> | ||||||
)} | ||||||
</FeatureTitle> | ||||||
<div /> | ||||||
<FeatureDescription>{info.description}</FeatureDescription> | ||||||
</Feature> | ||||||
); | ||||||
})} | ||||||
</FeatureRow> | ||||||
<Row> | ||||||
<StyledButton> | ||||||
<ButtonContent | ||||||
|
@@ -149,30 +199,12 @@ function ProductSelect({ | |||||
) : ( | ||||||
<Fragment> | ||||||
<IconAdd /> | ||||||
{tct(' Add for [cost]/[billingInterval]', { | ||||||
cost, | ||||||
billingInterval, | ||||||
})} | ||||||
{t('Add to plan')} | ||||||
</Fragment> | ||||||
)} | ||||||
</ButtonContent> | ||||||
</StyledButton> | ||||||
</Row> | ||||||
<Row justifyContent="center"> | ||||||
<Subtitle> | ||||||
{tct( | ||||||
'Includes [includedBudget]/mo of credits for [productName] services. Additional usage is drawn from your [budgetTerm] budget.', | ||||||
{ | ||||||
includedBudget, | ||||||
budgetTerm: | ||||||
activePlan.budgetTerm === 'pay-as-you-go' | ||||||
? 'PAYG' | ||||||
: activePlan.budgetTerm, | ||||||
productName: toTitleCase(productInfo.productName), | ||||||
} | ||||||
)} | ||||||
</Subtitle> | ||||||
</Row> | ||||||
<IllustrationContainer> | ||||||
<Star1 src={prefersDarkMode ? largeStarDark : largeStarLight} /> | ||||||
<Star2 src={prefersDarkMode ? mediumStarDark : mediumStarLight} /> | ||||||
|
@@ -298,8 +330,7 @@ const Column = styled('div')<{alignItems?: string}>` | |||||
const Row = styled('div')<{justifyContent?: string}>` | ||||||
display: flex; | ||||||
gap: ${space(4)}; | ||||||
justify-content: ${p => p.justifyContent ?? 'flex-start'}; | ||||||
align-items: center; | ||||||
justify-content: ${p => p.justifyContent ?? 'space-between'}; | ||||||
`; | ||||||
|
||||||
const ProductLabel = styled('div')<{productColor: string}>` | ||||||
|
@@ -316,11 +347,54 @@ const ProductName = styled('div')` | |||||
font-weight: 600; | ||||||
`; | ||||||
|
||||||
const Subtitle = styled('p')` | ||||||
font-size: ${p => p.theme.fontSizeSmall}; | ||||||
const ProductDescription = styled('p')` | ||||||
margin: ${space(0.5)} 0 ${space(2)}; | ||||||
font-weight: 600; | ||||||
text-wrap: balance; | ||||||
`; | ||||||
|
||||||
const PriceContainer = styled(Column)` | ||||||
gap: 0px; | ||||||
`; | ||||||
|
||||||
const PriceHeader = styled('div')` | ||||||
color: ${p => p.theme.subText}; | ||||||
text-align: center; | ||||||
margin: 0; | ||||||
font-size: ${p => p.theme.fontSizeSmall}; | ||||||
text-transform: uppercase; | ||||||
font-weight: bold; | ||||||
`; | ||||||
|
||||||
const Price = styled('div')` | ||||||
display: inline-grid; | ||||||
grid-template-columns: repeat(3, auto); | ||||||
color: ${p => p.theme.textColor}; | ||||||
`; | ||||||
|
||||||
const Currency = styled('span')` | ||||||
padding-top: ${space(0.5)}; | ||||||
`; | ||||||
|
||||||
const Amount = styled('span')` | ||||||
font-size: 24px; | ||||||
align-self: end; | ||||||
`; | ||||||
|
||||||
const BillingInterval = styled('span')` | ||||||
font-size: ${p => p.theme.fontSizeMedium}; | ||||||
align-self: end; | ||||||
padding-bottom: ${space(0.25)}; | ||||||
`; | ||||||
|
||||||
const EventPriceTag = styled(Tag)` | ||||||
display: flex; | ||||||
align-items: center; | ||||||
line-height: normal; | ||||||
width: fit-content; | ||||||
font-weight: normal; | ||||||
`; | ||||||
|
||||||
const IconContainer = styled('div')` | ||||||
margin-right: ${space(1)}; | ||||||
`; | ||||||
|
||||||
const StyledButton = styled(Button)` | ||||||
|
@@ -335,40 +409,41 @@ const ButtonContent = styled('div')<{color: string}>` | |||||
color: ${p => p.color}; | ||||||
`; | ||||||
|
||||||
const FeatureRow = styled('div')` | ||||||
display: grid; | ||||||
grid-template-columns: 1fr 1fr; | ||||||
column-gap: ${space(2)}; | ||||||
`; | ||||||
|
||||||
const Feature = styled('div')` | ||||||
display: grid; | ||||||
grid-template-columns: auto auto; | ||||||
font-size: ${p => p.theme.fontSizeSmall}; | ||||||
row-gap: ${space(0.5)}; | ||||||
`; | ||||||
|
||||||
const FeatureTitle = styled('div')` | ||||||
font-weight: 600; | ||||||
display: flex; | ||||||
gap: ${space(1)}; | ||||||
align-items: center; | ||||||
align-content: center; | ||||||
svg { | ||||||
flex-shrink: 0; | ||||||
flex-wrap: wrap; | ||||||
|
||||||
> span { | ||||||
margin-right: ${space(0.5)}; | ||||||
} | ||||||
font-size: ${p => p.theme.fontSizeSmall}; | ||||||
`; | ||||||
|
||||||
const FeatureDescription = styled('div')` | ||||||
text-wrap: balance; | ||||||
`; | ||||||
|
||||||
const IllustrationContainer = styled('div')` | ||||||
display: none; | ||||||
|
||||||
@media (min-width: ${p => p.theme.breakpoints.small}) { | ||||||
display: block; | ||||||
position: absolute; | ||||||
bottom: 84px; | ||||||
right: 12px; | ||||||
height: 175px; | ||||||
width: 200px; | ||||||
overflow: hidden; | ||||||
border-radius: 0 ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0; | ||||||
pointer-events: none; | ||||||
} | ||||||
|
||||||
@media (min-width: ${p => p.theme.breakpoints.large}) { | ||||||
display: none; | ||||||
} | ||||||
|
||||||
@media (min-width: ${p => p.theme.breakpoints.xlarge}) { | ||||||
@media (min-width: ${p => p.theme.breakpoints.xsmall}) { | ||||||
display: block; | ||||||
position: absolute; | ||||||
bottom: 84px; | ||||||
bottom: 0px; | ||||||
right: 12px; | ||||||
height: 175px; | ||||||
width: 200px; | ||||||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor spacing issues i was asked to fix