Skip to content

Commit 202b2a9

Browse files
Merge pull request #4138 from OneCommunityGlobal/deep-fix-volunteer-status-pie-chart
Deep fix volunteer status pie chart
2 parents de4fb7a + 3d46c29 commit 202b2a9

10 files changed

Lines changed: 722 additions & 125 deletions

src/components/TotalOrgSummary/TotalOrgSummary.jsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@ function TotalOrgSummary(props) {
177177
await new Promise(resolve => setTimeout(resolve, 5000));
178178

179179
// 3. Replace Chart.js canvas elements with images in the live DOM.
180-
const chartCanvases = document.querySelectorAll('.volunteer-status-chart canvas');
180+
const chartCanvases = document.querySelectorAll(
181+
'[data-chart="volunteer-status"] canvas, [data-chart="mentor-status"] canvas',
182+
);
181183
const originalCanvases = [];
182184
chartCanvases.forEach(canvasElem => {
183185
try {
@@ -673,7 +675,11 @@ ${
673675
</Col>
674676
<Col lg={{ size: 6 }}>
675677
<div
676-
className={clsx(styles.componentContainer, styles.componentBorder)}
678+
className={clsx(
679+
styles.componentContainer,
680+
styles.componentBorder,
681+
styles.componentBorderLoose,
682+
)}
677683
data-pdf-block
678684
>
679685
<div
@@ -688,6 +694,7 @@ ${
688694
<VolunteerStatusChart
689695
isLoading={isLoading}
690696
volunteerNumberStats={volunteerStats?.volunteerNumberStats}
697+
mentorNumberStats={volunteerStats?.mentorNumberStats}
691698
comparisonType={selectedComparison}
692699
/>
693700
</div>

src/components/TotalOrgSummary/TotalOrgSummary.module.css

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1+
/* Chart labels stay dark in dark mode for readability against light label chips */
12
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer .recharts-wrapper text,
23
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer .recharts-wrapper tspan {
34
fill: #000 !important;
45
color: #000 !important;
56
text-shadow: 1px 1px 3px rgba(0,0,0,0.25), 0 0 2px #fff;
67
}
7-
/* Chart title stays white, but chart numbers/labels inside the donut graph are black for better contrast */
8-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer .recharts-wrapper text,
9-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer .recharts-wrapper tspan {
10-
fill: #000 !important;
11-
color: #000 !important;
12-
}
138
/* Chart and graph titles/text should be white in dark mode for visibility */
149
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer h3,
1510
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer p,
@@ -73,11 +68,6 @@
7368
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.4) !important;
7469
}
7570

76-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentBorder {
77-
background-color: #1c2541 !important;
78-
border: none !important;
79-
}
80-
8171
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentContainer {
8272
background-color: #1c2541 !important;
8373
border: none !important;
@@ -279,25 +269,6 @@
279269
}
280270

281271
/* Dark mode dropdown consistency */
282-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .reportHeaderActions :global(.dropdown-toggle) {
283-
background-color: #6f42c1 !important;
284-
border-color: #6f42c1 !important;
285-
}
286-
287-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .reportHeaderActions :global(.dropdown-menu) {
288-
background-color: #1c2541 !important;
289-
border-color: #6f42c1 !important;
290-
}
291-
292-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .reportHeaderActions :global(.dropdown-item) {
293-
background-color: #1c2541 !important;
294-
color: #fff !important;
295-
}
296-
297-
.containerTotalOrgWrapper:global(.bg-oxford-blue) .reportHeaderActions :global(.dropdown-item):hover {
298-
background-color: #6f42c1 !important;
299-
}
300-
301272
/* Component containers - Clean borderless design */
302273
.componentContainer {
303274
margin: 15px 0;
@@ -315,6 +286,10 @@
315286
background-color: #fff;
316287
overflow: hidden;
317288
}
289+
290+
.componentBorderLoose {
291+
overflow: visible;
292+
}
318293
.containerTotalOrgWrapper:global(.bg-oxford-blue) .componentBorder {
319294
background-color: #1c2541 !important;
320295
border: 1.5px solid #2f4157 !important;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import PropTypes from 'prop-types';
2+
import { Doughnut } from 'react-chartjs-2';
3+
import { Chart, ArcElement } from 'chart.js';
4+
import styles from './MentorStatusPieChart.module.css';
5+
import externalLabelGuidesPlugin from './externalLabelGuidesPlugin';
6+
7+
Chart.register(ArcElement);
8+
9+
function MentorStatusPieChart({
10+
data: { totalMentors, percentageChange, data: mentorData },
11+
comparisonType,
12+
}) {
13+
const chartData = {
14+
labels: mentorData.map(item => item.label),
15+
datasets: [
16+
{
17+
data: mentorData.map(item => item.value),
18+
backgroundColor: ['#287D5A', '#2D9DA6', '#F26B38'],
19+
borderWidth: 1,
20+
},
21+
],
22+
};
23+
24+
const options = {
25+
plugins: {
26+
datalabels: {
27+
display: false,
28+
},
29+
legend: {
30+
display: false,
31+
},
32+
tooltip: {
33+
enabled: false,
34+
},
35+
externalLabelGuides: {
36+
offset: 20,
37+
horizontalSpread: 32,
38+
horizontalSpreadMap: { 0: 32, 1: 46, 2: 5 },
39+
verticalOffsetMap: { 0: 34, 1: -20, 2: -46 },
40+
sideMap: { 0: 1, 1: -1, 2: 1 },
41+
total: totalMentors,
42+
formatter: ({ value, percentage }) => [`${value}`, `(${percentage}%)`],
43+
},
44+
},
45+
maintainAspectRatio: false,
46+
cutout: '60%',
47+
layout: {
48+
padding: 20,
49+
},
50+
};
51+
52+
const percentageChangeColor = percentageChange >= 0 ? 'green' : 'red';
53+
54+
return (
55+
<section className={styles.mentorStatusContainer} aria-label="Mentor Status Overview">
56+
<div
57+
className={styles.mentorStatusChart}
58+
data-chart="mentor-status"
59+
role="img"
60+
aria-label="Mentor Status Pie Chart"
61+
>
62+
<Doughnut data={chartData} options={options} plugins={[externalLabelGuidesPlugin]} />
63+
<div className={styles.mentorStatusCenter}>
64+
<h2 className={styles.mentorStatusHeading}>TOTAL MENTORS</h2>
65+
<p className={styles.mentorCount}>{totalMentors}</p>
66+
{comparisonType !== 'No Comparison' && (
67+
<p
68+
className={styles.mentorPercentageChange}
69+
style={{ color: percentageChangeColor }}
70+
aria-label={`Mentor percentage change: ${percentageChange}% ${comparisonType.toLowerCase()}`}
71+
>
72+
{percentageChange >= 0
73+
? `+${percentageChange}% ${comparisonType.toUpperCase()}`
74+
: `${percentageChange}% ${comparisonType.toUpperCase()}`}
75+
</p>
76+
)}
77+
</div>
78+
</div>
79+
<div className={styles.mentorStatusLabels}>
80+
{mentorData.map((item, index) => (
81+
<div key={item.label} className={styles.mentorStatusLabel}>
82+
<span
83+
className={styles.mentorStatusColor}
84+
style={{ backgroundColor: chartData.datasets[0].backgroundColor[index] }}
85+
aria-hidden="true"
86+
/>
87+
<span>{item.label}</span>
88+
</div>
89+
))}
90+
</div>
91+
</section>
92+
);
93+
}
94+
95+
MentorStatusPieChart.propTypes = {
96+
data: PropTypes.shape({
97+
totalMentors: PropTypes.number.isRequired,
98+
percentageChange: PropTypes.number.isRequired,
99+
data: PropTypes.arrayOf(
100+
PropTypes.shape({
101+
label: PropTypes.string.isRequired,
102+
value: PropTypes.number.isRequired,
103+
}),
104+
).isRequired,
105+
}).isRequired,
106+
comparisonType: PropTypes.string.isRequired,
107+
};
108+
109+
export default MentorStatusPieChart;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
.mentorStatusContainer {
2+
margin-top: 24px;
3+
display: flex;
4+
flex-direction: column;
5+
align-items: center;
6+
justify-content: flex-start;
7+
width: 100%;
8+
height: 100%;
9+
gap: 12px;
10+
}
11+
12+
.mentorStatusChart {
13+
position: relative;
14+
width: min(320px, 100%);
15+
max-width: 320px;
16+
aspect-ratio: 1 / 1;
17+
overflow: visible;
18+
}
19+
20+
.mentorStatusCenter {
21+
position: absolute;
22+
top: 50%;
23+
left: 50%;
24+
transform: translate(-50%, -50%);
25+
text-align: center;
26+
font-size: 14px;
27+
display: flex;
28+
flex-direction: column;
29+
align-items: center;
30+
gap: 6px;
31+
}
32+
33+
.mentorStatusHeading {
34+
color: #828282;
35+
font-size: 1.2rem;
36+
letter-spacing: 0.5px;
37+
text-transform: uppercase;
38+
transform: translateY(6px);
39+
width: 100%;
40+
text-align: center;
41+
}
42+
43+
.mentorCount {
44+
color: #6c6c6c;
45+
font-size: 1.8rem;
46+
font-weight: 800;
47+
line-height: 1.18;
48+
margin-top: -10px;
49+
}
50+
51+
.mentorPercentageChange {
52+
font-weight: 600;
53+
}
54+
55+
.mentorStatusLabels {
56+
display: flex;
57+
justify-content: center;
58+
align-items: center;
59+
gap: 16px;
60+
margin-top: 48px;
61+
margin-bottom: 24px;
62+
flex-wrap: nowrap;
63+
padding: 0 20px;
64+
width: max-content;
65+
max-width: 100%;
66+
box-sizing: border-box;
67+
}
68+
69+
.mentorStatusLabel {
70+
display: flex;
71+
align-items: center;
72+
gap: 6px;
73+
font-size: 0.875rem;
74+
white-space: nowrap;
75+
}
76+
77+
.mentorStatusColor {
78+
display: inline-block;
79+
width: 12px;
80+
height: 12px;
81+
border-radius: 2px;
82+
}
83+
84+
:global(.bg-oxford-blue) .mentorStatusHeading {
85+
color: #f1f5ff;
86+
}
87+
88+
:global(.bg-oxford-blue) .mentorCount {
89+
color: #ffffff;
90+
}
91+
92+
@media (max-width: 768px) {
93+
.mentorStatusChart {
94+
width: min(280px, 100%);
95+
}
96+
97+
.mentorStatusLabels {
98+
gap: 12px;
99+
}
100+
}
101+
102+
@media (min-width: 768px) {
103+
.mentorStatusChart {
104+
max-width: 280px;
105+
}
106+
}

0 commit comments

Comments
 (0)