11import React , { useMemo } from 'react' ;
2+ import PropTypes from 'prop-types' ;
23import {
34 BarChart ,
45 Bar ,
@@ -10,38 +11,41 @@ import {
1011 Label ,
1112} from 'recharts' ;
1213
14+ const truncate = ( str , max = 22 ) =>
15+ typeof str === 'string' && str . length > max ? str . slice ( 0 , max ) + '…' : str ;
16+
1317const CustomTooltip = ( { active, payload, isDark, usePercentage } ) => {
1418 if ( active && payload && payload . length ) {
15- const job = payload [ 0 ] . payload ;
19+ const job = payload [ 0 ] ?. payload || { } ;
20+
1621 return (
1722 < div
1823 className = { `p-2 rounded shadow ${
1924 isDark
20- ? 'bg-space-cadet border border-yinmn-blue text-light '
25+ ? 'bg-space-cadet border border-yinmn-blue text-gray-100 '
2126 : 'bg-white border border-gray-300 text-gray-900'
2227 } `}
2328 style = { { fontSize : '0.875rem' } }
2429 >
25- < p > < span className = "font-semibold" > Role:</ span > { job . title } </ p >
26- < p > < span className = "font-semibold" > Conversion Rate:</ span > { ' ' }
27- { usePercentage ? `${ job . conversionRate } %` : job . conversionRate }
28- </ p >
29- < p > < span className = "font-semibold" > Hits:</ span > { job . hits } </ p >
30- < p > < span className = "font-semibold" > Applications:</ span > { job . applications } </ p >
30+ < p > < strong > Role:</ strong > { job . title } </ p >
31+ < p > < strong > Conversion Rate:</ strong > { usePercentage ? `${ job . conversionRate } %` : job . conversionRate } </ p >
32+ < p > < strong > Hits:</ strong > { job . hits } </ p >
33+ < p > < strong > Applications:</ strong > { job . applications } </ p >
3134 </ div >
3235 ) ;
3336 }
3437 return null ;
3538} ;
3639
37- function ConvertedApplicationGraph ( { data, usePercentage, isDark } ) {
40+ function ConvertedApplicationGraph ( { data = [ ] , usePercentage, isDark } ) {
3841 const sortedData = useMemo ( ( ) => {
3942 const key = usePercentage ? 'conversionRate' : 'applications' ;
40- const toNum = ( val ) => ( val == null ? 0 : Number ( val ) ) ;
43+ const toNum = ( v ) => Number ( v ) || 0 ;
44+
45+ const cloned = [ ...data ] ;
46+ cloned . sort ( ( a , b ) => toNum ( b [ key ] ) - toNum ( a [ key ] ) ) ;
4147
42- return [ ...data ]
43- . sort ( ( a , b ) => toNum ( b [ key ] ) - toNum ( a [ key ] ) )
44- . slice ( 0 , 10 ) ;
48+ return cloned . slice ( 0 , 10 ) ;
4549 } , [ data , usePercentage ] ) ;
4650
4751 return (
@@ -53,56 +57,47 @@ function ConvertedApplicationGraph({ data, usePercentage, isDark }) {
5357 < h2 className = { `text-lg font-semibold mb-2 ${ isDark ? 'text-azure' : '' } ` } >
5458 Top 10 Job Postings by { usePercentage ? 'Conversion Rate' : 'Applications' }
5559 </ h2 >
60+
5661 { sortedData . length === 0 ? (
5762 < p > No data available for the selected date range.</ p >
5863 ) : (
5964 < ResponsiveContainer width = "100%" height = { 400 } >
60- < BarChart
61- layout = "vertical"
62- data = { sortedData }
63- margin = { { top : 20 , right : 20 , bottom : 40 , left : 150 } }
64- >
65+ < BarChart layout = "vertical" data = { sortedData } margin = { { top : 20 , right : 90 , bottom : 40 , left : 180 } } >
6566 < XAxis
6667 type = "number"
6768 domain = { usePercentage ? [ 0 , 100 ] : [ 'auto' , 'auto' ] }
68- unit = { usePercentage ? '%' : '' }
69- stroke = { isDark ? '#4682B4' : '#374151' } // Azure in dark mode
69+ stroke = { isDark ? '#e2e8f0' : '#374151' }
7070 >
7171 < Label
72- value = {
73- usePercentage
74- ? 'Percentage of hits converted to applications'
75- : 'Applications'
76- }
72+ value = { usePercentage ? 'Percentage of hits converted to applications' : 'Applications' }
7773 position = "bottom"
78- offset = { 0 }
79- fill = { isDark ? '#4682B4' : '#374151' }
74+ fill = { isDark ? '#e2e8f0' : '#374151' }
8075 />
8176 </ XAxis >
77+
8278 < YAxis
8379 type = "category"
8480 dataKey = "title"
85- width = { 140 }
86- stroke = { isDark ? '#4682B4' : '#374151' }
81+ width = { 180 }
82+ tickFormatter = { ( v ) => v }
83+ tick = { { fill : isDark ? '#e2e8f0' : '#374151' , fontSize : 12 } }
84+ stroke = { isDark ? '#e2e8f0' : '#374151' }
8785 >
8886 < Label
8987 value = "Job Role"
9088 angle = { - 90 }
9189 position = "left"
92- offset = { - 5 }
93- style = { { textAnchor : 'middle' } }
94- fill = { isDark ? '#4682B4' : '#374151' }
90+ fill = { isDark ? '#e2e8f0' : '#374151' }
9591 />
9692 </ YAxis >
97- < Tooltip
98- content = { < CustomTooltip isDark = { isDark } usePercentage = { usePercentage } /> }
99- />
93+
94+ < Tooltip content = { < CustomTooltip isDark = { isDark } usePercentage = { usePercentage } /> } />
95+
10096 < Bar dataKey = { usePercentage ? 'conversionRate' : 'applications' } fill = "#4CAF50" >
10197 < LabelList
10298 dataKey = { usePercentage ? 'conversionRate' : 'applications' }
10399 position = "right"
104- formatter = { ( value ) => `${ value } ${ usePercentage ? '%' : '' } ` }
105- fill = { isDark ? '#4682B4' : '#374151' }
100+ style = { { fill : isDark ? '#FFFFFF' : '#374151' , fontWeight : 600 } }
106101 />
107102 </ Bar >
108103 </ BarChart >
@@ -112,4 +107,17 @@ function ConvertedApplicationGraph({ data, usePercentage, isDark }) {
112107 ) ;
113108}
114109
110+ ConvertedApplicationGraph . propTypes = {
111+ data : PropTypes . arrayOf (
112+ PropTypes . shape ( {
113+ title : PropTypes . string ,
114+ hits : PropTypes . number ,
115+ applications : PropTypes . number ,
116+ conversionRate : PropTypes . number ,
117+ } )
118+ ) ,
119+ usePercentage : PropTypes . bool . isRequired ,
120+ isDark : PropTypes . bool . isRequired ,
121+ } ;
122+
115123export default ConvertedApplicationGraph ;
0 commit comments