Skip to content

Commit

Permalink
feat: pay gate events (#20878)
Browse files Browse the repository at this point in the history
* add event for when pay gate is shown

* even when click pay gate CTA

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `chromium` (2)

* rename some things

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
raquelmsmith and github-actions[bot] authored Mar 13, 2024
1 parent 2149ec3 commit 1bce547
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 35 deletions.
77 changes: 42 additions & 35 deletions frontend/src/lib/components/PayGateMini/PayGateMini.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { lowercaseFirstLetter } from 'lib/utils'
import posthog from 'posthog-js'
import { useEffect } from 'react'
import { billingLogic } from 'scenes/billing/billingLogic'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { getProductIcon } from 'scenes/products/Products'
import { sceneLogic } from 'scenes/sceneLogic'
import { userLogic } from 'scenes/userLogic'

import { AvailableFeature } from '~/types'

import { PayGateMiniButton } from './PayGateMiniButton'
import { payGateMiniLogic } from './payGateMiniLogic'

export interface PayGateMiniProps {
feature: AvailableFeature
Expand All @@ -39,29 +41,23 @@ export function PayGateMini({
background = true,
isGrandfathered,
}: PayGateMiniProps): JSX.Element | null {
const { preflight, isCloudOrDev } = useValues(preflightLogic)
const { hasAvailableFeature, availableFeature } = useValues(userLogic)
const { productWithFeature, featureInfo, featureAvailableOnOrg, gateVariant } = useValues(
payGateMiniLogic({ featureKey: feature, currentUsage })
)
const { preflight } = useValues(preflightLogic)
const { billing, billingLoading } = useValues(billingLogic)
const { featureFlags } = useValues(featureFlagLogic)
const { hideUpgradeModal } = useActions(sceneLogic)

const product = billing?.products.find((product) => product.features?.some((f) => f.key === feature))
const featureInfo = product?.features.find((f) => f.key === feature)
const featureDetailsWithLimit = availableFeature(feature)
const minimumPlan = product?.plans.find((plan) => plan.features?.some((f) => f.key === feature))

let gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null = null
if (!overrideShouldShowGate && !hasAvailableFeature(feature, currentUsage)) {
if (isCloudOrDev) {
if (!minimumPlan || minimumPlan.contact_support) {
gateVariant = 'contact-sales'
} else {
gateVariant = 'add-card'
}
} else {
gateVariant = 'move-to-cloud'
useEffect(() => {
if (gateVariant) {
posthog.capture('pay gate shown', {
product_key: productWithFeature?.type,
feature: feature,
gate_variant: gateVariant,
})
}
}
}, [gateVariant])

if (billingLoading) {
return null
Expand All @@ -72,17 +68,19 @@ export function PayGateMini({
}

return featureFlags[FEATURE_FLAGS.SUBSCRIBE_FROM_PAYGATE] === 'test' ? (
gateVariant && product && featureInfo ? (
gateVariant && productWithFeature && featureInfo && !overrideShouldShowGate ? (
<div
className={clsx(
className,
background && 'bg-side border border-border',
'PayGateMini rounded flex flex-col items-center p-4 text-center'
)}
>
<div className="flex text-4xl text-warning">{getProductIcon(product.name, featureInfo.icon_key)}</div>
<div className="flex text-4xl text-warning">
{getProductIcon(productWithFeature.name, featureInfo.icon_key)}
</div>
<h3>{featureInfo.name}</h3>
{featureDetailsWithLimit?.limit && gateVariant !== 'move-to-cloud' ? (
{featureAvailableOnOrg?.limit && gateVariant !== 'move-to-cloud' ? (
<div>
<p>
You've reached your usage limit for{' '}
Expand All @@ -97,11 +95,11 @@ export function PayGateMini({
<p className="border border-border bg-side rounded p-4">
<b>Your current plan limit:</b>{' '}
<span>
{featureDetailsWithLimit.limit} {featureDetailsWithLimit.unit}
{featureAvailableOnOrg.limit} {featureAvailableOnOrg.unit}
</span>
</p>
<p>
Please upgrade your <b>{product.name}</b> plan to create more {featureInfo.name}
Please upgrade your <b>{productWithFeature.name}</b> plan to create more {featureInfo.name}
</p>
</div>
) : (
Expand All @@ -112,7 +110,7 @@ export function PayGateMini({
<>This feature is only available on PostHog Cloud.</>
) : (
<>
Upgrade your <b>{product?.name}</b> plan to use this feature.
Upgrade your <b>{productWithFeature?.name}</b> plan to use this feature.
</>
)}
</p>
Expand All @@ -136,22 +134,24 @@ export function PayGateMini({
</>
</div>
)}
<PayGateMiniButton product={product} featureInfo={featureInfo} gateVariant={gateVariant} />
<PayGateMiniButton product={productWithFeature} featureInfo={featureInfo} gateVariant={gateVariant} />
</div>
) : (
<div className={className}>{children}</div>
)
) : gateVariant && product && featureInfo ? (
) : gateVariant && productWithFeature && featureInfo && !overrideShouldShowGate ? (
<div
className={clsx(
className,
background && 'bg-side border border-border',
'PayGateMini rounded flex flex-col items-center p-4 text-center'
)}
>
<div className="flex text-4xl text-warning">{getProductIcon(product.name, featureInfo.icon_key)}</div>
<div className="flex text-4xl text-warning">
{getProductIcon(productWithFeature.name, featureInfo.icon_key)}
</div>
<h3>{featureInfo.name}</h3>
{featureDetailsWithLimit?.limit && gateVariant !== 'move-to-cloud' ? (
{featureAvailableOnOrg?.limit && gateVariant !== 'move-to-cloud' ? (
<div>
<p>
You've reached your usage limit for{' '}
Expand All @@ -166,11 +166,11 @@ export function PayGateMini({
<p className="border border-border bg-side rounded p-4">
<b>Your current plan limit:</b>{' '}
<span>
{featureDetailsWithLimit.limit} {featureDetailsWithLimit.unit}
{featureAvailableOnOrg.limit} {featureAvailableOnOrg.unit}
</span>
</p>
<p>
Please upgrade your <b>{product.name}</b> plan to create more {featureInfo.name}
Please upgrade your <b>{productWithFeature.name}</b> plan to create more {featureInfo.name}
</p>
</div>
) : (
Expand All @@ -179,7 +179,7 @@ export function PayGateMini({
<>On PostHog Cloud, you can </>
) : (
<>
Upgrade your <b>{product?.name}</b> plan to{' '}
Upgrade your <b>{productWithFeature?.name}</b> plan to{' '}
</>
)}
{featureInfo.description ? lowercaseFirstLetter(featureInfo.description) : 'use this feature.'}
Expand All @@ -206,7 +206,7 @@ export function PayGateMini({
<LemonButton
to={
gateVariant === 'add-card'
? `/organization/billing?products=${product.type}`
? `/organization/billing?products=${productWithFeature.type}`
: gateVariant === 'contact-sales'
? `mailto:[email protected]?subject=Inquiring about ${featureInfo.name}`
: gateVariant === 'move-to-cloud'
Expand All @@ -215,11 +215,18 @@ export function PayGateMini({
}
type="primary"
center
onClick={hideUpgradeModal}
onClick={() => {
hideUpgradeModal()
posthog.capture('pay gate CTA clicked', {
product_key: productWithFeature?.type,
feature: feature,
gate_variant: gateVariant,
})
}}
>
{gateVariant === 'add-card'
? billing?.has_active_subscription
? `Upgrade ${product?.name}`
? `Upgrade ${productWithFeature?.name}`
: 'Subscribe now'
: gateVariant === 'contact-sales'
? 'Contact sales'
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/components/PayGateMini/PayGateMiniButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LemonButton } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import posthog from 'posthog-js'
import { billingProductLogic } from 'scenes/billing/billingProductLogic'
import { PlanComparisonModal } from 'scenes/billing/PlanComparison'
import { urls } from 'scenes/urls'
Expand Down Expand Up @@ -36,6 +37,11 @@ export const PayGateMiniButton = ({
if (gateVariant === 'add-card') {
toggleIsPlanComparisonModalOpen(featureInfo.key)
}
posthog.capture('pay gate CTA clicked', {
product_key: product?.type,
feature: featureInfo.key,
gate_variant: gateVariant,
})
onClick?.()
}}
>
Expand Down
83 changes: 83 additions & 0 deletions frontend/src/lib/components/PayGateMini/payGateMiniLogic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { actions, connect, kea, key, path, props, selectors } from 'kea'
import { billingLogic } from 'scenes/billing/billingLogic'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { userLogic } from 'scenes/userLogic'

import { AvailableFeature } from '~/types'

import type { payGateMiniLogicType } from './payGateMiniLogicType'

export interface PayGateMiniLogicProps {
featureKey: AvailableFeature
currentUsage?: number
}

export type GateVariantType = 'add-card' | 'contact-sales' | 'move-to-cloud' | null

export const payGateMiniLogic = kea<payGateMiniLogicType>([
props({} as PayGateMiniLogicProps),
path(['lib', 'components', 'payGateMini', 'payGateMiniLogic']),
key((props) => props.featureKey),
connect(() => ({
values: [
billingLogic,
['billing', 'billingLoading'],
userLogic,
['user', 'hasAvailableFeature', 'availableFeature'],
preflightLogic,
['isCloudOrDev'],
],
actions: [],
})),
actions({
setGateVariant: (gateVariant: GateVariantType) => ({ gateVariant }),
}),
selectors(({ values, props }) => ({
productWithFeature: [
(s) => [s.billing],
(billing) =>
billing?.products?.find((product) => product.features?.some((f) => f.key === props.featureKey)),
],
featureInfo: [
(s) => [s.productWithFeature],
(productWithFeature) => productWithFeature?.features.find((f) => f.key === props.featureKey),
],
featureAvailableOnOrg: [
(s) => [s.user, (_, props) => props.featureKey],
(_user, featureKey) => {
return values.availableFeature(featureKey)
},
],
minimumPlanWithFeature: [
(s) => [s.productWithFeature],
(productWithFeature) =>
productWithFeature?.plans.find((plan) => plan.features?.some((f) => f.key === props.featureKey)),
],
gateVariant: [
(s) => [
s.billingLoading,
s.hasAvailableFeature,
s.minimumPlanWithFeature,
(_, props) => props.featureKey,
(_, props) => props.currentUsage,
],
(billingLoading, hasAvailableFeature, minimumPlanWithFeature, featureKey, currentUsage) => {
if (hasAvailableFeature(featureKey, currentUsage)) {
return null
}
if (billingLoading) {
return null
}
if (values.isCloudOrDev) {
if (!minimumPlanWithFeature || minimumPlanWithFeature.contact_support) {
return 'contact-sales'
} else {
return 'add-card'
}
} else {
return 'move-to-cloud'
}
},
],
})),
])

0 comments on commit 1bce547

Please sign in to comment.