1- import { useEffect , useState , useRef } from 'react' ;
1+ import { useEffect , useState , useRef , useMemo } from 'react' ;
22import DatePicker from 'react-datepicker' ;
33import 'react-datepicker/dist/react-datepicker.css' ;
44import { useDispatch , useSelector } from 'react-redux' ;
88 XAxis ,
99 YAxis ,
1010 CartesianGrid ,
11- Tooltip ,
1211 ResponsiveContainer ,
1312 LabelList ,
1413} from 'recharts' ;
@@ -18,24 +17,36 @@ import {
1817 fetchLongestOpenIssues ,
1918 setProjectFilter ,
2019} from '../../../actions/bmdashboard/issueChartActions' ;
21- import './issueCharts .css' ;
20+ import styles from './issueChart.module .css' ;
2221
2322function IssueCharts ( ) {
2423 const dispatch = useDispatch ( ) ;
24+
2525 const { issues, loading, error, selectedProjects } = useSelector ( state => state . bmissuechart ) ;
2626 const projects = useSelector ( state => state . bmProjects ) ;
27+ const darkMode = useSelector ( state => state . theme ?. darkMode ) ;
28+
2729 const [ startDate , setStartDate ] = useState ( null ) ;
2830 const [ endDate , setEndDate ] = useState ( null ) ;
2931 const chartContainerRef = useRef ( null ) ;
3032 const [ containerWidth , setContainerWidth ] = useState ( window . innerWidth ) ;
3133
34+ // Normalize issues for chart
35+ const normalizedIssues = useMemo ( ( ) => {
36+ return ( issues || [ ] ) . map ( ( item , index ) => ( {
37+ issueName : item . issueName || `Issue #${ index + 1 } ` ,
38+ durationOpen : item . durationOpen ?? 0 ,
39+ } ) ) ;
40+ } , [ issues ] ) ;
41+
42+ // Load projects on mount
3243 useEffect ( ( ) => {
3344 dispatch ( fetchBMProjects ( ) ) ;
3445 } , [ dispatch ] ) ;
3546
47+ // Fetch issues when filters change
3648 useEffect ( ( ) => {
3749 let dateRange = [ ] ;
38-
3950 if ( startDate && endDate ) {
4051 dateRange = [
4152 `${ startDate . toISOString ( ) . split ( 'T' ) [ 0 ] } ,${ endDate . toISOString ( ) . split ( 'T' ) [ 0 ] } ` ,
@@ -50,14 +61,15 @@ function IssueCharts() {
5061 dispatch ( fetchLongestOpenIssues ( dateRange , selectedProjects ) ) ;
5162 } , [ dispatch , startDate , endDate , selectedProjects ] ) ;
5263
64+ // Handle chart container width
5365 useEffect ( ( ) => {
5466 function handleResize ( ) {
5567 if ( chartContainerRef . current ) {
5668 setContainerWidth ( chartContainerRef . current . offsetWidth ) ;
5769 }
5870 }
5971 window . addEventListener ( 'resize' , handleResize ) ;
60- handleResize ( ) ; // Initial set
72+ handleResize ( ) ;
6173 return ( ) => window . removeEventListener ( 'resize' , handleResize ) ;
6274 } , [ ] ) ;
6375
@@ -70,9 +82,7 @@ function IssueCharts() {
7082 label : project . name ,
7183 } ) ) ;
7284
73- // Calculate margins and YAxis width based on container width
7485 const getChartLayout = ( ) => {
75- // Margins and YAxis width scale with container width
7686 const leftRightMargin = Math . max ( 20 , Math . min ( 200 , containerWidth * 0.12 ) ) ;
7787 const yAxisWidth = Math . max ( 60 , Math . min ( 180 , containerWidth * 0.13 ) ) ;
7888 return {
@@ -83,96 +93,80 @@ function IssueCharts() {
8393
8494 const { margin, yAxisWidth } = getChartLayout ( ) ;
8595
86- if ( loading ) return < div > Loading...</ div > ;
87- if ( error ) return < div > Error: { error } </ div > ;
96+ if ( loading ) return < div className = { styles . loading } > Loading...</ div > ;
97+ if ( error ) return < div className = { styles . error } > Error: { error } </ div > ;
8898
8999 return (
90- < div className = "issue-chart-container" >
91- < h2 > Longest Open Issues</ h2 >
92-
93- < div className = "filters-container" >
94- < div className = "filter" >
95- < label className = "issue-chart-label" htmlFor = "start-date" >
96- Date Range:
97- </ label >
98- < div className = "date-range-picker" >
100+ < div className = { darkMode ? styles . issueChartContainerDark : styles . issueChartContainer } >
101+ < h2 className = { darkMode ? styles . titleDark : styles . title } > Longest Open Issues</ h2 >
102+
103+ < div className = { styles . filterCenterWrapper } >
104+ { /* Row 1: Date picker */ }
105+ < div className = { styles . dateRow } >
106+ < span className = { styles . dateLabel } > From</ span >
107+ < div className = { styles . dateField } >
99108 < DatePicker
100- id = "start-date"
101109 selected = { startDate }
102110 onChange = { date => setStartDate ( date ) }
103- selectsStart
104- startDate = { startDate }
105- endDate = { endDate }
106- maxDate = { endDate }
107111 placeholderText = "Start Date"
108- isClearable
109- className = "filter-select"
112+ className = { darkMode ? styles . dateDark : styles . dateLight }
110113 />
111- < span > to</ span >
114+ </ div >
115+ < span className = { styles . dateLabel } > to</ span >
116+ < div className = { styles . dateField } >
112117 < DatePicker
113118 selected = { endDate }
114119 onChange = { date => setEndDate ( date ) }
115- selectsEnd
116- startDate = { startDate }
117- endDate = { endDate }
118- minDate = { startDate }
119- maxDate = { new Date ( ) }
120120 placeholderText = "End Date"
121- isClearable
122- className = "filter-select"
121+ className = { darkMode ? styles . dateDark : styles . dateLight }
123122 />
124123 </ div >
125124 </ div >
126125
127- < div className = "filter" >
128- < label className = "issue-chart-label" htmlFor = "start-date" >
129- Projects:
130- </ label >
126+ { /* Row 2: Project selector */ }
127+ < div className = { styles . projectRow } >
131128 < Select
132- id = "start-date"
133129 isMulti
130+ id = "project-select"
134131 options = { projectOptions }
135132 onChange = { handleProjectChange }
136133 value = { projectOptions . filter ( option => ( selectedProjects ?? [ ] ) . includes ( option . value ) ) }
137- className = "filter-select"
138134 classNamePrefix = "select"
135+ className = { `${ styles . selectReact } ${ darkMode ? styles . selectDark : '' } ` }
139136 />
140137 </ div >
141138 </ div >
142139
143- < div className = "chart-container" ref = { chartContainerRef } >
144- { ! issues || issues . length === 0 ? (
145- < div className = "no-data-message" >
146- < div className = "no-data-content" >
147- < h3 > No Open Issues Found</ h3 >
148- < p > There are currently no open issues matching your selected criteria.</ p >
149- < p > Try adjusting your date range or project filters to see more results.</ p >
150- </ div >
151- </ div >
140+ { /* Chart */ }
141+ < div className = { styles . chartContainer } ref = { chartContainerRef } >
142+ { normalizedIssues . length === 0 ? (
143+ < p className = { styles . noData } > No issues found.</ p >
152144 ) : (
153145 < ResponsiveContainer width = "100%" height = "100%" >
154- < BarChart data = { issues } layout = "vertical" margin = { margin } >
146+ < BarChart data = { normalizedIssues } layout = "vertical" margin = { margin } >
155147 < CartesianGrid strokeDasharray = "3 3" />
156148 < XAxis
157149 type = "number"
158- label = { { value : 'Duration in Months' , position : 'insideBottom' , offset : - 5 } }
150+ label = { {
151+ value : 'Duration in Months' ,
152+ position : 'insideBottom' ,
153+ offset : - 5 ,
154+ fill : darkMode ? '#fff' : '#000' ,
155+ } }
156+ tick = { { fill : darkMode ? '#ccc' : '#333' } }
159157 />
160158 < YAxis
161159 dataKey = "issueName"
162160 type = "category"
163- tick = { { fontSize : 14 , fontWeight : 500 } }
161+ tick = { { fontSize : 14 , fontWeight : 500 , fill : darkMode ? '#fff' : '#000' } }
164162 width = { yAxisWidth }
165163 />
166- < Tooltip
167- formatter = { value => `${ value } months` }
168- labelFormatter = { label => `Issue: ${ label } ` }
169- />
170164 < Bar dataKey = "durationOpen" fill = "#6495ED" barSize = { 30 } >
171165 < LabelList
172166 dataKey = "durationOpen"
173167 position = "right"
174168 formatter = { v => `${ v } mo` }
175- className = "recharts-label"
169+ fill = { darkMode ? '#fff' : '#000' }
176170 />
177171 </ Bar >
178172 </ BarChart >
0 commit comments