-
+
{plan.name}
{isCurrentPlan ? (
Current plan
- ) : plan.new ? (
-
New
- ) : (
- <>>
- )}
+ ) : plan.nameBadge ? (
+
+ {plan.nameBadge}
+
+ ) : null}
-
{plan.preface}
+
{plan.description}
-
-
-
-
-
+
+
+
- {!isTeamTierEnabled &&
}
+
+
+ {plan.features.map((feature) => (
+ -
+
+ {feature}
+
+ ))}
+
-
- {plan.features.map((feature) => (
- -
-
-
-
- {feature}
-
- ))}
-
+
+
+
+
)
}
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx
index eae5482a505..27346959ee4 100644
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx
+++ b/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx
@@ -10,8 +10,8 @@ import { includes, without } from 'lodash'
import { useRouter } from 'next/router'
import { useReducer, useRef, useState } from 'react'
import { Button, Input, Modal } from 'ui'
-import { CANCELLATION_REASONS } from './Tier.constants'
import ProjectUpdateDisabledTooltip from '../../ProjectUpdateDisabledTooltip'
+import { CANCELLATION_REASONS } from '../../Billing.constants'
export interface ExitSurveyModalProps {
visible: boolean
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/Tier.constants.ts b/studio/components/interfaces/BillingV2/Subscription/Tier/Tier.constants.ts
deleted file mode 100644
index 20ad87687bc..00000000000
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/Tier.constants.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-export interface PricingInformation {
- id: string
- new: boolean
- name: string
- preface?: string
- features: string[]
- footer?: string
-}
-
-// This only contains the meta information of plans like description and features, prices are from the API
-// Also ideally shift this to common folder (e.g packages/data)
-export const SUBSCRIPTION_PLANS: PricingInformation[] = [
- {
- id: 'tier_free',
- new: false,
- name: 'Free',
- preface: undefined,
- features: [
- 'Social OAuth providers',
- 'Up to 500MB database & 1GB file storage',
- 'Up to 2GB bandwidth',
- 'Up to 50MB file uploads',
- '50,000 monthly active users',
- 'Up to 500K Edge Function invocations',
- '200 concurrent Realtime connections',
- '2 million Realtime messages',
- '1-day log retention',
- 'Community support',
- ],
- footer: undefined,
- },
- {
- id: 'tier_pro',
- new: false,
- name: 'Pro',
- preface: undefined,
- features: [
- 'No project pausing',
- '8GB database & 100GB file storage',
- '50GB bandwidth',
- '5GB file uploads',
- '100,000 monthly active users',
- '2M Edge Function invocations',
- '500 concurrent Realtime connections',
- '5 million Realtime messages',
- '7-day log retention',
- 'Email support',
- 'Daily backups',
- ],
- footer:
- 'Your cost control settings determine if you wish to pay for anything more than the above.',
- },
- {
- id: 'tier_team',
- new: true,
- name: 'Team',
- preface: 'Everything in Pro, plus:',
- features: [
- 'Organization member roles (ABAC)',
- 'Standardised Security Questionnaire',
- 'SOC2',
- 'SSO for Supabase Dashboard',
- 'Priority email support & SLAs',
- '14 day backups',
- '28 day log retention',
- ],
- footer: undefined,
- },
- {
- id: 'tier_enterprise',
- new: false,
- name: 'Enterprise',
- preface: 'For large-scale applications managing serious workloads',
- features: [`Designated Support manager & SLAs`, `SSO/ SAML`, `Custom contracts & invoicing`],
- footer: undefined,
- },
-]
-
-export const CANCELLATION_REASONS = [
- 'Pricing',
- "My project isn't getting traction",
- 'Poor customer service',
- 'Missing feature',
- "I didn't see the value",
- "Supabase didn't meet my needs",
- 'Dashboard is too complicated',
- 'Postgres is too complicated',
- 'Problem not solved',
- 'Bug issues',
- 'I decided to use something else',
- 'My work has finished/discontinued',
- 'I’m migrating to/starting a new project',
- 'None of the above',
-]
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/TierUpdateSidePanel.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/TierUpdateSidePanel.tsx
index 9f7321bf02a..4e69db4e9fc 100644
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/TierUpdateSidePanel.tsx
+++ b/studio/components/interfaces/BillingV2/Subscription/Tier/TierUpdateSidePanel.tsx
@@ -5,9 +5,7 @@ import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-l
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useProjectPlansQuery } from 'data/subscriptions/project-plans-query'
import { useProjectSubscriptionUpdateMutation } from 'data/subscriptions/project-subscription-update-mutation'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
-import { useFlag, useStore } from 'hooks'
-import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
+import { useStore } from 'hooks'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useSubscriptionPageStateSnapshot } from 'state/subscription-page'
@@ -16,7 +14,7 @@ import EnterpriseCard from './EnterpriseCard'
import ExitSurveyModal from './ExitSurveyModal'
import MembersExceedLimitModal from './MembersExceedLimitModal'
import PaymentMethodSelection from './PaymentMethodSelection'
-import { SUBSCRIPTION_PLANS } from './Tier.constants'
+import { plans as subscriptionsPlans } from 'shared-data/plans'
const TierUpdateSidePanel = () => {
const { ui } = useStore()
@@ -36,16 +34,13 @@ const TierUpdateSidePanel = () => {
const { data: plans, isLoading: isLoadingPlans } = useProjectPlansQuery({ projectRef })
const { data: addons } = useProjectAddonsQuery({ projectRef })
const { data: membersExceededLimit } = useFreeProjectLimitCheckQuery({ slug })
- const { data: subscription, isLoading } = useProjectSubscriptionV2Query({ projectRef })
const { mutateAsync: updateSubscriptionTier } = useProjectSubscriptionUpdateMutation()
const availablePlans = plans ?? []
const subscriptionAddons = addons?.selected_addons ?? []
- const userIsOnTeamTier = subscription?.plan?.id === 'team'
- const teamTierEnabled = useFlag('teamTier') || userIsOnTeamTier
const hasMembersExceedingFreeTierLimit = (membersExceededLimit || []).length > 0
- const selectedTierMeta = SUBSCRIPTION_PLANS.find((tier) => tier.id === selectedTier)
+ const selectedTierMeta = subscriptionsPlans.find((tier) => tier.id === selectedTier)
const selectedPlanMeta = availablePlans.find(
(plan) => plan.id === selectedTier?.split('tier_')[1]
)
@@ -118,22 +113,15 @@ const TierUpdateSidePanel = () => {
>
- {SUBSCRIPTION_PLANS.map((plan) => {
+ {subscriptionsPlans.map((plan) => {
const planMeta = availablePlans.find((p) => p.id === plan.id.split('tier_')[1])
+ const tierMeta = subscriptionsPlans.find((it) => it.id === plan.id)
const price = planMeta?.price ?? 0
const isDowngradeOption = planMeta?.change_type === 'downgrade'
const isCurrentPlan = planMeta?.is_current ?? false
- if (plan.id === 'tier_team' && !teamTierEnabled) return null
if (plan.id === 'tier_enterprise') {
- return (
-
- )
+ return
}
return (
@@ -141,7 +129,7 @@ const TierUpdateSidePanel = () => {
key={plan.id}
className={clsx(
'border rounded-md px-4 py-4 flex flex-col items-start justify-between',
- teamTierEnabled && plan.id === 'tier_enterprise' ? 'col-span-12' : 'col-span-4',
+ plan.id === 'tier_enterprise' ? 'col-span-12' : 'col-span-12 md:col-span-4',
plan.id === 'tier_enterprise' ? 'bg-scale-200' : 'bg-scale-300'
)}
>
@@ -152,9 +140,9 @@ const TierUpdateSidePanel = () => {
Current plan
- ) : plan.new ? (
+ ) : plan.nameBadge ? (
- New
+ {plan.nameBadge}
) : (
<>>
@@ -169,32 +157,33 @@ const TierUpdateSidePanel = () => {
) : (
${price}
)}
-
per month
+
{tierMeta?.costUnit}
-
-
- Usage-based plan
+
{isCurrentPlan ? (
- ) : (
+ ) : plan.id !== 'tier_team' ? (
+ ) : (
+
+
+
+
+
)}
diff --git a/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx b/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx
index 7a80c2d4b9f..b1fe6d9553f 100644
--- a/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx
+++ b/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx
@@ -8,7 +8,7 @@ import { useStore, checkPermissions } from 'hooks'
import { API_URL, PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import { delete_, post } from 'lib/common/fetch'
import TextConfirmModal from 'components/ui/Modals/TextConfirmModal'
-import { CANCELLATION_REASONS } from 'components/interfaces/Billing/Billing.constants'
+import { CANCELLATION_REASONS } from 'components/interfaces/BillingV2/Billing.constants'
interface Props {
type?: 'danger' | 'default'
diff --git a/studio/components/interfaces/Settings/Logs/UpgradePrompt.tsx b/studio/components/interfaces/Settings/Logs/UpgradePrompt.tsx
index 59fde57debf..25a45b84b75 100644
--- a/studio/components/interfaces/Settings/Logs/UpgradePrompt.tsx
+++ b/studio/components/interfaces/Settings/Logs/UpgradePrompt.tsx
@@ -17,10 +17,6 @@ const UpgradePrompt: React.FC
= ({ show, setShowUpgradePrompt, subscripti
const router = useRouter()
const { ref } = router.query
- // Team plan is enabled when the flag is turned on OR the user is already on the team plan (manually assigned by us)
- const userIsOnTeamTier = subscription?.tier?.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.TEAM
- const teamTierEnabled = useFlag('teamTier') || userIsOnTeamTier
-
return (
= ({ show, setShowUpgradePrompt, subscripti
Pro
{TIER_QUERY_LIMITS.PRO.text}
- {teamTierEnabled && (
-
-
Team
-
{TIER_QUERY_LIMITS.TEAM.text}
-
- )}
+
+
Team
+
{TIER_QUERY_LIMITS.TEAM.text}
+
Enterprise
{TIER_QUERY_LIMITS.ENTERPRISE.text}
diff --git a/studio/next.config.js b/studio/next.config.js
index 517f1b8e1cf..5305c2d290e 100644
--- a/studio/next.config.js
+++ b/studio/next.config.js
@@ -4,7 +4,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
})
// this is required to use shared packages in the packages directory
-const withTM = require('next-transpile-modules')(['ui', 'common'])
+const withTM = require('next-transpile-modules')(['ui', 'common', 'shared-data'])
// Required for nextjs standalone build
const path = require('path')
diff --git a/studio/pages/project/[ref]/settings/billing/update/index.tsx b/studio/pages/project/[ref]/settings/billing/update/index.tsx
index 024c2d06407..b1da19e8d66 100644
--- a/studio/pages/project/[ref]/settings/billing/update/index.tsx
+++ b/studio/pages/project/[ref]/settings/billing/update/index.tsx
@@ -3,7 +3,7 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { Button, Modal } from 'ui'
-import { useFlag, useStore } from 'hooks'
+import { useStore } from 'hooks'
import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query'
import { get } from 'lib/common/fetch'
import { API_URL, PRICING_TIER_PRODUCT_IDS, STRIPE_PRODUCT_IDS } from 'lib/constants'
@@ -103,10 +103,6 @@ const BillingUpdate: NextPageWithLayout = () => {
}
}
- // Team tier is enabled when the flag is turned on OR the user is already on the team tier (manually assigned by us)
- const userIsOnTeamTier = subscription?.tier?.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.TEAM
- const teamTierEnabled = useFlag('teamTier') || userIsOnTeamTier
-
if (isLoadingProducts || isEnterprise) {
return (
@@ -115,13 +111,11 @@ const BillingUpdate: NextPageWithLayout = () => {
)
}
- const productTiers = (products?.tiers ?? []).filter(
- (tier) => teamTierEnabled || tier.id !== STRIPE_PRODUCT_IDS.TEAM
- )
+ const productTiers = (products?.tiers ?? [])
return (
<>
-