From 90976b798848ccc5bcb9c9b67a302f51609882ad Mon Sep 17 00:00:00 2001 From: "kemal.earth" <606977+kemaldotearth@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:35:54 +0100 Subject: [PATCH] fix(studio): human readable formatting for total consumed times (#39011) * fix: human readable formatting for total consumed times * chore: accidental turkish spelling of performance * fix: imports for renamed utils file * feat: add tests for formatDuration util --- .../QueryPerformance/QueryDetail.tsx | 21 +++------------ .../QueryPerformance.utils.test.ts | 24 +++++++++++++++++ .../QueryPerformance.utils.ts | 26 +++++++++++++++++++ .../QueryPerformance/QueryPerformanceGrid.tsx | 9 +++---- 4 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts create mode 100644 apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx index 78f7b82912..f35e1e9e30 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx @@ -11,6 +11,7 @@ import { QUERY_PERFORMANCE_COLUMNS, QUERY_PERFORMANCE_REPORT_TYPES, } from './QueryPerformance.constants' +import { formatDuration } from './QueryPerformance.utils' interface QueryDetailProps { reportType: QUERY_PERFORMANCE_REPORT_TYPES @@ -41,22 +42,6 @@ export const QueryDetail = ({ selectedRow, onClickViewSuggestion }: QueryDetailP const [isExpanded, setIsExpanded] = useState(false) - const formatDuration = (seconds: number) => { - const dur = dayjs.duration(seconds, 'seconds') - - const minutes = Math.floor(dur.asMinutes()) - const remainingSeconds = dur.seconds() + dur.milliseconds() / 1000 - - const parts = [] - if (minutes > 0) parts.push(`${minutes}m`) - if (remainingSeconds > 0) { - const formattedSeconds = remainingSeconds.toFixed(2) - parts.push(`${formattedSeconds}s`) - } - - return parts.join(' ') - } - return ( @@ -146,10 +131,10 @@ export const QueryDetail = ({ selectedRow, onClickViewSuggestion }: QueryDetailP - {formatDuration(totalTime / 1000)} + {formatDuration(totalTime)}

) : ( diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts new file mode 100644 index 0000000000..fe898f94d9 --- /dev/null +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from 'vitest' +import { formatDuration } from './QueryPerformance.utils' + +describe('formatDuration', () => { + it('should format seconds', () => { + expect(formatDuration(1000)).toBe('1.00s') + expect(formatDuration(30000)).toBe('30.00s') + }) + + it('should format minutes and seconds', () => { + expect(formatDuration(60000)).toBe('1m') + expect(formatDuration(125000)).toBe('2m 5s') + }) + + it('should format hours, minutes and seconds', () => { + expect(formatDuration(3600000)).toBe('1h') + expect(formatDuration(3661000)).toBe('1h 1m 1s') + }) + + it('should format days, hours, minutes and seconds', () => { + expect(formatDuration(86400000)).toBe('1d') + expect(formatDuration(90061000)).toBe('1d 1h 1m 1s') + }) +}) diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts new file mode 100644 index 0000000000..ce7d211123 --- /dev/null +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.utils.ts @@ -0,0 +1,26 @@ +import dayjs from 'dayjs' +import duration from 'dayjs/plugin/duration' + +dayjs.extend(duration) + +export const formatDuration = (milliseconds: number) => { + const duration = dayjs.duration(milliseconds, 'milliseconds') + + const days = Math.floor(duration.asDays()) + const hours = duration.hours() + const minutes = duration.minutes() + const seconds = duration.seconds() + const totalSeconds = duration.asSeconds() + + if (totalSeconds < 60) { + return `${totalSeconds.toFixed(2)}s` + } + + const parts = [] + if (days > 0) parts.push(`${days}d`) + if (hours > 0) parts.push(`${hours}h`) + if (minutes > 0) parts.push(`${minutes}m`) + if (seconds > 0) parts.push(`${seconds}s`) + + return parts.length > 0 ? parts.join(' ') : '0s' +} diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx index 62e3bb4973..fc8122627a 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx @@ -32,6 +32,7 @@ import { QUERY_PERFORMANCE_ROLE_DESCRIPTION, } from './QueryPerformance.constants' import { useQueryPerformanceSort } from './hooks/useQueryPerformanceSort' +import { formatDuration } from './QueryPerformance.utils' interface QueryPerformanceGridProps { queryPerformanceQuery: DbQueryHook @@ -173,14 +174,10 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance / - {(totalTime / 1000).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })} - s + {formatDuration(totalTime)} ) : (