Skip to content

Commit

Permalink
feat(billing): support initial billing limit for data warehouse (#23384)
Browse files Browse the repository at this point in the history
  • Loading branch information
raquelmsmith authored Jul 3, 2024
1 parent 69dbda6 commit c2a6c45
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 53 deletions.
27 changes: 20 additions & 7 deletions frontend/src/scenes/billing/BillingLimit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import { billingProductLogic } from './billingProductLogic'
export const BillingLimit = ({ product }: { product: BillingProductV2Type }): JSX.Element | null => {
const limitInputRef = useRef<HTMLInputElement | null>(null)
const { billing, billingLoading } = useValues(billingLogic)
const { isEditingBillingLimit, customLimitUsd } = useValues(
const { isEditingBillingLimit, customLimitUsd, currentAndUpgradePlans } = useValues(
billingProductLogic({ product, billingLimitInputRef: limitInputRef })
)
const { setIsEditingBillingLimit, setBillingLimitInput, submitBillingLimitInput } = useActions(
billingProductLogic({ product })
)

const initialBillingLimit = currentAndUpgradePlans?.currentPlan?.initial_billing_limit
const usingInitialBillingLimit = customLimitUsd === initialBillingLimit

if (billing?.billing_period?.interval !== 'month' || !product.subscribed) {
return null
}
Expand All @@ -32,12 +35,22 @@ export const BillingLimit = ({ product }: { product: BillingProductV2Type }): JS
<div className="flex items-center justify-center gap-1">
{customLimitUsd ? (
<>
<Tooltip title="Set a billing limit to control your recurring costs. Some features may stop working if your usage exceeds your limit.">
<span>
You have a <b>${customLimitUsd}</b> billing limit set for{' '}
{product?.name?.toLowerCase()}.
</span>
</Tooltip>
{usingInitialBillingLimit ? (
<Tooltip title="Initial limits protect you from accidentally incurring large unexpected charges. Some features may stop working and data may be dropped if your usage exceeds your limit.">
<span>
This product has a default initial billing limit of{' '}
<b>${initialBillingLimit}</b>.
</span>
</Tooltip>
) : (
<Tooltip title="Set a billing limit to control your recurring costs. Some features may stop working and data may be dropped if your usage exceeds your limit.">
<span>
You have a <b>${customLimitUsd}</b> billing limit set for{' '}
{product?.name?.toLowerCase()}.
</span>
</Tooltip>
)}

<LemonButton
onClick={() => setIsEditingBillingLimit(true)}
status="danger"
Expand Down
41 changes: 41 additions & 0 deletions frontend/src/scenes/billing/InitialBillingLimitNotice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useValues } from 'kea'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { urls } from 'scenes/urls'

import { BillingProductV2Type, ProductKey } from '~/types'

import { billingLogic } from './billingLogic'
import { billingProductLogic } from './billingProductLogic'

const InitialBillingLimitNoticeContents = ({ product }: { product: BillingProductV2Type }): JSX.Element | null => {
const { currentAndUpgradePlans, customLimitUsd } = useValues(billingProductLogic({ product }))
const initialBillingLimit = currentAndUpgradePlans?.currentPlan?.initial_billing_limit
const isUsingInitialBillingLimit = currentAndUpgradePlans?.currentPlan?.initial_billing_limit == customLimitUsd

return isUsingInitialBillingLimit ? (
<LemonBanner
type="info"
className="my-4"
action={{
type: 'primary',
children: 'Change limit',
to: urls.organizationBilling([product.type as ProductKey]),
}}
dismissKey={`initial-billing-limit-notice-${product.type}`}
>
<p className="flex-1 min-w-full sm:min-w-0">
Default initial billing limit of <b className="text-primary">${initialBillingLimit}</b> active.
</p>
<p className="font-normal">
This protects you from accidentally incurring large unexpected charges. Some features may stop working
and data may be dropped if your usage exceeds your limit.
</p>
</LemonBanner>
) : null
}

export const InitialBillingLimitNotice = ({ product_key }: { product_key: ProductKey }): JSX.Element | null => {
const { billing } = useValues(billingLogic)
const product = billing?.products.find((p) => p.type === product_key)
return product ? <InitialBillingLimitNoticeContents product={product} /> : null
}
43 changes: 8 additions & 35 deletions frontend/src/scenes/billing/billingProductLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,41 +288,14 @@ export const billingProductLogic = kea<billingProductLogicType>([
},
setScrollToProductKey: ({ scrollToProductKey }) => {
if (scrollToProductKey && scrollToProductKey === props.product.type) {
const { currentPlan } = values.currentAndUpgradePlans

if (currentPlan?.initial_billing_limit) {
actions.setProductSpecificAlert({
status: 'warning',
title: 'Billing Limit Automatically Applied',
pathName: '/organization/billing',
dismissKey: `auto-apply-billing-limit-${props.product.type}`,
message: `To protect your costs and ours, we've automatically applied a $${currentPlan?.initial_billing_limit} billing limit for ${props.product.name}.`,
action: {
onClick: () => {
actions.setIsEditingBillingLimit(true)
setTimeout(() => {
if (props.billingLimitInputRef?.current) {
props.billingLimitInputRef?.current.focus()
props.billingLimitInputRef?.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
}
}, 0)
},
children: 'Update billing limit',
},
})
} else {
setTimeout(() => {
if (props.productRef?.current) {
props.productRef?.current.scrollIntoView({
behavior: 'smooth',
block: 'center',
})
}
}, 0)
}
setTimeout(() => {
if (props.productRef?.current) {
props.productRef?.current.scrollIntoView({
behavior: 'smooth',
block: 'center',
})
}
}, 0)
}
},
initiateProductUpgrade: ({ plan, product, redirectPath }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useValues } from 'kea'
import { InitialBillingLimitNotice } from 'scenes/billing/InitialBillingLimitNotice'

import { ProductKey } from '~/types'

import { dataWarehouseSettingsLogic } from './settings/dataWarehouseSettingsLogic'

export const DataWarehouseInitialBillingLimitNotice = (): JSX.Element | null => {
const { dataWarehouseSources, selfManagedTables } = useValues(dataWarehouseSettingsLogic)

const hasSources =
(dataWarehouseSources?.results && dataWarehouseSources?.results.length > 0) || selfManagedTables?.length > 0

return hasSources ? <InitialBillingLimitNotice product_key={ProductKey.DATA_WAREHOUSE} /> : null
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SceneExport } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'

import { DataWarehouseBetaNotice } from '../DataWarehouseBetaNotice'
import { DataWarehouseInitialBillingLimitNotice } from '../DataWarehouseInitialBillingLimitNotice'
import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic'
import { DataWarehouseTables } from './DataWarehouseTables'

Expand Down Expand Up @@ -73,6 +74,7 @@ export function DataWarehouseExternalScene(): JSX.Element {
}
/>
<DataWarehouseBetaNotice />
<DataWarehouseInitialBillingLimitNotice />
<BindLogic logic={insightSceneLogic} props={{}}>
<DataWarehouseTables />
</BindLogic>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/scenes/data-warehouse/new/NewSourceWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SceneExport } from 'scenes/sceneTypes'
import { ManualLinkSourceType, SourceConfig } from '~/types'

import { DataWarehouseBetaNotice } from '../DataWarehouseBetaNotice'
import { DataWarehouseInitialBillingLimitNotice } from '../DataWarehouseInitialBillingLimitNotice'
import PostgresSchemaForm from '../external/forms/PostgresSchemaForm'
import SourceForm from '../external/forms/SourceForm'
import { SyncProgressStep } from '../external/forms/SyncProgressStep'
Expand Down Expand Up @@ -73,6 +74,7 @@ export function NewSourceWizard(): JSX.Element {
}
/>
<DataWarehouseBetaNotice />
<DataWarehouseInitialBillingLimitNotice />
<>
<h3>{modalTitle}</h3>
<p>{modalCaption}</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { urls } from 'scenes/urls'
import { DataWarehouseSettingsTab } from '~/types'

import { DataWarehouseBetaNotice } from '../DataWarehouseBetaNotice'
import { DataWarehouseInitialBillingLimitNotice } from '../DataWarehouseInitialBillingLimitNotice'
import { DataWarehouseManagedSourcesTable } from './DataWarehouseManagedSourcesTable'
import { DataWarehouseSelfManagedSourcesTable } from './DataWarehouseSelfManagedSourcesTable'
import { dataWarehouseSettingsLogic, humanFriendlyDataWarehouseSettingsTabName } from './dataWarehouseSettingsLogic'
Expand Down Expand Up @@ -46,6 +47,7 @@ export function DataWarehouseSettingsScene(): JSX.Element {
}
/>
<DataWarehouseBetaNotice />
<DataWarehouseInitialBillingLimitNotice />
<LemonTabs
activeKey={currentTab}
onChange={(tab) => router.actions.push(urls.dataWarehouseSettings(tab as DataWarehouseSettingsTab))}
Expand Down
12 changes: 1 addition & 11 deletions frontend/src/scenes/onboarding/OnboardingBillingStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IconCheckCircle } from '@posthog/icons'
import { LemonBanner, LemonButton } from '@posthog/lemon-ui'
import { LemonButton } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { BillingUpgradeCTA } from 'lib/components/BillingUpgradeCTA'
import { StarHog } from 'lib/components/hedgehogs'
Expand Down Expand Up @@ -33,7 +33,6 @@ export const OnboardingBillingStep = ({
const { currentAndUpgradePlans } = useValues(billingProductLogic({ product }))
const { reportBillingUpgradeClicked } = useActions(eventUsageLogic)
const plan = currentAndUpgradePlans?.upgradePlan
const currentPlan = currentAndUpgradePlans?.currentPlan

const [showPlanComp, setShowPlanComp] = useState(false)

Expand Down Expand Up @@ -92,15 +91,6 @@ export const OnboardingBillingStep = ({
>
{showPlanComp ? 'Hide' : 'Show'} plans
</LemonButton>
{currentPlan?.initial_billing_limit && (
<div className="mt-2">
<LemonBanner type="info">
To protect your costs and ours, this product has an initial billing limit of $
{currentPlan.initial_billing_limit}. You can change or remove this limit on the
Billing page.
</LemonBanner>
</div>
)}
</div>
)}

Expand Down

0 comments on commit c2a6c45

Please sign in to comment.