diff --git a/frontend/src/components/ActivityTimeline.vue b/frontend/src/components/ActivityTimeline.vue index 64be34f..f1353b9 100644 --- a/frontend/src/components/ActivityTimeline.vue +++ b/frontend/src/components/ActivityTimeline.vue @@ -8,6 +8,7 @@ import 'chartjs-adapter-date-fns' import { CHART_COMMON_OPTIONS, CHART_SCALE_X_OPTIONS, + formatSI, resampleTimedData, setChartDatetimeRange, } from '@/utils/commonCharts.js' @@ -64,7 +65,7 @@ const chartOptions = computed(() => { x: scaleXOptions, packets: { ticks: { - callback: (v) => `${v} ${CHART_UNITS[0]}`, + callback: (v) => formatSI(v, CHART_UNITS[0]), color: CHART_COLORS[0], }, grid: { tickColor: CHART_COLORS[0], drawOnChartArea: false }, @@ -73,7 +74,7 @@ const chartOptions = computed(() => { }, flows: { ticks: { - callback: (v) => `${v} ${CHART_UNITS[1]}`, + callback: (v) => formatSI(v, CHART_UNITS[1]), color: CHART_COLORS[1], }, grid: { tickColor: CHART_COLORS[1], drawOnChartArea: false }, @@ -82,7 +83,7 @@ const chartOptions = computed(() => { }, bytes: { ticks: { - callback: (v) => `${v} ${CHART_UNITS[2]}`, + callback: (v) => formatSI(v, CHART_UNITS[2]), color: CHART_COLORS[2], }, grid: { tickColor: CHART_COLORS[2] }, @@ -96,7 +97,7 @@ const chartOptions = computed(() => { callbacks: { label: (item) => { let unit = CHART_UNITS[item.datasetIndex] - return `${item.formattedValue} ${unit}` + return formatSI(item.parsed.y, unit, 2) }, }, usePointStyle: true, diff --git a/frontend/src/utils/commonCharts.js b/frontend/src/utils/commonCharts.js index 2653585..da4a3a9 100644 --- a/frontend/src/utils/commonCharts.js +++ b/frontend/src/utils/commonCharts.js @@ -110,3 +110,30 @@ export function resampleTimedData(data, dtKey, unitCount, unit, reduceFn, addEmp // Ensure correct order of buckets return result.sort((a, b) => a[dtKey] - b[dtKey]) } + +/** + * Formats a number using SI prefixes (k, M, G, ...) + * + * Values < 1e3 are not formatted. + * + * @param {Number} value Value to format + * @param {String} unit Unit to append to the formatted value (e.g. 'B', 'Hz') + * @param {Number} decimalPlaces Number of decimal places to show (only for values + * >= 1e3). Default is 1. + * @returns {String} Formatted string + */ +export function formatSI(value, unit = '', decimalPlaces = 1) { + const absValue = Math.abs(value) + if (absValue < 1e3) return `${value} ${unit}` + + const units = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + let i = 0 + let formattedValue = value + + while (i < units.length - 1 && Math.abs(formattedValue) >= 1e3) { + formattedValue /= 1e3 + i++ + } + + return `${formattedValue.toFixed(decimalPlaces)} ${units[i]}${unit}` +}