Merge branch 'master' into views

This commit is contained in:
jingsam authored and GitHub committed 2024-07-30 19:20:10 +08:00
commit f4f8b2d0ee
22 files changed
+235 -157

No files matched your search

+1
View File
@@ -34,6 +34,7 @@ Guilherme Souza
Haydn Maley
Hieu Pham
Inian P
Jenny Kibiri
Jess Shears
Joel Lee
John Pena
+1 -1
View File
@@ -1,7 +1,7 @@
clispec: '001'
info:
id: cli
version: 1.187.3
version: 1.187.10
title: Supabase CLI
language: sh
source: https://github.com/supabase/cli
@@ -6,9 +6,10 @@ export interface Metric {
key: string
name: string
units: string
anchor: string
anchor?: string
category: string
unitName: string
unitName?: string
tip?: string
}
export const BILLING_BREAKDOWN_METRICS: Metric[] = [
@@ -58,7 +59,6 @@ export const BILLING_BREAKDOWN_METRICS: Metric[] = [
units: 'absolute',
anchor: 'storageImageTransformations',
category: 'Storage',
unitName: 'image',
},
{
key: PricingMetric.REALTIME_PEAK_CONNECTIONS,
@@ -66,7 +66,6 @@ export const BILLING_BREAKDOWN_METRICS: Metric[] = [
units: 'absolute',
anchor: 'realtimePeakConnections',
category: 'Realtime',
unitName: 'connection',
},
{
key: PricingMetric.REALTIME_MESSAGE_COUNT,
@@ -74,7 +73,6 @@ export const BILLING_BREAKDOWN_METRICS: Metric[] = [
units: 'absolute',
anchor: 'realtimeMessageCount',
category: 'Realtime',
unitName: 'message',
},
{
key: PricingMetric.FUNCTION_INVOCATIONS,
@@ -82,6 +80,13 @@ export const BILLING_BREAKDOWN_METRICS: Metric[] = [
units: 'absolute',
anchor: 'funcInvocations',
category: 'Edge Functions',
unitName: 'invocation',
},
{
key: PricingMetric.DISK_SIZE_GB_HOURS,
name: 'Disk Size',
units: 'absolute',
unitName: 'GB-Hrs',
category: 'Database',
tip: 'Each project gets provisioned with 8 GB of disk. When you get close to the disk size limit, we autoscale your disk by 1.5x. The first 8 GB of disk is free for every single project.',
},
]
@@ -52,7 +52,7 @@ const BillingBreakdown = () => {
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
{!canReadSubscriptions ? (
<NoPermission resourceText="view this organization's billing" />
<NoPermission resourceText="view this organization's billing breakdown" />
) : (
<>
{isLoadingSubscription && (
@@ -36,21 +36,30 @@ const BillingMetric = ({
} else if (
(usageMeta.cost && usageMeta.cost > 0) ||
!relativeToSubscription ||
usageMeta.unlimited
usageMeta.unlimited ||
usageMeta.pricing_free_units === 0
) {
return metric.units === 'bytes'
return metric.units === 'bytes' || metric.units === 'gigabytes'
? `${usageMeta.usage.toLocaleString() ?? 0} GB`
: usageMeta.usage.toLocaleString()
: usageMeta.usage.toLocaleString() + (metric.unitName ? ` ${metric.unitName}` : '')
} else {
return metric.units === 'bytes'
return metric.units === 'bytes' || metric.units === 'gigabytes'
? `${usageMeta.usage.toLocaleString() ?? 0} / ${usageMeta.pricing_free_units ?? 0} GB`
: `${usageMeta.usage.toLocaleString()} / ${usageMeta.pricing_free_units?.toLocaleString()}`
: `${usageMeta.usage.toLocaleString()} / ${usageMeta.pricing_free_units?.toLocaleString()}` +
(metric.unitName ? ` ${metric.unitName}` : '')
}
}, [usageMeta, relativeToSubscription, metric])
const sortedProjectAllocations = useMemo(() => {
if (!usageMeta || !usageMeta.project_allocations) return []
return usageMeta.project_allocations.sort((a, b) => b.usage - a.usage)
}, [usageMeta])
if (!usageMeta) return null
const usageRatio = usageMeta.usage / (usageMeta.pricing_free_units ?? 0)
const usageRatio =
usageMeta.usage === 0 ? 0 : usageMeta.usage / (usageMeta.pricing_free_units ?? 0)
const isUsageBillingEnabled = subscription?.usage_billing_enabled === true
@@ -61,24 +70,32 @@ const BillingMetric = ({
const unit = billingMetricUnit(usageMeta.metric as PricingMetric)
const percentageLabel =
usageRatio < 0.01 ? '<1%' : `${(+(usageRatio * 100).toFixed(0)).toLocaleString()}%`
usageMeta.usage === 0 || usageMeta.pricing_free_units === 0
? ''
: usageRatio < 0.01
? '(<1%)'
: `(${(+(usageRatio * 100).toFixed(0)).toLocaleString()}%)`
return (
<div className="flex items-center justify-between">
<div>
<Link href={`/org/${slug}/usage#${metric.anchor}`}>
<div className="group flex items-center space-x-2">
<p className="text-sm text-foreground-light group-hover:text-foreground transition cursor-pointer">
{metric.name}
</p>
<IconChevronRight strokeWidth={1.5} size={16} className="transition" />
</div>
</Link>
{metric.anchor ? (
<Link href={`/org/${slug}/usage#${metric.anchor}`}>
<div className="group flex items-center space-x-2">
<p className="text-sm text-foreground-light group-hover:text-foreground transition cursor-pointer">
{metric.name}
</p>
<IconChevronRight strokeWidth={1.5} size={16} className="transition" />
</div>
</Link>
) : (
<p className="text-sm text-foreground-light">{metric.name}</p>
)}
<span className="text-sm">{usageLabel}</span>&nbsp;
{usageMeta.cost && usageMeta.cost > 0 ? (
{relativeToSubscription && usageMeta.cost && usageMeta.cost > 0 ? (
<span className="text-sm">({formatCurrency(usageMeta.cost)})</span>
) : usageMeta.available_in_plan && !usageMeta.unlimited && relativeToSubscription ? (
<span className="text-sm">({percentageLabel})</span>
<span className="text-sm">{percentageLabel}</span>
) : null}
</div>
@@ -130,39 +147,10 @@ const BillingMetric = ({
<div className="text-xs text-foreground space-y-2">
<p className="font-medium">{usageMeta.unit_price_desc}</p>
{usageMeta.project_allocations && usageMeta.project_allocations.length > 0 && (
<table className="list-disc w-full">
<thead>
<tr>
<th className="text-left">Project</th>
<th className="text-right">Usage</th>
</tr>
</thead>
<tbody>
{usageMeta.project_allocations.map((allocation) => (
<tr key={`${usageMeta.metric}_${allocation.ref}`}>
<td>{allocation.name}</td>
<td className="text-right">
{formatUsage(usageMeta.metric as PricingMetric, allocation.usage)}
</td>
</tr>
))}
<tr></tr>
</tbody>
<tfoot>
<tr>
<td className="py-2 border-t text-left">
Total{unit && <span> ({unit})</span>}
</td>
<td className="py-2 border-t text-right">
{formatUsage(
usageMeta.metric as PricingMetric,
usageMeta.usage_original
)}{' '}
</td>
</tr>
</tfoot>
</table>
{metric.tip && (
<div className="my-2">
<p className="text-xs">{metric.tip}</p>
</div>
)}
{subscription.usage_billing_enabled === false &&
@@ -176,6 +164,40 @@ const BillingMetric = ({
</p>
</div>
)}
{sortedProjectAllocations && sortedProjectAllocations.length > 0 && (
<table className="list-disc w-full">
<thead>
<tr>
<th className="text-left">Project</th>
<th className="text-right">Usage</th>
</tr>
</thead>
<tbody>
{sortedProjectAllocations.map((allocation) => (
<tr key={`${usageMeta.metric}_${allocation.ref}`}>
<td>{allocation.name}</td>
<td className="text-right">
{formatUsage(usageMeta.metric as PricingMetric, allocation)}
</td>
</tr>
))}
<tr></tr>
</tbody>
<tfoot>
<tr>
<td className="py-2 border-t text-left">
Total{unit && <span> ({unit})</span>}
</td>
<td className="py-2 border-t text-right">
{formatUsage(usageMeta.metric as PricingMetric, {
usage: usageMeta.usage_original,
})}{' '}
</td>
</tr>
</tfoot>
</table>
)}
</div>
</div>
</Tooltip.Content>
@@ -20,15 +20,21 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
const usageMeta = usage.usages.find((x) => x.metric === metric.key)
const usageLabel = useMemo(() => {
if (usageMeta?.cost && usageMeta.cost > 0) {
return `${usageMeta?.usage?.toLocaleString() ?? 0} hours`
} else {
if (usageMeta?.pricing_free_units) {
return `${usageMeta?.usage?.toLocaleString() ?? 0} / ${
usageMeta?.pricing_free_units ?? 0
} hours`
} else {
return `${usageMeta?.usage?.toLocaleString() ?? 0} hours`
}
}, [usageMeta])
const sortedProjectAllocations = useMemo(() => {
if (!usageMeta || !usageMeta.project_allocations) return []
return usageMeta.project_allocations.sort((a, b) => b.usage - a.usage)
}, [usageMeta])
return (
<div className="flex items-center justify-between">
<div>
@@ -57,40 +63,7 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
<div className="text-xs text-foreground space-y-2">
<p className="font-medium">{usageMeta?.unit_price_desc}</p>
{usageMeta?.project_allocations && usageMeta.project_allocations.length > 0 && (
<table className="list-disc w-full">
<thead>
<tr>
<th className="text-left">Project</th>
<th className="text-right">Usage</th>
</tr>
</thead>
<tbody>
{usageMeta.project_allocations.map((allocation) => (
<tr key={`${usageMeta.metric}_${allocation.ref}`}>
<td>{allocation.name}</td>
<td className="text-right">
{formatUsage(usageMeta.metric as PricingMetric, allocation.usage)}
</td>
</tr>
))}
<tr></tr>
</tbody>
<tfoot>
<tr>
<td className="py-2 border-t text-left">Total (Hours)</td>
<td className="py-2 border-t text-right">
{formatUsage(
usageMeta.metric as PricingMetric,
usageMeta.usage_original
)}
</td>
</tr>
</tfoot>
</table>
)}
<div className="mt-2">
<div className="my-2">
<p className="text-xs">
Every project is a dedicated server and database. For every hour your project
is active, it incurs compute costs based on the compute size of your project.
@@ -104,6 +77,38 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
</Link>
</p>
</div>
{usageMeta && sortedProjectAllocations.length > 0 && (
<table className="list-disc w-full">
<thead>
<tr>
<th className="text-left">Project</th>
<th className="text-right">Usage</th>
</tr>
</thead>
<tbody>
{sortedProjectAllocations.map((allocation) => (
<tr key={`${usageMeta.metric}_${allocation.ref}`}>
<td>{allocation.name}</td>
<td className="text-right">
{formatUsage(usageMeta.metric as PricingMetric, allocation)}
</td>
</tr>
))}
<tr></tr>
</tbody>
<tfoot>
<tr>
<td className="py-2 border-t text-left">Total (Hours)</td>
<td className="py-2 border-t text-right">
{formatUsage(usageMeta.metric as PricingMetric, {
usage: usageMeta.usage_original,
})}
</td>
</tr>
</tfoot>
</table>
)}
</div>
</div>
</Tooltip.Content>
@@ -193,7 +193,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
</td>
<td className="py-2 pr-4 text-sm text-right tabular-nums max-w-[100px]">
{fee.usage_original
? `${formatUsage(fee.usage_metric!, fee.usage_original)}`
? `${formatUsage(fee.usage_metric!, { usage: fee.usage_original })}`
: fee.quantity
? fee.quantity
: null}
@@ -223,7 +223,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
{breakdown.project_name}
</td>
<td className="pb-1 text-xs tabular-nums text-right pr-4">
{formatUsage(fee.usage_metric!, breakdown.usage)}
{formatUsage(fee.usage_metric!, breakdown)}
</td>
<td />
<td />
@@ -6,11 +6,32 @@ const pricingMetricBytes = [
PricingMetric.STORAGE_SIZE,
]
export const formatUsage = (pricingMetric: PricingMetric, usage: number) => {
export const formatUsage = (
pricingMetric: PricingMetric,
allocation: { usage: number; hours?: number }
) => {
if (allocation.hours) {
return (
allocation.usage.toLocaleString() +
' (' +
Math.round(allocation.usage / allocation.hours).toLocaleString() +
'x' +
allocation.hours.toLocaleString() +
' hours)'
)
}
if (pricingMetricBytes.includes(pricingMetric)) {
return +(usage / 1e9).toFixed(2).toLocaleString()
const formattedUsage = +(allocation.usage / 1e9).toFixed(2).toLocaleString()
// To avoid very low usage displaying as "0", we will show "<0.01" instead
if (allocation.usage > 0 && formattedUsage === 0) {
return '<0.01'
} else {
return formattedUsage
}
} else {
return usage.toLocaleString()
return allocation.usage.toLocaleString()
}
}
@@ -36,7 +36,7 @@ const SidePanelGitHubRepoLinker = ({ projectRef }: SidePanelGitHubRepoLinkerProp
const sidePanelStateSnapshot = useSidePanelsStateSnapshot()
const { data: gitHubAuthorization, isLoading: isLoadingGitHubAuthorization } =
useGitHubAuthorizationQuery()
useGitHubAuthorizationQuery({ enabled: sidePanelStateSnapshot.githubConnectionsOpen })
// [Alaister]: temp override with <any> until the typegen is fixed
const { data: githubReposData, isLoading: isLoadingGitHubRepos } = useGitHubRepositoriesQuery<
@@ -61,7 +61,7 @@ const OAuthApps = () => {
if (!canReadOAuthApps) {
return (
<ScaffoldContainerLegacy>
<NoPermission resourceText="view invoices" />
<NoPermission resourceText="view OAuth apps" />
</ScaffoldContainerLegacy>
)
}
@@ -58,9 +58,13 @@ const TotalUsage = ({
)
const sortedBillingMetrics = useMemo(() => {
if (!usage) return BILLING_BREAKDOWN_METRICS
if (!usage) return []
return BILLING_BREAKDOWN_METRICS.slice().sort((a, b) => {
const breakdownMetrics = BILLING_BREAKDOWN_METRICS.filter((metric) =>
usage.usages.some((usage) => !usage.available_in_plan || usage.metric === metric.key)
)
return breakdownMetrics.slice().sort((a, b) => {
const usageMetaA = usage.usages.find((x) => x.metric === a.key)
const usageRatioA =
typeof usageMetaA !== 'number'
@@ -92,8 +96,8 @@ const TotalUsage = ({
section={{
name: 'Usage Summary',
description: isUsageBillingEnabled
? 'Your plan includes a limited amount of usage. If the usage on your organization 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 organization 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.',
? `Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, your subscription will be charged for the overages. It may take up to ${subscription?.usage_based_billing_project_addons ? '1 hour' : '24 hours'} for usage stats to update.`
: `Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, you may experience restrictions, as you are currently not billed for overages. It may take up to ${subscription?.usage_based_billing_project_addons ? '1 hour' : '24 hours'} for usage stats to update.`,
links: [
{
name: 'How billing works',
@@ -184,7 +188,8 @@ const TotalUsage = ({
<div
className={clsx(
'col-span-12 md:col-span-6 space-y-4 py-4 border-overlay',
i % 2 === 0 ? 'md:border-r md:pr-4' : 'md:pl-4'
(i + sortedBillingMetrics.length) % 2 === 0 ? 'md:border-r md:pr-4' : 'md:pl-4',
'border-b'
)}
key={metric}
>
@@ -47,7 +47,7 @@ export interface CategoryAttribute {
key: string // Property from organization usage
attributes: Attribute[] // For querying against stats-daily / infra-monitoring
name: string
unit: 'bytes' | 'absolute' | 'percentage' | 'hours'
unit: 'bytes' | 'absolute' | 'percentage' | 'hours' | 'gigabytes'
links?: {
name: string
url: string
@@ -56,7 +56,7 @@ export interface CategoryAttribute {
chartPrefix?: 'Max' | 'Average' | 'Cumulative'
chartSuffix?: string
chartDescription: string
additionalInfo?: (subscription?: OrgSubscription, usage?: OrgUsageResponse) => JSX.Element | null
additionalInfo?: (usage?: OrgUsageResponse) => JSX.Element | null
}
export type CategoryMetaKey = 'bandwidth' | 'sizeCount' | 'activity' | 'compute'
@@ -68,7 +68,9 @@ export interface CategoryMeta {
attributes: CategoryAttribute[]
}
export const USAGE_CATEGORIES: CategoryMeta[] = [
export const USAGE_CATEGORIES: (subscription?: OrgSubscription) => CategoryMeta[] = (
subscription
) => [
{
key: 'bandwidth',
name: 'Bandwidth',
@@ -106,15 +108,25 @@ export const USAGE_CATEGORIES: CategoryMeta[] = [
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.',
subscription?.usage_based_billing_project_addons === true
? 'Database size refers to the monthly average database space usage, as reported by Postgres. Paid Plans use auto-scaling disks and are billed based on provisioned disk size, rather than database space used.'
: 'Database size refers to the monthly average database space 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',
},
...(subscription?.usage_based_billing_project_addons === true
? [
{
name: 'Disk Management',
url: 'https://supabase.com/docs/guides/platform/database-size#disk-management',
},
]
: []),
],
chartDescription: 'The data refreshes every 24 hours.',
additionalInfo: (subscription?: OrgSubscription, usage?: OrgUsageResponse) => {
additionalInfo: (usage?: OrgUsageResponse) => {
const usageMeta = usage?.usages.find((x) => x.metric === PricingMetric.DATABASE_SIZE)
const usageRatio =
typeof usageMeta !== 'number'
@@ -38,6 +38,8 @@ export const ChartYFormatterCompactNumber = (number: number | string, unit: stri
const formattedBytes = formatBytesCompact(number).replace(/\s/g, '')
return formattedBytes === '0bytes' ? '0' : formattedBytes
} else if (unit === 'gigabytes') {
return compactNumberFormatter.format(number) + 'GB'
} else {
return compactNumberFormatter.format(number)
}
@@ -53,6 +55,8 @@ export const ChartTooltipValueFormatter = (number: number | string, unit: string
const formattedBytes = formatBytesPrecision(number).replace(/\s/g, '')
return formattedBytes === '0bytes' ? '0' : formattedBytes
} else if (unit === 'gigabytes') {
return compactNumberFormatter.format(number) + 'GB'
} else {
return compactNumberFormatter.format(number)
}
@@ -21,7 +21,7 @@ export interface UsageBarChartProps {
data: DataPoint[]
name: string // Used within the tooltip
attributes: Attribute[]
unit: 'bytes' | 'absolute' | 'percentage' | 'hours'
unit: 'bytes' | 'absolute' | 'percentage' | 'hours' | 'gigabytes'
yLimit?: number
yMin?: number
yLeftMargin?: number
@@ -4,7 +4,7 @@ import { Attribute, COLOR_MAP } from './Usage.constants'
export interface SingleAttributeTooltipContentProps {
name: string
unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
value: any
isAfterToday: boolean
tooltipFormatter?: (value: any) => any
@@ -37,7 +37,7 @@ export interface MultiAttributeTooltipContentProps {
values: Payload<ValueType, string | number>[]
isAfterToday: boolean
tooltipFormatter?: (value: any) => any
unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
}
const AttributeContent = ({
@@ -51,7 +51,7 @@ const AttributeContent = ({
attributeMeta?: Payload<ValueType, string | number>
sumValue: number
tooltipFormatter?: (value: any) => any
unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
}) => {
const attrValue = Number(attributeMeta?.value ?? 0)
const percentageContribution = ((attrValue / sumValue) * 100).toFixed(1)
@@ -93,7 +93,7 @@ const AttributeUsage = ({
{isSuccess && (
<>
{usageMeta?.available_in_plan ? (
{!usageMeta || usageMeta?.available_in_plan ? (
<>
{!projectRef && (
<div className="space-y-2">
@@ -150,7 +150,7 @@ const AttributeUsage = ({
</div>
</div>
{currentBillingCycleSelected && !usageMeta.unlimited && (
{currentBillingCycleSelected && usageMeta && !usageMeta.unlimited && (
<SparkBar
type="horizontal"
barClass={clsx(
@@ -169,8 +169,8 @@ const AttributeUsage = ({
/>
)}
{
<div>
<div>
{usageMeta && (
<div className="flex items-center justify-between border-b py-1">
<p className="text-xs text-foreground-light">
Included in {subscription?.plan?.name.toLowerCase()} plan
@@ -179,43 +179,42 @@ const AttributeUsage = ({
<p className="text-xs">Unlimited</p>
) : (
<p className="text-xs">
{attribute.unit === 'bytes'
{attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
? `${usageMeta.pricing_free_units ?? 0} GB`
: (usageMeta.pricing_free_units ?? 0).toLocaleString()}
</p>
)}
</div>
{currentBillingCycleSelected && (
<div className="flex items-center justify-between py-1">
<p className="text-xs text-foreground-light">
{attribute.chartPrefix || 'Used '} in period
</p>
<p className="text-xs">
{attribute.unit === 'bytes'
? `${(usageMeta?.usage ?? 0).toFixed(2)} GB`
: (usageMeta?.usage ?? 0).toLocaleString()}
</p>
</div>
)}
{currentBillingCycleSelected &&
(usageMeta?.pricing_free_units ?? 0) > 0 && (
<div className="flex items-center justify-between border-t py-1">
<p className="text-xs text-foreground-light">Overage in period</p>
<p className="text-xs">
{(usageMeta?.pricing_free_units ?? 0) === -1 || usageExcess < 0
? `0${attribute.unit === 'bytes' ? ' GB' : ''}`
: attribute.unit === 'bytes'
? `${usageExcess.toFixed(2)} GB`
: usageExcess.toLocaleString()}
</p>
</div>
)}
</div>
}
)}
{currentBillingCycleSelected && usageMeta && (
<div className="flex items-center justify-between py-1">
<p className="text-xs text-foreground-light">
{attribute.chartPrefix || 'Used '} in period
</p>
<p className="text-xs">
{attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
? `${(usageMeta?.usage ?? 0).toFixed(2)} GB`
: (usageMeta?.usage ?? 0).toLocaleString()}
</p>
</div>
)}
{currentBillingCycleSelected && (usageMeta?.pricing_free_units ?? 0) > 0 && (
<div className="flex items-center justify-between border-t py-1">
<p className="text-xs text-foreground-light">Overage in period</p>
<p className="text-xs">
{(usageMeta?.pricing_free_units ?? 0) === -1 || usageExcess < 0
? `0${attribute.unit === 'bytes' || attribute.unit === 'gigabytes' ? ' GB' : ''}`
: attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
? `${usageExcess.toFixed(2)} GB`
: usageExcess.toLocaleString()}
</p>
</div>
)}
</div>
</div>
)}
{attribute.additionalInfo?.(subscription, usage)}
{attribute.additionalInfo?.(usage)}
<div className="space-y-1">
<p className="text-sm">
@@ -35,7 +35,9 @@ const UsageSection = ({
isSuccess: isSuccessUsage,
} = useOrgUsageQuery({ orgSlug })
const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === categoryKey)
const categoryMeta = USAGE_CATEGORIES(subscription).find(
(category) => category.key === categoryKey
)
if (!categoryMeta) return null
return (
+1 -1
View File
@@ -22,7 +22,7 @@ const NoPermission = ({ resourceText, isFullPage = false }: NoPermissionProps) =
<p className="text-sm">You need additional permissions to {resourceText}</p>
<div>
<p className="text-sm text-foreground-light">
Contact your organization owner or adminstrator for assistance.
Contact your organization owner or administrator for assistance.
</p>
</div>
</div>
@@ -21,6 +21,7 @@ export enum PricingMetric {
EGRESS = 'EGRESS',
DATABASE_SIZE = 'DATABASE_SIZE',
STORAGE_SIZE = 'STORAGE_SIZE',
DISK_SIZE_GB_HOURS = 'DISK_SIZE_GB_HOURS',
MONTHLY_ACTIVE_USERS = 'MONTHLY_ACTIVE_USERS',
MONTHLY_ACTIVE_SSO_USERS = 'MONTHLY_ACTIVE_SSO_USERS',
FUNCTION_INVOCATIONS = 'FUNCTION_INVOCATIONS',
+1 -1
View File
@@ -10,7 +10,7 @@ version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:20240722-c670708
image: supabase/studio:20240729-ce42139
restart: unless-stopped
healthcheck:
test:
+3 -2
View File
@@ -7844,8 +7844,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"license": "MIT"
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
+3 -3
View File
@@ -3911,7 +3911,7 @@ export interface components {
| 'STORAGE_IMAGES_TRANSFORMED'
| 'REALTIME_MESSAGE_COUNT'
| 'REALTIME_PEAK_CONNECTIONS'
| 'DISK_GB_HOURS'
| 'DISK_SIZE_GB_HOURS'
| 'COMPUTE_HOURS_BRANCH'
| 'COMPUTE_HOURS_XS'
| 'COMPUTE_HOURS_SM'
@@ -3941,7 +3941,6 @@ export interface components {
usage_original: number
}
OrgUsageResponse: {
slugs: string[]
usage_billing_enabled: boolean
usages: components['schemas']['OrgMetricUsage'][]
}
@@ -4308,6 +4307,7 @@ export interface components {
price_type: components['schemas']['ProjectAddonVariantPricingType']
}
ProjectAllocation: {
hours?: number
name: string
ref: string
usage: number
@@ -7803,7 +7803,7 @@ export interface operations {
| 'STORAGE_IMAGES_TRANSFORMED'
| 'REALTIME_MESSAGE_COUNT'
| 'REALTIME_PEAK_CONNECTIONS'
| 'DISK_GB_HOURS'
| 'DISK_SIZE_GB_HOURS'
| 'COMPUTE_HOURS_BRANCH'
| 'COMPUTE_HOURS_XS'
| 'COMPUTE_HOURS_SM'