diff --git a/src/components/TotalOrgSummary/DonutChart/DonutChart.jsx b/src/components/TotalOrgSummary/DonutChart/DonutChart.jsx index 9fbd8eba09..24b96ba370 100644 --- a/src/components/TotalOrgSummary/DonutChart/DonutChart.jsx +++ b/src/components/TotalOrgSummary/DonutChart/DonutChart.jsx @@ -1,49 +1,92 @@ import PropTypes from 'prop-types'; import { Doughnut } from 'react-chartjs-2'; import ChartDataLabels from 'chartjs-plugin-datalabels'; -import { Chart, ArcElement } from 'chart.js'; +import { Chart, ArcElement, Tooltip, Legend } from 'chart.js'; import styles from './DonutChart.module.css'; -Chart.register(ArcElement); +Chart.register(ArcElement, Tooltip, Legend); function DonutChart(props) { const { title, totalCount, percentageChange, data, colors, comparisonType, darkMode } = props; + const filtered = data + .map((item, i) => ({ item, color: colors[i] })) + .filter(({ item }) => (item.value / totalCount) * 100 >= 0.05); + const filteredData = filtered.map(({ item }) => item); + const filteredColors = filtered.map(({ color }) => color); + + const effectiveTotal = filteredData.reduce((sum, item) => sum + item.value, 0) || totalCount || 0; + + if (!filteredData.length) { + return ( +
+
+
+ {title} +
+
No data available yet
+
+
+ ); + } + const chartData = { - labels: data.map(item => item.label), + labels: filteredData.map(item => item.label), datasets: [ { - data: data.map(item => item.value), - backgroundColor: colors, + data: filteredData.map(item => item.value), + backgroundColor: filteredColors, borderWidth: 1, + hoverOffset: 8, }, ], }; + const labelColor = darkMode ? '#F7FAFC' : '#1A202C'; + const options = { plugins: { datalabels: { - color: '#000000', - font: { - size: 16, - }, + color: labelColor, + font: { size: 12, weight: '500' }, formatter: value => { - if (totalCount === 0 || isNaN(totalCount) || !isFinite(totalCount)) { - return `${value}`; - } - const percentage = ((value / totalCount) * 100).toFixed(0); - return `${value}\n(${percentage}%)`; + if (!effectiveTotal || value <= 0) return ''; + const pct = (value / effectiveTotal) * 100; + return `${value}\n(${pct.toFixed(0)}%)`; }, + anchor: 'end', + align: 'end', + offset: 6, + clamp: false, + display: 'auto', + textStrokeColor: darkMode ? '#1A202C' : '#FFFFFF', + textStrokeWidth: 3, }, legend: { display: false, }, tooltip: { - enabled: false, + enabled: true, + callbacks: { + label: ctx => { + const label = ctx.label || ''; + const value = ctx.parsed; + const pct = effectiveTotal ? ((value / effectiveTotal) * 100).toFixed(0) : 0; + return `${label}: ${value} (${pct}%)`; + }, + }, }, }, maintainAspectRatio: false, cutout: '55%', + layout: { + padding: 40, + }, + onHover: (event, elements) => { + const target = event?.native?.target; + if (!target) return; + target.style.cursor = elements && elements.length ? 'pointer' : 'default'; + }, }; const percentageChangeColor = percentageChange >= 0 ? 'var(--success)' : 'var(--danger)'; @@ -54,10 +97,12 @@ function DonutChart(props) {
-
+
{title}
-

{totalCount}

+

+ {totalCount} +

{comparisonType !== 'No Comparison' && (
+
- {data.map((item, index) => ( + {filteredData.map((item, index) => (
- {item.label} + {item.label}
))}
@@ -98,6 +144,11 @@ DonutChart.propTypes = { ).isRequired, colors: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, comparisonType: PropTypes.string.isRequired, + darkMode: PropTypes.bool, +}; + +DonutChart.defaultProps = { + darkMode: false, }; export default DonutChart; diff --git a/src/components/TotalOrgSummary/DonutChart/DonutChart.module.css b/src/components/TotalOrgSummary/DonutChart/DonutChart.module.css index 916bda89ea..d7de9274c1 100644 --- a/src/components/TotalOrgSummary/DonutChart/DonutChart.module.css +++ b/src/components/TotalOrgSummary/DonutChart/DonutChart.module.css @@ -8,21 +8,18 @@ margin-top: 20px; margin-bottom: -35px; } - .donutScrollable { display: grid; grid-template-columns: 3fr 2fr; width: 100%; } - .donutChart { position: relative; width: 100%; - max-width: 21.25rem; - height: 21.25rem; + max-width: 24rem; + height: 24rem; margin-left: auto; } - .donutCenter { position: absolute; top: 50%; @@ -31,51 +28,41 @@ text-align: center; font-size: 0.6em; } - .donutCenter > * { margin: 0.2rem; } - .donutCenter :global(.donut-heading) { - color: var(--text-secondary); font-size: 0.88rem; } - .donutCenter :global(.donut-count) { - color: var(--text-primary); font-size: 2rem; } - .donutComparisonPercent { font-size: 0.7rem; } - .donutCenter div { margin: 2px 0; } - .donutLabels { display: flex; flex-direction: column; justify-content: center; margin-top: 1.8rem; width: 100%; - color: var(--text-primary); } - .donutLabel { display: flex; align-items: center; margin-left: 20px; + margin-bottom: 4px; } - .donutColor { display: inline-block; min-width: 1rem; min-height: 1rem; margin-right: 1rem; + flex-shrink: 0; } - .donutNoData { display: flex; flex-direction: column; @@ -85,18 +72,15 @@ width: 100%; text-align: center; } - .noDataText { - color: var(--text-primary); font-size: 24px !important; font-weight: 600; margin-top: 1.5rem; line-height: 1.2; } - @media (width <= 615px) { .donutContainer { overflow-x: scroll; justify-content: start; } -} +} \ No newline at end of file