From f34872115c20a44c6b20890bfe8700bc13feeda9 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Tue, 19 Nov 2024 15:49:35 -0500 Subject: [PATCH] feat: add self serve credit override (#26260) --- frontend/src/lib/constants.tsx | 1 + frontend/src/scenes/billing/CreditCTAHero.tsx | 30 ++++++++++++------- .../scenes/billing/PurchaseCreditsModal.tsx | 7 +++-- frontend/src/scenes/billing/billingLogic.tsx | 13 +++++--- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index cb5831d6a05..29105a42624 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -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] diff --git a/frontend/src/scenes/billing/CreditCTAHero.tsx b/frontend/src/scenes/billing/CreditCTAHero.tsx index 0f076245c0a..85742aa8ef1 100644 --- a/frontend/src/scenes/billing/CreditCTAHero.tsx +++ b/frontend/src/scenes/billing/CreditCTAHero.tsx @@ -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 (
{
)}
- {creditOverview.eligible && creditOverview.status === 'pending' && ( + {isEligible && creditOverview.status === 'pending' && ( <>

We're applying your credits

@@ -78,7 +86,7 @@ export const CreditCTAHero = (): JSX.Element | null => { )} )} - {creditOverview.eligible && creditOverview.status === 'none' && ( + {isEligible && (!creditOverview || creditOverview.status === 'none') && ( <>

Stop burning money.{' '} @@ -87,20 +95,20 @@ export const CreditCTAHero = (): JSX.Element | null => {

Based on your usage, your monthly bill is forecasted to be an average of{' '} - ${creditOverview.estimated_monthly_credit_amount_usd.toFixed(0)}/month over - the next year. + ${estimatedMonthlyCreditAmountUsd.toFixed(0)}/month over the next year.

This qualifies you for a {computedDiscount * 100}% discount by pre-purchasing usage credits. Which gives you a net savings of{' '} $ - {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, + } + )} {' '} over the next year.

diff --git a/frontend/src/scenes/billing/PurchaseCreditsModal.tsx b/frontend/src/scenes/billing/PurchaseCreditsModal.tsx index 5c2d36dc79a..60eb63fc2b0 100644 --- a/frontend/src/scenes/billing/PurchaseCreditsModal.tsx +++ b/frontend/src/scenes/billing/PurchaseCreditsModal.tsx @@ -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 ( showPurchaseCreditsModal(false)} @@ -56,7 +59,7 @@ export const PurchaseCreditsModal = (): JSX.Element | null => { Based on your usage, we think you'll use{' '} $ - {(+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{' '} $ - {(+creditOverview.estimated_monthly_credit_amount_usd * 12).toLocaleString('en-US', { + {(+estimatedMonthlyCreditAmountUsd * 12).toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0, })} diff --git a/frontend/src/scenes/billing/billingLogic.tsx b/frontend/src/scenes/billing/billingLogic.tsx index 4db25f0fc36..ac78f13424b 100644 --- a/frontend/src/scenes/billing/billingLogic.tsx +++ b/frontend/src/scenes/billing/billingLogic.tsx @@ -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([ 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([ 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([ // 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([ 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 }) => {