diff --git a/src/components/TotalOrgSummary/PieChart.jsx b/src/components/TotalOrgSummary/PieChart.jsx
index a55b0f6e38..495033f954 100644
--- a/src/components/TotalOrgSummary/PieChart.jsx
+++ b/src/components/TotalOrgSummary/PieChart.jsx
@@ -1,44 +1,52 @@
-// // import * as React from 'react';
-// import { DefaultizedPieValueType } from '@mui/x-charts/models';
-// import { PieChart, pieArcLabelClasses } from '@mui/x-charts/PieChart';
+import * as React from 'react';
+import { PieChart, pieArcLabelClasses } from '@mui/x-charts/PieChart';
-// const data = [
-// { label: 'Group A', value: 400, color: '#0088FE' },
-// { label: 'Group B', value: 300, color: '#00C49F' },
-// { label: 'Group C', value: 300, color: '#FFBB28' },
-// { label: 'Group D', value: 200, color: '#FF8042' },
-// ];
+const data = [
+ { id: 0, label: 'Administrator', value: 6, color: '#fb0505' },
+ { id: 1, label: 'Volunteer', value: 4, color: '#8ebfff' },
+ { id: 2, label: 'Owner', value: 3, color: '#f68d42' },
+ { id: 3, label: 'Mentor', value: 1, color: '#f2ff00' },
+];
-// const sizing = {
-// margin: { right: 5 },
-// width: 200,
-// height: 200,
-// legend: { hidden: true },
-// };
-// const TOTAL = data.map(item => item.value).reduce((a, b) => a + b, 0);
+const TOTAL = data.reduce((sum, item) => sum + item.value, 0);
-// const getArcLabel = (params: DefaultizedPieValueType) => {
-// const percent = params.value / TOTAL;
-// return `${(percent * 100).toFixed(0)}%`;
-// };
+const getArcLabel = params => {
+ const percent = params.value / TOTAL;
+ return `${params.value}\n(${(percent * 100).toFixed(0)}%)`;
+};
-// export default function PieChartWithCustomizedLabel() {
-// return (
-//
-// );
-// }
+export default function RoleDistributionPieChart() {
+ return (
+
+ );
+}
diff --git a/src/components/TotalOrgSummary/TotalOrgSummary.module.css b/src/components/TotalOrgSummary/TotalOrgSummary.module.css
index 7e39bd65bd..f83cce2b7b 100644
--- a/src/components/TotalOrgSummary/TotalOrgSummary.module.css
+++ b/src/components/TotalOrgSummary/TotalOrgSummary.module.css
@@ -34,7 +34,7 @@
padding-top: 10px !important;
padding-bottom: 10px !important;
}
-
+
.containerTotalOrgWrapper:global(.mb-5) {
margin-bottom: 20px !important;
}
@@ -215,6 +215,29 @@
color: #fff !important;
}
+/* Role Distribution slice label overrides */
+.containerTotalOrgWrapper:global(.bg-oxford-blue)
+ .componentContainer
+ :global(.role-distribution-label-dark),
+.containerTotalOrgWrapper:global(.bg-oxford-blue)
+ .componentContainer
+ :global(.role-distribution-label-dark)
+ tspan {
+ fill: #000 !important;
+ color: #000 !important;
+}
+
+.containerTotalOrgWrapper:global(.bg-oxford-blue)
+ .componentContainer
+ :global(.role-distribution-label-light),
+.containerTotalOrgWrapper:global(.bg-oxford-blue)
+ .componentContainer
+ :global(.role-distribution-label-light)
+ tspan {
+ fill: #fff !important;
+ color: #fff !important;
+}
+
.containerTotalOrgWrapper.bg-oxford-blue h3 {
color: #fff;
}
@@ -382,8 +405,6 @@
transform: translateX(4px) !important;
}
-/* Dark mode dropdown consistency */
-
/* Component containers - Clean borderless design */
.componentContainer {
margin: 0 0 15px;
@@ -696,4 +717,4 @@
}
}
-/* stylelint-enable no-descending-specificity */
+/* stylelint-enable no-descending-specificity */
\ No newline at end of file
diff --git a/src/components/TotalOrgSummary/VolunteerRolesTeamDynamics/RoleDistributionPieChart.jsx b/src/components/TotalOrgSummary/VolunteerRolesTeamDynamics/RoleDistributionPieChart.jsx
index 54f126f1dd..fe95b7296b 100644
--- a/src/components/TotalOrgSummary/VolunteerRolesTeamDynamics/RoleDistributionPieChart.jsx
+++ b/src/components/TotalOrgSummary/VolunteerRolesTeamDynamics/RoleDistributionPieChart.jsx
@@ -1,21 +1,22 @@
+import React from 'react';
import { ResponsiveContainer, PieChart, Pie, Cell, Legend, Tooltip } from 'recharts';
import Loading from '~/components/common/Loading';
+import CustomTooltip from '../../CustomTooltip';
const COLORS = [
- '#2F80ED', // blue
- '#56CCF2', // light blue
- '#27AE60', // green
- '#6FCF97', // light green
- '#F2994A', // orange
- '#F2C94C', // yellow
- '#E14848', // red
- '#9B51E0', // purple
- '#F765A3', // pink
- '#4F4F4F', // dark
- '#828282', // grey
+ '#2F80ED',
+ '#56CCF2',
+ '#27AE60',
+ '#6FCF97',
+ '#F2994A',
+ '#F2C94C',
+ '#E14848',
+ '#9B51E0',
+ '#F765A3',
+ '#4F4F4F',
+ '#828282',
];
-// Explicit role-to-color mapping to keep key roles visually distinct and stable.
const ROLE_COLOR_MAP = {
Volunteer: '#8ebfff',
Manager: '#27AE60',
@@ -25,7 +26,19 @@ const ROLE_COLOR_MAP = {
Mentor: '#f2ff00',
};
-import CustomTooltip from '../../CustomTooltip';
+const RADIAN = Math.PI / 180;
+
+const getContrastTextColor = hexColor => {
+ const hex = hexColor.replace('#', '');
+
+ const r = parseInt(hex.substring(0, 2), 16);
+ const g = parseInt(hex.substring(2, 4), 16);
+ const b = parseInt(hex.substring(4, 6), 16);
+
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
+
+ return brightness > 160 ? '#000000' : '#FFFFFF';
+};
const RoleDistributionPieChart = ({ roleDistributionStats = [], isLoading, darkMode }) => {
if (isLoading) {
@@ -38,111 +51,153 @@ const RoleDistributionPieChart = ({ roleDistributionStats = [], isLoading, darkM
);
}
- roleDistributionStats.sort((a, b) => b.count - a.count);
- const data = roleDistributionStats.map((item, index) => ({
+ const sortedStats = [...roleDistributionStats].sort((a, b) => b.count - a.count);
+
+ const data = sortedStats.map((item, index) => ({
name: item._id,
value: item.count,
- // Use a stable role mapping first, otherwise fallback by index.
color: ROLE_COLOR_MAP[item._id] || COLORS[index % COLORS.length],
}));
+
const totalValue = data.reduce((sum, entry) => sum + entry.value, 0);
- const RADIAN = Math.PI / 180;
- const renderCustomizedLabel = ({
- cx,
- cy,
- midAngle,
- innerRadius,
- outerRadius,
- percent,
- index,
- }) => {
- const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
+ const renderLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent, index }) => {
+ if (percent <= 0.01 || !data[index]) return null;
+
+ const slice = data[index];
+
+ const isSmallSlice = percent < 0.1;
+
+ const labelColor = getContrastTextColor(slice.color);
+
+ const labelClass =
+ labelColor === '#000000' ? 'role-distribution-label-dark' : 'role-distribution-label-light';
+
+ const radius =
+ innerRadius + (outerRadius - innerRadius) * (slice.name === 'Mentor' ? 0.34 : 0.52);
+
const x = cx + radius * Math.cos(-midAngle * RADIAN);
+
const y = cy + radius * Math.sin(-midAngle * RADIAN);
- return (
-
-
- {percent > 0.01 && (
- <>
-
- {`${data[index].value}`}
-
-
- {`(${(percent * 100).toFixed(0)}%)`}
-
- >
- )}
-
-
-
- TOTAL ROLES
-
-
- {data.length}
-
+ if (isSmallSlice) {
+ return (
+
+ {`${slice.value} (${(percent * 100).toFixed(0)}%)`}
-
- );
- };
-
- const renderCustomLegend = props => {
- const { payload } = props; // payload is an array of legend items provided by Recharts
+ );
+ }
return (
-
- {payload.map(entry => {
- // 'entry.value' here corresponds to the 'nameKey' of the Pie (which we set to 'name')
- const itemName = entry.value;
- // Find the original data object to get the count and original color
- const itemData = data.find(d => d.name === itemName);
-
- // If for some reason data is not found, skip rendering this legend item
- if (!itemData) {
- return null;
- }
-
- const { value, color } = itemData; // 'value' is the count
- const percentage = totalValue > 0 ? (value / totalValue) * 100 : 0;
-
- return (
- -
-
-
- {`${itemName}: ${value} (${percentage.toFixed(1)}%)`}
-
-
- );
- })}
-
+
+ {slice.value}
+
+
+
+ {`(${(percent * 100).toFixed(0)}%)`}
+
+
);
};
+ const renderCustomLegend = ({ payload }) => (
+
+ {payload.map(entry => {
+ const itemName = entry.value;
+
+ const itemData = data.find(d => d.name === itemName);
+
+ if (!itemData) return null;
+
+ const { value, color } = itemData;
+
+ const percentage = totalValue > 0 ? (value / totalValue) * 100 : 0;
+
+ return (
+ -
+
+
+
+ {`${itemName}: ${value} (${percentage.toFixed(1)}%)`}
+
+
+ );
+ })}
+
+ );
+
return (
-
-
+
+
{data.map(entry => (
|
))}
+
+
} />