Skip to content

Commit

Permalink
feat: teams plan to teams addon (#21741)
Browse files Browse the repository at this point in the history
* Add flat rate product attribute

* Add flat rate addon pricing

* Clean up show upgrade card logic

* Update BillingProduct.tsx

* Update types.ts

* Improve upgrade button copy

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Get addon scroll to working

* Show addon feature list for teams

* Swap out ordering to look at addons for product features first

* Update support response times panel

* Move logic to kea billingLogic

* Update flat rate usage

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (1)

* udpate fixture types

* Fix merge conflict

* Update payGateMiniLogic.tsx

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* Add a check for no addons for platform and support

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
zlwaterfield and github-actions[bot] authored May 1, 2024
1 parent 32de2ec commit 6402f5a
Show file tree
Hide file tree
Showing 28 changed files with 169 additions and 121 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,7 @@ const Section = ({ title, children }: { title: string; children: React.ReactNode
}

const SupportFormBlock = ({ onCancel }: { onCancel: () => void }): JSX.Element => {
const { billing } = useValues(billingLogic)

// TODO(@zach): remove after updated plans w/ support levels are shipped
const supportResponseTimes = {
[AvailableFeature.EMAIL_SUPPORT]: '24 hours',
[AvailableFeature.PRIORITY_SUPPORT]: '12 hours',
}
const { supportPlans, hasSupportAddonPlan } = useValues(billingLogic)

return (
<Section title="Email an engineer">
Expand All @@ -90,30 +84,27 @@ const SupportFormBlock = ({ onCancel }: { onCancel: () => void }): JSX.Element =
<Link to={urls.organizationBilling([ProductKey.PLATFORM_AND_SUPPORT])}>Explore options</Link>
</div>
</div>
{billing?.products
?.find((product) => product.type == ProductKey.PLATFORM_AND_SUPPORT)
?.plans?.map((plan) => (
{supportPlans?.map((plan) => {
// If they have an addon plan, only show the addon plan
const currentPlan = plan.current_plan && (!hasSupportAddonPlan || plan.plan_key?.includes('addon'))
return (
<React.Fragment key={`support-panel-${plan.plan_key}`}>
<div className={plan.current_plan ? 'font-bold' : undefined}>
<div className={currentPlan ? 'font-bold' : undefined}>
{plan.name}
{plan.current_plan && (
{currentPlan && (
<>
{' '}
<span className="font-normal opacity-60 text-sm">(your plan)</span>
</>
)}
</div>
<div className={plan.current_plan ? 'font-bold' : undefined}>
<div className={currentPlan ? 'font-bold' : undefined}>
{/* TODO(@zach): remove fallback after updated plans w/ support levels are shipped */}
{plan.features.find((f) => f.key == AvailableFeature.SUPPORT_RESPONSE_TIME)?.note ??
(plan.features.some((f) => f.key == AvailableFeature.PRIORITY_SUPPORT)
? supportResponseTimes[AvailableFeature.PRIORITY_SUPPORT]
: plan.features.some((f) => f.key == AvailableFeature.EMAIL_SUPPORT)
? supportResponseTimes[AvailableFeature.EMAIL_SUPPORT]
: 'Community support only')}
{plan.features.find((f) => f.key == AvailableFeature.SUPPORT_RESPONSE_TIME)?.note}
</div>
</React.Fragment>
))}
)
})}
</div>
<SupportForm />
<LemonButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const PayGateMiniButtonVariant = ({
center
onClick={onCtaClick}
>
{getCtaLabel(gateVariant, productWithFeature, billing)}
{getCtaLabel(gateVariant, billing)}
</LemonButton>
)
}
Expand All @@ -46,11 +46,10 @@ const getCtaLink = (

const getCtaLabel = (
gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null,
productWithFeature: BillingProductV2AddonType | BillingProductV2Type,
billing: BillingV2Type | null
): string => {
if (gateVariant === 'add-card') {
return billing?.has_active_subscription ? `Upgrade ${productWithFeature?.name}` : 'Subscribe now'
return billing?.has_active_subscription ? 'Upgrade now' : 'Subscribe now'
} else if (gateVariant === 'contact-sales') {
return 'Contact sales'
} else {
Expand Down
18 changes: 10 additions & 8 deletions frontend/src/lib/components/PayGateMini/payGateMiniLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ export const payGateMiniLogic = kea<payGateMiniLogicType>([
productWithFeature: [
(s) => [s.billing],
(billing) => {
let foundProduct: BillingProductV2Type | BillingProductV2AddonType | undefined =
billing?.products?.find((product) => product.features?.some((f) => f.key === props.featureKey))
// check addons first since their features are rolled up into the parent
const allAddons = billing?.products?.map((product) => product.addons).flat() || []
let foundProduct: BillingProductV2Type | BillingProductV2AddonType | undefined = allAddons.find(
(addon) => addon.features?.some((f) => f.key === props.featureKey)
)
if (!foundProduct) {
const allAddons = billing?.products?.map((product) => product.addons).flat() || []
foundProduct = allAddons.find((addon) => addon.features?.some((f) => f.key === props.featureKey))
foundProduct = billing?.products?.find((product) =>
product.features?.some((f) => f.key === props.featureKey)
)
}
return foundProduct
},
Expand Down Expand Up @@ -85,12 +89,10 @@ export const payGateMiniLogic = kea<payGateMiniLogicType>([
if (values.isCloudOrDev) {
if (!minimumPlanWithFeature || minimumPlanWithFeature.contact_support) {
return 'contact-sales'
} else {
return 'add-card'
}
} else {
return 'move-to-cloud'
return 'add-card'
}
return 'move-to-cloud'
},
],
})),
Expand Down
47 changes: 23 additions & 24 deletions frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,31 +218,30 @@ function convertToMenuSingle<T>(
items,
custom: doOptionsContainCustomControl(childOptions),
} as LemonMenuItemNode
} else {
acc.push(option)
if (option.hidden) {
// Add hidden options to the accumulator, but don't show
return null
}
const { value, label, labelInMenu, ...leaf } = option
let CustomControl: LemonSelectCustomControl<T> | undefined
if (typeof labelInMenu === 'function') {
CustomControl = labelInMenu
}
return {
...leaf,
label: CustomControl
? function LabelWrapped() {
if (!CustomControl) {
throw new Error('CustomControl became undefined')
}
return <CustomControl onSelect={onSelect} />
}
: labelInMenu || label,
active: value === activeValue,
onClick: () => onSelect(value),
} as LemonMenuItemLeaf
}
acc.push(option)
if (option.hidden) {
// Add hidden options to the accumulator, but don't show
return null
}
const { value, label, labelInMenu, ...leaf } = option
let CustomControl: LemonSelectCustomControl<T> | undefined
if (typeof labelInMenu === 'function') {
CustomControl = labelInMenu
}
return {
...leaf,
label: CustomControl
? function LabelWrapped() {
if (!CustomControl) {
throw new Error('CustomControl became undefined')
}
return <CustomControl onSelect={onSelect} />
}
: labelInMenu || label,
active: value === activeValue,
onClick: () => onSelect(value),
} as LemonMenuItemLeaf
}

export function isLemonSelectSection<T>(
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/mocks/fixtures/_billing_v2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/product-analytics',
note: null,
unit: 'event',
flat_rate: false,
free_allocation: 1000000,
features: [
{
Expand Down Expand Up @@ -99,6 +100,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/product-analytics',
note: null,
unit: 'event',
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -437,6 +439,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/product-analytics/group-analytics',
note: null,
unit: 'event',
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -628,6 +631,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/cdp/batch-exports',
note: null,
unit: 'event',
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -890,6 +894,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/session-replay',
note: null,
unit: 'recording',
flat_rate: false,
free_allocation: 5000,
features: [
{
Expand Down Expand Up @@ -1025,6 +1030,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/session-replay',
note: null,
unit: 'recording',
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -1460,6 +1466,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/feature-flags',
note: null,
unit: 'request',
flat_rate: false,
free_allocation: 1000000,
features: [
{
Expand Down Expand Up @@ -1601,6 +1608,7 @@ export const billingJson: BillingV2Type = {
image_url: 'https://posthog.com/images/products/feature-flags/feature-flags.png',
docs_url: 'https://posthog.com/docs/feature-flags',
note: null,
flat_rate: false,
unit: 'request',
free_allocation: null,
features: [
Expand Down Expand Up @@ -2014,6 +2022,7 @@ export const billingJson: BillingV2Type = {
image_url: 'https://posthog.com/images/products/surveys/surveys.png',
docs_url: 'https://posthog.com/docs/surveys',
note: null,
flat_rate: false,
unit: 'survey response',
free_allocation: 250,
features: [
Expand Down Expand Up @@ -2101,6 +2110,7 @@ export const billingJson: BillingV2Type = {
image_url: 'https://posthog.com/images/products/surveys/surveys.png',
docs_url: 'https://posthog.com/docs/surveys',
note: null,
flat_rate: false,
unit: 'survey response',
free_allocation: null,
features: [
Expand Down Expand Up @@ -2435,6 +2445,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/cdp',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -2495,6 +2506,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs/cdp',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -2648,6 +2660,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -2723,6 +2736,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -2816,6 +2830,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down Expand Up @@ -3012,6 +3027,7 @@ export const billingJson: BillingV2Type = {
docs_url: 'https://posthog.com/docs',
note: null,
unit: null,
flat_rate: false,
free_allocation: null,
features: [
{
Expand Down
Loading

0 comments on commit 6402f5a

Please sign in to comment.