diff --git a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png index 737014b62b7f5..8eeeafbc340a1 100644 Binary files a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png and b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2-with-discount.png differ diff --git a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png index 4922e2d6ca640..3df423c0ff684 100644 Binary files a/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png and b/frontend/__snapshots__/scenes-other-billing-v2--billing-v-2.png differ diff --git a/frontend/src/lib/components/BillingAlertsV2.tsx b/frontend/src/lib/components/BillingAlertsV2.tsx index 1748f0c91d4c2..21a4023a6262a 100644 --- a/frontend/src/lib/components/BillingAlertsV2.tsx +++ b/frontend/src/lib/components/BillingAlertsV2.tsx @@ -7,7 +7,7 @@ import { LemonBanner } from 'lib/lemon-ui/LemonBanner' export function BillingAlertsV2(): JSX.Element | null { const { billingAlert } = useValues(billingLogic) - const { reportBillingAlertShown } = useActions(billingLogic) + const { reportBillingAlertShown, reportBillingAlertActionClicked } = useActions(billingLogic) const { currentLocation } = useValues(router) const [alertHidden, setAlertHidden] = useState(false) @@ -21,14 +21,19 @@ export function BillingAlertsV2(): JSX.Element | null { return null } - const showButton = currentLocation.pathname !== urls.organizationBilling() + const showButton = billingAlert.contactSupport || currentLocation.pathname !== urls.organizationBilling() const buttonProps = billingAlert.contactSupport ? { to: 'mailto:sales@posthog.com', - children: 'Contact support', + children: billingAlert.buttonCTA || 'Contact support', + onClick: () => reportBillingAlertActionClicked(billingAlert), + } + : { + to: urls.organizationBilling(), + children: 'Manage billing', + onClick: () => reportBillingAlertActionClicked(billingAlert), } - : { to: urls.organizationBilling(), children: 'Manage billing' } return (
@@ -36,6 +41,7 @@ export function BillingAlertsV2(): JSX.Element | null { type={billingAlert.status} action={showButton ? buttonProps : undefined} onClose={billingAlert.status !== 'error' ? () => setAlertHidden(true) : undefined} + dismissKey={billingAlert.dismissKey} > {billingAlert.title}
diff --git a/frontend/src/scenes/billing/Billing.tsx b/frontend/src/scenes/billing/Billing.tsx index 87f2ab775409b..481b89aa9defd 100644 --- a/frontend/src/scenes/billing/Billing.tsx +++ b/frontend/src/scenes/billing/Billing.tsx @@ -264,7 +264,7 @@ export function Billing(): JSX.Element {
-
+

Products

{isOnboarding && upgradeAllProductsLink && ( ): BillingV2Type => { @@ -54,6 +56,7 @@ export const billingLogic = kea([ actions({ setShowLicenseDirectInput: (show: boolean) => ({ show }), reportBillingAlertShown: (alertConfig: BillingAlertConfig) => ({ alertConfig }), + reportBillingAlertActionClicked: (alertConfig: BillingAlertConfig) => ({ alertConfig }), reportBillingV2Shown: true, registerInstrumentationProps: true, setRedirectPath: true, @@ -130,9 +133,22 @@ export const billingLogic = kea([ (s) => [s.preflight, s.billing], (preflight, billing): boolean => !!preflight?.is_debug && !billing?.billing_period, ], + projectedTotalAmountUsd: [ + (s) => [s.billing], + (billing: BillingV2Type): number => { + if (!billing) { + return 0 + } + let projectedTotal = 0 + for (const product of billing.products || []) { + projectedTotal += parseFloat(product.projected_amount_usd || '0') + } + return projectedTotal + }, + ], billingAlert: [ - (s) => [s.billing, s.preflight], - (billing, preflight): BillingAlertConfig | undefined => { + (s) => [s.billing, s.preflight, s.projectedTotalAmountUsd], + (billing, preflight, projectedTotalAmountUsd): BillingAlertConfig | undefined => { if (!billing || !preflight?.cloud) { return } @@ -163,7 +179,7 @@ export const billingLogic = kea([ } } - const productOverLimit = billing.products?.find((x) => { + const productOverLimit = billing.products?.find((x: BillingProductV2Type) => { return x.percentage_usage > 1 }) @@ -190,6 +206,21 @@ export const billingLogic = kea([ )}% of your ${productApproachingLimit.usage_key.toLowerCase()} allocation.`, } } + + if ( + billing.current_total_amount_usd_after_discount && + (parseFloat(billing.current_total_amount_usd_after_discount) > 1000 || + projectedTotalAmountUsd > 1000) && + billing.billing_period?.interval === 'month' + ) { + return { + status: 'info', + title: `Switch to annual up-front billing to save up to 20% on your bill.`, + contactSupport: true, + buttonCTA: 'Contact sales', + dismissKey: 'annual-billing-cta', + } + } }, ], }), @@ -231,6 +262,11 @@ export const billingLogic = kea([ ...alertConfig, }) }, + reportBillingAlertActionClicked: ({ alertConfig }) => { + posthog.capture('billing alert action clicked', { + ...alertConfig, + }) + }, loadBillingSuccess: () => { if ( router.values.location.pathname.includes('/organization/billing') && @@ -276,7 +312,6 @@ export const billingLogic = kea([ } }, })), - afterMount(({ actions }) => { actions.loadBilling() }),