@@ -20,23 +20,7 @@ import styles from './InjuriesOverTimeChart.module.css';
2020const { Option } = Select ;
2121const { RangePicker } = DatePicker ;
2222
23- const MONTHS = [
24- 'January' ,
25- 'February' ,
26- 'March' ,
27- 'April' ,
28- 'May' ,
29- 'June' ,
30- 'July' ,
31- 'August' ,
32- 'September' ,
33- 'October' ,
34- 'November' ,
35- 'December' ,
36- ] ;
37-
3823const shortId = id => ( id ? String ( id ) . slice ( - 6 ) : 'unknown' ) ;
39-
4024const generateColors = n =>
4125 Array . from ( { length : n } , ( _ , i ) => `hsl(${ Math . round ( ( 360 / Math . max ( n , 1 ) ) * i ) } ,60%,55%)` ) ;
4226
@@ -46,7 +30,7 @@ function CustomTooltip({ active, payload, label, darkMode }) {
4630 < div className = { `${ styles . tooltip } ${ darkMode ? styles . tooltipDark : '' } ` } >
4731 < div style = { { fontWeight : 600 , marginBottom : 6 } } > { label } </ div >
4832 { payload
49- . filter ( p => p . value != null && Number ( p . value ) > 0 )
33+ . filter ( p => p ? .value != null && Number ( p . value ) > 0 )
5034 . map ( p => (
5135 < div key = { p . dataKey } style = { { display : 'flex' , gap : 8 , alignItems : 'center' } } >
5236 < span
@@ -144,38 +128,59 @@ function InjuriesOverTimeLine({ darkMode = false }) {
144128 ) ;
145129
146130 const chartData = useMemo ( ( ) => {
131+ const keysSet = new Set ( filtered . map ( r => dayjs ( r . date ) . format ( 'YYYY-MM' ) ) ) ;
132+ const monthKeys = Array . from ( keysSet ) . sort ( ) ;
133+
147134 const totals = new Map ( ) ;
148135 filtered . forEach ( r => {
149- const monthIdx = dayjs ( r . date ) . month ( ) ;
136+ const k = dayjs ( r . date ) . format ( 'YYYY-MM' ) ;
150137 const pid = String ( r . projectId ) ;
151- const key = `${ monthIdx } |${ pid } ` ;
152- totals . set ( key , ( totals . get ( key ) || 0 ) + ( Number ( r . count ) || 0 ) ) ;
138+ const mapKey = `${ k } |${ pid } ` ;
139+ totals . set ( mapKey , ( totals . get ( mapKey ) || 0 ) + ( Number ( r . count ) || 0 ) ) ;
153140 } ) ;
154141
155- const rows = MONTHS . map ( ( month , idx ) => {
156- const row = { month } ;
142+ return monthKeys . map ( k => {
143+ const label = dayjs ( k + '-01' ) . format ( 'MMM YYYY' ) ;
144+ const row = { month : label , _k : k } ;
157145 visibleProjects . forEach ( ( { id } ) => {
158- const v = totals . get ( `${ idx } |${ id } ` ) || 0 ;
146+ const v = totals . get ( `${ k } |${ id } ` ) || 0 ;
159147 row [ id ] = v > 0 ? v : null ;
160148 } ) ;
161149 return row ;
162150 } ) ;
163-
164- return rows ;
165151 } , [ filtered , visibleProjects ] ) ;
166152
153+ const { yTicks, yDomain } = useMemo ( ( ) => {
154+ let dataMax = 0 ;
155+ for ( const row of chartData ) {
156+ for ( const { id } of visibleProjects ) {
157+ const v = row [ id ] ;
158+ if ( typeof v === 'number' && v > dataMax ) dataMax = v ;
159+ }
160+ }
161+
162+ const divisions = 5 ;
163+ if ( dataMax <= 0 ) {
164+ return {
165+ yTicks : Array . from ( { length : divisions + 1 } , ( _ , i ) => i ) ,
166+ yDomain : [ 0 , divisions ] ,
167+ } ;
168+ }
169+
170+ const rawStep = Math . ceil ( dataMax / divisions ) ;
171+ const step = Math . max ( 1 , rawStep ) ;
172+ const niceMax = step * divisions ;
173+ const ticks = Array . from ( { length : divisions + 1 } , ( _ , i ) => i * step ) ;
174+ return { yTicks : ticks , yDomain : [ 0 , niceMax ] } ;
175+ } , [ chartData , visibleProjects ] ) ;
176+
167177 const lineColors = useMemo ( ( ) => generateColors ( visibleProjects . length || 1 ) , [
168178 visibleProjects . length ,
169179 ] ) ;
170180
171- const maxY = Math . max ( ...chartData . flatMap ( row => visibleProjects . map ( p => row [ p . id ] || 0 ) ) , 0 ) ;
172-
173- const step = Math . ceil ( maxY / 5 ) ;
174- const ticks = Array . from ( { length : 6 } , ( _ , i ) => i * step ) ;
175-
176181 return (
177182 < div className = { `${ styles . wrapper } ${ darkMode ? styles . wrapperDark : '' } ` } >
178- < h2 className = { styles . title } > Injuries over time</ h2 >
183+ < h2 className = { styles . title } > Injuries over time (Jan 1 2024 - Dec 31 2024) </ h2 >
179184
180185 < div className = { styles . filters } >
181186 < Select
@@ -261,13 +266,17 @@ function InjuriesOverTimeLine({ darkMode = false }) {
261266 < div className = { `${ styles . chartCard } ${ darkMode ? styles . chartCardDark : '' } ` } >
262267 < ResponsiveContainer width = "100%" height = "100%" >
263268 < LineChart data = { chartData } margin = { { top : 20 , right : 30 , left : 20 , bottom : 5 } } >
264- < CartesianGrid strokeDasharray = "3 3" strokeOpacity = { darkMode ? 0.2 : 1 } />
269+ < CartesianGrid
270+ stroke = { darkMode ? '#2f3b4a' : '#e5e7eb' }
271+ strokeDasharray = "3 3"
272+ strokeOpacity = { darkMode ? 0.2 : 1 }
273+ />
265274 < XAxis dataKey = "month" height = { 60 } angle = { - 25 } textAnchor = "end" interval = { 0 } />
266275 < YAxis
267276 label = { { value : 'Injury Count' , angle : - 90 , position : 'insideLeft' } }
268277 allowDecimals = { false }
269- domain = { [ 0 , maxY ] }
270- ticks = { ticks }
278+ domain = { yDomain }
279+ ticks = { yTicks }
271280 />
272281 < Tooltip content = { < CustomTooltip darkMode = { darkMode } /> } />
273282 < Legend verticalAlign = "top" align = "left" wrapperStyle = { { paddingBottom : 20 } } />
0 commit comments