diff --git a/src/components/BMDashboard/Issues/issueChart.module.css b/src/components/BMDashboard/Issues/issueChart.module.css index 76633c35e7..bf6d4d6a38 100644 --- a/src/components/BMDashboard/Issues/issueChart.module.css +++ b/src/components/BMDashboard/Issues/issueChart.module.css @@ -179,3 +179,210 @@ .errorTextDark { color: #fca5a5; } + +.chartContainer { + width: 100%; + height: 500px; +} + +/* Container for entire chart section */ +.issueChartContainer { + width: 50vw; /* full viewport width */ + padding: 20px; + box-sizing: border-box; /* include padding in width */ + background: #f9f9f9; + border-radius: 8px; + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); +} + + +.filterCenterWrapper { + max-width: 650px; /* filters centered and constrained */ + margin: 0 auto 20px auto; +} + + +.issueChartContainerDark { + width: 50vw; /* full viewport width */ + padding: 20px; + box-sizing: border-box; /* include padding in width */ + background: #121212; + box-shadow: 0 4px 12px rgba(0,0,0,0.9); + color: #ccd1dc; +} + +/* Chart title */ +.title { + text-align: center; + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; + color: #333; +} + +.titleDark { + text-align: center; + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; + color: #fff; +} + +/* Filter rows */ +.dateRow { + display: grid; + grid-template-columns: auto 1fr auto 1fr; + align-items: center; + gap: 10px; + width: 100%; + margin-bottom: 15px; +} + +.projectRow { + width: 100%; +} + +/* Labels */ +.dateLabelLight { + font-size: 14px; + font-weight: 500; + color: #333; +} + +.dateLabelDark { + font-size: 14px; + font-weight: 500; + color: #cfd7e3; +} + +/* Date picker */ +.dateField { + width: 100%; +} + +.dateLight, +.dateDark { + width: 100%; + padding: 8px 10px; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 14px; +} + +.dateDark { + background: #22272e; + color: #cfd7e3; + border-color: #3d444d; +} + +/* React select */ +.selectReact { + width: 100%; +} + +:global(.react-datepicker__close-icon::after) { + background: none; + color: #b9b9b9; + font-size: 18px; +} + +/* DARK MODE CONTROL */ +.controlDark { + background: #22272e !important; + border-color: #3d444d !important; + color: #cfd7e3 !important; +} + +/* VALUE TEXT */ +.valueDark { + color: #cfd7e3 !important; +} + +/* MENU */ +.menuDark { + background: #22272e !important; +} + +/* OPTIONS — keep only base default color */ +.optionDark { + color: #cfd7e3 !important; +} + +/* MULTI VALUE */ +.multiValueDark { + background: #3a3f45 !important; + color: #cfd7e3 !important; +} + +.multiValueLabelDark { + color: #cfd7e3 !important; +} + +.multiValueRemoveDark { + color: #cfd7e3 !important; +} + +/* ===== LIGHT MODE ===== */ + +.controlLight { + background: white !important; + border-color: #ccc !important; + color: black !important; +} + +.menuLight { + background: white !important; +} + +.optionLight { + color: black !important; +} + +.multiValueLight { + background: #dceeff !important; + color: black !important; +} + +.multiValueLabelLight { + color: black !important; +} + +.multiValueRemoveLight { + color: black !important; +} + + +/* No data text */ +.noData { + text-align: center; + color: #888; + font-size: 16px; + padding: 40px 0; +} + +/* Loading/Error */ +.loading, +.error { + text-align: center; + padding: 20px 0; + font-size: 16px; +} + +.rowLabel { + margin: 8px 0 8px; + font-weight: 600; + font-size: 16px; +} + + +/* Responsive */ +@media (max-width: 650px) { + .dateRow { + grid-template-columns: 1fr; + gap: 10px; + } + + .dateRow span.dateLabel { + display: none; /* hide labels for small screens */ + } +} diff --git a/src/components/BMDashboard/Issues/openIssueCharts.jsx b/src/components/BMDashboard/Issues/openIssueCharts.jsx index 6af97f9248..729cc19032 100644 --- a/src/components/BMDashboard/Issues/openIssueCharts.jsx +++ b/src/components/BMDashboard/Issues/openIssueCharts.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { useDispatch, useSelector } from 'react-redux'; @@ -8,7 +8,6 @@ import { XAxis, YAxis, CartesianGrid, - Tooltip, ResponsiveContainer, LabelList, } from 'recharts'; @@ -18,13 +17,15 @@ import { fetchLongestOpenIssues, setProjectFilter, } from '../../../actions/bmdashboard/issueChartActions'; -import styles from './issueCharts.module.css'; +import styles from './issueChart.module.css'; function IssueCharts() { const dispatch = useDispatch(); - const darkMode = useSelector(state => state.theme.darkMode); + const { issues, loading, error, selectedProjects } = useSelector(state => state.bmissuechart); const projects = useSelector(state => state.bmProjects); + const darkMode = useSelector(state => state.theme?.darkMode); + const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const chartContainerRef = useRef(null); @@ -36,13 +37,22 @@ function IssueCharts() { const tooltipBg = darkMode ? '#2d3748' : '#ffffff'; const tooltipBorder = darkMode ? '#4a5568' : '#e2e8f0'; + // Normalize issues for chart + const normalizedIssues = useMemo(() => { + return (issues || []).map((item, index) => ({ + issueName: item.issueName || `Issue #${index + 1}`, + durationOpen: item.durationOpen ?? 0, + })); + }, [issues]); + + // Load projects on mount useEffect(() => { dispatch(fetchBMProjects()); }, [dispatch]); + // Fetch issues when filters change useEffect(() => { let dateRange = []; - if (startDate && endDate) { dateRange = [ `${startDate.toISOString().split('T')[0]},${endDate.toISOString().split('T')[0]}`, @@ -57,6 +67,7 @@ function IssueCharts() { dispatch(fetchLongestOpenIssues(dateRange, selectedProjects)); }, [dispatch, startDate, endDate, selectedProjects]); + // Handle chart container width useEffect(() => { function handleResize() { if (chartContainerRef.current) { @@ -64,7 +75,7 @@ function IssueCharts() { } } window.addEventListener('resize', handleResize); - handleResize(); // Initial set + handleResize(); return () => window.removeEventListener('resize', handleResize); }, []); @@ -77,9 +88,7 @@ function IssueCharts() { label: project.name, })); - // Calculate margins and YAxis width based on container width const getChartLayout = () => { - // Margins and YAxis width scale with container width const leftRightMargin = Math.max(20, Math.min(200, containerWidth * 0.12)); const yAxisWidth = Math.max(60, Math.min(180, containerWidth * 0.13)); return { @@ -111,100 +120,132 @@ function IssueCharts() { darkMode ? styles.issueChartContainerDark : '' }`; const labelClass = `${styles.issueChartLabel} ${darkMode ? styles.issueChartLabelDark : ''}`; - const filterSelectClass = `${styles.filterSelect} ${darkMode ? styles.filterSelectDark : ''}`; - const chartContainerClass = `${styles.chartContainer} ${ - darkMode ? styles.chartContainerDark : '' - }`; - const noDataMessageClass = `${styles.noDataMessage} ${darkMode ? styles.noDataMessageDark : ''}`; - const noDataContentClass = `${styles.noDataContent} ${darkMode ? styles.noDataContentDark : ''}`; return (

Longest Open Issues

+ +
+ {/* Label: Date */} +
Date
-
-
- -
+ {/* Row 1: Date picker */} +
+ From +
setStartDate(date)} - selectsStart - startDate={startDate} - endDate={endDate} - maxDate={endDate} placeholderText="Start Date" isClearable - className={filterSelectClass} + className={darkMode ? styles.dateDark : styles.dateLight} /> - to +
+ to +
setEndDate(date)} - selectsEnd - startDate={startDate} - endDate={endDate} - minDate={startDate} - maxDate={new Date()} placeholderText="End Date" isClearable - className={filterSelectClass} + className={darkMode ? styles.dateDark : styles.dateLight} />
-
- + {/* Row 2: Project selector */} + +