mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
This reverts commit 34241f1f66.
This commit is contained in:
1 parent
c65935d0d3
commit
40712e94c8
4 files changed
+174
-18
No files matched your search
@@ -13,6 +13,16 @@ import HashAnchorScroll from './HashAnchorScroll'
|
||||
import PricingComparisonSection from './PricingComparisonSection'
|
||||
import PricingPlansSection from './PricingPlansSection'
|
||||
|
||||
export type PricingPageExperimentVariant =
|
||||
| 'control'
|
||||
// GROWTH-694: flexibility/hourly billing
|
||||
| 'flexibility' // concrete FAQ example as section between cards and buttons
|
||||
| 'flexibility_card' // "no lock-in" messaging on Pro card warning
|
||||
| 'hourly_rate' // actual hourly compute rate on Pro card warning
|
||||
// GROWTH-697: pro project cost visibility
|
||||
| 'multi_project' // "first project included, additional from $10/mo" on Pro card
|
||||
| 'estimate_cta' // "estimate your cost" link on Pro card scrolling to calculator
|
||||
|
||||
export default function PricingContent() {
|
||||
return (
|
||||
<DefaultLayout>
|
||||
|
||||
@@ -2,15 +2,71 @@
|
||||
|
||||
import PricingPlans from '~/components/Pricing/PricingPlans'
|
||||
import { useOrganizations } from '~/data/organizations'
|
||||
import { hasConsented, posthogClient } from 'common'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import type { PricingPageExperimentVariant } from './PricingContent'
|
||||
|
||||
const EXPERIMENT_ID = 'pricingPageExperiment' as const
|
||||
|
||||
const VALID_VARIANTS: PricingPageExperimentVariant[] = [
|
||||
'control',
|
||||
'flexibility',
|
||||
'flexibility_card',
|
||||
'hourly_rate',
|
||||
'multi_project',
|
||||
'estimate_cta',
|
||||
]
|
||||
|
||||
export default function PricingPlansSection() {
|
||||
const { isLoading, organizations } = useOrganizations()
|
||||
const hasExistingOrganizations = !isLoading && organizations.length > 0
|
||||
|
||||
// Pricing value/flexibility A/B experiment.
|
||||
// Uses client-side PostHog directly — server-side evaluation lacks full person context for www pages.
|
||||
// DevToolbar overrides (x-ph-flag-overrides cookie) are respected in local dev via posthogClient.getFeatureFlag.
|
||||
const [flagValue, setFlagValue] = useState<PricingPageExperimentVariant | false | undefined>(
|
||||
() =>
|
||||
posthogClient.getFeatureFlag(EXPERIMENT_ID) as
|
||||
| PricingPageExperimentVariant
|
||||
| false
|
||||
| undefined
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return posthogClient.onFeatureFlags(() => {
|
||||
const value = posthogClient.getFeatureFlag(EXPERIMENT_ID)
|
||||
setFlagValue(value as PricingPageExperimentVariant | false | undefined)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const isInExperiment = VALID_VARIANTS.includes(flagValue as PricingPageExperimentVariant)
|
||||
const showFlexibilitySection = flagValue === 'flexibility'
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInExperiment) return
|
||||
|
||||
posthogClient.captureExperimentExposure(EXPERIMENT_ID, { variant: flagValue }, hasConsented())
|
||||
}, [isInExperiment, flagValue])
|
||||
|
||||
return (
|
||||
<PricingPlans
|
||||
organizations={organizations}
|
||||
hasExistingOrganizations={hasExistingOrganizations}
|
||||
/>
|
||||
<>
|
||||
<PricingPlans
|
||||
organizations={organizations}
|
||||
hasExistingOrganizations={hasExistingOrganizations}
|
||||
experimentVariant={isInExperiment ? (flagValue as PricingPageExperimentVariant) : undefined}
|
||||
/>
|
||||
|
||||
{showFlexibilitySection && (
|
||||
<div className="mx-auto max-w-xl px-8 mt-10 xl:mt-16 text-center">
|
||||
<p className="text-foreground text-sm">
|
||||
Need more power for a launch? Scale up instantly.{' '}
|
||||
<span className="text-foreground-light">
|
||||
Scale back down after — you only pay for the hours you use.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { PricingPageExperimentVariant } from '~/app/pricing/PricingContent'
|
||||
import { Organization } from '~/data/organizations'
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { Check } from 'lucide-react'
|
||||
@@ -12,9 +13,14 @@ import UpgradePlan from './UpgradePlan'
|
||||
interface PricingPlansProps {
|
||||
organizations?: Organization[]
|
||||
hasExistingOrganizations?: boolean
|
||||
experimentVariant?: PricingPageExperimentVariant
|
||||
}
|
||||
|
||||
const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansProps) => {
|
||||
const PricingPlans = ({
|
||||
organizations,
|
||||
hasExistingOrganizations,
|
||||
experimentVariant,
|
||||
}: PricingPlansProps) => {
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
|
||||
return (
|
||||
@@ -66,10 +72,36 @@ const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansP
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn('flex flex-col', isProPlan && 'xl:mb-12')}>
|
||||
<p className="text-foreground-light text-sm 2xl:pr-4 mb-4">
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isProPlan &&
|
||||
(!experimentVariant || experimentVariant === 'control') &&
|
||||
'xl:mb-12'
|
||||
)}
|
||||
>
|
||||
<p
|
||||
className={cn(
|
||||
'text-foreground-light text-sm 2xl:pr-4',
|
||||
isUpgradablePlan &&
|
||||
(experimentVariant === 'flexibility_card' ||
|
||||
experimentVariant === 'hourly_rate')
|
||||
? 'mb-2'
|
||||
: 'mb-4'
|
||||
)}
|
||||
>
|
||||
{plan.description}
|
||||
</p>
|
||||
{isUpgradablePlan && experimentVariant === 'flexibility_card' && (
|
||||
<p className="text-brand text-xs mb-4">
|
||||
No lock-in. Scale up or down anytime — you only pay for the hours you use.
|
||||
</p>
|
||||
)}
|
||||
{isUpgradablePlan && experimentVariant === 'hourly_rate' && (
|
||||
<p className="text-brand text-xs mb-4">
|
||||
Compute from $0.01/hr. Scale anytime, pay only for what you use.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{isUpgradablePlan && hasExistingOrganizations ? (
|
||||
<UpgradePlan
|
||||
@@ -120,20 +152,48 @@ const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansP
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{plan.warning && (
|
||||
{isUpgradablePlan && experimentVariant === 'multi_project' ? (
|
||||
<div className="mt-4 flex flex-col gap-1">
|
||||
<span className="text-[13px] leading-4 inline-flex gap-1 items-center">
|
||||
{plan.warning}
|
||||
<span className="text-[13px] leading-4">
|
||||
First project included. Additional projects from $10/mo.
|
||||
</span>
|
||||
{(plan.name === 'Pro' || plan.name === 'Team') && (
|
||||
<Link
|
||||
href="#addon-compute"
|
||||
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
|
||||
>
|
||||
Need more compute?
|
||||
</Link>
|
||||
)}
|
||||
<Link
|
||||
href="#addon-compute"
|
||||
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
|
||||
>
|
||||
See how pricing scales
|
||||
</Link>
|
||||
</div>
|
||||
) : isUpgradablePlan && experimentVariant === 'estimate_cta' ? (
|
||||
<div className="mt-4 flex flex-col gap-1">
|
||||
<span className="text-[13px] leading-4">{plan.warning}</span>
|
||||
<Link
|
||||
href="#addon-compute"
|
||||
className="hover:underline text-brand text-[13px] m-0 p-0 leading-3 font-medium"
|
||||
>
|
||||
Estimate your cost ↓
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
plan.warning && (
|
||||
<div className="mt-4 flex flex-col gap-1">
|
||||
<span
|
||||
className={cn(
|
||||
'text-[13px] leading-4 inline-flex gap-1 items-center'
|
||||
)}
|
||||
>
|
||||
{plan.warning}
|
||||
</span>
|
||||
{(plan.name === 'Pro' || plan.name === 'Team') && (
|
||||
<Link
|
||||
href="#addon-compute"
|
||||
className="hover:underline text-foreground-lighter text-[13px] m-0 p-0 leading-3"
|
||||
>
|
||||
Need more compute?
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3115,6 +3115,35 @@ export interface StoragePublicBucketSelectPolicyWarningDismissButtonClickedEvent
|
||||
groups: TelemetryGroups
|
||||
}
|
||||
|
||||
/**
|
||||
* User was exposed to the pricing value/flexibility experiment on the /pricing page.
|
||||
*
|
||||
* @group Events
|
||||
* @source www
|
||||
* @page /pricing
|
||||
*/
|
||||
export interface PricingPageExperimentExposedEvent {
|
||||
action: 'pricing_page_experiment_exposed'
|
||||
properties: {
|
||||
/**
|
||||
* Experiment identifier for tracking
|
||||
*/
|
||||
experiment_id: 'pricingPageExperiment'
|
||||
/**
|
||||
* Experiment variant.
|
||||
* GROWTH-694: flexibility (section), flexibility_card (on card), hourly_rate (on card)
|
||||
* GROWTH-697: multi_project (on card), estimate_cta (on card)
|
||||
*/
|
||||
variant:
|
||||
| 'control'
|
||||
| 'flexibility'
|
||||
| 'flexibility_card'
|
||||
| 'hourly_rate'
|
||||
| 'multi_project'
|
||||
| 'estimate_cta'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when an access token is successfully created.
|
||||
*
|
||||
@@ -3377,6 +3406,7 @@ export type TelemetryEvent =
|
||||
| ComputeBadgeUpgradeClickedEvent
|
||||
| FreeMicroUpgradeBannerDismissedEvent
|
||||
| FreeMicroUpgradeBannerCtaClickedEvent
|
||||
| PricingPageExperimentExposedEvent
|
||||
| HeaderUpgradeCtaClickedEvent
|
||||
| AccessTokenCreatedEvent
|
||||
| AccessTokenRemovedEvent
|
||||
|
||||
Reference in new issue
Block a user