From 9dcea70f9fbe01e79a00d837f857a9ba40ed4cac Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 30 Jul 2024 06:03:55 +0200 Subject: [PATCH 1/7] fix /Update package-lock.json (#28256) Update package-lock.json --- package-lock.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 480c239c932..6fede8c2768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", From 9de6ee58b15c029b6d9bb94b40f888b146426bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Tue, 30 Jul 2024 14:37:54 +0800 Subject: [PATCH 2/7] feat(orb): support new usage stats (#28208) - Add new metric Disk Size GB-Hrs (only exposed to paid plans on Orb) - Adjusted some texts as preparation of new billing plans - Fixed some visual bugs with the usage summary grid - Project allocations in tooltips are now sorted by usage (descending) - Project allocations with too little usage (<0.01) now display "<0.01" instead of just "0" - Usage formatter supports usage that is broken down to hours - Stop showing percentages when the usage is 0 (previously showed "<1%" This change is backwards compatible and can be merged. Note that there will be full overhaul in the feature of the entire usage page, this is just the least invasive change to make the transition work. --- .../BillingBreakdown.constants.ts | 17 ++- .../BillingBreakdown/BillingMetric.tsx | 122 +++++++++++------- .../BillingBreakdown/ComputeMetric.tsx | 79 ++++++------ .../BillingBreakdown/UpcomingInvoice.tsx | 4 +- .../Organization/BillingSettings/helpers.ts | 27 +++- .../Organization/Usage/TotalUsage.tsx | 15 ++- .../Organization/Usage/Usage.constants.tsx | 22 +++- .../Organization/Usage/Usage.utils.ts | 4 + .../Organization/Usage/UsageBarChart.tsx | 2 +- .../Organization/Usage/UsageChartTooltips.tsx | 6 +- .../Usage/UsageSection/AttributeUsage.tsx | 65 +++++----- .../Usage/UsageSection/UsageSection.tsx | 4 +- .../data/analytics/org-daily-stats-query.ts | 1 + packages/api-types/types/api.d.ts | 6 +- 14 files changed, 225 insertions(+), 149 deletions(-) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.constants.ts b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.constants.ts index 26eb514fece..ba64d28edf3 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.constants.ts +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.constants.ts @@ -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.', }, ] diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx index 5943872e241..c95e5190d34 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx @@ -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 (
- -
-

- {metric.name} -

- -
- + {metric.anchor ? ( + +
+

+ {metric.name} +

+ +
+ + ) : ( +

{metric.name}

+ )} {usageLabel}  - {usageMeta.cost && usageMeta.cost > 0 ? ( + {relativeToSubscription && usageMeta.cost && usageMeta.cost > 0 ? ( ({formatCurrency(usageMeta.cost)}) ) : usageMeta.available_in_plan && !usageMeta.unlimited && relativeToSubscription ? ( - ({percentageLabel}) + {percentageLabel} ) : null}
@@ -130,39 +147,10 @@ const BillingMetric = ({

{usageMeta.unit_price_desc}

- {usageMeta.project_allocations && usageMeta.project_allocations.length > 0 && ( - - - - - - - - - {usageMeta.project_allocations.map((allocation) => ( - - - - - ))} - - - - - - - - -
ProjectUsage
{allocation.name} - {formatUsage(usageMeta.metric as PricingMetric, allocation.usage)} -
- Total{unit && ({unit})} - - {formatUsage( - usageMeta.metric as PricingMetric, - usageMeta.usage_original - )}{' '} -
+ {metric.tip && ( +
+

{metric.tip}

+
)} {subscription.usage_billing_enabled === false && @@ -176,6 +164,40 @@ const BillingMetric = ({

)} + + {sortedProjectAllocations && sortedProjectAllocations.length > 0 && ( + + + + + + + + + {sortedProjectAllocations.map((allocation) => ( + + + + + ))} + + + + + + + + +
ProjectUsage
{allocation.name} + {formatUsage(usageMeta.metric as PricingMetric, allocation)} +
+ Total{unit && ({unit})} + + {formatUsage(usageMeta.metric as PricingMetric, { + usage: usageMeta.usage_original, + })}{' '} +
+ )}
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/ComputeMetric.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/ComputeMetric.tsx index 925015cc208..a319efb3056 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/ComputeMetric.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/ComputeMetric.tsx @@ -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 (
@@ -57,40 +63,7 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM

{usageMeta?.unit_price_desc}

- {usageMeta?.project_allocations && usageMeta.project_allocations.length > 0 && ( - - - - - - - - - {usageMeta.project_allocations.map((allocation) => ( - - - - - ))} - - - - - - - - -
ProjectUsage
{allocation.name} - {formatUsage(usageMeta.metric as PricingMetric, allocation.usage)} -
Total (Hours) - {formatUsage( - usageMeta.metric as PricingMetric, - usageMeta.usage_original - )} -
- )} - -
+

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

+ + {usageMeta && sortedProjectAllocations.length > 0 && ( + + + + + + + + + {sortedProjectAllocations.map((allocation) => ( + + + + + ))} + + + + + + + + +
ProjectUsage
{allocation.name} + {formatUsage(usageMeta.metric as PricingMetric, allocation)} +
Total (Hours) + {formatUsage(usageMeta.metric as PricingMetric, { + usage: usageMeta.usage_original, + })} +
+ )}
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx index 9a9a2fe5cdf..d3fe1d030e1 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx @@ -193,7 +193,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => { {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} - {formatUsage(fee.usage_metric!, breakdown.usage)} + {formatUsage(fee.usage_metric!, breakdown)} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts b/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts index 431fe037dd0..62677c4ba83 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts +++ b/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts @@ -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() } } diff --git a/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx b/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx index f56de7d4206..f664536c08a 100644 --- a/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx @@ -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 = ({
diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx b/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx index 60317e059eb..fbf88f42ebb 100644 --- a/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx @@ -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' diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts b/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts index 4e68b9e278a..62dcce33ae1 100644 --- a/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts +++ b/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts @@ -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) } diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx index 003943ddfa5..937f36c09af 100644 --- a/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx @@ -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 diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx index da6f20200c6..685c7afb46e 100644 --- a/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx @@ -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[] 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 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) diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx index e0a01bd76c1..bebc034cb83 100644 --- a/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx @@ -93,7 +93,7 @@ const AttributeUsage = ({ {isSuccess && ( <> - {usageMeta?.available_in_plan ? ( + {!usageMeta || usageMeta?.available_in_plan ? ( <> {!projectRef && (
@@ -150,7 +150,7 @@ const AttributeUsage = ({
- {currentBillingCycleSelected && !usageMeta.unlimited && ( + {currentBillingCycleSelected && usageMeta && !usageMeta.unlimited && ( )} - { -
+
+ {usageMeta && (

Included in {subscription?.plan?.name.toLowerCase()} plan @@ -179,43 +179,42 @@ const AttributeUsage = ({

Unlimited

) : (

- {attribute.unit === 'bytes' + {attribute.unit === 'bytes' || attribute.unit === 'gigabytes' ? `${usageMeta.pricing_free_units ?? 0} GB` : (usageMeta.pricing_free_units ?? 0).toLocaleString()}

)}
- {currentBillingCycleSelected && ( -
-

- {attribute.chartPrefix || 'Used '} in period -

-

- {attribute.unit === 'bytes' - ? `${(usageMeta?.usage ?? 0).toFixed(2)} GB` - : (usageMeta?.usage ?? 0).toLocaleString()} -

-
- )} - {currentBillingCycleSelected && - (usageMeta?.pricing_free_units ?? 0) > 0 && ( -
-

Overage in period

-

- {(usageMeta?.pricing_free_units ?? 0) === -1 || usageExcess < 0 - ? `0${attribute.unit === 'bytes' ? ' GB' : ''}` - : attribute.unit === 'bytes' - ? `${usageExcess.toFixed(2)} GB` - : usageExcess.toLocaleString()} -

-
- )} -
- } + )} + {currentBillingCycleSelected && usageMeta && ( +
+

+ {attribute.chartPrefix || 'Used '} in period +

+

+ {attribute.unit === 'bytes' || attribute.unit === 'gigabytes' + ? `${(usageMeta?.usage ?? 0).toFixed(2)} GB` + : (usageMeta?.usage ?? 0).toLocaleString()} +

+
+ )} + {currentBillingCycleSelected && (usageMeta?.pricing_free_units ?? 0) > 0 && ( +
+

Overage in period

+

+ {(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()} +

+
+ )} +
)} - {attribute.additionalInfo?.(subscription, usage)} + {attribute.additionalInfo?.(usage)}

diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx index f5a4fe47b8f..21f118c093f 100644 --- a/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx @@ -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 ( diff --git a/apps/studio/data/analytics/org-daily-stats-query.ts b/apps/studio/data/analytics/org-daily-stats-query.ts index 8b74d5e8d30..fa4e3797cc1 100644 --- a/apps/studio/data/analytics/org-daily-stats-query.ts +++ b/apps/studio/data/analytics/org-daily-stats-query.ts @@ -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', diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index f1ad7619ef2..a56e891dd08 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -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' From 46a7c73a82ed2c9acd8027b086924d517fcce403 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Tue, 30 Jul 2024 08:53:06 +0200 Subject: [PATCH 3/7] chore: update self-hosted image versions (#28272) chore: update image versions for docker/docker-compose.yml --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e59cfb968a2..9edd76f50bc 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -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: From a389866b5d8e5bc5ec5b6fff49953d1a16dc4036 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:03:59 +0200 Subject: [PATCH 4/7] chore: update cli reference doc (#28245) * chore: update cli reference doc * chore: update cli reference doc --- apps/docs/spec/cli_v1_commands.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/spec/cli_v1_commands.yaml b/apps/docs/spec/cli_v1_commands.yaml index 27da229776d..c682e88a220 100644 --- a/apps/docs/spec/cli_v1_commands.yaml +++ b/apps/docs/spec/cli_v1_commands.yaml @@ -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 From d674ccbfae5e13e7ed9bc1de4366971a519cf724 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Tue, 30 Jul 2024 17:07:39 +0800 Subject: [PATCH 5/7] fix: improve github authorization calls (#28274) --- .../IntegrationSettings/SidePanelGitHubRepoLinker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx b/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx index cba30fb97df..1a3b423f313 100644 --- a/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx +++ b/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx @@ -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 until the typegen is fixed const { data: githubReposData, isLoading: isLoadingGitHubRepos } = useGitHubRepositoriesQuery< From b4adc8d6ba1cbe6f1c63775b0421d4b5ce5dddb3 Mon Sep 17 00:00:00 2001 From: jenny kibiri <46620256+jenniekibiri@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:00:14 +0300 Subject: [PATCH 6/7] chore: add jenniekibiri to humans.txt (#28187) Co-authored-by: Jenny kibiri --- apps/docs/public/humans.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index ee2983ba130..25cb1920e0b 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -34,6 +34,7 @@ Guilherme Souza Haydn Maley Hieu Pham Inian P +Jenny Kibiri Jess Shears Joel Lee John Pena From 4b2c4acaab24cdbd586d38a593b56f099017db6e Mon Sep 17 00:00:00 2001 From: Thomas <31189692+ecktoteckto@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:01:45 +0200 Subject: [PATCH 7/7] Adjust texts shown when user doesn't have permission for org settings (#28210) --- .../BillingSettings/BillingBreakdown/BillingBreakdown.tsx | 2 +- .../components/interfaces/Organization/OAuthApps/OAuthApps.tsx | 2 +- apps/studio/components/ui/NoPermission.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.tsx index 4292616e26f..b22f205965e 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingBreakdown.tsx @@ -52,7 +52,7 @@ const BillingBreakdown = () => { {!canReadSubscriptions ? ( - + ) : ( <> {isLoadingSubscription && ( diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx index 8744880f1a8..512de2049f4 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx @@ -61,7 +61,7 @@ const OAuthApps = () => { if (!canReadOAuthApps) { return ( - + ) } diff --git a/apps/studio/components/ui/NoPermission.tsx b/apps/studio/components/ui/NoPermission.tsx index ab96619caca..3fb1a0b8554 100644 --- a/apps/studio/components/ui/NoPermission.tsx +++ b/apps/studio/components/ui/NoPermission.tsx @@ -22,7 +22,7 @@ const NoPermission = ({ resourceText, isFullPage = false }: NoPermissionProps) =

You need additional permissions to {resourceText}

- Contact your organization owner or adminstrator for assistance. + Contact your organization owner or administrator for assistance.