diff --git a/studio/components/interfaces/BillingV2/Usage/Activity.tsx b/studio/components/interfaces/BillingV2/Usage/Activity.tsx index d6b874ca7ed..e8d87d74041 100644 --- a/studio/components/interfaces/BillingV2/Usage/Activity.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Activity.tsx @@ -8,6 +8,7 @@ import { UsageMetric, useProjectUsageQuery, } from 'data/usage/project-usage-query' +import dayjs from 'dayjs' import { Button, IconAlertTriangle } from 'ui' import { USAGE_APPROACHING_THRESHOLD } from '../Billing.constants' import BarChart from './BarChart' @@ -84,31 +85,37 @@ const Activity = ({ projectRef }: ActivityProps) => { monthly_active_users: { isLoading: isLoadingMauData, data: mauData?.data ?? [], + showLastUpdated: mauData?.hasNoData === false, margin: 18, }, monthly_active_sso_users: { isLoading: isLoadingMauSSOData, data: mauSSOData?.data ?? [], + showLastUpdated: mauSSOData?.hasNoData === false, margin: 20, }, storage_image_render_count: { isLoading: isLoadingAssetTransformationsData, data: assetTransformationsData?.data ?? [], + showLastUpdated: assetTransformationsData?.hasNoData === false, margin: 0, }, func_invocations: { isLoading: isLoadingFuncInvocationsData, data: funcInvocationsData?.data ?? [], + showLastUpdated: funcInvocationsData?.hasNoData === false, margin: 26, }, realtime_message_count: { isLoading: isLoadingRealtimeMessagesData, data: realtimeMessagesData?.data ?? [], + showLastUpdated: realtimeMessagesData?.hasNoData === false, margin: 38, }, realtime_peak_connection: { isLoading: isLoadingRealtimeConnectionsData, data: realtimeConnectionsData?.data ?? [], + showLastUpdated: realtimeConnectionsData?.hasNoData === false, margin: 0, }, } @@ -125,6 +132,19 @@ const Activity = ({ projectRef }: ActivityProps) => { typeof usageMeta !== 'number' ? (usageMeta?.usage ?? 0) / (usageMeta?.limit ?? 0) : 0 const usageExcess = (usageMeta?.usage ?? 0) - (usageMeta?.limit ?? 0) + const chartData = chartMeta[attribute.key]?.data ?? [] + + // [Joshen] Ideally this should come from the API imo, foresee some discrepancies + const lastZeroValue = chartData.find( + (x: any) => x.loopId > 0 && x[attribute.attribute] === 0 + ) + const lastKnownValue = + lastZeroValue !== undefined + ? dayjs(lastZeroValue.period_start) + .subtract(1, 'day') + .format('DD MMM YYYY, HH:mma (ZZ)') + : undefined + return ( { <>

{attribute.name} over time

-

{attribute.chartDescription}

+ {attribute.chartDescription.split('\n').map((paragraph, idx) => ( +

+ {paragraph} +

+ ))} + {lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && ( + + Last updated at: {lastKnownValue} + + )}
{chartMeta[attribute.key].isLoading ? (
@@ -207,7 +236,7 @@ const Activity = ({ projectRef }: ActivityProps) => { value.toLocaleString()} diff --git a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx index 7947033f423..bdf99d0de5e 100644 --- a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx @@ -8,6 +8,7 @@ import { UsageMetric, useProjectUsageQuery, } from 'data/usage/project-usage-query' +import dayjs from 'dayjs' import { USAGE_APPROACHING_THRESHOLD } from 'lib/constants' import { formatBytes } from 'lib/helpers' import { Button, IconAlertTriangle } from 'ui' @@ -45,8 +46,16 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => { }) const chartMeta: any = { - db_egress: { isLoading: isLoadingDbEgressData, data: dbEgressData?.data ?? [] }, - storage_egress: { isLoading: isLoadingStorageEgressData, data: storageEgressData?.data ?? [] }, + db_egress: { + isLoading: isLoadingDbEgressData, + data: dbEgressData?.data ?? [], + showLastUpdated: dbEgressData?.hasNoData === false, + }, + storage_egress: { + isLoading: isLoadingStorageEgressData, + data: storageEgressData?.data ?? [], + showLastUpdated: storageEgressData?.hasNoData === false, + }, } if (categoryMeta === undefined) return null @@ -61,6 +70,19 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => { typeof usageMeta !== 'number' ? (usageMeta?.usage ?? 0) / (usageMeta?.limit ?? 0) : 0 const usageExcess = (usageMeta?.usage ?? 0) - (usageMeta?.limit ?? 0) + const chartData = chartMeta[attribute.key]?.data ?? [] + + // [Joshen] Ideally this should come from the API imo, foresee some discrepancies + const lastZeroValue = chartData.find( + (x: any) => x.loopId > 0 && x[attribute.attribute] === 0 + ) + const lastKnownValue = + lastZeroValue !== undefined + ? dayjs(lastZeroValue.period_start) + .subtract(1, 'day') + .format('DD MMM YYYY, HH:mma (ZZ)') + : undefined + return (
@@ -118,7 +140,14 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {

{attribute.name} over time

-

{attribute.chartDescription}

+ {attribute.chartDescription.split('\n').map((paragraph, idx) => ( +

+ {paragraph} +

+ ))} + {lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && ( + Last updated at: {lastKnownValue} + )}
{chartMeta[attribute.key]?.isLoading ? (
@@ -130,7 +159,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => { formatBytes(value, 1, 'GB').replace(/\s/g, '')} diff --git a/studio/components/interfaces/BillingV2/Usage/BarChart.tsx b/studio/components/interfaces/BillingV2/Usage/BarChart.tsx index 9d8bfb4b526..ff32750c910 100644 --- a/studio/components/interfaces/BillingV2/Usage/BarChart.tsx +++ b/studio/components/interfaces/BillingV2/Usage/BarChart.tsx @@ -1,14 +1,13 @@ import { DataPoint } from 'data/analytics/constants' import { - ResponsiveContainer, - ComposedChart, - CartesianGrid, - YAxis, - XAxis, Bar, - ReferenceLine, + CartesianGrid, Cell, - Label, + ComposedChart, + ReferenceLine, + ResponsiveContainer, + XAxis, + YAxis, } from 'recharts' import { Y_DOMAIN_CEILING_MULTIPLIER } from './Usage.constants' @@ -50,7 +49,7 @@ const BarChart = ({ - + { }) const chartMeta: any = { - cpu_usage: { isLoading: isLoadingCpuUsageData, data: cpuUsageData?.data ?? [] }, - ram_usage: { isLoading: isLoadingMemoryUsageData, data: memoryUsageData?.data ?? [] }, - disk_io_budget: { isLoading: isLoadingIoBudgetData, data: ioBudgetData?.data ?? [] }, + cpu_usage: { + isLoading: isLoadingCpuUsageData, + data: cpuUsageData?.data ?? [], + }, + ram_usage: { + isLoading: isLoadingMemoryUsageData, + data: memoryUsageData?.data ?? [], + }, + disk_io_budget: { + isLoading: isLoadingIoBudgetData, + data: ioBudgetData?.data ?? [], + }, } if (categoryMeta === undefined) return null @@ -55,6 +65,19 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { <> {categoryMeta.attributes.map((attribute) => { + const chartData = chartMeta[attribute.key]?.data ?? [] + + // [Joshen] Ideally this should come from the API imo, foresee some discrepancies + const lastZeroValue = chartData.find( + (x: any) => x.loopId > 0 && x[attribute.attribute] === 0 + ) + const lastKnownValue = + lastZeroValue !== undefined + ? dayjs(lastZeroValue.period_start) + .subtract(1, 'day') + .format('DD MMM YYYY, HH:mma (ZZ)') + : undefined + return (
@@ -74,6 +97,9 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { {paragraph}

))} + {lastKnownValue !== undefined && ( + Last updated at: {lastKnownValue} + )}
{chartMeta[attribute.key].isLoading ? (
@@ -84,7 +110,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { ) : ( `${value}%`} yLimit={100} /> diff --git a/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx b/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx index 7e713d6ab57..981e1778627 100644 --- a/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx +++ b/studio/components/interfaces/BillingV2/Usage/SizeAndCounts.tsx @@ -8,6 +8,7 @@ import { UsageMetric, useProjectUsageQuery, } from 'data/usage/project-usage-query' +import dayjs from 'dayjs' import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' import { formatBytes } from 'lib/helpers' import Link from 'next/link' @@ -55,9 +56,21 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => { }) const chartMeta: any = { - db_size: { isLoading: isLoadingDbSizeData, data: dbSizeData?.data ?? [] }, - storage_size: { isLoading: isLoadingStorageSizeData, data: storageSizeData?.data ?? [] }, - func_count: { isLoading: isLoadingFunctionCountData, data: functionCountData?.data ?? [] }, + db_size: { + isLoading: isLoadingDbSizeData, + data: dbSizeData?.data ?? [], + showLastUpdated: dbSizeData?.hasNoData === false, + }, + storage_size: { + isLoading: isLoadingStorageSizeData, + data: storageSizeData?.data ?? [], + showLastUpdated: storageSizeData?.hasNoData === false, + }, + func_count: { + isLoading: isLoadingFunctionCountData, + data: functionCountData?.data ?? [], + showLastUpdated: functionCountData?.hasNoData === false, + }, } if (categoryMeta === undefined) return null @@ -72,6 +85,19 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => { typeof usageMeta !== 'number' ? (usageMeta?.usage ?? 0) / (usageMeta?.limit ?? 0) : 0 const usageExcess = (usageMeta?.usage ?? 0) - (usageMeta?.limit ?? 0) + const chartData = chartMeta[attribute.key]?.data ?? [] + + // [Joshen] Ideally this should come from the API imo, foresee some discrepancies + const lastZeroValue = chartData.find( + (x: any) => x.loopId > 0 && x[attribute.attribute] === 0 + ) + const lastKnownValue = + lastZeroValue !== undefined + ? dayjs(lastZeroValue.period_start) + .subtract(1, 'day') + .format('DD MMM YYYY, HH:mma (ZZ)') + : undefined + return (
@@ -164,7 +190,14 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => {

{attribute.name} over time

-

{attribute.chartDescription}

+ {attribute.chartDescription.split('\n').map((paragraph, idx) => ( +

+ {paragraph} +

+ ))} + {lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && ( + Last updated at: {lastKnownValue} + )}
{chartMeta[attribute.key].isLoading ? (
@@ -176,7 +209,7 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => { diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts index 2c5dc8637b2..618d6a89ba7 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts +++ b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts @@ -49,7 +49,7 @@ export const USAGE_CATEGORIES: { description: 'SSD Disks are attached to your servers and the disk performance of your workload is determined by the Disk IO bandwidth of this connection.', chartDescription: - 'If the remaining bandwidth is less than 100%, it indicates that your workload has burst beyond the baseline IO throughput during the day.\nIf this metric drops to zero, the workload has used up all the burst IO throughput minutes during the day and is running at the baseline performance.\nThe amount of remaining bandwidth resets at the beginning of each day, and the data shown here is refreshed over a period of 24 hours.', + 'The amount of remaining bandwidth resets at the beginning of each day, and the data shown here is refreshed over a period of 24 hours.', }, ], }, diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx index 8b4ec1fe140..69b744f0898 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Usage.tsx @@ -3,6 +3,7 @@ import { useParams } from 'common' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { useProjectsQuery } from 'data/projects/projects-query' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' +import { useProjectUsageQuery } from 'data/usage/project-usage-query' import Link from 'next/link' import { useRef, useState } from 'react' import { Button, IconAlertCircle, IconCheckCircle, IconLoader, Listbox } from 'ui' @@ -10,9 +11,8 @@ import Activity from './Activity' import Bandwidth from './Bandwidth' import Infrastructure from './Infrastructure' import SizeAndCounts from './SizeAndCounts' -import { useProjectUsageQuery } from 'data/usage/project-usage-query' -import { getUsageStatus } from './Usage.utils' import { USAGE_CATEGORIES, USAGE_STATUS } from './Usage.constants' +import { getUsageStatus } from './Usage.utils' const Usage = () => { const { ref } = useParams() diff --git a/studio/data/analytics/constants.ts b/studio/data/analytics/constants.ts index ef173509814..d12ba4a1c19 100644 --- a/studio/data/analytics/constants.ts +++ b/studio/data/analytics/constants.ts @@ -1,6 +1,7 @@ export type DataPoint = { loopId: number | string period_start: string + periodStartFormatted?: string } & { // Attribute name will be the key [key: string]: string @@ -12,4 +13,5 @@ export interface AnalyticsData { total: number totalAverage: number yAxisLimit: number + hasNoData?: boolean } diff --git a/studio/data/analytics/daily-stats-query.ts b/studio/data/analytics/daily-stats-query.ts index b7283e76b8a..e0695189203 100644 --- a/studio/data/analytics/daily-stats-query.ts +++ b/studio/data/analytics/daily-stats-query.ts @@ -1,10 +1,10 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' +import dayjs from 'dayjs' import { get } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' -import { analyticsKeys } from './keys' import { AnalyticsData } from './constants' -import dayjs from 'dayjs' +import { analyticsKeys } from './keys' export type DailyStatsVariables = { projectRef?: string @@ -141,19 +141,21 @@ export const useDailyStatsQuery = ( return { loopId: idx, period_start: dayjs(startDate).add(idx, 'day').format(dateFormat), + periodStartFormatted: dayjs(startDate).add(idx, 'day').format(dateFormat), [attribute]: 0, } }) - return { ...data, data: mockData } + return { ...data, data: mockData, hasNoData: true } } else { return { ...data, + hasNoData: false, data: data.data.map((x) => { return { ...x, [attribute]: modifier !== undefined ? modifier(Number(x[attribute])) : Number(x[attribute]), - period_start: + periodStartFormatted: Number(x.period_start) > 0 ? dayjs(x.period_start).format(dateFormat) : x.period_start, diff --git a/studio/data/analytics/infra-monitoring-query.ts b/studio/data/analytics/infra-monitoring-query.ts index cf0323ebfba..4c5aef011d6 100644 --- a/studio/data/analytics/infra-monitoring-query.ts +++ b/studio/data/analytics/infra-monitoring-query.ts @@ -1,10 +1,10 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' +import dayjs from 'dayjs' import { get } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' -import { analyticsKeys } from './keys' import { AnalyticsData } from './constants' -import dayjs from 'dayjs' +import { analyticsKeys } from './keys' export type InfraMonitoringVariables = { projectRef?: string @@ -73,7 +73,7 @@ export const useInfraMonitoringQuery = ( ...x, [attribute]: modifier !== undefined ? modifier(Number(x[attribute])) : Number(x[attribute]), - period_start: dayjs(x.period_start).format(dateFormat), + periodStartFormatted: dayjs(x.period_start).format(dateFormat), } }), }