From cb73de11795340e3d1ab6c7e1e3d5c36d22425df Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:55:24 -0700 Subject: [PATCH 01/19] feat(IssuesBreakdownChart): add filter state management foundation Add comprehensive state management for filtering functionality in the Issues Breakdown Chart component. This establishes the foundation for implementing multi-dimensional filtering capabilities. Changes: - Add filter state variables for projects, date range, and issue types * selectedProjects: array to store selected project IDs * startDate: string for start date filter (YYYY-MM-DD format) * endDate: string for end date filter (YYYY-MM-DD format) * selectedIssueTypes: array to store selected issue type filters - Add state for available filter options * availableIssueTypes: stores fetched issue type options * availableProjects: stores available project list for selection - Preserve all existing functionality and states * Maintains data, loading, error, and darkMode states * No changes to existing data fetching or rendering logic --- .../WeeklyProjectSummary/IssuesBreakdownChart.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index 3a3e5c9a0a..645aac6d1a 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -25,6 +25,16 @@ export default function IssuesBreakdownChart() { const [error, setError] = useState(null); const darkMode = useSelector(state => state.theme.darkMode); + // Filter states + const [selectedProjects, setSelectedProjects] = useState([]); + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); + const [selectedIssueTypes, setSelectedIssueTypes] = useState([]); + + // Available options states + const [availableIssueTypes, setAvailableIssueTypes] = useState([]); + const [availableProjects, setAvailableProjects] = useState([]); + const rootStyles = getComputedStyle(document.body); const textColor = rootStyles.getPropertyValue('--text-color') || '#666'; const gridColor = rootStyles.getPropertyValue('--grid-color') || (darkMode ? '#444' : '#ccc'); From ef60f24370bec69656199a0d8c3819b959ca532e Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:01:19 -0700 Subject: [PATCH 02/19] feat(IssuesBreakdownChart): fetch available issue types on mount Add API integration to fetch available issue types for filter dropdown. This populates the availableIssueTypes state that will be used for the Issue Type filter UI in subsequent steps. Changes: - Add new useEffect hook that runs on component mount - Fetch issue types from /api/issues/types endpoint - Extract and store issueTypes from response.data.issueTypes - Implement graceful error handling * Falls back to empty array if fetch fails * Non-blocking: chart continues to work without filters * Errors don't affect primary chart functionality Implementation details: - Follows existing async/await pattern used in fetchData - Validates response data structure before setting state - Uses consistent endpoint format with REACT_APP_APIENDPOINT - Matches project code style and conventions --- .../IssuesBreakdownChart.jsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index 645aac6d1a..2afcf323ce 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -58,6 +58,22 @@ export default function IssuesBreakdownChart() { fetchData(); }, []); + useEffect(() => { + const fetchIssueTypes = async () => { + try { + const response = await httpService.get(`${process.env.REACT_APP_APIENDPOINT}/issues/types`); + if (response.data && response.data.issueTypes) { + setAvailableIssueTypes(response.data.issueTypes); + } + } catch (err) { + // Chart can work without issue types filter - set to empty array + setAvailableIssueTypes([]); + } + }; + + fetchIssueTypes(); + }, []); + if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!data || data.length === 0) return
No data available
; From a5b10c12d7f87e43c89dda3db06d48ad43984d5f Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:04:53 -0700 Subject: [PATCH 03/19] feat(IssuesBreakdownChart): add hybrid project data sourcing Implement flexible project data fetching that prioritizes Redux store and falls back to API when needed. This populates the availableProjects state for the Project filter dropdown in subsequent steps. Changes: - Add Redux selector for project data * Checks state.bmProjects first * Falls back to state.allProjects?.projects * Defaults to empty array if neither exists - Add useEffect hook with hybrid data strategy * Primary: Use Redux store if projects available * Fallback: Fetch from /projects API endpoint * Dependencies: Re-runs when reduxProjects changes - Implement graceful error handling * Falls back to empty array if API fetch fails * Non-blocking: chart continues to work without filters * Errors don't affect primary chart functionality Implementation details: - Follows existing async/await pattern used in other fetches - Validates data structure before setting state (Array.isArray check) - Uses consistent endpoint format with REACT_APP_APIENDPOINT - Reactive to Redux state changes via dependency array - Matches project code style and conventions Benefits: - Efficient: Reuses existing Redux data when available - Resilient: API fallback ensures data availability - Performance: Avoids unnecessary API calls when data exists in store --- .../IssuesBreakdownChart.jsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index 2afcf323ce..fe131637b3 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -25,6 +25,9 @@ export default function IssuesBreakdownChart() { const [error, setError] = useState(null); const darkMode = useSelector(state => state.theme.darkMode); + // Get projects from Redux + const reduxProjects = useSelector(state => state.bmProjects || state.allProjects?.projects || []); + // Filter states const [selectedProjects, setSelectedProjects] = useState([]); const [startDate, setStartDate] = useState(null); @@ -74,6 +77,29 @@ export default function IssuesBreakdownChart() { fetchIssueTypes(); }, []); + useEffect(() => { + const fetchProjects = async () => { + // Use Redux store if available + if (reduxProjects && reduxProjects.length > 0) { + setAvailableProjects(reduxProjects); + return; + } + + // Fetch from API if not in Redux + try { + const response = await httpService.get(`${process.env.REACT_APP_APIENDPOINT}/projects`); + if (response.data && Array.isArray(response.data)) { + setAvailableProjects(response.data); + } + } catch (err) { + // Chart can work without projects filter - set to empty array + setAvailableProjects([]); + } + }; + + fetchProjects(); + }, [reduxProjects]); + if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!data || data.length === 0) return
No data available
; From 5b53c55cab27ece16b4d502f345d9cc52f21350d Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:08:27 -0700 Subject: [PATCH 04/19] feat(IssuesBreakdownChart): integrate filters with API and add debouncing Implement complete filter integration with the API endpoint, including query parameter construction, debounced requests, and enhanced error handling. This enables dynamic chart updates based on filter selections. Changes: - Add useRef hook for debouncing timeout management * Stores timeout reference to prevent memory leaks * Allows cleanup of pending timeouts - Refactor fetchData function to accept filters parameter * Build query string using URLSearchParams * Add projects filter as comma-separated project IDs * Add startDate and endDate in YYYY-MM-DD format * Add issueTypes filter as comma-separated strings * Construct clean URL with conditional query string - Enhance error handling for validation errors * Specifically handle 400 status errors * Extract user-friendly messages from API response * Check err.response.data?.error and err.response.data?.message * Fallback to generic message for unexpected error formats * Preserve existing error handling for other status codes - Update useEffect hook with filter integration * Add all filter states to dependency array * Implement 300ms debouncing to prevent excessive API calls * Clear existing timeout before setting new one * Include cleanup function to prevent memory leaks * Auto-fetch data when any filter changes Implementation details: - Uses URLSearchParams for query string building (standard pattern) - Debouncing improves performance and reduces server load - Proper cleanup prevents memory leaks on unmount - Validation error handling provides better UX - Follows React best practices and project conventions Benefits: - Performance: Debouncing reduces unnecessary API calls - UX: User-friendly error messages for validation issues - Reliability: Proper cleanup prevents memory leaks - Maintainability: Clean, readable query string construction --- .../IssuesBreakdownChart.jsx | 86 +++++++++++++++---- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index fe131637b3..320fa0da2c 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { useSelector } from 'react-redux'; import { BarChart, @@ -38,28 +38,84 @@ export default function IssuesBreakdownChart() { const [availableIssueTypes, setAvailableIssueTypes] = useState([]); const [availableProjects, setAvailableProjects] = useState([]); + // Ref for debouncing timeout + const debounceTimeoutRef = 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'; - useEffect(() => { - const fetchData = async () => { - try { - const response = await httpService.get( - `${process.env.REACT_APP_APIENDPOINT}/issues/breakdown`, - ); - setData(response.data); - setError(null); - } catch (err) { + const fetchData = async filters => { + try { + setLoading(true); + setError(null); + + // Build query string with URLSearchParams + const params = new URLSearchParams(); + + if (filters.projects && filters.projects.length > 0) { + params.append('projects', filters.projects.join(',')); + } + + if (filters.startDate) { + params.append('startDate', filters.startDate); + } + + if (filters.endDate) { + params.append('endDate', filters.endDate); + } + + if (filters.issueTypes && filters.issueTypes.length > 0) { + params.append('issueTypes', filters.issueTypes.join(',')); + } + + const queryString = params.toString(); + const url = `${process.env.REACT_APP_APIENDPOINT}/issues/breakdown${ + queryString ? `?${queryString}` : '' + }`; + + const response = await httpService.get(url); + setData(response.data); + setError(null); + } catch (err) { + // Handle 400 errors (validation errors) with user-friendly messages + if (err.response && err.response.status === 400) { + const errorMessage = + err.response.data?.error || err.response.data?.message || 'Invalid filter parameters'; + setError(errorMessage); + } else { setError(err.message || 'Failed to fetch issue statistics'); - } finally { - setLoading(false); } - }; + } finally { + setLoading(false); + } + }; - fetchData(); - }, []); + useEffect(() => { + // Clear any existing timeout + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + + // Debounce API calls to prevent excessive requests + debounceTimeoutRef.current = setTimeout(() => { + const filters = { + projects: selectedProjects, + startDate, + endDate, + issueTypes: selectedIssueTypes, + }; + fetchData(filters); + }, 300); + + // Cleanup function to clear timeout on unmount or when dependencies change + return () => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + }; + }, [selectedProjects, startDate, endDate, selectedIssueTypes]); useEffect(() => { const fetchIssueTypes = async () => { From 6c590da9b8aca3e218a12567b99f2c8a2f7535c0 Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:36:54 -0700 Subject: [PATCH 05/19] feat(IssuesBreakdownChart): add data normalization with processChartData Implement robust data processing function to normalize API responses into the chart's fixed three-category structure. This ensures consistent data format regardless of API response variations and handles diverse property naming conventions. Changes: - Add processChartData function for data normalization * Validates input data (null/array checks) * Extracts projectId and projectName with fallbacks * Initializes all three issue types to 0 by default * Returns empty array for invalid input - Implement two-phase property matching strategy Phase 1 - Exact matches: * Checks for equipmentIssues, laborIssues, materialIssues * Handles materialsIssues variant (plural) * Tracks matched keys to prevent double-counting Phase 2 - Pattern matching for unmapped properties: * Equipment patterns: equipment, tool, machine * Labor patterns: labor, labour * Material patterns: material, supply, resource * Skips project metadata (_id, name, projectId, projectName) * Only processes properties not matched in Phase 1 - Integrate processChartData into fetchData * Processes API response before setting state (line 173) * Ensures chart always receives normalized data structure * Maintains backward compatibility with existing API format Edge case handling: - Empty/null API data returns empty array - Missing properties default to 0 - Non-numeric values coerced to number or default to 0 - Zero values skipped in pattern matching (optimization) - Prevents accumulation if exact match already found Implementation details: - Uses Set for efficient matched key tracking - Case-insensitive pattern matching (toLowerCase) - Additive accumulation for pattern matches - Comprehensive JSDoc documentation - Follows project code style and conventions Benefits: - Flexibility: Handles various API response formats - Reliability: Consistent output structure guaranteed - Maintainability: Clear separation of data transformation logic - Performance: Efficient tracking prevents double-counting - Extensibility: Easy to add new pattern matching rules --- .../IssuesBreakdownChart.jsx | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index 320fa0da2c..d2e02ac5bb 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -46,6 +46,99 @@ export default function IssuesBreakdownChart() { const gridColor = rootStyles.getPropertyValue('--grid-color') || (darkMode ? '#444' : '#ccc'); const tooltipBg = rootStyles.getPropertyValue('--section-bg') || '#fff'; + /** + * Process API response data to map to three fixed issue types + * Ensures each project has equipmentIssues, laborIssues, and materialIssues properties + */ + const processChartData = apiData => { + if (!apiData || !Array.isArray(apiData)) { + return []; + } + + return apiData.map(project => { + // Extract projectId and projectName + const processedProject = { + projectId: project.projectId || project._id || '', + projectName: project.projectName || project.name || '', + equipmentIssues: 0, + laborIssues: 0, + materialIssues: 0, + }; + + // Track which properties we've already matched exactly + const matchedKeys = new Set(); + + // Check if API returns properties matching the fixed types directly + if (project.equipmentIssues !== undefined) { + processedProject.equipmentIssues = Number(project.equipmentIssues) || 0; + matchedKeys.add('equipmentIssues'); + } + if (project.laborIssues !== undefined) { + processedProject.laborIssues = Number(project.laborIssues) || 0; + matchedKeys.add('laborIssues'); + } + if (project.materialIssues !== undefined) { + processedProject.materialIssues = Number(project.materialIssues) || 0; + matchedKeys.add('materialIssues'); + } + if (project.materialsIssues !== undefined) { + processedProject.materialIssues = Number(project.materialsIssues) || 0; + matchedKeys.add('materialsIssues'); + } + + // Map remaining properties that don't match exactly + // This handles cases where API returns different property names + Object.keys(project).forEach(key => { + // Skip project metadata and already matched keys + if ( + key === 'projectId' || + key === 'projectName' || + key === '_id' || + key === 'name' || + matchedKeys.has(key) + ) { + return; + } + + const value = Number(project[key]) || 0; + if (value === 0) return; // Skip zero values + + const lowerKey = key.toLowerCase(); + + // Map to equipmentIssues if not already set or if pattern matches + if ( + !matchedKeys.has('equipmentIssues') && + (lowerKey.includes('equipment') || + lowerKey.includes('tool') || + lowerKey.includes('machine')) + ) { + processedProject.equipmentIssues += value; + } + // Map to laborIssues if not already set or if pattern matches + else if ( + !matchedKeys.has('laborIssues') && + (lowerKey.includes('labor') || lowerKey.includes('labour') || lowerKey === 'labor') + ) { + processedProject.laborIssues += value; + } + // Map to materialIssues if not already set or if pattern matches + else if ( + !matchedKeys.has('materialIssues') && + (lowerKey.includes('material') || + lowerKey.includes('supply') || + lowerKey.includes('resource')) + ) { + processedProject.materialIssues += value; + } + // For other types (Safety, Weather, METs quality / functionality, etc.): + // If all three fixed types are already set from exact matches, ignore + // Otherwise, we could aggregate into "Other" but for now we maintain the three fixed structure + }); + + return processedProject; + }); + }; + const fetchData = async filters => { try { setLoading(true); @@ -76,7 +169,9 @@ export default function IssuesBreakdownChart() { }`; const response = await httpService.get(url); - setData(response.data); + // Process API response to map to three fixed issue types + const processedData = processChartData(response.data); + setData(processedData); setError(null); } catch (err) { // Handle 400 errors (validation errors) with user-friendly messages From ea8ceb9bd6ea47afa702fdd3c556e5ef05929fe6 Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:41:57 -0700 Subject: [PATCH 06/19] feat(IssuesBreakdownChart): add project multi-select filter UI Implement project filter dropdown using react-select with multi-selection capability, dark mode support, and comprehensive styling. This provides users with an intuitive interface to filter issue data by projects. Changes - Component (IssuesBreakdownChart.jsx): - Add react-select import for multi-select dropdown - Create filters section between legend and chart * Positioned using filtersRow container * Wrapped in filterGroup for consistent layout - Implement react-select multi-select dropdown * isMulti prop enables multiple project selection * classNamePrefix="customSelect" for custom styling * Maps availableProjects to option objects: { value: project._id || project.projectId, label: project.name || project.projectName } * Handles different property name conventions - Add onChange handler for filter updates * Updates selectedProjects state with array of IDs * Extracts values from selected options * Handles null/undefined (sets empty array) * Triggers debounced API call via state change - Implement comprehensive dark mode styling * Control: #2c3344 background, #364156 border * Menu: #2c3344 background matching control * Options: #0d55b3 selected, #364156 focused * Multi-value tags: #375071 background * Hover states: #0d55b3 with white text * Text colors: #e0e0e0 normal, #fff selected - Add light mode styling * Multi-value tags: #e2e7ee background * Consistent font sizing (14px menu, 12px tags) * Hover states match project patterns - User experience features * Placeholder: "Select Projects" * isClearable for quick deselection * isDisabled when no projects available * Proper label association (htmlFor/id) * Minimum height 38px for consistency Changes - Styles (IssueBreakdownChart.module.css): - Add .filtersRow class * Flex container with 24px gap * flex-wrap for responsive layout * 24px top margin, 20px bottom margin * Full width - Add .filterGroup class * Flex column layout for label + control * min-width: 250px for usability * flex: 1 for equal distribution - Add .filterLabel class * 14px font size, 600 weight * 8px bottom margin for spacing * Uses var(--text-color) for theme support Implementation details: - Follows patterns from ToolsHorizontalBarChart and ProjectRiskProfileOverview - Handles property name variations (_id vs projectId, name vs projectName) - Dark mode colors match project design system - Value extraction uses controlled component pattern - Filter state changes trigger existing debounced fetch logic Benefits: - UX: Intuitive multi-select with clear visual feedback - Accessibility: Proper label associations and disabled states - Consistency: Matches existing project filter patterns - Theme support: Seamless dark/light mode switching - Responsive: Flexible layout adapts to container width --- .../IssueBreakdownChart.module.css | 23 ++++ .../IssuesBreakdownChart.jsx | 120 ++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css index a9fbb4f12c..165601b636 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css @@ -60,6 +60,29 @@ font-size: 22px; } +.filtersRow { + display: flex; + gap: 24px; + flex-wrap: wrap; + margin-top: 24px; + margin-bottom: 20px; + width: 100%; +} + +.filterGroup { + display: flex; + flex-direction: column; + min-width: 250px; + flex: 1; +} + +.filterLabel { + font-size: 14px; + font-weight: 600; + margin-bottom: 8px; + color: var(--text-color, #333); +} + .chartContainer { width: 100%; height: 500px; diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index d2e02ac5bb..c461f6838b 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -10,6 +10,7 @@ import { ResponsiveContainer, LabelList, } from 'recharts'; +import Select from 'react-select'; import httpService from '../../../services/httpService'; import styles from './IssueBreakdownChart.module.css'; @@ -279,6 +280,124 @@ export default function IssuesBreakdownChart() { Materials Issues + + {/* Filters Section */} +
+
+ + elements * Start Date and End Date with "to" separator * Format: YYYY-MM-DD (native date input standard) * HTML validation: min attribute on endDate tied to startDate * JavaScript validation in handlers for cross-field logic - Dark mode styling for react-select * Control, menu, option backgrounds (#2c3344, #364156) * Selected state (#0d55b3), focused state (#364156) * Multi-value tags with appropriate colors (#375071) * Hover states for remove buttons * Light mode styling for standard appearance - Add filters section layout * Positioned between legend and chart container * Uses filtersRow container with flex layout * Two filterGroup sections (Project, Date Range) Changes - CSS (IssueBreakdownChart.module.css): - .filtersRow: Flex container with 24px gap, flex-wrap support - .filterGroup: Column layout, min-width 250px, flex: 1 - .filterLabel: 14px font, 600 weight, 8px bottom margin - .datePickerGroup: Flex container with 8px gap for inputs - .datePicker: Native date input styling * Flex: 1 for equal width distribution * Border, padding, border-radius consistent with design * CSS variables for theme support * Min-height: 38px matches react-select height - .datePicker:focus: Focus states * Primary color border * Box shadow for visual feedback - .dateSeparator: "to" text styling, nowrap Accessibility features: - Proper label association with htmlFor and id attributes - aria-label on both date inputs ("Start date", "End date") - Semantic HTML structure - Keyboard navigation support (native inputs and react-select) Validation approach: - HTML min attribute prevents invalid selection in date picker - JavaScript handlers ensure data consistency in state - Bidirectional validation (start adjusts end, end adjusts start) Implementation details: - Follows patterns from ToolsHorizontalBarChart.jsx - Follows patterns from ProjectRiskProfileOverview.jsx - Consistent with project's react-select usage patterns - Dark mode colors match project theme standards - CSS variables enable automatic theme switching Benefits: - UX: Intuitive multi-select and date range controls - Accessibility: ARIA labels and semantic HTML - Consistency: Matches existing project UI patterns - Validation: Prevents invalid date ranges - Theming: Full dark/light mode support --- .../IssueBreakdownChart.module.css | 29 +++++++++++ .../IssuesBreakdownChart.jsx | 48 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css index 165601b636..92e738ff9f 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssueBreakdownChart.module.css @@ -83,6 +83,35 @@ color: var(--text-color, #333); } +.datePickerGroup { + display: flex; + align-items: center; + gap: 8px; +} + +.datePicker { + flex: 1; + padding: 8px 12px; + border: 1px solid var(--border-color, #ddd); + border-radius: 6px; + font-size: 14px; + background: var(--section-bg, #fff); + color: var(--text-color, #333); + min-height: 38px; +} + +.datePicker:focus { + outline: none; + border-color: var(--primary-color, #0d55b3); + box-shadow: 0 0 0 2px rgba(13, 85, 179, 0.1); +} + +.dateSeparator { + color: var(--text-color, #666); + font-size: 14px; + white-space: nowrap; +} + .chartContainer { width: 100%; height: 500px; diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index c461f6838b..96404bb243 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -140,6 +140,28 @@ export default function IssuesBreakdownChart() { }); }; + const handleStartDateChange = e => { + const newStartDate = e.target.value; + setStartDate(newStartDate); + + // Validate: endDate must be >= startDate + if (endDate && newStartDate && endDate < newStartDate) { + // If endDate is before new startDate, adjust endDate to startDate + setEndDate(newStartDate); + } + }; + + const handleEndDateChange = e => { + const newEndDate = e.target.value; + setEndDate(newEndDate); + + // Validate: endDate must be >= startDate + if (startDate && newEndDate && newEndDate < startDate) { + // If new endDate is before startDate, adjust startDate to endDate + setStartDate(newEndDate); + } + }; + const fetchData = async filters => { try { setLoading(true); @@ -397,6 +419,32 @@ export default function IssuesBreakdownChart() { } />
+ +
+ +
+ + to + +
+
From 2f3522a15d3714cb0f43f76568aa2fb587713308 Mon Sep 17 00:00:00 2001 From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:50:53 -0700 Subject: [PATCH 08/19] feat(IssuesBreakdownChart): add issue type filter with fixed categories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement issue type filter UI with a fixed set of three issue categories, including display name to API property name mapping. This completes the full filtering suite for the Issues Breakdown Chart. Changes: - Add FIXED_ISSUE_TYPES constant * Array with three fixed display names: - 'Equipment Issues' - 'Labor Issues' - 'Materials Issues' * Does not use availableIssueTypes from API * Ensures filter options match chart categories exactly - Add ISSUE_TYPE_MAPPING constant * Maps user-friendly display names to API property names * 'Equipment Issues' → 'equipmentIssues' * 'Labor Issues' → 'laborIssues' * 'Materials Issues' → 'materialIssues' * Enables seamless translation between UI and API - Update fetchData function with display name mapping * Maps selected display names to API property names before sending * Uses ISSUE_TYPE_MAPPING for translation * Filters out undefined values with .filter(Boolean) * Only appends issueTypes param if valid mapped values exist * Prevents sending invalid or empty issue type filters - Add Issue Type Filter UI * Positioned in filtersRow after Date Range Filter * Uses react-select with isMulti for multiple selection * Options derived from FIXED_ISSUE_TYPES (not from API) * Controlled component synced with selectedIssueTypes state * onChange updates state with array of display names * Handles null/undefined gracefully (defaults to empty array) - Dark mode styling for Issue Type Filter * Control, menu, option backgrounds (#2c3344, #364156) * Selected state (#0d55b3), focused state (#364156) * Multi-value tags (#375071 dark, #e2e7ee light) * Hover states for remove buttons * Placeholder color (#aaaaaa) * Consistent with Project Filter styling - Filter integration * Placeholder: "Select Issue Types" * isClearable for easy deselection * No isDisabled prop (always available) * Triggers debounced API call via useEffect hook * Works seamlessly with other filters Accessibility features: - Proper label association (htmlFor="issue-type-filter") - Semantic HTML structure - Keyboard navigation support via react-select - Clear visual feedback for selection Design decisions: - Fixed types ensure consistency with chart structure - Display names provide better UX than camelCase property names - Mapping layer separates UI concerns from API concerns - No dependency on API /issues/types endpoint for filter options - Filter always shows three options regardless of data availability Implementation details: - Follows established react-select patterns in codebase - Matches styling of Project Filter for visual consistency - Integrates with existing debounced filter system - No new CSS classes required (reuses existing layout) Benefits: - UX: User-friendly display names instead of technical property names - Consistency: Filter options always match chart categories - Maintainability: Clear separation between display and API layers - Reliability: No dependency on external API for filter options - Integration: Works seamlessly with existing filter architecture --- .../IssuesBreakdownChart.jsx | 130 +++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx index 96404bb243..7aadadef00 100644 --- a/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx +++ b/src/components/BMDashboard/WeeklyProjectSummary/IssuesBreakdownChart.jsx @@ -20,6 +20,16 @@ const COLORS = { materialIssues: '#F3C13A', // yellow }; +// Fixed issue types for filter (display names) +const FIXED_ISSUE_TYPES = ['Equipment Issues', 'Labor Issues', 'Materials Issues']; + +// Map display names to API property names +const ISSUE_TYPE_MAPPING = { + 'Equipment Issues': 'equipmentIssues', + 'Labor Issues': 'laborIssues', + 'Materials Issues': 'materialIssues', +}; + export default function IssuesBreakdownChart() { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); @@ -183,7 +193,13 @@ export default function IssuesBreakdownChart() { } if (filters.issueTypes && filters.issueTypes.length > 0) { - params.append('issueTypes', filters.issueTypes.join(',')); + // Map display names to API property names + const apiIssueTypes = filters.issueTypes + .map(displayName => ISSUE_TYPE_MAPPING[displayName] || displayName) + .filter(Boolean); // Remove any undefined values + if (apiIssueTypes.length > 0) { + params.append('issueTypes', apiIssueTypes.join(',')); + } } const queryString = params.toString(); @@ -445,6 +461,118 @@ export default function IssuesBreakdownChart() { /> + +
+ +