1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-22 22:03:39 +03:00

B OpenNebula/one#6638: Fix charting (#3133)

* Fixes handling of showback + accounting records
    - Previously only worked for groups
* Fixes rendering of multichart XAxis labels
    - Now uses a custom XAxis component to truncate long label names
    - Displays the full name on hover
* Fixes rendering of showback data in non-fullscreen
    - Now all 3 charts have sufficient screen space in both modes

Signed-off-by: Victor Hansson <vhansson@opennebula.io>
This commit is contained in:
vichansson 2024-07-01 16:55:57 +03:00 committed by GitHub
parent 42c0df339b
commit 262f435cdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 76 additions and 10 deletions

View File

@ -17,6 +17,44 @@ import PropTypes from 'prop-types'
import { CartesianGrid, PolarAngleAxis, Cell } from 'recharts' import { CartesianGrid, PolarAngleAxis, Cell } from 'recharts'
import { Component, Fragment } from 'react' import { Component, Fragment } from 'react'
/**
* Truncate long labels with ellipsis.
*
* @param {string} label - The label to be truncated.
* @param {number} maxLength - The maximum length of the label.
* @returns {string} The truncated label.
*/
const truncateLabel = (label, maxLength) => {
if (label.length > maxLength) {
return `${label.substring(0, maxLength)}...`
}
return label
}
/**
* Custom tick component for the XAxis that displays the full label on hover.
*
* @param {object} props - Props.
* @param {number} props.x - The x position of the tick.
* @param {number} props.y - The y position of the tick.
* @param {object} props.payload - The payload of the tick.
* @returns {Component} The rendered tick.
*/
export const CustomXAxisTick = ({ x, y, payload }) => {
const fullLabel = payload?.value
const truncatedLabel = truncateLabel(fullLabel, 10)
return (
<g transform={`translate(${x},${y})`}>
<title>{fullLabel}</title>
<text x={0} y={0} dy={16}>
{truncatedLabel}
</text>
</g>
)
}
/** /**
* Generates a color based on the metric, datasetId, and theme type. * Generates a color based on the metric, datasetId, and theme type.
* *
@ -234,6 +272,12 @@ export const GetChartElementConfig = (
} }
} }
CustomXAxisTick.propTypes = {
x: PropTypes.number,
y: PropTypes.number,
payload: PropTypes.object,
}
GetChartElementConfig.propTypes = { GetChartElementConfig.propTypes = {
chartType: PropTypes.string.isRequired, chartType: PropTypes.string.isRequired,
metric: PropTypes.object.isRequired, metric: PropTypes.object.isRequired,

View File

@ -23,6 +23,7 @@ import {
GetChartDefs, GetChartDefs,
GetChartConfig, GetChartConfig,
GetChartElementConfig, GetChartElementConfig,
CustomXAxisTick,
} from 'client/components/Charts/MultiChart/helpers/scripts/chartDefs' } from 'client/components/Charts/MultiChart/helpers/scripts/chartDefs'
import { exportDataToPDF } from 'client/components/Charts/MultiChart/helpers/scripts/exportPDF' import { exportDataToPDF } from 'client/components/Charts/MultiChart/helpers/scripts/exportPDF'
import { exportDataToCSV } from 'client/components/Charts/MultiChart/helpers/scripts/exportCSV' import { exportDataToCSV } from 'client/components/Charts/MultiChart/helpers/scripts/exportCSV'
@ -35,6 +36,7 @@ export {
GetChartDefs, GetChartDefs,
GetChartConfig, GetChartConfig,
GetChartElementConfig, GetChartElementConfig,
CustomXAxisTick,
exportDataToPDF, exportDataToPDF,
exportDataToCSV, exportDataToCSV,
} }

View File

@ -42,6 +42,7 @@ import {
generateColorByMetric, generateColorByMetric,
GetChartConfig, GetChartConfig,
GetChartElementConfig, GetChartElementConfig,
CustomXAxisTick,
} from 'client/components/Charts/MultiChart/helpers/scripts' } from 'client/components/Charts/MultiChart/helpers/scripts'
import { import {
FormatPolarDataset, FormatPolarDataset,
@ -174,7 +175,11 @@ export const ChartRenderer = ({
{coordinateType === 'CARTESIAN' && ( {coordinateType === 'CARTESIAN' && (
<> <>
<XAxis interval={0} dataKey={groupBy} /> <XAxis
interval={0}
dataKey={groupBy}
tick={<CustomXAxisTick />}
/>
<YAxis /> <YAxis />
</> </>
)} )}

View File

@ -69,7 +69,6 @@ const topMetricNames = {
} }
const commonStyles = { const commonStyles = {
minHeight: '250px',
width: '100%', width: '100%',
position: 'relative', position: 'relative',
marginTop: 2, marginTop: 2,
@ -204,7 +203,15 @@ const generateShowbackInfoTab = ({ groups }) => {
) )
return ( return (
<Box padding={2} display="flex" flexDirection="column" height="100%"> <Box
padding={2}
display="flex"
flexDirection="column"
sx={{
height: '100vh',
minHeight: '1000px',
}}
>
<Box <Box
display="flex" display="flex"
justifyContent="space-between" justifyContent="space-between"
@ -231,8 +238,9 @@ const generateShowbackInfoTab = ({ groups }) => {
flexDirection="row" flexDirection="row"
justifyContent="space-between" justifyContent="space-between"
mb={2} mb={2}
flex={5}
> >
<Box flexGrow={1} mr={1} {...commonStyles}> <Box flex={1} mr={1} {...commonStyles}>
<MultiChart <MultiChart
datasets={topChartsData} datasets={topChartsData}
chartType={'table'} chartType={'table'}
@ -242,7 +250,7 @@ const generateShowbackInfoTab = ({ groups }) => {
/> />
</Box> </Box>
<Box flexGrow={1} ml={1} {...commonStyles}> <Box flex={1} ml={1} {...commonStyles}>
<MultiChart <MultiChart
datasets={topChartsData} datasets={topChartsData}
chartType={'bar'} chartType={'bar'}
@ -254,7 +262,7 @@ const generateShowbackInfoTab = ({ groups }) => {
</Box> </Box>
</Box> </Box>
<Box flexGrow={1} minHeight="400px" {...commonStyles}> <Box flex={7} {...commonStyles}>
<MultiChart <MultiChart
datasets={[ datasets={[
{ {

View File

@ -96,6 +96,8 @@ const accounting = (
responseData.HISTORY_RECORDS.HISTORY = history.filter( responseData.HISTORY_RECORDS.HISTORY = history.filter(
(item) => item.VM.GID === groupId (item) => item.VM.GID === groupId
) )
} else {
responseData.HISTORY_RECORDS.HISTORY = history
} }
} }
@ -179,16 +181,21 @@ const showback = (
const responseData = value const responseData = value
// Filter if there is not userId and there is a groupId // Filter if there is not userId and there is a groupId
if (!userId && groupId && responseData && responseData.SHOWBACK_RECORDS) { if (responseData && responseData.SHOWBACK_RECORDS) {
// Filter data by group id // Filter data by group id
const showbackHistory = Array.isArray( const showbackHistory = Array.isArray(
responseData.SHOWBACK_RECORDS.SHOWBACK responseData.SHOWBACK_RECORDS.SHOWBACK
) )
? responseData.SHOWBACK_RECORDS.SHOWBACK ? responseData.SHOWBACK_RECORDS.SHOWBACK
: [responseData.SHOWBACK_RECORDS.SHOWBACK] : [responseData.SHOWBACK_RECORDS.SHOWBACK]
responseData.SHOWBACK_RECORDS.SHOWBACK = showbackHistory.filter(
(item) => item.GID === groupId if (!userId && groupId) {
) responseData.SHOWBACK_RECORDS.SHOWBACK = showbackHistory.filter(
(item) => item.GID === groupId
)
} else {
responseData.SHOWBACK_RECORDS.SHOWBACK = showbackHistory
}
} }
// Return response // Return response