0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 13:39:22 +01:00

feat: add self serve credit override (#26260)

This commit is contained in:
Zach Waterfield 2024-11-19 15:49:35 -05:00 committed by GitHub
parent 02d74072a4
commit f34872115c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 17 deletions

View File

@ -230,6 +230,7 @@ export const FEATURE_FLAGS = {
EDIT_DWH_SOURCE_CONFIG: 'edit_dwh_source_config', // owner: @Gilbert09 #team-data-warehouse
AI_SURVEY_RESPONSE_SUMMARY: 'ai-survey-response-summary', // owner: @pauldambra
CUSTOM_CHANNEL_TYPE_RULES: 'custom-channel-type-rules', // owner: @robbie-c #team-web-analytics
SELF_SERVE_CREDIT_OVERRIDE: 'self-serve-credit-override', // owner: @zach
EXPERIMENTS_MIGRATION_DISABLE_UI: 'experiments-migration-disable-ui', // owner: @jurajmajerik #team-experiments
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

View File

@ -2,19 +2,25 @@ import { IconX } from '@posthog/icons'
import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { BurningMoneyHog } from 'lib/components/hedgehogs'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import useResizeObserver from 'use-resize-observer'
import { billingLogic } from './billingLogic'
import { PurchaseCreditsModal } from './PurchaseCreditsModal'
export const DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD = 500
export const CreditCTAHero = (): JSX.Element | null => {
const { width, ref: heroRef } = useResizeObserver()
const { featureFlags } = useValues(featureFlagLogic)
const { creditOverview, isPurchaseCreditsModalOpen, isCreditCTAHeroDismissed, computedDiscount } =
useValues(billingLogic)
const { showPurchaseCreditsModal, toggleCreditCTAHeroDismissed } = useActions(billingLogic)
if (!creditOverview.eligible || creditOverview.status === 'paid') {
const isEligible = creditOverview.eligible || featureFlags[FEATURE_FLAGS.SELF_SERVE_CREDIT_OVERRIDE]
if (creditOverview.status === 'paid' || !isEligible) {
return null
}
@ -37,6 +43,8 @@ export const CreditCTAHero = (): JSX.Element | null => {
)
}
const estimatedMonthlyCreditAmountUsd =
creditOverview?.estimated_monthly_credit_amount_usd || DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD
return (
<div
className="flex relative justify-between items-start rounded-lg bg-bg-light border mb-2 gap-2"
@ -56,7 +64,7 @@ export const CreditCTAHero = (): JSX.Element | null => {
</div>
)}
<div className="p-4 flex-1">
{creditOverview.eligible && creditOverview.status === 'pending' && (
{isEligible && creditOverview.status === 'pending' && (
<>
<h1 className="mb-0">We're applying your credits</h1>
<p className="mt-2 mb-0 max-w-xl">
@ -78,7 +86,7 @@ export const CreditCTAHero = (): JSX.Element | null => {
)}
</>
)}
{creditOverview.eligible && creditOverview.status === 'none' && (
{isEligible && (!creditOverview || creditOverview.status === 'none') && (
<>
<h2 className="mb-0">
Stop burning money.{' '}
@ -87,20 +95,20 @@ export const CreditCTAHero = (): JSX.Element | null => {
</h2>
<p className="mt-2 mb-0 max-w-xl">
Based on your usage, your monthly bill is forecasted to be an average of{' '}
<strong>${creditOverview.estimated_monthly_credit_amount_usd.toFixed(0)}/month</strong> over
the next year.
<strong>${estimatedMonthlyCreditAmountUsd.toFixed(0)}/month</strong> over the next year.
</p>
<p className="mt-2 mb-0 max-w-xl">
This qualifies you for a <strong>{computedDiscount * 100}% discount</strong> by
pre-purchasing usage credits. Which gives you a net savings of{' '}
<strong>
$
{Math.round(
creditOverview.estimated_monthly_credit_amount_usd * computedDiscount * 12
).toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})}
{Math.round(estimatedMonthlyCreditAmountUsd * computedDiscount * 12).toLocaleString(
'en-US',
{
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}
)}
</strong>{' '}
over the next year.
</p>

View File

@ -8,6 +8,7 @@ import { LemonRadio } from 'lib/lemon-ui/LemonRadio'
import { BillingGauge } from './BillingGauge'
import { billingLogic } from './billingLogic'
import { DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD } from './CreditCTAHero'
import { BillingGaugeItemKind } from './types'
export const PurchaseCreditsModal = (): JSX.Element | null => {
@ -16,6 +17,8 @@ export const PurchaseCreditsModal = (): JSX.Element | null => {
const { openSupportForm } = useActions(supportLogic)
const creditInputValue: number = +creditForm.creditInput || 0
const estimatedMonthlyCreditAmountUsd =
creditOverview.estimated_monthly_credit_amount_usd || DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD
return (
<LemonModal
onClose={() => showPurchaseCreditsModal(false)}
@ -56,7 +59,7 @@ export const PurchaseCreditsModal = (): JSX.Element | null => {
Based on your usage, we think you'll use{' '}
<b>
$
{(+creditOverview.estimated_monthly_credit_amount_usd).toLocaleString('en-US', {
{(+estimatedMonthlyCreditAmountUsd).toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})}
@ -64,7 +67,7 @@ export const PurchaseCreditsModal = (): JSX.Element | null => {
of credits per month, for a total of{' '}
<b>
$
{(+creditOverview.estimated_monthly_credit_amount_usd * 12).toLocaleString('en-US', {
{(+estimatedMonthlyCreditAmountUsd * 12).toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})}

View File

@ -18,6 +18,7 @@ import { userLogic } from 'scenes/userLogic'
import { BillingPlanType, BillingProductV2Type, BillingType, ProductKey } from '~/types'
import type { billingLogicType } from './billingLogicType'
import { DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD } from './CreditCTAHero'
export const ALLOCATION_THRESHOLD_ALERT = 0.85 // Threshold to show warning of event usage near limit
export const ALLOCATION_THRESHOLD_BLOCK = 1.2 // Threshold to block usage
@ -325,7 +326,7 @@ export const billingLogic = kea<billingLogicType>([
creditOverview: [
{
eligible: false,
estimated_monthly_credit_amount_usd: 0,
estimated_monthly_credit_amount_usd: DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD,
status: 'none',
invoice_url: null,
collection_method: null,
@ -340,7 +341,10 @@ export const billingLogic = kea<billingLogicType>([
if (!values.creditForm.creditInput) {
actions.setCreditFormValue(
'creditInput',
Math.round(response.estimated_monthly_credit_amount_usd * 12)
Math.round(
(response.estimated_monthly_credit_amount_usd ||
DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD) * 12
)
)
}
@ -352,7 +356,7 @@ export const billingLogic = kea<billingLogicType>([
// Return default values if not subscribed
return {
eligible: false,
estimated_monthly_credit_amount_usd: 0,
estimated_monthly_credit_amount_usd: DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD,
status: 'none',
invoice_url: null,
collection_method: null,
@ -531,7 +535,8 @@ export const billingLogic = kea<billingLogicType>([
posthog.capture('credits cta shown', {
eligible: creditOverview.eligible,
status: creditOverview.status,
estimated_monthly_credit_amount_usd: creditOverview.estimated_monthly_credit_amount_usd,
estimated_monthly_credit_amount_usd:
creditOverview.estimated_monthly_credit_amount_usd || DEFAULT_ESTIMATED_MONTHLY_CREDIT_AMOUNT_USD,
})
},
toggleCreditCTAHeroDismissed: ({ isDismissed }) => {