Skip to content

Commit

Permalink
New pricing page test (#8532)
Browse files Browse the repository at this point in the history
* new pricing page init

* dummy pricing table content

* logic

* automatic pricing tables

* addons

* pricing breakdown general layout

* format pricing breakdown differently for the test

* adjust defult tab scale to prevent overflow-x issues

* calc component structure

* componentized it

* refactored

* basic plan content

* plans content

* pricing explanation content

* responsive

* bullet checks

* tabbed calculator

* fix

* spacing

* addons section

* tightening text

* pricing calc polish

* reworked pricing calc layout

* mobile tweaks

* fixed mobile overflow

* text

* sort of breaking the top pricing details section

tab ui rework in progress

* get inclusion_only addon info correctly for addons section

* show pricing properly in product table

* mark included

* clean up

* links in pricing explanation section

* active class

* show/hide full plan comparison

* smooth scroll fail

* polish

* faq scroll

* chevron bullets, enterprise content

* tab button hover style, spacing between sections on mobile

* inclusion only calculations

* dark mode fixes

* restore padding to plan comparison cells

* center pricing breakdown, make plans look more like tabs

* better responsiveness for plans table

* polish

* add tooltips

* conditional up_to

* try to explain how to calculate person profile usage

* tooltip tab spacing

* side panel spacing

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* Update src/components/Pricing/PricingCalculator/Tabbed.tsx

Co-authored-by: Raquel Smith <[email protected]>

* dark mode

* Slight copy reduction

* ff

* dark mode fix

* person profile clarifications

* more person profile clarifications

---------

Co-authored-by: Eli Kinsey <[email protected]>
Co-authored-by: Raquel Smith <[email protected]>
Co-authored-by: Joe Martin <[email protected]>
  • Loading branch information
4 people authored May 31, 2024
1 parent 0fd7f4a commit 2b05b2a
Show file tree
Hide file tree
Showing 17 changed files with 2,181 additions and 142 deletions.
25 changes: 18 additions & 7 deletions src/components/Pricing/Addons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ export const Addons = ({ billingProducts }: AddonsProps) => {
return product.addons
})
.flat()
.filter((addon: any) => !addon.inclusion_only)
}, [billingProducts])

const getAddonPrice = (addon: any): number | null => {
const tiers = addon.plans[0]?.tiers
if (tiers.length > 0) {
const isInclusionOnly = addon.inclusion_only
let tiers
if (isInclusionOnly) {
tiers = addon.plans.find((plan) => plan.included_if == 'has_parent_subscription')?.tiers
} else {
tiers = addon.plans[0]?.tiers
}
if (tiers?.length > 0) {
// If the first tier is free, return the second tier price
if (+tiers[0].flat_amount_usd === 0) {
return tiers[1]?.unit_amount_usd
Expand All @@ -56,8 +61,14 @@ export const Addons = ({ billingProducts }: AddonsProps) => {
}

const getAddonFreeAllocation = (addon: any): number | null => {
const tiers = addon.plans[0]?.tiers
if (tiers.length > 0) {
const isInclusionOnly = addon.inclusion_only
let tiers
if (isInclusionOnly) {
tiers = addon.plans.find((plan) => plan.included_if == 'has_parent_subscription')?.tiers
} else {
tiers = addon.plans[0]?.tiers
}
if (tiers?.length > 0) {
// If the first tier is free, return the it's allocation
if (+tiers[0].flat_amount_usd === 0) {
return tiers[0]?.up_to
Expand Down Expand Up @@ -86,7 +97,7 @@ export const Addons = ({ billingProducts }: AddonsProps) => {
<div className="col-span-8 md:col-span-5 lg:col-span-4 md:border-t-0 h-full border-light dark:border-dark pt-4 md:pb-4 flex items-center px-2">
<p className="mb-0">
<span className="space-x-0.5">
<strong>${getAddonPrice(addon).toLocaleString()}</strong>
<strong>${getAddonPrice(addon)?.toLocaleString()}</strong>
<span className="opacity-50 font-medium text-[13px]">/</span>
<span className="opacity-50 font-medium text-[13px]">
{addon.unit || addon.plans[0].unit}
Expand All @@ -95,7 +106,7 @@ export const Addons = ({ billingProducts }: AddonsProps) => {
{!addon?.plans[0].flat_rate && (
<p className="mt-0.5 opacity-70 leading-tight font-medium text-[13px] mb-0">
<em>
First {getAddonFreeAllocation(addon).toLocaleString()} {addon.unit}s free
First {getAddonFreeAllocation(addon)?.toLocaleString()} {addon.unit}s free
every month
</em>
</p>
Expand Down
147 changes: 81 additions & 66 deletions src/components/Pricing/Plans/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const Heading = ({ title, subtitle, className = '' }: { title?: string; subtitle
}

const Row = ({ children, className = '' }: { children: React.ReactNode; className?: string }) => {
return <div className={`flex items-center space-x-4 px-2 lg:px-4 py-1.5 rounded ${className}`}>{children}</div>
return <div className={`flex items-center gap-4 py-1.5 px-4 rounded ${className}`}>{children}</div>
}

const Feature = ({ feature }: { feature: BillingV2FeatureType }) => {
Expand All @@ -49,7 +49,7 @@ const Title = ({ title, className = '' }: { title: string; className?: string })
return <h5 className={`m-0 text-[15px] opacity-70 font-medium ${className}`}>{title}</h5>
}

const InclusionOnlyRow = ({ plans }) => (
export const InclusionOnlyRow = ({ plans }) => (
<Row className="!py-1">
<div className="flex-grow" />
{plans.map(({ included_if, plan_key }, index) => (
Expand All @@ -64,7 +64,7 @@ const InclusionOnlyRow = ({ plans }) => (

const ENTERPRISE_PRICING_TABLE = 'enterprise-pricing-table'

const PricingTiers = ({ plans, unit, compact = false, type }) => {
export const PricingTiers = ({ plans, unit, compact = false, type, test = false }) => {
const posthog = usePostHog()
const [enterprise_flag_enabled, set_enterprise_flag_enabled] = useState(false)

Expand Down Expand Up @@ -93,34 +93,38 @@ const PricingTiers = ({ plans, unit, compact = false, type }) => {
index === 0
? `First ${formatCompactNumber(up_to)} ${unit}s`
: !up_to
? `${formatCompactNumber(plans[plans.length - 1].tiers[index - 1].up_to)}+`
? `${formatCompactNumber(plans[plans.length - 1].tiers[index - 1]?.up_to)}+`
: `${
formatCompactNumber(plans[plans.length - 1].tiers[index - 1].up_to).split(/ |k/)[0]
formatCompactNumber(plans[plans.length - 1].tiers[index - 1]?.up_to).split(/ |k/)[0]
}-${formatCompactNumber(up_to)}`
}
/>
{!compact && (
<Title
className="font-bold max-w-[25%] w-full min-w-[105px]"
className="hidden font-bold max-w-[25%] w-full min-w-[105px]"
title={plans[0].free_allocation === up_to ? 'Free' : '-'}
/>
)}
<div className="flex max-w-[25%] w-full min-w-[105px]">
<div className={`flex ${test ? 'shrink-0' : 'max-w-[25%] w-full min-w-[105px]'}`}>
<Title
className={`font-bold ${compact ? 'text-sm' : ''}`}
className={`${compact ? 'text-sm' : ''}`}
title={
plans[0].free_allocation === up_to ? (
'Free'
<strong>Free</strong>
) : type === 'product_analytics' && index === tiers.length - 1 ? (
<div className="flex items-center">
$
{parseFloat(unit_amount_usd).toFixed(
Math.max(
...plans[plans.length - 1].tiers.map(
(tier) => tier.unit_amount_usd.split('.')[1]?.length ?? 0
// last row
<div className="flex items-center -mr-5">
<strong>
$
{parseFloat(unit_amount_usd).toFixed(
Math.max(
...plans[plans.length - 1].tiers.map(
(tier) => tier.unit_amount_usd.split('.')[1]?.length ?? 0
)
)
)
)}
)}
</strong>
/{unit}
<Tooltip
content={() => (
<div>
Expand All @@ -142,13 +146,19 @@ const PricingTiers = ({ plans, unit, compact = false, type }) => {
</Tooltip>
</div>
) : (
`$${parseFloat(unit_amount_usd).toFixed(
Math.max(
...plans[plans.length - 1].tiers.map(
(tier) => tier.unit_amount_usd.split('.')[1]?.length ?? 0
)
)
)}`
<>
<strong>
$
{parseFloat(unit_amount_usd).toFixed(
Math.max(
...plans[plans.length - 1].tiers.map(
(tier) => tier.unit_amount_usd.split('.')[1]?.length ?? 0
)
)
)}
</strong>
/{unit}
</>
)
}
/>
Expand All @@ -171,16 +181,23 @@ const formatCompactNumber = (number) => {
return formatter.format(number).toLowerCase()
}

const AddonTooltipContent = ({ addon }) => {
const referencePlan = addon.plans?.[0]
const AddonTooltipContent = ({ addon }: { addon: BillingProductV2Type }) => {
const isInclusionOnly = addon.inclusion_only
let referencePlan
if (isInclusionOnly) {
referencePlan = addon.plans.find((plan) => plan.included_if == 'has_parent_subscription')
} else {
referencePlan = addon.plans[0]
}
console.log(addon.name, 'referencePlan', referencePlan)
const tiers = referencePlan?.tiers
const isFirstTierFree = parseFloat(tiers?.[0].unit_amount_usd || '') === 0
const [showDiscounts, setShowDiscounts] = useState(false)

return (
<div className="p-2 max-w-sm">
<p className="font-bold text-[15px] mb-2">
{addon.name} <Label className="ml-2" text="Addon" />
{addon.name} <Label className="ml-2" text="Add-on" />
</p>
<p className="text-sm mb-3">{addon.description}</p>
<p className="text-sm opacity-70 mb-3">
Expand Down Expand Up @@ -268,6 +285,7 @@ const allProductsData = graphql`
plan_key
product_key
unit
included_if
features {
description
key
Expand Down Expand Up @@ -434,45 +452,42 @@ export default function Plans({
</Row>
)
})}
{addons
.filter((addon: BillingProductV2Type) => !addon.inclusion_only)
.map((addon: BillingProductV2Type) => {
return (
<Row
className="hover:bg-accent/60 dark:hover:bg-accent-dark/70"
key={addon.type}
>
<div className="flex-grow">
<AddonTooltip addon={addon} parentProductName={name}>
<Title
className="border-b border-dashed border-border dark:border-dark inline-block cursor-default"
title={addon.name}
/>
<Label className="ml-2" text="Addon" />
</AddonTooltip>
</div>
{plans.map((plan) => {
return (
<div
className="max-w-[25%] w-full min-w-[105px]"
key={`${addon.type}-${plan.plan_key}`}
>
{plan.free_allocation ? (
<Close opacity={1} className="text-red w-4" />
) : (
<AddonTooltip addon={addon} parentProductName={name}>
<Title
className="border-b border-dashed border-border dark:border-dark inline-block cursor-default"
title="Available"
/>
</AddonTooltip>
)}
</div>
)
})}
</Row>
)
})}
{addons.map((addon: BillingProductV2Type) => {
return (
<Row className="hover:bg-accent/60 dark:hover:bg-accent-dark/70" key={addon.type}>
<div className="flex-grow">
<AddonTooltip addon={addon} parentProductName={name}>
<Title
className="border-b border-dashed border-border dark:border-dark inline-block cursor-default"
title={addon.name}
/>
<Label className="ml-2" text="Add-on" />
</AddonTooltip>
</div>
{plans.map((plan, i) => {
return (
<div
className="max-w-[25%] w-full min-w-[105px]"
key={`${addon.type}-${plan.plan_key}`}
>
{plan.free_allocation && !plan.included_if ? (
<Close opacity={1} className="text-red w-4" />
) : plan.included_if == 'no_active_parent_subscription' ? (
<span>Included</span>
) : (
<AddonTooltip addon={addon} parentProductName={name}>
<Title
className="border-b border-dashed border-border dark:border-dark inline-block cursor-default"
title="Available"
/>
</AddonTooltip>
)}
</div>
)
})}
</Row>
)
})}
</div>
<div>
<Row className="bg-accent dark:bg-accent-dark my-2">
Expand Down
12 changes: 12 additions & 0 deletions src/components/Pricing/Platform/usePlatform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useStaticQuery } from 'gatsby'
import { allProductsData } from '../Pricing'

export const usePlatform = () => {
const {
allProductData: {
nodes: [{ products: billingProducts }],
},
} = useStaticQuery(allProductsData)

return billingProducts.find((product) => product.type === 'platform_and_support')
}
5 changes: 3 additions & 2 deletions src/components/Pricing/Pricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export const gridCellBottom = cntl`
rounded-b-md
`

const allProductsData = graphql`
export const allProductsData = graphql`
query {
allProductData {
nodes {
Expand Down Expand Up @@ -186,6 +186,7 @@ const allProductsData = graphql`
unit
flat_rate
unit_amount_usd
included_if
features {
description
key
Expand Down Expand Up @@ -334,7 +335,7 @@ const Pricing = ({
</div>
)}
>
<span className="border-b border-dashed border-primary/50 dark:primary-dark/50">
<span className="border-b border-dashed border-primary/50 dark:border-primary-dark/50">
value-based pricing
</span>
</Tooltip>
Expand Down
Loading

0 comments on commit 2b05b2a

Please sign in to comment.