mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Show text on when last data point was added for chart
This commit is contained in:
1 parent
3b0f0766ce
commit
010903fd26
10 files changed
+153
-33
No files matched your search
@@ -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 (
|
||||
<SectionContent
|
||||
key={attribute.key}
|
||||
@@ -195,7 +215,16 @@ const Activity = ({ projectRef }: ActivityProps) => {
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<p>{attribute.name} over time</p>
|
||||
<p className="text-sm text-scale-1000">{attribute.chartDescription}</p>
|
||||
{attribute.chartDescription.split('\n').map((paragraph, idx) => (
|
||||
<p key={`para-${idx}`} className="text-sm text-scale-1000">
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && (
|
||||
<span className="text-sm text-scale-1000">
|
||||
Last updated at: {lastKnownValue}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{chartMeta[attribute.key].isLoading ? (
|
||||
<div className="space-y-2">
|
||||
@@ -207,7 +236,7 @@ const Activity = ({ projectRef }: ActivityProps) => {
|
||||
<BarChart
|
||||
hasQuota
|
||||
attribute={attribute.attribute}
|
||||
data={chartMeta[attribute.key]?.data ?? []}
|
||||
data={chartData}
|
||||
yLimit={usageMeta?.limit ?? 0}
|
||||
yLeftMargin={chartMeta[attribute.key].margin}
|
||||
yFormatter={(value) => value.toLocaleString()}
|
||||
|
||||
@@ -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 (
|
||||
<SectionContent key={attribute.key} section={attribute}>
|
||||
<div className="space-y-2">
|
||||
@@ -118,7 +140,14 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p>{attribute.name} over time</p>
|
||||
<p className="text-sm text-scale-1000">{attribute.chartDescription}</p>
|
||||
{attribute.chartDescription.split('\n').map((paragraph, idx) => (
|
||||
<p key={`para-${idx}`} className="text-sm text-scale-1000">
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && (
|
||||
<span className="text-sm text-scale-1000">Last updated at: {lastKnownValue}</span>
|
||||
)}
|
||||
</div>
|
||||
{chartMeta[attribute.key]?.isLoading ? (
|
||||
<div className="space-y-2">
|
||||
@@ -130,7 +159,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
|
||||
<BarChart
|
||||
hasQuota
|
||||
attribute={attribute.attribute}
|
||||
data={chartMeta[attribute.key]?.data ?? []}
|
||||
data={chartData}
|
||||
yLimit={usageMeta?.limit ?? 0}
|
||||
yLeftMargin={14}
|
||||
yFormatter={(value) => formatBytes(value, 1, 'GB').replace(/\s/g, '')}
|
||||
|
||||
@@ -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 = ({
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<ComposedChart data={data} margin={{ top: 0, right: 0, left: yLeftMargin, bottom: 0 }}>
|
||||
<CartesianGrid vertical={false} strokeDasharray="3 3" className="stroke-scale-800" />
|
||||
<XAxis dataKey="period_start" />
|
||||
<XAxis dataKey="periodStartFormatted" />
|
||||
<YAxis
|
||||
width={28}
|
||||
axisLine={false}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query'
|
||||
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
|
||||
import dayjs from 'dayjs'
|
||||
import BarChart from './BarChart'
|
||||
import SectionContent from './SectionContent'
|
||||
import SectionHeader from './SectionHeader'
|
||||
@@ -44,9 +45,18 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
|
||||
})
|
||||
|
||||
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) => {
|
||||
<>
|
||||
<SectionHeader title="Infrastructure" description="Some description here" />
|
||||
{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 (
|
||||
<SectionContent key={attribute.key} section={attribute}>
|
||||
<div className="space-y-1">
|
||||
@@ -74,6 +97,9 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{lastKnownValue !== undefined && (
|
||||
<span className="text-sm text-scale-1000">Last updated at: {lastKnownValue}</span>
|
||||
)}
|
||||
</div>
|
||||
{chartMeta[attribute.key].isLoading ? (
|
||||
<div className="space-y-2">
|
||||
@@ -84,7 +110,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
|
||||
) : (
|
||||
<BarChart
|
||||
attribute={attribute.attribute}
|
||||
data={chartMeta[attribute.key]?.data ?? []}
|
||||
data={chartData}
|
||||
yFormatter={(value) => `${value}%`}
|
||||
yLimit={100}
|
||||
/>
|
||||
|
||||
@@ -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 (
|
||||
<SectionContent key={attribute.key} section={attribute}>
|
||||
<div className="space-y-2">
|
||||
@@ -164,7 +190,14 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => {
|
||||
|
||||
<div className="space-y-1">
|
||||
<p>{attribute.name} over time</p>
|
||||
<p className="text-sm text-scale-1000">{attribute.chartDescription}</p>
|
||||
{attribute.chartDescription.split('\n').map((paragraph, idx) => (
|
||||
<p key={`para-${idx}`} className="text-sm text-scale-1000">
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{lastKnownValue !== undefined && chartMeta[attribute.key].showLastUpdated && (
|
||||
<span className="text-sm text-scale-1000">Last updated at: {lastKnownValue}</span>
|
||||
)}
|
||||
</div>
|
||||
{chartMeta[attribute.key].isLoading ? (
|
||||
<div className="space-y-2">
|
||||
@@ -176,7 +209,7 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => {
|
||||
<BarChart
|
||||
hasQuota
|
||||
attribute={attribute.attribute}
|
||||
data={chartMeta[attribute.key]?.data ?? []}
|
||||
data={chartData}
|
||||
yLimit={usageMeta?.limit ?? 0}
|
||||
yLeftMargin={14}
|
||||
yFormatter={(value) =>
|
||||
|
||||
@@ -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.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 = <TData = DailyStatsData>(
|
||||
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,
|
||||
|
||||
@@ -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 = <TData = InfraMonitoringData>(
|
||||
...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),
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user