- Your plan includes a limited amount of usage. If the usage on your project exceeds
- these quotas, your subscription will be charged for the overages. It may take up to 24
- hours for usage stats to update.
-
- ) : (
-
- Your plan includes a limited amount of usage. If the usage on your project exceeds
- these quotas, you may experience restrictions, as you are currently not billed for
- overages. It may take up to 24 hours for usage stats to update.
-
- )}
-
- {hasExceededAnyLimits && (
-
- snap.setPanelKey(
- currentPlan?.id === 'free' ? 'subscriptionPlan' : 'costControl'
- )
- }
- >
- {currentPlan?.id === 'free' ? 'Upgrade plan' : 'Change spend cap'}
- ,
- ]}
- >
- Your project can become unresponsive or enter read only mode.{' '}
- {currentPlan?.id === 'free'
- ? 'Please upgrade to the Pro plan to ensure that your project remains available.'
- : 'Please disable spend cap to ensure that your project remains available.'}
-
- )}
-
- {isLoadingUsage ? (
-
- The following table shows your upcoming costs. Depending on your usage, the final amount
- may vary. Next invoice on{' '}
- {billingCycleEnd.format('MMM DD, YYYY')}.
-
-
- {isLoadingUpcomingInvoice ? (
-
-
-
-
-
- ) : upcomingInvoiceError !== null ? (
-
-
- ,
- ]}
- >
- Try refreshing your browser, but if the issue persists, please reach out to us via
- support.
-
- ) : (
-
- Control whether to use beyond your plans included quota
-
-
-
-
More information
-
-
-
-
Spend cap
-
-
-
-
-
-
-
-
- {isLoading ? (
-
-
-
-
-
- ) : (
-
- {['team', 'enterprise'].includes(currentPlan?.id || '') ? (
-
- {currentPlan?.name || ''} plan requires you to have spend cap off at all times. Your
- project will never become unresponsive. Only when your{' '}
-
- included usage
- {' '}
- is exceeded will you be charged for any additional usage.
-
- ) : (
-
- You can control whether your project is charged for additional usage beyond the{' '}
-
- included quota
- {' '}
- of your subscription plan. If you need to go beyond the included quota, simply
- switch off your spend cap to pay for additional usage.
-
- )}
-
-
-
-
-
-
-
-
-
- Spend cap is {isUsageBillingEnabled ? 'disabled' : 'enabled'}
-
-
- {isUsageBillingEnabled ? (
- You will be charged for any usage above the included quota.
- ) : (
-
- You won't be charged any extra for usage. However, your project could become
- unresponsive or enter read only mode if you exceed the included quota.
-
- )}
-
- {isUsageBillingEnabled && (
-
- Your project will never become unresponsive. Only when your usage reaches the
- quota limit will you be charged for any excess usage.
-
- }>
-
- About spend cap
-
-
-
- }
- tooltip={!canUpdateSpendCap ? 'You do not have permission to update spend cap' : undefined}
- >
-
-
-
- Use the spend cap to manage project usage and costs, and control whether the project can
- exceed the included quota allowance of any billed line item in a billing cycle
-
-
-
-
-
-
-
- How are each resource charged after exceeding the included quota?
-
-
- {selectedOption === 'on' ? (
-
- Exceeding the included quota allowance with spend cap enabled can cause your project
- to become unresponsive or enter read only mode.
-
- ) : (
-
- Your project will always remain responsive and active, and charges only apply when
- exceeding the included quota limit.
-
- )}
-
- {hasChanges && (
- <>
-
- {selectedOption === 'on'
- ? 'Upon clicking confirm, spend cap will be enabled for your project and you will no longer be charged any extra for usage.'
- : 'Upon clicking confirm, spend cap will be disabled for your project and you will be charged for any usage beyong the included quota.'}
-
- This organization uses the legacy project-based billing. We’ve recently made some
- big improvements to our billing system. To migrate to the new organization-based
- billing, head over to your{' '}
-
- organization billing settings
-
- .
-
- >
- )
-}
-
-export default Subscription
diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx
deleted file mode 100644
index 047e9973a20..00000000000
--- a/studio/components/interfaces/BillingV2/Subscription/Tier/ExitSurveyModal.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import HCaptcha from '@hcaptcha/react-hcaptcha'
-import { useQueryClient } from '@tanstack/react-query'
-import { includes, without } from 'lodash'
-import { useRouter } from 'next/router'
-import { useReducer, useRef, useState } from 'react'
-
-import { useParams } from 'common'
-import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip'
-import { useSendDowngradeFeedbackMutation } from 'data/feedback/exit-survey-send'
-import { setProjectStatus } from 'data/projects/projects-query'
-import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
-import { useProjectSubscriptionUpdateMutation } from 'data/subscriptions/project-subscription-update-mutation'
-import { useFlag, useStore } from 'hooks'
-import { PROJECT_STATUS } from 'lib/constants'
-import { Button, Input, Modal } from 'ui'
-import { CANCELLATION_REASONS } from '../../Billing.constants'
-
-export interface ExitSurveyModalProps {
- visible: boolean
- onClose: (success?: boolean) => void
-}
-
-// [Joshen] For context - Exit survey is only when going to free plan from a paid plan
-
-const ExitSurveyModal = ({ visible, onClose }: ExitSurveyModalProps) => {
- const queryClient = useQueryClient()
- const { ui } = useStore()
- const { ref: projectRef } = useParams()
- const captchaRef = useRef(null)
- const router = useRouter()
-
- const [message, setMessage] = useState('')
- const [captchaToken, setCaptchaToken] = useState(null)
- const [selectedReasons, dispatchSelectedReasons] = useReducer(reducer, [])
-
- const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate')
- const { data: addons } = useProjectAddonsQuery({ projectRef })
- const { mutateAsync: sendExitSurvey, isLoading: isSubmittingFeedback } =
- useSendDowngradeFeedbackMutation()
- const { mutateAsync: updateSubscriptionTier, isLoading: isUpdatingSubscription } =
- useProjectSubscriptionUpdateMutation({
- onError: (error) => {
- return ui.setNotification({
- error,
- category: 'error',
- message: `Failed to cancel subscription: ${error.message}`,
- })
- },
- })
- const isSubmitting = isUpdatingSubscription || isSubmittingFeedback
- const subscriptionAddons = addons?.selected_addons ?? []
- const hasComputeInstance = subscriptionAddons.find((addon) => addon.type === 'compute_instance')
-
- function reducer(state: any, action: any) {
- if (includes(state, action.target.value)) {
- return without(state, action.target.value)
- } else {
- return [...state, action.target.value]
- }
- }
-
- const resetCaptcha = () => {
- setCaptchaToken(null)
- captchaRef.current?.resetCaptcha()
- }
-
- const onSubmit = async () => {
- if (selectedReasons.length === 0) {
- return ui.setNotification({
- category: 'error',
- message: 'Please select at least one reason for canceling your subscription',
- })
- }
-
- let token = captchaToken
-
- if (!token) {
- const captchaResponse = await captchaRef.current?.execute({ async: true })
- token = captchaResponse?.response ?? null
- await downgradeProject()
- }
- }
-
- const downgradeProject = async (values?: any) => {
- // Update the subscription first, followed by posting the exit survey if successful
- // If compute instance is present within the existing subscription, then a restart will be triggered
- if (!projectRef) return console.error('Project ref is required')
-
- try {
- await updateSubscriptionTier({ projectRef, tier: 'tier_free' })
- resetCaptcha()
- } catch (error) {
- return
- }
-
- try {
- await sendExitSurvey({
- projectRef,
- reasons: selectedReasons.reduce((a, b) => `${a}- ${b}\n`, ''),
- message,
- exitAction: 'downgrade',
- })
- } finally {
- }
-
- ui.setNotification({
- category: 'success',
- duration: hasComputeInstance ? 8000 : 4000,
- message: hasComputeInstance
- ? 'Your project has been downgraded and is currently restarting to update its instance size'
- : 'Successfully downgraded project to the free plan',
- })
- if (hasComputeInstance) {
- setProjectStatus(queryClient, projectRef, PROJECT_STATUS.RESTORING)
- router.push(`/project/${projectRef}`)
- }
- onClose(true)
- window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
- }
-
- if (!visible) return null
-
- return (
- <>
-
- {
- setCaptchaToken(token)
- if (document !== undefined) document.body.classList.remove('!pointer-events-auto')
- }}
- onExpire={() => setCaptchaToken(null)}
- onOpen={() => {
- // [Joshen] This is to ensure that hCaptcha popup remains clickable
- if (document !== undefined) document.body.classList.add('!pointer-events-auto')
- }}
- onClose={() => {
- if (document !== undefined) document.body.classList.remove('!pointer-events-auto')
- }}
- />
-
-
-
-
-
-
- We always strive to improve Supabase as much as we can. Please let us know the reasons
- you are canceling your subscription so that we can improve in the future.
-
- The following members have reached their maximum limits for the number of active free
- plan projects within organizations where they are an administrator or owner:
-
- Upon clicking confirm, all future charges will be deducted from the selected payment
- method. There are no immediate charges. Changing the payment method for this project
- does not affect the payment method for other projects.
-
-
- To remove unused or expired payment methods, head to your{' '}
-
- organization's billing settings
-
- .
-
- To manage your billing address, emails or Tax ID, head to your{' '}
-
- organization settings
-
- .
-
-
-
More information
-
-
-
-
Pricing
-
-
-
-
-
-
-
- {isLoading ? (
-
-
-
-
-
- ) : (
-
-
-
This project is currently on the plan:
-
{tierName}
-
-
-
-
-
-
- {!canChangeTier &&
- (projectUpdateDisabled ? (
-
- We have temporarily disabled project and subscription changes - our engineers are
- working on a fix.
-
- ) : (
-
-
-
-
-
,
- ]}
- >
- Please contact us if you'd like to change your plan.
-
- ))}
- {!subscription?.usage_billing_enabled && (
- snap.setPanelKey('subscriptionPlan')}>
- Upgrade Plan
-
- ) : (
-
- )
- }
- >
-
- When this project exceeds its{' '}
-
- included usage quotas
-
- , it may become unresponsive.{' '}
- {currentPlan?.id === 'free' ? (
-
- If you wish to exceed the included usage, you should upgrade to a paid plan.
-
- ) : (
-
- You currently have Spend Cap enabled - when you exceed your plan's limit, you
- will experience restrictions. To scale seamlessly and pay for over-usage, you
- can adjust your Cost Control settings.
-
- This organization uses the legacy project-based billing. We’ve recently made some
- big improvements to our billing system. To migrate to the new organization-based
- billing, head over to your{' '}
-
- organization billing settings
-
- .
-
-
- You do not have permission to change the subscription plan.
-
-
-
-
- ) : null}
-
- ) : (
-
- )}
-
-
-
-
- {plan.features.map((feature) => (
-
-
-
-
-
{feature}
-
- ))}
-
-
-
- {plan.footer && (
-
-
{plan.footer}
-
- )}
-
- )
- })}
-
-
-
-
- setSelectedTier(undefined)}
- onConfirm={onConfirmDowngrade}
- header={`Confirm to downgrade to ${selectedTierMeta?.name}`}
- >
- {/* [JOSHEN] We could make this better by only showing a danger warning if the project is already above the free plan limits */}
-
-
-
-
- If you're already past the limits of the free plan, your project could become
- unresponsive or enter read only mode.
-
- {subscriptionAddons.length > 0 && (
- <>
-
- Your project's add ons will also be removed, which includes:
-
-
-
-
- setSelectedTier(undefined)}
- onConfirm={onUpdateSubscription}
- overlayClassName="pointer-events-none"
- header={`Confirm to upgrade to ${selectedTierMeta?.name}`}
- >
-
-
-
- Upon clicking confirm, the amount of ${selectedPlanMeta?.price ?? 'Unknown'} will be
- added to your monthly invoice and your credit card will be charged immediately.
- Changing the plan resets your billing cycle and may result in a prorated charge for
- previous usage.
-
-
- You will also be able to change your project's add-ons after upgrading your project's
- plan.
-
- Your current compute can has a baseline and maximum disk throughput of{' '}
- {currentComputeInstanceSpecs.max_disk_io_mbs?.toLocaleString()} Mbps.
-
- ) : (
-
- Your current compute can burst up to{' '}
- {currentComputeInstanceSpecs.max_disk_io_mbs?.toLocaleString()} Mbps for 30
- minutes a day and reverts to the baseline performance of{' '}
- {currentComputeInstanceSpecs.baseline_disk_io_mbs?.toLocaleString()} Mbps.
-
- )
-}
-
-export default SectionHeader
diff --git a/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx b/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx
deleted file mode 100644
index 6527a636ee7..00000000000
--- a/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { useDailyStatsQuery } from 'data/analytics/daily-stats-query'
-import { DataPoint } from 'data/analytics/constants'
-import { ProjectSubscriptionResponse } from 'data/subscriptions/project-subscription-v2-query'
-import UsageSection from './UsageSection'
-
-export interface SizeAndCountsProps {
- projectRef: string
- startDate: string | undefined
- endDate: string | undefined
- subscription: ProjectSubscriptionResponse | undefined
- currentBillingCycleSelected: boolean
-}
-
-const SizeAndCounts = ({
- projectRef,
- startDate,
- endDate,
- subscription,
- currentBillingCycleSelected,
-}: SizeAndCountsProps) => {
- const { data: dbSizeData, isLoading: isLoadingDbSizeData } = useDailyStatsQuery({
- projectRef,
- attribute: 'total_db_size_bytes',
- interval: '1d',
- startDate,
- endDate,
- })
-
- const { data: storageSizeData, isLoading: isLoadingStorageSizeData } = useDailyStatsQuery({
- projectRef,
- attribute: 'total_storage_size_bytes',
- interval: '1d',
- startDate,
- endDate,
- })
-
- const { data: functionCountData, isLoading: isLoadingFunctionCountData } = useDailyStatsQuery({
- projectRef,
- attribute: 'total_func_count',
- interval: '1d',
- startDate,
- endDate,
- })
-
- const chartMeta: {
- [key: string]: { data: DataPoint[]; margin: number; isLoading: boolean; hasNoData: boolean }
- } = {
- db_size: {
- isLoading: isLoadingDbSizeData,
- margin: 14,
- data: dbSizeData?.data ?? [],
- hasNoData: dbSizeData?.hasNoData ?? false,
- },
- storage_size: {
- isLoading: isLoadingStorageSizeData,
- margin: 14,
- data: storageSizeData?.data ?? [],
- hasNoData: storageSizeData?.hasNoData ?? false,
- },
- func_count: {
- isLoading: isLoadingFunctionCountData,
- margin: 0,
- data: functionCountData?.data ?? [],
- hasNoData: functionCountData?.hasNoData ?? false,
- },
- }
-
- return (
-
- )
-}
-
-export default SizeAndCounts
diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx
deleted file mode 100644
index a97b8149470..00000000000
--- a/studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx
+++ /dev/null
@@ -1,365 +0,0 @@
-import { ProjectSubscriptionResponse } from 'data/subscriptions/project-subscription-v2-query'
-import { ProjectUsageData } from 'data/usage/project-usage-query'
-import Link from 'next/link'
-import { Alert, Badge, Button, IconExternalLink } from 'ui'
-import { USAGE_APPROACHING_THRESHOLD } from '../Billing.constants'
-
-export const Y_DOMAIN_CEILING_MULTIPLIER = 4 / 3
-
-export const USAGE_STATUS = {
- NORMAL: 'NORMAL',
- APPROACHING: 'APPROACHING',
- EXCEEDED: 'EXCEEDED',
-}
-
-export interface CategoryAttribute {
- anchor: string
- key: string // Property from project usage
- attribute: string // For querying against stats-daily / infra-monitoring
- name: string
- chartPrefix?: string
- unit: 'bytes' | 'absolute' | 'percentage'
- links?: {
- name: string
- url: string
- }[]
- description: string
- chartDescription: string
- additionalInfo?: (
- ref: string,
- subscription?: ProjectSubscriptionResponse,
- usage?: ProjectUsageData
- ) => JSX.Element | null
-}
-
-export type CategoryMetaKey = 'infra' | 'bandwidth' | 'sizeCount' | 'activity'
-
-export interface CategoryMeta {
- key: 'infra' | 'bandwidth' | 'sizeCount' | 'activity'
- name: string
- description: string
- attributes: CategoryAttribute[]
-}
-
-export const USAGE_CATEGORIES: CategoryMeta[] = [
- {
- key: 'infra',
- name: 'Infrastructure',
- description: 'Usage statistics related to your server instance',
- attributes: [
- {
- anchor: 'cpu',
- key: 'max_cpu_usage',
- attribute: 'max_cpu_usage',
- name: 'CPU',
- unit: 'percentage',
- description: 'Max CPU usage of your server',
- chartDescription: '',
- links: [
- {
- name: 'Compute Add-Ons',
- url: 'https://supabase.com/docs/guides/platform/compute-add-ons',
- },
- { name: 'High CPU Usage', url: 'https://supabase.com/docs/guides/platform/exhaust-cpu' },
- {
- name: 'Metrics',
- url: 'https://supabase.com/docs/guides/platform/metrics',
- },
- ],
- },
- {
- anchor: 'ram',
- key: 'ram_usage',
- attribute: 'ram_usage',
- name: 'Memory',
- unit: 'percentage',
- description:
- 'Memory usage of your server.\nYou might observe elevated memory usage, even with little to no load. Besides Postgres, a wide range of services is running under the hood resulting in an elevated base memory usage.',
- chartDescription: '',
- links: [
- {
- name: 'Compute Add-Ons',
- url: 'https://supabase.com/docs/guides/platform/compute-add-ons',
- },
- { name: 'High RAM Usage', url: 'https://supabase.com/docs/guides/platform/exhaust-ram' },
- {
- name: 'Metrics',
- url: 'https://supabase.com/docs/guides/platform/metrics',
- },
- ],
- },
- {
- anchor: 'disk_io',
- key: 'disk_io_consumption',
- attribute: 'disk_io_consumption',
- name: 'Disk IO Bandwidth',
- unit: 'percentage',
- links: [
- {
- name: 'Disk Throughput and IOPS',
- url: 'https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops',
- },
- {
- name: 'Metrics',
- url: 'https://supabase.com/docs/guides/platform/metrics',
- },
- ],
- description:
- 'The disk performance of your workload is determined by the Disk IO bandwidth.\nSmaller compute instances (below 4XL) can burst up to their largest throughput and IOPS for 30 minutes in a day. Beyond that, the performance reverts to the baseline. Your disk budget gets replenished throughout the day.',
- chartDescription: '',
- },
- ],
- },
- {
- key: 'bandwidth',
- name: 'Bandwidth',
- description: 'Amount of data transmitted over all network connections',
- attributes: [
- {
- anchor: 'dbEgress',
- key: 'db_egress',
- attribute: 'total_egress_modified',
- name: 'Database Egress',
- unit: 'bytes',
- description:
- 'Contains any outgoing traffic (egress) from your database.\nBilling is based on the total sum of egress in GB throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- },
- {
- anchor: 'storageEgress',
- key: 'storage_egress',
- attribute: 'total_storage_egress',
- name: 'Storage Egress',
- unit: 'bytes',
- description:
- 'All requests to view and download your storage items go through our CDN. We sum up all outgoing traffic (egress) for storage related requests through our CDN. We do not differentiate between cache and no cache hits.\nBilling is based on the total amount of egress in GB throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- },
- ],
- },
- {
- key: 'sizeCount',
- name: 'Size & Counts',
- description: 'Amount of resources your project is consuming',
- attributes: [
- {
- anchor: 'dbSize',
- key: 'db_size',
- attribute: 'total_db_size_bytes',
- name: 'Database size',
- chartPrefix: 'Average ',
- unit: 'bytes',
- description:
- 'Database size refers to the monthly average storage usage, as reported by Postgres. Paid plans use auto-scaling disks.\nBilling is based on the average daily database size used in GB throughout the billing period. Billing is independent of the provisioned disk size.',
- links: [
- {
- name: 'Documentation',
- url: 'https://supabase.com/docs/guides/platform/database-size',
- },
- ],
- chartDescription: 'The data refreshes every 24 hours.',
- additionalInfo: (
- ref: string,
- subscription?: ProjectSubscriptionResponse,
- usage?: ProjectUsageData
- ) => {
- const usageMeta = usage?.['db_size'] ?? undefined
- const usageRatio =
- typeof usageMeta !== 'number' ? (usageMeta?.usage ?? 0) / (usageMeta?.limit ?? 0) : 0
- const hasLimit = usageMeta && usageMeta.limit > 0
-
- const isApproachingLimit = hasLimit && usageRatio >= USAGE_APPROACHING_THRESHOLD
- const isExceededLimit = hasLimit && usageRatio >= 1
-
- return subscription?.plan?.id !== 'free' ? (
-
-
-
-
Disk size:
-
{usage?.disk_volume_size_gb} GB
-
- Auto-scaling
-
-
- }>
-
- What is disk size?
-
-
-
-
- If you reach 95% of the disk space, your project will enter read-only mode. Your
- disk storage expands automatically when the database reaches 90% of the disk size.
- The disk is expanded to be 50% larger. Auto-scaling can only take place once every 6
- hours. You can also{' '}
-
- preprovision
- {' '}
- disk for loading larger amounts of data.
-
- When you reach your database size limit, your project can go into read-only
- mode. Please upgrade your plan.
-
-
-
- )}
-
- )
- },
- },
- {
- anchor: 'storageSize',
- key: 'storage_size',
- attribute: 'total_storage_size_bytes',
- name: 'Storage Size',
- chartPrefix: 'Average ',
- unit: 'bytes',
- description:
- 'Sum of all objects in your storage buckets.\nBilling is based on the average daily size in GB throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- links: [
- {
- name: 'Storage',
- url: 'https://supabase.com/docs/guides/storage',
- },
- ],
- },
- {
- anchor: 'funcCount',
- key: 'func_count',
- attribute: 'total_func_count',
- name: 'Edge Function Count',
- chartPrefix: 'Max ',
- unit: 'absolute',
- description:
- 'Number of serverless functions in your project.\nBilling is based on the maximum amount of functions at any point in time throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- },
- ],
- },
- {
- key: 'activity',
- name: 'Activity',
- description: 'Usage statistics that reflect the activity of your project',
- attributes: [
- {
- anchor: 'mau',
- key: 'monthly_active_users',
- attribute: 'total_auth_billing_period_mau',
- name: 'Monthly Active Users',
- unit: 'absolute',
- description:
- 'Users who log in or refresh their token count towards MAU.\nBilling is based on the sum of distinct users requesting your API throughout the billing period. Resets every billing cycle.',
- chartDescription:
- 'The data is refreshed over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.',
- links: [
- {
- name: 'Auth',
- url: 'https://supabase.com/docs/guides/auth',
- },
- ],
- },
- {
- anchor: 'mauSso',
- key: 'monthly_active_sso_users',
- attribute: 'total_auth_billing_period_sso_mau',
- name: 'Monthly Active SSO Users',
- unit: 'absolute',
- description:
- 'SSO users who log in or refresh their token count towards SSO MAU.\nBilling is based on the sum of distinct Single Sign-On users requesting your API throughout the billing period. Resets every billing cycle.',
- chartDescription:
- 'The data refreshes over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.',
- links: [
- {
- name: 'SSO with SAML 2.0',
- url: 'https://supabase.com/docs/guides/auth/sso/auth-sso-saml',
- },
- ],
- },
- {
- anchor: 'storageImageTransformations',
- key: 'storage_image_render_count',
- attribute: 'total_storage_image_render_count',
- name: 'Storage Image Transformations',
- unit: 'absolute',
- description:
- 'We count all images that were transformed in the billing period, ignoring any transformations.\nUsage example: You transform one image with four different size transformations and another image with just a single transformation. It counts as two, as only two images were transformed.\nBilling is based on the count of (origin) images that used transformations throughout the billing period. Resets every billing cycle.',
- chartDescription:
- 'The data refreshes every 24 hours.\nThe data points are relative to the beginning of your billing period.',
- links: [
- {
- name: 'Documentation',
- url: 'https://supabase.com/docs/guides/storage/serving/image-transformations',
- },
- ],
- },
- {
- anchor: 'functionInvocations',
- key: 'func_invocations',
- attribute: 'total_func_invocations',
- name: 'Edge Function Invocations',
- unit: 'absolute',
- description:
- 'Every serverless function invocation independent of response status is counted.\nBilling is based on the sum of all invocations throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- links: [
- {
- name: 'Edge Functions',
- url: 'https://supabase.com/docs/guides/functions',
- },
- ],
- },
- {
- anchor: 'realtimeMessageCount',
- key: 'realtime_message_count',
- attribute: 'total_realtime_message_count',
- name: 'Realtime Message Count',
- unit: 'absolute',
- description:
- "Count of messages going through Realtime.\nUsage example: If you do a database change and 5 clients listen to that change via Realtime, that's 5 messages. If you broadcast a message and 4 clients listen to that, that's 5 messages (1 message sent, 4 received).\nBilling is based on the total amount of messages throughout your billing period.",
- chartDescription: 'The data refreshes every 24 hours.',
- links: [
- {
- name: 'Realtime Quotas',
- url: 'https://supabase.com/docs/guides/realtime/quotas',
- },
- ],
- },
- {
- anchor: 'realtimePeakConnection',
- key: 'realtime_peak_connection',
- attribute: 'total_realtime_peak_connection',
- name: 'Realtime Peak Connections',
- chartPrefix: 'Max ',
- unit: 'absolute',
- description:
- 'Total number of successful connections. Connections attempts are not counted towards usage.\nBilling is based on the maximum amount of concurrent peak connections throughout your billing period.',
- chartDescription: 'The data refreshes every 24 hours.',
- links: [
- {
- name: 'Realtime Quotas',
- url: 'https://supabase.com/docs/guides/realtime/quotas',
- },
- ],
- },
- ],
- },
-]
diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx
deleted file mode 100644
index f50f4ba277d..00000000000
--- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx
+++ /dev/null
@@ -1,327 +0,0 @@
-import clsx from 'clsx'
-import { useParams } from 'common'
-import dayjs from 'dayjs'
-import { useMemo, useRef, useState } from 'react'
-import { InView } from 'react-intersection-observer'
-import { Button, cn, IconAlertCircle, IconExternalLink, IconInfo, IconLoader } from 'ui'
-
-import DateRangePicker from 'components/to-be-cleaned/DateRangePicker'
-import InformationBox from 'components/ui/InformationBox'
-import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
-import { useProjectUsageQuery } from 'data/usage/project-usage-query'
-import { useSelectedOrganization } from 'hooks'
-import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants'
-import Link from 'next/link'
-import Activity from './Activity'
-import Bandwidth from './Bandwidth'
-import Infrastructure from './Infrastructure'
-import SizeAndCounts from './SizeAndCounts'
-import { USAGE_CATEGORIES, USAGE_STATUS } from './Usage.constants'
-import { getUsageStatus } from './Usage.utils'
-
-type UsageSectionIds = 'infra' | 'bandwidth' | 'sizeCount' | 'activity'
-
-const Usage = () => {
- const { ref } = useParams()
- const organization = useSelectedOrganization()
- const isOrgBilling = !!organization?.subscription_id
-
- // [Joshen] Using a state here for now as in the future, we'd have this usage page support showing
- // stats for different projects. So we'll pass the selected ref as a prop into the individual components
- const [selectedProjectRef] = useState(ref as string)
- const [activeTab, setActiveTab] = useState('infra')
- const [dateRange, setDateRange] = useState()
-
- const infrastructureRef = useRef(null)
- const bandwidthRef = useRef(null)
- const sizeAndCountsRef = useRef(null)
- const activityRef = useRef(null)
-
- const { data: usage } = useProjectUsageQuery({ projectRef: ref })
- const { data: subscription, isLoading: isLoadingSubscription } = useProjectSubscriptionV2Query({
- projectRef: selectedProjectRef,
- })
- const usageBillingEnabled = subscription?.usage_billing_enabled
- const billingCycleStart = dayjs.unix(subscription?.current_period_start ?? 0).utc()
- const billingCycleEnd = dayjs.unix(subscription?.current_period_end ?? 0).utc()
-
- const currentBillingCycleSelected = useMemo(() => {
- // Selected by default
- if (!dateRange?.period_start || !dateRange?.period_end || !subscription) return true
-
- const { current_period_start, current_period_end } = subscription
-
- return (
- dayjs(dateRange.period_start.date).isSame(new Date(current_period_start * 1000)) &&
- dayjs(dateRange.period_end.date).isSame(new Date(current_period_end * 1000))
- )
- }, [dateRange, subscription])
-
- const startDate = useMemo(() => {
- // If end date is in future, set end date to now
- if (!dateRange?.period_start?.date) {
- return undefined
- } else {
- // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
- return new Date(dateRange?.period_start?.date).toISOString().slice(0, -5) + 'Z'
- }
- }, [dateRange, subscription])
-
- const endDate = useMemo(() => {
- // If end date is in future, set end date to end of current day
- if (dateRange?.period_end?.date && dayjs(dateRange.period_end.date).isAfter(dayjs())) {
- // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
- // In order to have full days from Prometheus metrics when using 1d interval,
- // the time needs to be greater or equal than the time of the start date
- return dayjs().endOf('day').toISOString().slice(0, -5) + 'Z'
- } else if (dateRange?.period_end?.date) {
- // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
- return new Date(dateRange.period_end.date).toISOString().slice(0, -5) + 'Z'
- }
- }, [dateRange, subscription])
-
- const { data: ioBudgetData } = useInfraMonitoringQuery({
- projectRef: selectedProjectRef,
- attribute: 'disk_io_budget',
- interval: '1d',
- startDate: dateRange?.period_start?.date,
- endDate: dateRange?.period_end?.date,
- })
- const currentDayIoBudget = Number(
- ioBudgetData?.data.find((x) => x.periodStartFormatted === dayjs().format('DD MMM'))?.[
- 'disk_io_budget'
- ] ?? 100
- )
-
- const scrollTo = (id: UsageSectionIds) => {
- switch (id) {
- case 'infra':
- if (infrastructureRef.current) {
- infrastructureRef.current.scrollIntoView({
- behavior: 'smooth',
- block: 'start', // or 'end' to scroll to the bottom
- inline: 'start', //
- })
- }
- break
- case 'bandwidth':
- if (bandwidthRef.current) {
- bandwidthRef.current.scrollIntoView({
- behavior: 'smooth',
- block: 'start', // or 'end' to scroll to the bottom
- inline: 'start', //
- })
- }
- break
- case 'sizeCount':
- if (sizeAndCountsRef.current) {
- sizeAndCountsRef.current.scrollIntoView({
- behavior: 'smooth',
- block: 'start', // or 'end' to scroll to the bottom
- inline: 'start', //
- })
- }
- break
- case 'activity':
- if (activityRef.current) {
- activityRef.current.scrollIntoView({
- behavior: 'smooth',
- block: 'start', // or 'end' to scroll to the bottom
- inline: 'start', //
- })
- }
- break
- }
- }
-
- return (
- <>
-
- This organization uses the legacy project-based billing. We’ve recently made some
- big improvements to our billing system. To migrate to the new organization-based
- billing, head over to your{' '}
-
- organization billing settings
-
- .
-
- The organization "{name}" still uses the legacy project-based billing. We've recently
- made some big improvements to our billing system and require your action. To migrate
- to the new organization-based billing, head over to your{' '}
-
- organization billing settings
-
- .
-
-
-
- Please do this until the 18th of October, as
- remaining organizations will be migrated.
-
-
- {!canMigrateOrg
- ? 'You need additional permissions to migrate this organization'
- : 'Migrations are temporarily disabled, please try again later.'}
-
-
- With organization-based billing, you'll have a single subscription for your entire
- organization. For a detailed breakdown of changes, see{' '}
-
- Billing Migration Docs
-
- . To transfer projects to a different organization, visit{' '}
-
- general project settings
-
- .
-
- The pricing plan, along with included usage limits will apply to your entire
- organization. See{' '}
-
- Pricing
- {' '}
- for more details.
-
-
- {tier !== '' && tier !== 'FREE' && (
-
-
- Paid plans come with one compute instance included. Additional projects will at
- least cost the compute instance hours used (min $10/month). See{' '}
-
- Compute Instance Usage Billing
- {' '}
- for more details.
-
- When enabled, usage is limited to the plan's quota, with restrictions when
- limits are exceeded. When disabled, you scale beyond Pro limits without
- restrictions and pay for over-usage beyond the quota.
-
- You previously had the spend cap disabled to scale seamlessly beyond the
- included quota. You will run into restrictions in case you exceed the
- included quota.
-
- ) : (
-
- With the Spend Cap enabled, your projects will be restricted once you
- exhaust the included quota. To scale seamlessly beyond the included
- quota, disable the Spend Cap.
-
-
- {migrationPreviewData.old_tiers.some((it) => it === 'tier_free') && (
-
-
- While paused projects do not incur compute costs, the breakdown assumes
- that none of your projects are paused. Unpausing projects is self-serve
- and pausing projects on paid plans is currently only possible via support
- due to infrastructure restrictions.
-
-
- )}
-
- }
- />
-
- )}
-
-
-
- The migration can take up to 30 seconds, please do not cancel the request or close
- your browser.
-
-
-
-
-
-
- >
- )
-})
-
-export default MigrateOrganizationBillingButton
diff --git a/studio/components/interfaces/Organization/GeneralSettings/OrganizationBillingMigrationPanel.tsx b/studio/components/interfaces/Organization/GeneralSettings/OrganizationBillingMigrationPanel.tsx
deleted file mode 100644
index 5c6953a564f..00000000000
--- a/studio/components/interfaces/Organization/GeneralSettings/OrganizationBillingMigrationPanel.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { observer } from 'mobx-react-lite'
-import Link from 'next/link'
-import { Badge, Button, IconExternalLink, IconTruck } from 'ui'
-
-import Panel from 'components/ui/Panel'
-import MigrateOrganizationBillingButton from './MigrateOrganizationBillingButton'
-
-const OrganizationBillingMigrationPanel = observer(() => {
- return (
-
-
-
Organization-based billing
-
-
- New
-
-
-
-
Migrate this project to organization-based billing
-
-
-
-
-
- You will have a single subscription for all projects inside the organization, rather
- than individual subscriptions per project.
-
-
-
- }>
-
- Documentation
-
-
-
-
-
-
-
-
- )
-})
-
-export default OrganizationBillingMigrationPanel
diff --git a/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx b/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx
index acdb93e2612..6845cd7f0fc 100644
--- a/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx
+++ b/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx
@@ -2,8 +2,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import Link from 'next/link'
import { useEffect, useState } from 'react'
-import { InvoiceStatusBadge } from 'components/interfaces/BillingV2'
-import { InvoiceStatus } from 'components/interfaces/BillingV2/Invoices.types'
+import { InvoiceStatus } from 'components/interfaces/Billing/Invoices.types'
import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
@@ -14,6 +13,7 @@ import { useInvoicesCountQuery } from 'data/invoices/invoices-count-query'
import { useInvoicesQuery } from 'data/invoices/invoices-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { Button, IconChevronLeft, IconChevronRight, IconDownload, IconFileText } from 'ui'
+import InvoiceStatusBadge from 'components/interfaces/Billing/InvoiceStatusBadge'
const PAGE_LIMIT = 10
diff --git a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx
index d112c3128e6..4cf76281f04 100644
--- a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx
+++ b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx
@@ -16,7 +16,7 @@ import {
} from 'ui'
import { useParams } from 'common'
-import { SpendCapModal } from 'components/interfaces/BillingV2'
+import SpendCapModal from 'components/interfaces/Billing/SpendCapModal'
import InformationBox from 'components/ui/InformationBox'
import Panel from 'components/ui/Panel'
import { useOrganizationCreateMutation } from 'data/organizations/organization-create-mutation'
diff --git a/studio/components/interfaces/Organization/NewProject/EmptyPaymentMethodWarning.tsx b/studio/components/interfaces/Organization/NewProject/EmptyPaymentMethodWarning.tsx
index 7cc2b3e5243..931fa17584a 100644
--- a/studio/components/interfaces/Organization/NewProject/EmptyPaymentMethodWarning.tsx
+++ b/studio/components/interfaces/Organization/NewProject/EmptyPaymentMethodWarning.tsx
@@ -1,10 +1,10 @@
import { useState } from 'react'
-import { AddNewPaymentMethodModal } from 'components/interfaces/BillingV2'
import InformationBox from 'components/ui/InformationBox'
import { useSelectedOrganization } from 'hooks'
import { getURL } from 'lib/helpers'
import { Button, IconAlertCircle } from 'ui'
+import AddNewPaymentMethodModal from 'components/interfaces/Billing/Payment/AddNewPaymentMethodModal'
const EmptyPaymentMethodWarning = ({
onPaymentMethodAdded,
diff --git a/studio/components/interfaces/Organization/Usage/Usage.constants.tsx b/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
index 085424a9dbe..1f7b77f13dd 100644
--- a/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
+++ b/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
@@ -1,4 +1,4 @@
-import { USAGE_APPROACHING_THRESHOLD } from 'components/interfaces/BillingV2/Billing.constants'
+import { USAGE_APPROACHING_THRESHOLD } from 'components/interfaces/Billing/Billing.constants'
import { EgressType, PricingMetric } from 'data/analytics/org-daily-stats-query'
import { OrgSubscription } from 'data/subscriptions/org-subscription-query'
import { OrgUsageResponse } from 'data/usage/org-usage-query'
diff --git a/studio/components/interfaces/Organization/Usage/Usage.utils.ts b/studio/components/interfaces/Organization/Usage/Usage.utils.ts
index 8ec8d3d9f8e..574f9f844da 100644
--- a/studio/components/interfaces/Organization/Usage/Usage.utils.ts
+++ b/studio/components/interfaces/Organization/Usage/Usage.utils.ts
@@ -1,8 +1,5 @@
import { DataPoint } from 'data/analytics/constants'
-import { ProjectUsageResponse } from 'data/usage/project-usage-query'
-import { USAGE_APPROACHING_THRESHOLD } from 'components/interfaces/BillingV2/Billing.constants'
-import { CategoryAttribute, USAGE_STATUS } from './Usage.constants'
-import { ProjectSubscriptionResponse } from 'data/subscriptions/project-subscription-v2-query'
+import { OrgSubscription } from 'data/subscriptions/org-subscription-query'
// [Joshen] This is just for development to generate some test data for chart rendering
export const generateUsageData = (attribute: string, days: number): DataPoint[] => {
@@ -16,25 +13,7 @@ export const generateUsageData = (attribute: string, days: number): DataPoint[]
})
}
-export const getUsageStatus = (attributes: CategoryAttribute[], usage?: ProjectUsageResponse) => {
- if (!usage) return USAGE_STATUS.NORMAL
-
- const attributeStatuses = attributes.map((attribute) => {
- const usageMeta = usage?.[attribute.key as keyof ProjectUsageResponse]
- const usageRatio =
- typeof usageMeta !== 'number' ? (usageMeta?.usage ?? 0) / (usageMeta?.limit ?? 0) : 0
- if (usageRatio >= 1) return USAGE_STATUS.EXCEEDED
- else if (usageRatio >= USAGE_APPROACHING_THRESHOLD) return USAGE_STATUS.APPROACHING
- else return USAGE_STATUS.NORMAL
- })
-
- if (attributeStatuses.find((x) => x === USAGE_STATUS.EXCEEDED)) return USAGE_STATUS.EXCEEDED
- else if (attributeStatuses.find((x) => x === USAGE_STATUS.APPROACHING))
- return USAGE_STATUS.APPROACHING
- else return USAGE_STATUS.NORMAL
-}
-
-export const getUpgradeUrl = (slug: string, subscription?: ProjectSubscriptionResponse) => {
+export const getUpgradeUrl = (slug: string, subscription?: OrgSubscription) => {
if (!subscription) {
return `/org/${slug}/billing`
}
diff --git a/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx b/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
index 394c9ab25de..31067597203 100644
--- a/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
+++ b/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
@@ -21,7 +21,6 @@ import {
} from '../Usage.utils'
import UsageBarChart from '../UsageBarChart'
import { ChartMeta } from './UsageSection'
-import { useIsFeatureEnabled } from 'hooks'
export interface AttributeUsageProps {
slug: string
diff --git a/studio/components/interfaces/Organization/index.ts b/studio/components/interfaces/Organization/index.ts
index c76c6de58b9..d98a1842db3 100644
--- a/studio/components/interfaces/Organization/index.ts
+++ b/studio/components/interfaces/Organization/index.ts
@@ -1,5 +1,4 @@
import BillingSettings from './BillingSettings/BillingSettings'
-import BillingSettingsV2 from './BillingSettingsV2/BillingSettings'
import GeneralSettings from './GeneralSettings/GeneralSettings'
import IntegrationSettings from './IntegrationSettings/IntegrationSettings'
import InvoicesSettings from './InvoicesSettings/InvoicesSettings'
@@ -12,7 +11,6 @@ import Documents from './Documents/Documents'
export {
BillingSettings,
- BillingSettingsV2,
GeneralSettings,
IntegrationSettings,
InvoicesSettings,
diff --git a/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx b/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx
index 5cdb27d22d8..5c2496d76e1 100644
--- a/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx
+++ b/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx
@@ -3,12 +3,13 @@ import { useParams } from 'common'
import { useSqlTitleGenerateMutation } from 'data/ai/sql-title-mutation'
import { SqlSnippet } from 'data/content/sql-snippets-query'
import { isError } from 'data/utils/error-check'
-import { useFlag, useStore } from 'hooks'
+import { useFlag, useSelectedOrganization, useStore } from 'hooks'
import { useEffect, useState } from 'react'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { AiIconAnimation, Button, Form, Input, Modal } from 'ui'
-import { subscriptionHasHipaaAddon } from '../BillingV2/Subscription/Subscription.utils'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
+import { subscriptionHasHipaaAddon } from '../Billing/Subscription/Subscription.utils'
+import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
+
export interface RenameQueryModalProps {
snippet: SqlSnippet
visible: boolean
@@ -19,9 +20,10 @@ export interface RenameQueryModalProps {
const RenameQueryModal = ({ snippet, visible, onCancel, onComplete }: RenameQueryModalProps) => {
const { ui } = useStore()
const { ref } = useParams()
+ const organization = useSelectedOrganization()
const snap = useSqlEditorStateSnapshot()
const supabaseAIEnabled = useFlag('sqlEditorSupabaseAI')
- const { data: subscription } = useProjectSubscriptionV2Query({ projectRef: ref })
+ const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
// Customers on HIPAA plans should not have access to Supabase AI
const hasHipaaAddon = subscriptionHasHipaaAddon(subscription)
diff --git a/studio/components/interfaces/SQLEditor/SQLEditor.tsx b/studio/components/interfaces/SQLEditor/SQLEditor.tsx
index 4d6e728e3fb..612816a975b 100644
--- a/studio/components/interfaces/SQLEditor/SQLEditor.tsx
+++ b/studio/components/interfaces/SQLEditor/SQLEditor.tsx
@@ -31,7 +31,6 @@ import { SqlSnippet } from 'data/content/sql-snippets-query'
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
import { useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
import { useFormatQueryMutation } from 'data/sql/format-sql-query'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
import { isError } from 'data/utils/error-check'
import {
useFlag,
@@ -46,7 +45,7 @@ import { uuidv4 } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import Telemetry from 'lib/telemetry'
import { getSqlEditorStateSnapshot, useSqlEditorStateSnapshot } from 'state/sql-editor'
-import { subscriptionHasHipaaAddon } from '../BillingV2/Subscription/Subscription.utils'
+import { subscriptionHasHipaaAddon } from '../Billing/Subscription/Subscription.utils'
import AISchemaSuggestionPopover from './AISchemaSuggestionPopover'
import AISettingsModal from './AISettingsModal'
import { sqlAiDisclaimerComment, untitledSnippetTitle } from './SQLEditor.constants'
@@ -64,6 +63,7 @@ import {
getDiffTypeDropdownLabel,
} from './SQLEditor.utils'
import UtilityPanel from './UtilityPanel/UtilityPanel'
+import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
// Load the monaco editor client-side only (does not behave well server-side)
const MonacoEditor = dynamic(() => import('./MonacoEditor'), { ssr: false })
@@ -98,6 +98,7 @@ const SQLEditor = () => {
const { profile } = useProfile()
const project = useSelectedProject()
+ const organization = useSelectedOrganization()
const snap = useSqlEditorStateSnapshot()
const { mutate: formatQuery } = useFormatQueryMutation()
@@ -113,7 +114,7 @@ const SQLEditor = () => {
const inputRef = useRef(null)
const supabaseAIEnabled = useFlag('sqlEditorSupabaseAI')
- const { data: subscription } = useProjectSubscriptionV2Query({ projectRef: project?.ref })
+ const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
// Customers on HIPAA plans should not have access to Supabase AI
const hasHipaaAddon = subscriptionHasHipaaAddon(subscription)
diff --git a/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityTabResults.tsx b/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityTabResults.tsx
index 0e54dac3d4c..aae6f0985e9 100644
--- a/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityTabResults.tsx
+++ b/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityTabResults.tsx
@@ -1,7 +1,6 @@
-import { subscriptionHasHipaaAddon } from 'components/interfaces/BillingV2/Subscription/Subscription.utils'
+import { subscriptionHasHipaaAddon } from 'components/interfaces/Billing/Subscription/Subscription.utils'
import { useSqlDebugMutation } from 'data/ai/sql-debug-mutation'
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
import { isError } from 'data/utils/error-check'
import {
useFlag,
@@ -17,6 +16,7 @@ import { AiIconAnimation, Button } from 'ui'
import { useSqlEditor } from '../SQLEditor'
import { sqlAiDisclaimerComment } from '../SQLEditor.constants'
import Results from './Results'
+import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
export type UtilityTabResultsProps = {
id: string
@@ -25,6 +25,7 @@ export type UtilityTabResultsProps = {
const UtilityTabResults = ({ id, isExecuting }: UtilityTabResultsProps) => {
const { ui } = useStore()
+ const organization = useSelectedOrganization()
const snap = useSqlEditorStateSnapshot()
const { mutateAsync: debugSql, isLoading: isDebugSqlLoading } = useSqlDebugMutation()
const { setDebugSolution, setAiInput, setSqlDiff, sqlDiff } = useSqlEditor()
@@ -50,7 +51,7 @@ const UtilityTabResults = ({ id, isExecuting }: UtilityTabResultsProps) => {
const isUtilityPanelCollapsed = (snippet?.splitSizes?.[1] ?? 0) === 0
const supabaseAIEnabled = useFlag('sqlEditorSupabaseAI')
- const { data: subscription } = useProjectSubscriptionV2Query({ projectRef: selectedProject?.ref })
+ const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
// Customers on HIPAA plans should not have access to Supabase AI
const hasHipaaAddon = subscriptionHasHipaaAddon(subscription)
diff --git a/studio/components/interfaces/Settings/Addons/Addons.tsx b/studio/components/interfaces/Settings/Addons/Addons.tsx
index 9932c1cf331..33268e24f8a 100644
--- a/studio/components/interfaces/Settings/Addons/Addons.tsx
+++ b/studio/components/interfaces/Settings/Addons/Addons.tsx
@@ -4,7 +4,7 @@ import { useMemo } from 'react'
import { useParams } from 'common'
import { useTheme } from 'next-themes'
-import { getAddons } from 'components/interfaces/BillingV2/Subscription/Subscription.utils'
+import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip'
import {
useIsProjectActive,
@@ -36,8 +36,10 @@ import {
IconChevronRight,
IconExternalLink,
} from 'ui'
-import { ComputeInstanceSidePanel, CustomDomainSidePanel, PITRSidePanel } from './'
import Image from 'next/image'
+import ComputeInstanceSidePanel from './ComputeInstanceSidePanel'
+import PITRSidePanel from './PITRSidePanel'
+import CustomDomainSidePanel from './CustomDomainSidePanel'
const Addons = () => {
const { resolvedTheme } = useTheme()
diff --git a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx
index c0fdcd033ff..79d4c53ae04 100644
--- a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx
+++ b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx
@@ -12,7 +12,6 @@ import { setProjectStatus } from 'data/projects/projects-query'
import { useProjectAddonRemoveMutation } from 'data/subscriptions/project-addon-remove-mutation'
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
-import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils'
import { BASE_PATH, PROJECT_STATUS } from 'lib/constants'
@@ -32,7 +31,7 @@ import {
} from 'ui'
import * as Tooltip from '@radix-ui/react-tooltip'
-import Image from 'next/image'
+import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
const COMPUTE_CATEGORY_OPTIONS: {
id: 'micro' | 'optimized'
@@ -62,7 +61,6 @@ const ComputeInstanceSidePanel = () => {
const { resolvedTheme } = useTheme()
const { project: selectedProject } = useProjectContext()
const organization = useSelectedOrganization()
- const isOrgBilling = !!organization?.subscription_id
const [showConfirmationModal, setShowConfirmationModal] = useState(false)
const [selectedCategory, setSelectedCategory] = useState<'micro' | 'optimized'>('micro')
@@ -78,7 +76,7 @@ const ComputeInstanceSidePanel = () => {
const onClose = () => snap.setPanelKey(undefined)
const { data: addons, isLoading } = useProjectAddonsQuery({ projectRef })
- const { data: subscription } = useProjectSubscriptionV2Query({ projectRef })
+ const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
const { mutate: updateAddon, isLoading: isUpdating } = useProjectAddonUpdateMutation({
onSuccess: () => {
ui.setNotification({
@@ -276,17 +274,11 @@ const ComputeInstanceSidePanel = () => {
variant="info"
title="Changing your compute size is only available on the Pro plan"
actions={
- isOrgBilling ? (
-
- ) : (
-
}
>
Upgrade your plan to change the compute size of your project
@@ -379,12 +371,7 @@ const ComputeInstanceSidePanel = () => {
)}
{hasChanges &&
- (selectedCategory === 'micro' && !isOrgBilling ? (
-
- Upon clicking confirm, the amount of that's unused during the current billing
- cycle will be returned as credits that can be used for subsequent billing cycles
-
- You will not be able to use the dashboard to make changes to the database
-
-
- Schema changes for database preview branches must be done via Git. We are
- nonetheless working on allowing the dashboard to make schema changes for
- preview branches.
-
+ You will not be able to use the dashboard to make changes to the database
+
+
+ Schema changes for database preview branches must be done via Git. We are
+ nonetheless working on allowing the dashboard to make schema changes for preview
+ branches.
+
}
+ organizationSlug={organizationSlug}
primaryText="Free Plan has a fixed upload file size limit of 50 MB."
projectRef={projectRef}
secondaryText="Upgrade to the Pro plan for a configurable upload file size limit of up to 5 GB."
diff --git a/studio/components/ui/OveragesBanner/OveragesBanner.tsx b/studio/components/ui/OveragesBanner/OveragesBanner.tsx
deleted file mode 100644
index 4e8ddd990d8..00000000000
--- a/studio/components/ui/OveragesBanner/OveragesBanner.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { useParams } from 'common'
-import Link from 'next/link'
-import { Alert, Button } from 'ui'
-
-import { useProjectUsageQuery } from 'data/usage/project-usage-query'
-import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
-import { getResourcesApproachingLimits, getResourcesExceededLimits } from './OveragesBanner.utils'
-
-interface OveragesBannerProps {
- tier: string
- minimal?: boolean
-}
-
-// Banner will not be shown for PAYG or Enterprise projects
-
-const OveragesBanner = ({ tier, minimal }: OveragesBannerProps) => {
- const { ref: projectRef } = useParams()
- const { data: usage, error, isLoading } = useProjectUsageQuery({ projectRef })
-
- const resourcesApproachingLimits = getResourcesApproachingLimits(usage)
- const isApproachingUsageLimits = resourcesApproachingLimits.length > 0
-
- const resourcesExceededLimits = getResourcesExceededLimits(usage)
- const isOverUsageLimits = resourcesExceededLimits.length > 0
-
- if (
- isLoading ||
- error ||
- (!isApproachingUsageLimits && !isOverUsageLimits) ||
- tier === PRICING_TIER_PRODUCT_IDS.PAYG ||
- tier === PRICING_TIER_PRODUCT_IDS.ENTERPRISE ||
- tier === PRICING_TIER_PRODUCT_IDS.TEAM
- ) {
- return <>>
- }
-
- const tierName =
- tier === PRICING_TIER_PRODUCT_IDS.FREE
- ? 'free'
- : tier === PRICING_TIER_PRODUCT_IDS.PRO
- ? 'pro'
- : tier === PRICING_TIER_PRODUCT_IDS.PAYG
- ? 'pro'
- : ''
-
- const minimalDescription =
- tier === PRICING_TIER_PRODUCT_IDS.FREE
- ? "You can check your project's usage details or upgrade to the pro plan to support the growth of your project."
- : tier === PRICING_TIER_PRODUCT_IDS.PRO
- ? "You can check your project's usage details or consider disabling spend cap to support the growth of your project."
- : tier === PRICING_TIER_PRODUCT_IDS.PAYG
- ? "As you have disabled spend cap, additional resources will be charged on a per-usage basis. You can check your project's usage details for more information."
- : ''
-
- const description =
- tier === PRICING_TIER_PRODUCT_IDS.FREE
- ? 'Upgrade to the pro plan to support the growth of your project.'
- : tier === PRICING_TIER_PRODUCT_IDS.PRO
- ? 'Consider disabling spend cap to support the growth of your project'
- : tier === PRICING_TIER_PRODUCT_IDS.PAYG
- ? "As you have disabled spend cap, additional resources will be charged on a per-usage basis. You can check your project's usage details for more information."
- : ''
-
- return (
-
- This organization uses the legacy project-based billing. We’ve recently made some
- big improvements to our billing system. To migrate to the new organization-based
- billing, head over to your{' '}
-
- organization billing settings
-
- .
-