@@ -8,77 +8,187 @@ import {
88 ResponsiveContainer ,
99 LabelList ,
1010} from 'recharts' ;
11- import { useSelector } from 'react-redux' ;
12- import styles from './ApplicationChart.module.css' ;
11+ import styles from './ApplicantsChart.module.css' ;
1312
14- function AgeChart ( { data, compareLabel } ) {
15- const darkMode = useSelector ( state => state . theme . darkMode ) ;
16- const axisColor = darkMode ? '#f3f4f6' : '#111827' ;
17- const axisLineColor = darkMode ? '#d1d5db' : '#374151' ;
18- const gridColor = darkMode ? '#9ca3af' : '#d1d5db' ;
19- const tickFontSize = 14 ;
13+ function AgeChart ( { data, compareLabel, darkMode } ) {
14+ // Guard against invalid data
15+ if ( ! data || ! Array . isArray ( data ) || data . length === 0 ) {
16+ return null ;
17+ }
2018
21- const formatTooltip = ( value , name , props ) => {
22- const { change } = props . payload ;
23- if ( compareLabel && change !== undefined ) {
24- let changeText = '' ;
19+ // Validate data structure
20+ const validData = data . filter (
21+ item => item && item . ageGroup && typeof item . applicants === 'number' && ! isNaN ( item . applicants ) ,
22+ ) ;
23+
24+ if ( validData . length === 0 ) {
25+ return null ;
26+ }
27+
28+ // Custom tooltip content component
29+ const CustomTooltip = ( { active, payload, label } ) => {
30+ if ( ! active || ! payload || ! payload . length ) {
31+ return null ;
32+ }
33+
34+ const dataPoint = payload [ 0 ] ;
35+ if ( ! dataPoint ) {
36+ return null ;
37+ }
38+
39+ // Try multiple ways to access the data - Recharts passes data in payload[0].payload
40+ const payloadData = dataPoint . payload || { } ;
41+ // Also check if label is passed directly (from XAxis)
42+ const ageGroup = label || payloadData . ageGroup || dataPoint . name || '' ;
43+ const applicants =
44+ dataPoint . value !== undefined && dataPoint . value !== null
45+ ? dataPoint . value
46+ : payloadData . applicants !== undefined
47+ ? payloadData . applicants
48+ : 0 ;
49+ const change = payloadData . change ;
50+
51+ // Debug: log to see what we're getting (remove in production)
52+ // console.log('Tooltip data:', { active, payload, label, dataPoint, payloadData, ageGroup, applicants, change });
53+
54+ let changeText = '' ;
55+ if ( compareLabel && change !== undefined && change !== null ) {
2556 if ( change > 0 ) {
26- changeText = `${ change } % more than ${ compareLabel } ` ;
57+ changeText = `( ${ change } % more than ${ compareLabel } ) ` ;
2758 } else if ( change < 0 ) {
28- changeText = `${ Math . abs ( change ) } % less than ${ compareLabel } ` ;
59+ changeText = `( ${ Math . abs ( change ) } % less than ${ compareLabel } ) ` ;
2960 } else {
30- changeText = `No change from ${ compareLabel } ` ;
61+ changeText = `( No change from ${ compareLabel } ) ` ;
3162 }
32- return [ `${ value } (${ changeText } )` , 'Applicants' ] ;
3363 }
34- return [ `${ value } ` , 'Applicants' ] ;
64+
65+ // Ensure we always render content, even if some values are missing
66+ const displayAgeGroup = ageGroup || 'N/A' ;
67+ const displayApplicants = applicants !== undefined && applicants !== null ? applicants : 0 ;
68+
69+ return (
70+ < div
71+ className = { styles . customTooltip }
72+ style = { {
73+ backgroundColor : '#ffffff' ,
74+ border : `1px solid ${ darkMode ? '#555' : '#ccc' } ` ,
75+ borderRadius : '4px' ,
76+ padding : '8px 12px' ,
77+ boxShadow : darkMode ? '0 2px 8px rgba(0, 0, 0, 0.3)' : '0 2px 8px rgba(0, 0, 0, 0.15)' ,
78+ zIndex : 1000 ,
79+ minWidth : '150px' ,
80+ position : 'relative' ,
81+ } }
82+ >
83+ < div
84+ className = { styles . tooltipAgeGroup }
85+ style = { {
86+ fontWeight : '600' ,
87+ color : '#000000' ,
88+ marginBottom : '4px' ,
89+ fontSize : '14px' ,
90+ display : 'block' ,
91+ } }
92+ >
93+ { displayAgeGroup }
94+ </ div >
95+ < div
96+ className = { styles . tooltipApplicants }
97+ style = { {
98+ color : '#000000' ,
99+ marginBottom : changeText ? '2px' : '0' ,
100+ fontSize : '13px' ,
101+ display : 'block' ,
102+ } }
103+ >
104+ Applicants :{ ' ' }
105+ < strong style = { { color : '#000000' , fontWeight : '700' } } > { displayApplicants } </ strong >
106+ </ div >
107+ { changeText && (
108+ < div
109+ className = { styles . tooltipChange }
110+ style = { {
111+ color : '#000000' ,
112+ fontSize : '12px' ,
113+ marginTop : '2px' ,
114+ display : 'block' ,
115+ } }
116+ >
117+ { changeText }
118+ </ div >
119+ ) }
120+ </ div >
121+ ) ;
35122 } ;
36123
37124 return (
38- < div className = { `${ styles . AgeChart } ${ darkMode ? 'darkMode' : '' } ` } >
39- < h2 style = { { color : axisColor } } > Applicants grouped by Age</ h2 >
40- < ResponsiveContainer width = "100%" height = { 400 } >
41- < BarChart data = { data } margin = { { top : 20 , right : 30 , left : 20 , bottom : 20 } } barSize = { 80 } >
42- < CartesianGrid stroke = { gridColor } strokeDasharray = "3 3" strokeWidth = { 1.5 } />
43- < XAxis
44- dataKey = "ageGroup"
45- stroke = { axisLineColor }
46- tick = { { fill : axisColor , fontSize : tickFontSize , fontWeight : 'bold' } }
47- label = { {
48- value : 'Age Group' ,
49- position : 'insideBottom' ,
50- offset : - 10 ,
51- fill : axisColor ,
52- fontWeight : 'bold' ,
53- } }
54- />
55- < YAxis
56- stroke = { axisLineColor }
57- tick = { { fill : axisColor , fontSize : tickFontSize , fontWeight : 'bold' } }
58- label = { {
59- value : 'Applicants' ,
60- angle : - 90 ,
61- position : 'insideLeft' ,
62- fill : axisColor ,
63- fontWeight : 'bold' ,
64- } }
65- />
66- < Tooltip
67- formatter = { formatTooltip }
68- contentStyle = { {
69- backgroundColor : darkMode ? '#1f2937' : '#ffffff' ,
70- fontWeight : 'bold' ,
71- } }
72- labelStyle = { {
73- color : darkMode ? '#f3f4f6' : '#111827' ,
74- fontWeight : 'bold' ,
125+ < div
126+ className = { darkMode ? 'bg-oxford-blue text-light' : 'bg-white text-black' }
127+ style = { {
128+ width : '100%' ,
129+ maxWidth : '100%' ,
130+ margin : '0 auto' ,
131+ padding : 'clamp(10px, 2vw, 20px) clamp(10px, 2vw, 20px) 0 clamp(10px, 2vw, 20px)' ,
132+ display : 'flex' ,
133+ flexDirection : 'column' ,
134+ alignItems : 'center' ,
135+ } }
136+ >
137+ < div
138+ style = { {
139+ width : '100%' ,
140+ height : 'calc(100vh - 150px)' ,
141+ minHeight : '600px' ,
142+ maxWidth : '100%' ,
143+ position : 'relative' ,
144+ backgroundColor : darkMode ? '#1b2a41' : '#fff' ,
145+ } }
146+ >
147+ < ResponsiveContainer width = "100%" height = "100%" debounce = { 1 } >
148+ < BarChart
149+ data = { validData }
150+ margin = { {
151+ top : 20 ,
152+ right : 30 ,
153+ left : 20 ,
154+ bottom : 20 ,
75155 } }
76- />
77- < Bar dataKey = "applicants" fill = "#3b82f6" >
78- < LabelList dataKey = "applicants" position = "top" fill = { axisColor } fontWeight = "bold" />
79- </ Bar >
80- </ BarChart >
81- </ ResponsiveContainer >
156+ barSize = { 60 }
157+ >
158+ < CartesianGrid strokeDasharray = "3 3" stroke = { darkMode ? '#555' : '#ccc' } />
159+ < XAxis
160+ dataKey = "ageGroup"
161+ label = { {
162+ value : 'Age Group' ,
163+ position : 'insideBottom' ,
164+ offset : - 5 ,
165+ fill : darkMode ? '#fff' : '#000' ,
166+ } }
167+ tick = { { fill : darkMode ? '#fff' : '#000' } }
168+ />
169+ < YAxis
170+ label = { {
171+ value : 'Number of Applicants' ,
172+ angle : - 90 ,
173+ position : 'insideLeft' ,
174+ offset : - 5 ,
175+ fill : darkMode ? '#fff' : '#000' ,
176+ } }
177+ tick = { { fill : darkMode ? '#fff' : '#000' } }
178+ domain = { [ 0 , 'auto' ] }
179+ />
180+ < Tooltip
181+ content = { CustomTooltip }
182+ wrapperStyle = { { zIndex : 1000 , pointerEvents : 'none' } }
183+ cursor = { { fill : 'rgba(0, 0, 0, 0.1)' } }
184+ allowEscapeViewBox = { { x : false , y : false } }
185+ />
186+ < Bar dataKey = "applicants" fill = { darkMode ? '#60a5fa' : '#3b82f6' } >
187+ < LabelList dataKey = "applicants" position = "top" fill = { darkMode ? '#fff' : '#000' } />
188+ </ Bar >
189+ </ BarChart >
190+ </ ResponsiveContainer >
191+ </ div >
82192 </ div >
83193 ) ;
84194}
0 commit comments