Issues breakdown by Type
From 943559aa25e4eb72240f0dcfe074c9961a5b312c Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:05:24 -0800
Subject: [PATCH 15/19] fix: Add comprehensive dark mode support to Issues
Breakdown Chart
- Fix 'no data available' message visibility in dark mode
- Add dark mode styling for chart background (#253342)
- Update grid lines color for dark mode (rgba(255,255,255,0.1))
- Update all text colors (axis labels, tooltips, loading, error states)
- Improve error message visibility with lighter red (#ff6b6b)
- Apply consistent color scheme matching project standards (#1b2a41, #ffffff, #d1d5db)
All dark mode colors now align with the project's established theme.
---
.../IssueBreakdownChart.module.css | 38 ++++++++++++++++++-
.../IssuesBreakdownChart.jsx | 20 +++++++---
2 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
index cd3a8bbad8..125fa08d91 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
@@ -230,6 +230,12 @@
width: 100%;
}
+:global(.dark-mode) .errorMessage {
+ background: rgba(244, 67, 54, 0.15);
+ border: 1px solid rgba(244, 67, 54, 0.4);
+ color: #ff6b6b;
+}
+
.errorMessage i {
font-size: 16px;
}
@@ -238,13 +244,17 @@
width: 100%;
height: 500px;
margin-top: 20px;
- background: var(--section-bg, transparent);
+ background: var(--section-bg, #ffffff);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
+:global(.dark-mode) .chartContainer {
+ background: #253342;
+}
+
.loadingContainer {
display: flex;
flex-direction: column;
@@ -262,6 +272,10 @@
font-weight: 500;
}
+:global(.dark-mode) .loadingText {
+ color: #d1d5db;
+}
+
.errorContainer {
display: flex;
flex-direction: column;
@@ -280,12 +294,21 @@
opacity: 0.7;
}
+:global(.dark-mode) .errorContainer i {
+ color: #ff6b6b;
+ opacity: 0.8;
+}
+
.errorText {
color: var(--text-color, #333);
font-size: 16px;
font-weight: 500;
}
+:global(.dark-mode) .errorText {
+ color: #ffffff;
+}
+
.emptyContainer {
display: flex;
flex-direction: column;
@@ -304,18 +327,31 @@
opacity: 0.5;
}
+:global(.dark-mode) .emptyContainer i {
+ color: #d1d5db;
+ opacity: 0.7;
+}
+
.emptyText {
color: var(--text-color, #666);
font-size: 18px;
font-weight: 500;
}
+:global(.dark-mode) .emptyText {
+ color: #ffffff;
+}
+
.emptySubtext {
color: var(--text-color, #999);
font-size: 14px;
margin-top: 4px;
}
+:global(.dark-mode) .emptySubtext {
+ color: #d1d5db;
+}
+
/* ===================== RESPONSIVE DESIGN ===================== */
/* Tablet and smaller screens */
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
index 4781fef5ec..bc0c97b8b9 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
@@ -60,10 +60,12 @@ export default function IssuesBreakdownChart() {
// Ref for abort controller to cancel API calls on unmount
const abortControllerRef = useRef(null);
- const rootStyles = getComputedStyle(document.body);
- const textColor = rootStyles.getPropertyValue('--text-color') || '#666';
- const gridColor = rootStyles.getPropertyValue('--grid-color') || (darkMode ? '#444' : '#ccc');
- const tooltipBg = rootStyles.getPropertyValue('--section-bg') || '#fff';
+ // Dark mode colors for chart elements
+ const textColor = darkMode ? '#ffffff' : '#666';
+ const gridColor = darkMode ? 'rgba(255, 255, 255, 0.1)' : '#e0e0e0';
+ const tooltipBg = darkMode ? '#1b2a41' : '#ffffff';
+ const tooltipTextColor = darkMode ? '#ffffff' : '#333';
+ const tooltipBorderColor = darkMode ? 'rgba(255, 255, 255, 0.2)' : '#ddd';
/**
* Process API response data to map to three fixed issue types
@@ -646,9 +648,15 @@ export default function IssuesBreakdownChart() {
{/* Fixed three bars: Equipment Issues, Labor Issues, Materials Issues */}
From 2d64a163597172fb8369ac326c4382ea04b933a3 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:12:54 -0800
Subject: [PATCH 16/19] feat: Update Issues Breakdown Chart to fetch only
projects with issues
- Change endpoint from /projects to /bm/projectsNames?withIssuesOnly=true
- Ensures project filter dropdown only shows projects with actual issue data
- Improves UX by preventing selection of projects without issues
- All selected projects will now have corresponding data in the chart
---
.../WeeklyProjectSummary/IssuesBreakdownChart.jsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
index bc0c97b8b9..82b4dd750a 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
@@ -274,9 +274,11 @@ export default function IssuesBreakdownChart() {
return;
}
- // Fetch from API if not in Redux
+ // Fetch from API if not in Redux - only fetch projects with issues
try {
- const response = await httpService.get(`${process.env.REACT_APP_APIENDPOINT}/projects`);
+ const response = await httpService.get(
+ `${process.env.REACT_APP_APIENDPOINT}/bm/projectsNames?withIssuesOnly=true`,
+ );
if (response.data && Array.isArray(response.data)) {
setAvailableProjects(response.data);
}
From b8ac54a8117724fedaafab0c1d82c52e431cb04e Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Mon, 3 Nov 2025 18:44:59 -0800
Subject: [PATCH 17/19] fix: Improve responsive design for Issues Breakdown
Chart filters
- Add flex-basis (300px) to prevent filter overlap at all screen sizes
- Add new breakpoint at 1280px to handle medium screens smoothly
- Add breakpoint at 900px for small tablets
- Fix overlap issue at ~1155px width where issue type filter overlapped date range
- Ensure consistent wrapping behavior across all screen sizes
- Adjust flex properties for better responsive behavior
The filters now properly wrap to new lines before overlapping, ensuring
a consistent and responsive layout from 1280px down to mobile sizes.
---
.../IssueBreakdownChart.module.css | 23 +++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
index 125fa08d91..d3c8ab17a3 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
@@ -74,7 +74,8 @@
display: flex;
flex-direction: column;
min-width: 250px;
- flex: 1;
+ flex: 1 1 300px;
+ max-width: 100%;
}
.filterLabel {
@@ -354,7 +355,15 @@
/* ===================== RESPONSIVE DESIGN ===================== */
-/* Tablet and smaller screens */
+/* Large tablets and medium screens - prevent filter overlap */
+@media (max-width: 1280px) {
+ .filterGroup {
+ min-width: 220px;
+ flex: 1 1 280px;
+ }
+}
+
+/* Tablet screens - tighter spacing */
@media (max-width: 1024px) {
.filtersRow {
gap: 16px;
@@ -362,6 +371,15 @@
.filterGroup {
min-width: 200px;
+ flex: 1 1 250px;
+ }
+}
+
+/* Small tablets - prevent overlap by wrapping earlier */
+@media (max-width: 900px) {
+ .filterGroup {
+ min-width: 180px;
+ flex: 1 1 220px;
}
}
@@ -385,6 +403,7 @@
.filterGroup {
min-width: 100%;
width: 100%;
+ flex: 1 1 100%;
}
.datePickerGroup {
From 65e128ff8b51eb39111e994a8b38a351df97edb4 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Mon, 3 Nov 2025 19:03:03 -0800
Subject: [PATCH 18/19] fix: Prevent filter clipping on small screens in Issues
Breakdown Chart
Root cause: overflow-x: hidden was clipping Date Range and Issue Type filters
on screens < 768px, making them invisible while only showing Project filter.
Changes:
- Reduced base min-width from 280px to 250px for better wrapping
- Removed overflow-x: hidden from .container and .inner (was clipping content)
- Added 900px breakpoint for smooth transition to 2-column layout
- Changed flex properties from '1 1 100%' to '1 1 auto' for better flexibility
- Updated date picker widths from '100%' to 'auto' with flex: 1
- Added flex-shrink: 0 to date separator to prevent collapse
- Changed date separator width from '100%' to 'auto' at 480px
Result:
- All three filters now visible at all screen sizes
- No overflow or clipping issues
- Smooth responsive behavior from desktop to mobile
- Filters properly wrap without being cut off
---
.../IssueBreakdownChart.module.css | 82 ++++++++++++++-----
1 file changed, 60 insertions(+), 22 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
index d3c8ab17a3..f573f74889 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css
@@ -74,8 +74,16 @@
display: flex;
flex-direction: column;
min-width: 250px;
- flex: 1 1 300px;
+ flex: 1 1 350px;
max-width: 100%;
+ box-sizing: border-box;
+}
+
+/* Ensure react-select doesn't overflow */
+.filterGroup :global(.customSelect__control) {
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
}
.filterLabel {
@@ -89,6 +97,8 @@
display: flex;
align-items: center;
gap: 8px;
+ width: 100%;
+ box-sizing: border-box;
}
.datePicker {
@@ -100,7 +110,10 @@
background: var(--section-bg, #fff);
color: var(--text-color, #333);
min-height: 38px;
+ min-width: 0;
+ max-width: 100%;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ box-sizing: border-box;
/* Ensure browser adapts native controls to dark mode */
color-scheme: light dark;
}
@@ -355,38 +368,42 @@
/* ===================== RESPONSIVE DESIGN ===================== */
-/* Large tablets and medium screens - prevent filter overlap */
-@media (max-width: 1280px) {
+/* At 1155px, force issue type filter to wrap to next line */
+@media (max-width: 1155px) {
.filterGroup {
- min-width: 220px;
- flex: 1 1 280px;
+ /* Increase flex-basis to force wrapping */
+ flex: 1 1 370px;
}
}
-/* Tablet screens - tighter spacing */
+/* Tablet screens - reduce size for better fit */
@media (max-width: 1024px) {
.filtersRow {
gap: 16px;
}
.filterGroup {
- min-width: 200px;
- flex: 1 1 250px;
+ min-width: 220px;
+ flex: 1 1 300px;
}
}
-/* Small tablets - prevent overlap by wrapping earlier */
+/* Transition to vertical layout at 900px to prevent overflow */
@media (max-width: 900px) {
.filterGroup {
- min-width: 180px;
- flex: 1 1 220px;
+ min-width: 0;
+ flex: 1 1 calc(50% - 8px);
}
}
-/* Mobile devices */
+/* Mobile and smaller tablets - single column layout */
@media (max-width: 768px) {
.container {
- padding: 20px;
+ padding: 16px;
+ }
+
+ .inner {
+ width: 100%;
}
.heading {
@@ -395,24 +412,31 @@
.filtersRow {
flex-direction: column;
- gap: 20px;
+ gap: 16px;
margin-top: 20px;
margin-bottom: 16px;
}
.filterGroup {
- min-width: 100%;
+ min-width: 0;
width: 100%;
- flex: 1 1 100%;
+ flex: 1 1 auto;
}
.datePickerGroup {
flex-direction: row;
align-items: center;
+ width: 100%;
}
.datePicker {
min-width: 0;
+ width: auto;
+ flex: 1;
+ }
+
+ .dateSeparator {
+ flex-shrink: 0;
}
.resetButtonContainer {
@@ -434,7 +458,7 @@
/* Small mobile devices */
@media (max-width: 480px) {
.container {
- padding: 16px;
+ padding: 12px;
}
.heading {
@@ -443,30 +467,44 @@
}
.legend {
- gap: 16px;
+ gap: 12px;
justify-content: flex-start;
+ flex-wrap: wrap;
}
.legendLabel {
- font-size: 18px;
+ font-size: 16px;
}
.filtersRow {
- gap: 16px;
+ gap: 12px;
margin-top: 16px;
}
+ .filterGroup {
+ min-width: 0;
+ width: 100%;
+ }
+
.filterLabel {
font-size: 13px;
}
.datePickerGroup {
- flex-wrap: wrap;
+ flex-direction: column;
+ gap: 8px;
+ width: 100%;
}
- .dateSeparator {
+ .datePicker {
+ min-width: 0;
width: 100%;
+ }
+
+ .dateSeparator {
+ width: auto;
text-align: center;
+ margin: 0;
}
.chartContainer {
From af4ecefd7883d82a02eb6f40f5010715436db58f Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Mon, 3 Nov 2025 19:28:19 -0800
Subject: [PATCH 19/19] refactor: eliminate code duplication in
IssuesBreakdownChart by extracting getSelectStyles utility function
---
.../IssuesBreakdownChart.jsx | 264 ++++++------------
1 file changed, 92 insertions(+), 172 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
index 82b4dd750a..f8393a1d78 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx
@@ -37,6 +37,96 @@ const ISSUE_TYPE_MAPPING = {
'Materials Issues': 'materialIssues',
};
+/**
+ * Generate react-select custom styles based on dark mode
+ * @param {boolean} darkMode - Whether dark mode is enabled
+ * @returns {object} Custom styles object for react-select
+ */
+const getSelectStyles = darkMode => {
+ if (darkMode) {
+ return {
+ control: baseStyles => ({
+ ...baseStyles,
+ backgroundColor: '#2c3344',
+ borderColor: '#364156',
+ minHeight: '38px',
+ fontSize: '14px',
+ }),
+ menu: baseStyles => ({
+ ...baseStyles,
+ backgroundColor: '#2c3344',
+ fontSize: '14px',
+ }),
+ option: (baseStyles, state) => ({
+ ...baseStyles,
+ backgroundColor: state.isSelected ? '#0d55b3' : state.isFocused ? '#364156' : '#2c3344',
+ color: state.isSelected ? '#fff' : '#e0e0e0',
+ fontSize: '14px',
+ }),
+ multiValue: baseStyles => ({
+ ...baseStyles,
+ backgroundColor: '#375071',
+ borderRadius: '6px',
+ }),
+ multiValueLabel: baseStyles => ({
+ ...baseStyles,
+ color: '#fff',
+ fontSize: '12px',
+ }),
+ multiValueRemove: baseStyles => ({
+ ...baseStyles,
+ color: '#fff',
+ ':hover': {
+ backgroundColor: '#0d55b3',
+ color: '#fff',
+ },
+ }),
+ singleValue: baseStyles => ({
+ ...baseStyles,
+ color: '#e0e0e0',
+ }),
+ placeholder: baseStyles => ({
+ ...baseStyles,
+ color: '#aaaaaa',
+ }),
+ };
+ }
+
+ return {
+ control: baseStyles => ({
+ ...baseStyles,
+ minHeight: '38px',
+ fontSize: '14px',
+ }),
+ menu: baseStyles => ({
+ ...baseStyles,
+ fontSize: '14px',
+ }),
+ option: baseStyles => ({
+ ...baseStyles,
+ fontSize: '14px',
+ }),
+ multiValue: baseStyles => ({
+ ...baseStyles,
+ backgroundColor: '#e2e7ee',
+ borderRadius: '6px',
+ }),
+ multiValueLabel: baseStyles => ({
+ ...baseStyles,
+ color: '#333',
+ fontSize: '12px',
+ }),
+ multiValueRemove: baseStyles => ({
+ ...baseStyles,
+ color: '#333',
+ ':hover': {
+ backgroundColor: '#0d55b3',
+ color: '#fff',
+ },
+ }),
+ };
+};
+
export default function IssuesBreakdownChart() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
@@ -380,92 +470,7 @@ export default function IssuesBreakdownChart() {
placeholder="Select Projects"
isClearable
isDisabled={availableProjects.length === 0}
- styles={
- darkMode
- ? {
- control: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#2c3344',
- borderColor: '#364156',
- minHeight: '38px',
- fontSize: '14px',
- }),
- menu: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#2c3344',
- fontSize: '14px',
- }),
- option: (baseStyles, state) => ({
- ...baseStyles,
- backgroundColor: state.isSelected
- ? '#0d55b3'
- : state.isFocused
- ? '#364156'
- : '#2c3344',
- color: state.isSelected ? '#fff' : '#e0e0e0',
- fontSize: '14px',
- }),
- multiValue: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#375071',
- borderRadius: '6px',
- }),
- multiValueLabel: baseStyles => ({
- ...baseStyles,
- color: '#fff',
- fontSize: '12px',
- }),
- multiValueRemove: baseStyles => ({
- ...baseStyles,
- color: '#fff',
- ':hover': {
- backgroundColor: '#0d55b3',
- color: '#fff',
- },
- }),
- singleValue: baseStyles => ({
- ...baseStyles,
- color: '#e0e0e0',
- }),
- placeholder: baseStyles => ({
- ...baseStyles,
- color: '#aaaaaa',
- }),
- }
- : {
- control: baseStyles => ({
- ...baseStyles,
- minHeight: '38px',
- fontSize: '14px',
- }),
- menu: baseStyles => ({
- ...baseStyles,
- fontSize: '14px',
- }),
- option: baseStyles => ({
- ...baseStyles,
- fontSize: '14px',
- }),
- multiValue: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#e2e7ee',
- borderRadius: '6px',
- }),
- multiValueLabel: baseStyles => ({
- ...baseStyles,
- color: '#333',
- fontSize: '12px',
- }),
- multiValueRemove: baseStyles => ({
- ...baseStyles,
- color: '#333',
- ':hover': {
- backgroundColor: '#0d55b3',
- color: '#fff',
- },
- }),
- }
- }
+ styles={getSelectStyles(darkMode)}
/>
@@ -508,92 +513,7 @@ export default function IssuesBreakdownChart() {
onChange={handleIssueTypeChange}
placeholder="Select Issue Types"
isClearable
- styles={
- darkMode
- ? {
- control: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#2c3344',
- borderColor: '#364156',
- minHeight: '38px',
- fontSize: '14px',
- }),
- menu: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#2c3344',
- fontSize: '14px',
- }),
- option: (baseStyles, state) => ({
- ...baseStyles,
- backgroundColor: state.isSelected
- ? '#0d55b3'
- : state.isFocused
- ? '#364156'
- : '#2c3344',
- color: state.isSelected ? '#fff' : '#e0e0e0',
- fontSize: '14px',
- }),
- multiValue: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#375071',
- borderRadius: '6px',
- }),
- multiValueLabel: baseStyles => ({
- ...baseStyles,
- color: '#fff',
- fontSize: '12px',
- }),
- multiValueRemove: baseStyles => ({
- ...baseStyles,
- color: '#fff',
- ':hover': {
- backgroundColor: '#0d55b3',
- color: '#fff',
- },
- }),
- singleValue: baseStyles => ({
- ...baseStyles,
- color: '#e0e0e0',
- }),
- placeholder: baseStyles => ({
- ...baseStyles,
- color: '#aaaaaa',
- }),
- }
- : {
- control: baseStyles => ({
- ...baseStyles,
- minHeight: '38px',
- fontSize: '14px',
- }),
- menu: baseStyles => ({
- ...baseStyles,
- fontSize: '14px',
- }),
- option: baseStyles => ({
- ...baseStyles,
- fontSize: '14px',
- }),
- multiValue: baseStyles => ({
- ...baseStyles,
- backgroundColor: '#e2e7ee',
- borderRadius: '6px',
- }),
- multiValueLabel: baseStyles => ({
- ...baseStyles,
- color: '#333',
- fontSize: '12px',
- }),
- multiValueRemove: baseStyles => ({
- ...baseStyles,
- color: '#333',
- ':hover': {
- backgroundColor: '#0d55b3',
- color: '#fff',
- },
- }),
- }
- }
+ styles={getSelectStyles(darkMode)}
/>