Skip to content
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

feat: handle pricing & features better for addons in ingestion flow #15219

Merged
merged 6 commits into from
Apr 25, 2023
Merged
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
31 changes: 16 additions & 15 deletions frontend/src/scenes/billing/PlanTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,11 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
const { billing } = useValues(billingLogic)
const { reportBillingUpgradeClicked } = useActions(eventUsageLogic)

const plans = billing?.available_plans?.filter((plan) => plan.name !== 'Enterprise')

const excludedFeatures: string[] = [AvailableFeature.DASHBOARD_COLLABORATION]

const upgradeButtons = billing?.available_plans?.map((plan) => (
const upgradeButtons = plans?.map((plan) => (
<td key={`${plan.name}-cta`}>
<LemonButton
to={`/api/billing-v2/activation?plan=${plan.key}&redirect_path=${redirectPath}`}
Expand All @@ -147,15 +149,15 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
</td>
))

return !billing?.available_plans?.length ? (
return !plans?.length ? (
<Spinner />
) : (
<div className="PlanTable space-x-4">
<table className="w-full table-fixed">
<thead>
<tr>
<td />
{billing?.available_plans?.map((plan) => (
{plans?.map((plan) => (
<td key={plan.name}>
<h3 className="font-bold">{plan.name}</h3>
<p className="ml-0 text-xs">{plan.description}</p>
Expand All @@ -166,29 +168,28 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
<tbody>
<tr>
<th
colSpan={4}
colSpan={3}
className="PlanTable__th__section bg-muted-light text-muted justify-left rounded text-left mb-2"
>
<span>Pricing</span>
</th>
</tr>
<tr className="PlanTable__tr__border">
<td className="font-bold">Monthly base price</td>
{billing?.available_plans?.map((plan) => (
{plans?.map((plan) => (
<td key={`${plan.name}-basePrice`} className="text-sm font-bold">
{getPlanBasePrice(plan)}
</td>
))}
</tr>
{billing?.available_plans
? billing?.available_plans[billing?.available_plans.length - 1].products
{plans
? plans[plans.length - 1].products
.filter((product) => product.type !== 'base')
.map((product, i) => (
<tr
key={product.type}
className={
billing?.available_plans?.[0].products.length &&
i !== billing?.available_plans?.[0].products.length - 1
plans?.[0].products.length && i !== plans?.[0].products.length - 1
? 'PlanTable__tr__border'
: ''
}
Expand All @@ -199,7 +200,7 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
Priced per {product.type === 'events' ? 'event' : 'recording'}
</p>
</th>
{billing?.available_plans?.map((plan) => (
{plans?.map((plan) => (
<td key={`${plan.key}-${product}`}>{getProductTiers(plan, product.type)}</td>
))}
</tr>
Expand All @@ -211,15 +212,15 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
</tr>
<tr>
<th
colSpan={4}
colSpan={3}
className="PlanTable__th__section bg-muted-light text-muted justify-left rounded text-left mb-2"
>
<span>Features</span>
</th>
</tr>

{billing?.available_plans?.length > 0
? billing.available_plans[billing.available_plans.length - 1].products.map((product) =>
{plans?.length > 0
? plans[plans.length - 1].products.map((product) =>
product.feature_groups?.map((feature_group) => (
<>
<tr
Expand All @@ -228,7 +229,7 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
>
<th>{feature_group.name}</th>
{(product.type === 'events' || product.type === 'recordings') &&
billing?.available_plans?.map((plan) => (
plans?.map((plan) => (
<td key={`${plan.name}-${feature_group.name}`}>
<PlanIcon
feature={{
Expand Down Expand Up @@ -260,7 +261,7 @@ export function PlanTable({ redirectPath }: { redirectPath: string }): JSX.Eleme
<th className="PlanTable__th__subfeature text-muted text-xs">
<Tooltip title={feature.description}>{feature.name}</Tooltip>
</th>
{billing?.available_plans?.map((plan) => (
{plans?.map((plan) => (
<td key={`${plan.name}-${feature.name}`}>
<PlanIcon
feature={plan?.products
Expand Down
54 changes: 53 additions & 1 deletion frontend/src/scenes/billing/billing-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { dayjs } from 'lib/dayjs'
import { BillingV2TierType, BillingV2Type } from '~/types'
import { BillingProductV2Type, BillingV2TierType, BillingV2Type } from '~/types'

export const summarizeUsage = (usage: number | null): string => {
if (usage === null) {
Expand Down Expand Up @@ -101,3 +101,55 @@ export const convertAmountToUsage = (amount: string, tiers: BillingV2TierType[])

return Math.round(usage)
}

export const getUpgradeAllProductsLink = (
product: BillingProductV2Type,
upgradeToPlanKey: string,
redirectPath?: string
): string => {
let url = '/api/billing-v2/activation?products='
url += `${product.type}:${upgradeToPlanKey},`
if (product.addons?.length) {
for (const addon of product.addons) {
url += `${addon.type}:${addon.plans[0].plan_key},`
}
}
// remove the trailing comma that will be at the end of the url
url = url.slice(0, -1)
if (redirectPath) {
url += `&redirect_path=${redirectPath}`
}
return url
}

export const convertLargeNumberToWords = (
// The number to convert
num: number | null,
// The previous tier's number
previousNum: number | null,
// Whether we will be showing multiple tiers (to denote the first tier with 'first')
multipleTiers: boolean = false,
// The product type (to denote the unit)
productType: BillingProductV2Type['type'] | null = null
): string => {
if (num === null && previousNum) {
return `${convertLargeNumberToWords(previousNum, null)} +`
}
if (num === null) {
return ''
}

let denominator = 1

if (num >= 1000000) {
denominator = 1000000
} else if (num >= 1000) {
denominator = 1000
}

return `${previousNum ? `${(previousNum / denominator).toFixed(0)}-` : multipleTiers ? 'First ' : ''}${(
num / denominator
).toFixed(0)}${denominator === 1000000 ? ' million' : denominator === 1000 ? 'k' : ''}${
!previousNum && multipleTiers ? ` ${productType}s/mo` : ''
}`
}
Loading