mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix(studio): edge func exec time formatting in reports (#48539)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Fixes Edge Function Execution Time chart within our observability reports time formatting. This also fixes the non-hovered state which would lose the `ms` formatting. | Before | After | |--------|--------| | <img width="2160" height="652" alt="cleanshot_2026-07-29_at_02 15 53_2x" src="https://github.com/user-attachments/assets/cfd6dbc2-f283-4379-a133-581c76990cb5" /> | <img width="797" height="314" alt="Screenshot 2026-07-31 at 14 30 48" src="https://github.com/user-attachments/assets/f5f1ace5-b6ef-43db-aebd-e10d31631013" /> | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Improved execution-time chart formatting with clearer millisecond values, thousands separators, and configurable precision. * Chart highlights now support custom value formatting alongside existing number, percentage, and byte formats. * **Bug Fixes** * Non-finite execution-time values now display safely as `0ms`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
9b51678fcf
commit
bc95a2f19a
4 files changed
+55
-3
No files matched your search
@@ -73,6 +73,24 @@ export const compactNumberFormatter = (num: number): string => {
|
||||
return new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 }).format(num)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a duration in milliseconds with thousands separators and a unit suffix.
|
||||
*
|
||||
* @example
|
||||
* millisecondFormatter(123) // "123ms"
|
||||
* millisecondFormatter(90000) // "90,000ms"
|
||||
* millisecondFormatter(1234.56) // "1,235ms"
|
||||
* millisecondFormatter(1234.56, 2) // "1,234.56ms"
|
||||
*/
|
||||
export const millisecondFormatter = (value: number, precision = 0) => {
|
||||
if (!Number.isFinite(value)) return '0ms'
|
||||
|
||||
return `${value.toLocaleString('en-US', {
|
||||
minimumFractionDigits: precision,
|
||||
maximumFractionDigits: precision,
|
||||
})}ms`
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a percentage, trimming decimals at 100.
|
||||
*
|
||||
|
||||
@@ -220,6 +220,10 @@ export function ComposedChart({
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof format === 'function') {
|
||||
return format(value)
|
||||
}
|
||||
|
||||
if (shouldFormatBytes) {
|
||||
const bytesValue = isNetworkChart ? Math.abs(value) : value
|
||||
const formatted = isMemoryChart
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
isUnixMicro,
|
||||
unixMicroToIsoTimestamp,
|
||||
} from '@/components/interfaces/Settings/Logs/Logs.utils'
|
||||
import { millisecondFormatter } from '@/components/ui/Charts/Charts.utils'
|
||||
import type { AnalyticsInterval } from '@/data/analytics/constants'
|
||||
import {
|
||||
analyticsLiteral,
|
||||
@@ -290,10 +291,10 @@ export const edgeFunctionReports = ({
|
||||
defaultChartStyle: 'line',
|
||||
titleTooltip: 'Average execution time for edge functions.',
|
||||
YAxisProps: {
|
||||
width: 50,
|
||||
tickFormatter: (value: number) => `${value}ms`,
|
||||
width: 68,
|
||||
tickFormatter: (value: number) => millisecondFormatter(value),
|
||||
},
|
||||
format: (value: unknown) => `${Number(value).toFixed(0)}ms`,
|
||||
format: (value: unknown) => millisecondFormatter(Number(value)),
|
||||
dataProvider: async () => {
|
||||
const sql = METRIC_SQL.ExecutionTime(interval, filters)
|
||||
const rawData = await fetchLogs(projectRef, sql, startDate, endDate)
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
compactNumberFormatter,
|
||||
formatPercentage,
|
||||
isFloat,
|
||||
millisecondFormatter,
|
||||
numberFormatter,
|
||||
precisionFormatter,
|
||||
useStacked,
|
||||
@@ -138,6 +139,34 @@ describe('compactNumberFormatter', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('millisecondFormatter', () => {
|
||||
it('appends the ms unit', () => {
|
||||
expect(millisecondFormatter(0)).toBe('0ms')
|
||||
expect(millisecondFormatter(123)).toBe('123ms')
|
||||
})
|
||||
|
||||
it('adds thousands separators', () => {
|
||||
expect(millisecondFormatter(1000)).toBe('1,000ms')
|
||||
expect(millisecondFormatter(90000)).toBe('90,000ms')
|
||||
expect(millisecondFormatter(1_234_567)).toBe('1,234,567ms')
|
||||
})
|
||||
|
||||
it('rounds to whole milliseconds by default', () => {
|
||||
expect(millisecondFormatter(1234.56)).toBe('1,235ms')
|
||||
expect(millisecondFormatter(0.4)).toBe('0ms')
|
||||
})
|
||||
|
||||
it('respects an explicit precision', () => {
|
||||
expect(millisecondFormatter(1234.56, 2)).toBe('1,234.56ms')
|
||||
expect(millisecondFormatter(84.3, 2)).toBe('84.30ms')
|
||||
})
|
||||
|
||||
it('falls back to 0ms for non-finite values', () => {
|
||||
expect(millisecondFormatter(NaN)).toBe('0ms')
|
||||
expect(millisecondFormatter(Infinity)).toBe('0ms')
|
||||
})
|
||||
})
|
||||
|
||||
test('useStacked', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useStacked({
|
||||
|
||||
Reference in new issue
Block a user