From 73d7870aea3de9925669abb86babed999b48f483 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 7 Nov 2025 21:23:33 -0800
Subject: [PATCH 01/29] fix(bm-tools): handle new structured API response
format
- Extract data from structured response {success, data, message, ...}
- Add validation for success field in responses
- Display API message for empty results and errors
- Enhance error handling with specific messages for auth, network, and server errors
- Add console logging for debugging API failures
- Support backward compatibility with raw array responses
Fixes issue with component crashing when backend returns structured response format instead of raw arrays.
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 335 ++++++++++++++++++
1 file changed, 335 insertions(+)
create mode 100644 src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
new file mode 100644
index 0000000000..e9861e7979
--- /dev/null
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -0,0 +1,335 @@
+import { useState, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import Select from 'react-select';
+import DatePicker from 'react-datepicker';
+import 'react-datepicker/dist/react-datepicker.css';
+import { Row, Col, Button } from 'react-bootstrap';
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip as ChartTooltip,
+ Legend as ChartLegend,
+} from 'chart.js';
+import ChartDataLabels from 'chartjs-plugin-datalabels';
+import { Bar } from 'react-chartjs-2';
+import axios from 'axios';
+import { ENDPOINTS } from '../../../utils/URL';
+import styles from './ToolsStoppageHorizontalBarChart.module.css';
+
+// Register Chart.js components
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ ChartTooltip,
+ ChartLegend,
+ ChartDataLabels,
+);
+
+export default function ToolsStoppageHorizontalBarChart() {
+ const darkMode = useSelector(state => state.theme.darkMode);
+ const [projects, setProjects] = useState([]);
+ const [selectedProject, setSelectedProject] = useState(null);
+ const [dateRange, setDateRange] = useState([null, null]);
+ const [startDate, endDate] = dateRange;
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [data, setData] = useState([]);
+ const emptyData = [];
+
+ useEffect(() => {
+ const fetchProjects = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const response = await axios.get(ENDPOINTS.BM_TOOL_PROJECTS);
+ const responseData = response.data;
+
+ // Handle new structured response format
+ if (responseData.success === false) {
+ setError(responseData.message || 'Failed to load projects.');
+ setProjects([]);
+ return;
+ }
+
+ // Extract data array from structured response
+ const projectsData = responseData.data || responseData;
+ setProjects(Array.isArray(projectsData) ? projectsData : []);
+ } catch (err) {
+ console.error('Failed to load projects:', err);
+ if (err.response?.data?.message) {
+ setError(err.response.data.message);
+ } else if (err.response?.status === 401 || err.response?.status === 403) {
+ setError('Session expired. Please log in again.');
+ } else if (!err.response) {
+ setError('Network error. Please check your connection.');
+ } else {
+ setError('Failed to load projects. Please try again.');
+ }
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchProjects();
+ }, []);
+
+ useEffect(() => {
+ const fetchToolsStoppageData = async () => {
+ setLoading(true);
+ setError(null);
+ const formattedStart = startDate ? new Date(startDate).toISOString() : null;
+ const formattedEnd = endDate ? new Date(endDate).toISOString() : null;
+
+ try {
+ if (selectedProject) {
+ const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(
+ selectedProject?.value,
+ formattedStart,
+ formattedEnd,
+ );
+ const response = await axios.get(url);
+ const responseData = response.data;
+
+ // Handle new structured response format
+ if (responseData.success === false) {
+ setError(responseData.message || 'Failed to load stoppage data.');
+ setData(emptyData);
+ return;
+ }
+
+ // Extract data array from structured response
+ const stoppageData = responseData.data || responseData;
+
+ if (stoppageData && Array.isArray(stoppageData) && stoppageData.length > 0) {
+ const sortedData = [...stoppageData].map(item => ({
+ ...item,
+ name: item.toolName || item.name,
+ }));
+ setData(sortedData);
+ } else {
+ setData(emptyData);
+ // Use message from API if available
+ const message =
+ responseData.message || 'No tool stoppage reason data found for this project.';
+ setError(message);
+ }
+ } else if (projects.length > 0) {
+ const firstProject = projects[0];
+ setSelectedProject({ value: firstProject.projectId, label: firstProject.projectName });
+ const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(firstProject.projectId, null, null);
+ const response = await axios.get(url);
+ const responseData = response.data;
+
+ // Handle new structured response format
+ if (responseData.success === false) {
+ setError(responseData.message || 'Failed to load stoppage data.');
+ setData(emptyData);
+ return;
+ }
+
+ // Extract data array from structured response
+ const stoppageData = responseData.data || responseData;
+
+ if (stoppageData && Array.isArray(stoppageData) && stoppageData.length > 0) {
+ const sortedData = [...stoppageData].map(item => ({
+ ...item,
+ name: item.toolName || item.name,
+ }));
+ setData(sortedData);
+ } else {
+ setData(emptyData);
+ }
+ } else {
+ setData(emptyData);
+ }
+ } catch (err) {
+ console.error('Failed to load tools stoppage data:', err);
+ setData(emptyData);
+
+ // Enhanced error handling
+ if (err.response?.data?.message) {
+ setError(err.response.data.message);
+ } else if (err.response?.status === 401 || err.response?.status === 403) {
+ setError('Session expired. Please log in again.');
+ } else if (!err.response) {
+ setError('Network error. Please check your connection.');
+ } else if (err.response?.status >= 500) {
+ setError('Server error. Please try again later.');
+ } else {
+ setError('Failed to load tools stoppage reason data. Please try again.');
+ }
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchToolsStoppageData();
+ }, [selectedProject, startDate, endDate, projects]);
+
+ const projectOptions = projects.map(project => ({
+ value: project.projectId,
+ label: project.projectName,
+ }));
+
+ // Format date for display
+ const formatDate = date => date?.toISOString().split('T')[0];
+ const dateRangeLabel =
+ startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : '';
+
+ const selectDarkStyles = {
+ control: base => ({
+ ...base,
+ backgroundColor: '#2c3344',
+ borderColor: '#364156',
+ }),
+ menu: base => ({
+ ...base,
+ backgroundColor: '#2c3344',
+ }),
+ option: (base, state) => ({
+ ...base,
+ backgroundColor: state.isFocused ? '#364156' : '#2c3344',
+ color: '#e0e0e0',
+ }),
+ singleValue: base => ({
+ ...base,
+ color: '#e0e0e0',
+ }),
+ placeholder: base => ({
+ ...base,
+ color: '#aaaaaa',
+ }),
+ };
+
+ // ✅ Prepare Chart.js data
+ const chartData = {
+ labels: data.map(item =>
+ item.name.length > 20 ? `${item.name.substring(0, 18)}...` : item.name,
+ ),
+ datasets: [
+ {
+ label: 'Used its lifetime',
+ data: data.map(item => item.usedForLifetime || 0),
+ backgroundColor: '#4589FF',
+ barThickness: 30,
+ },
+ {
+ label: 'Damaged',
+ data: data.map(item => item.damaged || 0),
+ backgroundColor: '#FF0000',
+ barThickness: 30,
+ },
+ {
+ label: 'Lost',
+ data: data.map(item => item.lost || 0),
+ backgroundColor: '#FFB800',
+ barThickness: 30,
+ },
+ ],
+ };
+
+ // ✅ Chart.js options for horizontal stacked bars
+ const chartOptions = {
+ indexAxis: 'y',
+ maintainAspectRatio: false,
+ responsive: true,
+ plugins: {
+ legend: {
+ position: 'top',
+ labels: { color: darkMode ? '#e0e0e0' : '#000' },
+ },
+ tooltip: { enabled: true, color: darkMode ? '#FFFFFF' : '#000000' },
+ datalabels: {
+ display: true,
+ color: '#fff',
+ font: { weight: 'bold' },
+ },
+ },
+ scales: {
+ x: {
+ stacked: true,
+ grid: { color: darkMode ? '#364156' : '#e0e0e0' },
+ ticks: { color: darkMode ? '#e0e0e0' : '#000' },
+ },
+ y: {
+ title: { display: true, text: 'Tools', color: darkMode ? '#FFFFFF' : '#000000' },
+ stacked: true,
+ grid: { display: false },
+ ticks: { color: darkMode ? '#e0e0e0' : '#000' },
+ },
+ },
+ };
+
+ return (
+
+
+ Reason of Stoppage of Tools
+
+
+
+
+ {
+ setDateRange(update);
+ }}
+ placeholderText={dateRangeLabel || 'Filter by Date Range'}
+ className={`${styles.datePickerInput} form-control ${darkMode ? 'darkTheme' : ''}`}
+ calendarClassName={darkMode ? 'darkThemeCalendar' : 'customCalendar'}
+ />
+
+
+
+
+
+
+
+ {error &&
{error}
}
+ {loading &&
Loading tool availability data...
}
+
+ {!loading && selectedProject && data.length > 0 && (
+
+ )}
+
+ {!loading && selectedProject && data.length === 0 && (
+
+
No data available for the selected filters.
+
+ )}
+
+
+ );
+}
From b9ffc1c844f16816e24fee613e078cdc3c0a5a4a Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 7 Nov 2025 21:28:26 -0800
Subject: [PATCH 02/29] refactor(bm-tools): replace direct axios with
httpService wrapper
- Replace axios import with httpService for consistent auth handling
- Update all axios.get() calls to httpService.get() (3 locations)
- Enable automatic JWT token attachment to requests
- Enable automatic auth error interception (401/403 redirects)
- Align with codebase standard pattern used in other BM components
This ensures proper authentication handling and prevents intermittent failures when JWT tokens expire.
---
.../BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index e9861e7979..84e61c6468 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -15,7 +15,7 @@ import {
} from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { Bar } from 'react-chartjs-2';
-import axios from 'axios';
+import httpService from '../../../services/httpService';
import { ENDPOINTS } from '../../../utils/URL';
import styles from './ToolsStoppageHorizontalBarChart.module.css';
@@ -46,7 +46,7 @@ export default function ToolsStoppageHorizontalBarChart() {
setLoading(true);
setError(null);
try {
- const response = await axios.get(ENDPOINTS.BM_TOOL_PROJECTS);
+ const response = await httpService.get(ENDPOINTS.BM_TOOL_PROJECTS);
const responseData = response.data;
// Handle new structured response format
@@ -92,7 +92,7 @@ export default function ToolsStoppageHorizontalBarChart() {
formattedStart,
formattedEnd,
);
- const response = await axios.get(url);
+ const response = await httpService.get(url);
const responseData = response.data;
// Handle new structured response format
@@ -122,7 +122,7 @@ export default function ToolsStoppageHorizontalBarChart() {
const firstProject = projects[0];
setSelectedProject({ value: firstProject.projectId, label: firstProject.projectName });
const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(firstProject.projectId, null, null);
- const response = await axios.get(url);
+ const response = await httpService.get(url);
const responseData = response.data;
// Handle new structured response format
From 0a6d759edb0161f7ddd06e46d5d483d608e4c478 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 7 Nov 2025 21:34:19 -0800
Subject: [PATCH 03/29] refactor(bm-tools): split useEffect into auto-select
and data-fetch effects
- Create dedicated useEffect for auto-selecting first project
- Separate data-fetching useEffect with single responsibility
- Remove 'projects' from data-fetch dependencies (eliminates race condition)
- Add early return guard clause when no project selected
- Simplify logic by removing complex conditional branching
This follows React best practices (single responsibility principle) and eliminates the race condition that caused redundant API calls. The auto-select effect runs once when projects load, then the data-fetch effect runs when selectedProject changes.
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 100 ++++++++----------
1 file changed, 44 insertions(+), 56 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index 84e61c6468..501cbf32bc 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -78,74 +78,62 @@ export default function ToolsStoppageHorizontalBarChart() {
fetchProjects();
}, []);
+ // Auto-select first project when projects load
+ useEffect(() => {
+ if (!selectedProject && projects.length > 0) {
+ const firstProject = projects[0];
+ setSelectedProject({
+ value: firstProject.projectId,
+ label: firstProject.projectName,
+ });
+ }
+ }, [projects, selectedProject]);
+
+ // Fetch tools stoppage data when project or date filters change
useEffect(() => {
const fetchToolsStoppageData = async () => {
+ // Early return if no project selected
+ if (!selectedProject) {
+ setData(emptyData);
+ return;
+ }
+
setLoading(true);
setError(null);
const formattedStart = startDate ? new Date(startDate).toISOString() : null;
const formattedEnd = endDate ? new Date(endDate).toISOString() : null;
try {
- if (selectedProject) {
- const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(
- selectedProject?.value,
- formattedStart,
- formattedEnd,
- );
- const response = await httpService.get(url);
- const responseData = response.data;
-
- // Handle new structured response format
- if (responseData.success === false) {
- setError(responseData.message || 'Failed to load stoppage data.');
- setData(emptyData);
- return;
- }
-
- // Extract data array from structured response
- const stoppageData = responseData.data || responseData;
-
- if (stoppageData && Array.isArray(stoppageData) && stoppageData.length > 0) {
- const sortedData = [...stoppageData].map(item => ({
- ...item,
- name: item.toolName || item.name,
- }));
- setData(sortedData);
- } else {
- setData(emptyData);
- // Use message from API if available
- const message =
- responseData.message || 'No tool stoppage reason data found for this project.';
- setError(message);
- }
- } else if (projects.length > 0) {
- const firstProject = projects[0];
- setSelectedProject({ value: firstProject.projectId, label: firstProject.projectName });
- const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(firstProject.projectId, null, null);
- const response = await httpService.get(url);
- const responseData = response.data;
+ const url = ENDPOINTS.BM_TOOLS_STOPPAGE_BY_PROJECT(
+ selectedProject.value,
+ formattedStart,
+ formattedEnd,
+ );
+ const response = await httpService.get(url);
+ const responseData = response.data;
- // Handle new structured response format
- if (responseData.success === false) {
- setError(responseData.message || 'Failed to load stoppage data.');
- setData(emptyData);
- return;
- }
+ // Handle new structured response format
+ if (responseData.success === false) {
+ setError(responseData.message || 'Failed to load stoppage data.');
+ setData(emptyData);
+ return;
+ }
- // Extract data array from structured response
- const stoppageData = responseData.data || responseData;
+ // Extract data array from structured response
+ const stoppageData = responseData.data || responseData;
- if (stoppageData && Array.isArray(stoppageData) && stoppageData.length > 0) {
- const sortedData = [...stoppageData].map(item => ({
- ...item,
- name: item.toolName || item.name,
- }));
- setData(sortedData);
- } else {
- setData(emptyData);
- }
+ if (stoppageData && Array.isArray(stoppageData) && stoppageData.length > 0) {
+ const sortedData = [...stoppageData].map(item => ({
+ ...item,
+ name: item.toolName || item.name,
+ }));
+ setData(sortedData);
} else {
setData(emptyData);
+ // Use message from API if available
+ const message =
+ responseData.message || 'No tool stoppage reason data found for this project.';
+ setError(message);
}
} catch (err) {
console.error('Failed to load tools stoppage data:', err);
@@ -169,7 +157,7 @@ export default function ToolsStoppageHorizontalBarChart() {
};
fetchToolsStoppageData();
- }, [selectedProject, startDate, endDate, projects]);
+ }, [selectedProject, startDate, endDate]);
const projectOptions = projects.map(project => ({
value: project.projectId,
From 3f72f15d1abb1810a904f7e942ac771096e99822 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 7 Nov 2025 21:49:58 -0800
Subject: [PATCH 04/29] fix(bm-tools): enhance error handling with specific
categorized messages
- Add 500+ server error detection in projects fetch useEffect
- Include HTTP status codes in generic error fallbacks for debugging
- Maintain consistent error handling pattern across both useEffects
- Distinguish between auth (401/403), network, server (500+), and unknown errors
- Provide actionable error messages for each error category
Users now receive specific guidance (login, check network, wait for server) instead of generic error messages. Status codes help developers diagnose production issues.
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index 501cbf32bc..5f9cd93d8e 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -67,8 +67,13 @@ export default function ToolsStoppageHorizontalBarChart() {
setError('Session expired. Please log in again.');
} else if (!err.response) {
setError('Network error. Please check your connection.');
+ } else if (err.response?.status >= 500) {
+ setError('Server error. Please try again later.');
} else {
- setError('Failed to load projects. Please try again.');
+ setError(
+ `Failed to load projects. Please try again. (Status: ${err.response?.status ||
+ 'unknown'})`,
+ );
}
} finally {
setLoading(false);
@@ -149,7 +154,10 @@ export default function ToolsStoppageHorizontalBarChart() {
} else if (err.response?.status >= 500) {
setError('Server error. Please try again later.');
} else {
- setError('Failed to load tools stoppage reason data. Please try again.');
+ setError(
+ `Failed to load tools stoppage reason data. Please try again. (Status: ${err.response
+ ?.status || 'unknown'})`,
+ );
}
} finally {
setLoading(false);
From 3fc9fdeb39f8a003b46b69d70b46bc0aa30f76a8 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 7 Nov 2025 21:57:00 -0800
Subject: [PATCH 05/29] refactor(bm-tools): replace console.error with
logService for proper error tracking
- Import logService from services directory
- Replace console.error calls with logService.logError in both catch blocks
- Leverage Sentry integration for centralized error monitoring
- Eliminates ESLint warnings about console statements
- Aligns with codebase logging standards
Errors are now properly captured in production monitoring instead of just browser console.
---
.../BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index 5f9cd93d8e..b9e9fd770a 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -16,6 +16,7 @@ import {
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { Bar } from 'react-chartjs-2';
import httpService from '../../../services/httpService';
+import logService from '../../../services/logService';
import { ENDPOINTS } from '../../../utils/URL';
import styles from './ToolsStoppageHorizontalBarChart.module.css';
@@ -60,7 +61,7 @@ export default function ToolsStoppageHorizontalBarChart() {
const projectsData = responseData.data || responseData;
setProjects(Array.isArray(projectsData) ? projectsData : []);
} catch (err) {
- console.error('Failed to load projects:', err);
+ logService.logError(err);
if (err.response?.data?.message) {
setError(err.response.data.message);
} else if (err.response?.status === 401 || err.response?.status === 403) {
@@ -141,7 +142,7 @@ export default function ToolsStoppageHorizontalBarChart() {
setError(message);
}
} catch (err) {
- console.error('Failed to load tools stoppage data:', err);
+ logService.logError(err);
setData(emptyData);
// Enhanced error handling
From c970f70a05f1e6548a0bbbfff09a26f751224b1c Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 15:29:33 -0800
Subject: [PATCH 06/29] fix: prevent card stretching in Tools and Equipment
Tracking section
- Add align-items: start to grid container to prevent cards from stretching
- Change card min-heights from fixed 250px to auto for content-based sizing
- Cards now size to their content instead of matching tallest card height
---
.../WeeklyProjectSummary/WeeklyProjectSummary.module.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
index 2a9ad25ff0..36de700a50 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
@@ -306,7 +306,7 @@
.wideCard {
width: 100%;
- min-height: 250px;
+ min-height: auto; /* Let content determine height instead of forcing 250px */
grid-column: span 2;
}
@@ -320,6 +320,7 @@
max-width: 100%;
min-height: 250px;
box-sizing: border-box;
+ min-height: auto; /* Let content determine height instead of forcing 250px */
}
.mapCard {
@@ -359,6 +360,7 @@
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
+ align-items: start; /* Prevent cards from stretching to match tallest card */
border-top: 1px solid var(--card-shadow);
animation: fade-in 0.3s ease-in-out;
background: var(--section-bg);
From 8dc39924822c3414a750d0710c12d2ddeb336957 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 15:29:43 -0800
Subject: [PATCH 07/29] fix: prevent chart containers from forcing excessive
height
- Change ToolsHorizontalBarChart card height from 100% to auto
- Add height: auto and flex properties to donut chart wrapper
- Override tools-availability-page min-height 100vh to auto for card context
- Add styles for tools-horizontal-chart-container to prevent stretching
- Remove excessive padding from tools-availability-page wrapper
---
.../ToolStatusDonutChart.css | 114 ++++++++++++++++++
.../Tools/ToolsHorizontalBarChart.module.css | 6 +-
...ToolsStoppageHorizontalBarChart.module.css | 15 +++
3 files changed, 132 insertions(+), 3 deletions(-)
create mode 100644 src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
new file mode 100644
index 0000000000..cd93683818
--- /dev/null
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
@@ -0,0 +1,114 @@
+.tool-donut-wrapper {
+ --donut-text-color: #000;
+ --legend-text-color: #000;
+ width: 100%;
+ max-width: 400px;
+ margin: 0 auto;
+ text-align: center;
+ height: auto; /* Ensure wrapper doesn't stretch */
+ display: flex;
+ flex-direction: column; /* Stack content vertically */
+}
+
+.dark-mode.tool-donut-wrapper {
+ --donut-text-color: #fff;
+ --legend-text-color: #fff;
+}
+
+.tool-donut-title {
+ text-align: center;
+ font-weight: 600;
+ margin-bottom: 1.2rem;
+ font-size: 1.1rem;
+ color: var(--donut-text-color);
+}
+
+.tool-donut-filters {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 3rem;
+ margin-bottom: 1.5rem;
+}
+
+.filter-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ font-size: 14px;
+ text-align: center;
+}
+
+.filter-label {
+ font-weight: 600;
+ font-size: 14px;
+ color: var(--donut-text-color);
+}
+
+.filter-value {
+ font-size: 14px;
+ color: #888;
+ margin-top: 2px;
+ font-weight: 500;
+}
+
+.tool-donut-legend {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ margin-top: 1rem;
+ gap: 0.5rem;
+ width: 100%;
+}
+
+.tool-donut-legend-item {
+ color: #ffffff;
+ padding: 6px 16px;
+ border-radius: 0;
+ font-size: 14px;
+ font-weight: 500;
+ min-width: 140px;
+ text-align: center;
+}
+
+/* Responsive Donut Layout */
+.tools-tracking-layout {
+ display: flex;
+ flex-direction: row;
+ gap: 1rem;
+ justify-content: space-between;
+ align-items: stretch;
+ padding: 1rem;
+}
+
+.tools-donut-wrap {
+ flex: 0 0 60%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.tools-card-wrap {
+ flex: 0 0 40%;
+ background: #f9f9f9;
+ border-radius: 8px;
+ padding: 1rem;
+ font-size: 1rem;
+ font-weight: 500;
+ color: #333;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
+}
+
+@media (max-width: 768px) {
+ .tools-tracking-layout {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .tools-donut-wrap,
+ .tools-card-wrap {
+ flex: 1 1 100%;
+ max-width: 100%;
+ }
+}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
index 5a1a60d11b..46b4ecbfe9 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
@@ -5,12 +5,12 @@
background-color: var(--card-bg, #fff);
border-radius: 8px;
padding: 16px;
- height: 100%;
- box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
+ height: auto; /* Let card size to content instead of stretching to 100% */
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
transition: transform 0.2s ease;
- min-height: 280px;
+ min-height: 280px; /* Keep min-height for empty/loading states */
}
.tools-horizontal-bar-chart-card:hover {
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
index 2e78b44dff..0a56bcfa11 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
@@ -144,6 +144,21 @@
+/* Override min-height for tools-availability-page when used in card context */
+:global(.tools-availability-page) {
+ min-height: auto !important; /* Override 100vh from ToolsAvailabilityPage.css */
+ height: auto; /* Let content determine height */
+ padding: 8px; /* Remove excessive padding (24px) since card already has padding */
+}
+
+/* Chart container - prevent extra vertical space */
+:global(.tools-horizontal-chart-container) {
+ width: 100%;
+ height: auto; /* Ensure container doesn't force height */
+ display: flex;
+ flex-direction: column;
+}
+
/* Filter controls layout for small screens */
@media (width <= 768px) {
:global(.datepickerWrapper) {
From b2bdd6c68a08863e3637dc936e4a239f89bdae06 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 15:29:52 -0800
Subject: [PATCH 08/29] feat: standardize chart heights and improve bar spacing
- Reduce Reason of Stoppage chart height from 600px to 300px
- Add categoryPercentage (0.6) and barPercentage (0.9) to reduce bar spacing
- Replace barThickness with maxBarThickness (25px) for better scaling
- Standardize donut chart height to 300px on desktop, 280px on tablet
- Increase Tools by Availability chart height from 200px to 280px
- All charts now fit on screen without scrolling and have consistent sizing
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 10 ++++++----
.../ToolStatusDonutChart/ToolStatusDonutChart.jsx | 2 +-
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index b9e9fd770a..d32929c607 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -213,19 +213,19 @@ export default function ToolsStoppageHorizontalBarChart() {
label: 'Used its lifetime',
data: data.map(item => item.usedForLifetime || 0),
backgroundColor: '#4589FF',
- barThickness: 30,
+ maxBarThickness: 25, // Max thickness, allows scaling
},
{
label: 'Damaged',
data: data.map(item => item.damaged || 0),
backgroundColor: '#FF0000',
- barThickness: 30,
+ maxBarThickness: 25,
},
{
label: 'Lost',
data: data.map(item => item.lost || 0),
backgroundColor: '#FFB800',
- barThickness: 30,
+ maxBarThickness: 25,
},
],
};
@@ -258,6 +258,8 @@ export default function ToolsStoppageHorizontalBarChart() {
stacked: true,
grid: { display: false },
ticks: { color: darkMode ? '#e0e0e0' : '#000' },
+ categoryPercentage: 0.6, // Reduce spacing between categories (default 0.8)
+ barPercentage: 0.9, // Increase bar width within category (default 0.9)
},
},
};
@@ -318,7 +320,7 @@ export default function ToolsStoppageHorizontalBarChart() {
{loading && Loading tool availability data...
}
{!loading && selectedProject && data.length > 0 && (
-
+
)}
{!loading && selectedProject && data.length === 0 && (
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
index 4a595f35a9..9089bca3ac 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
@@ -214,7 +214,7 @@ export default function ToolStatusDonutChart() {
} else {
innerRadius = 35;
outerRadius = 60;
- chartHeight = 220;
+ chartHeight = 300;
}
const wrapperClass = `${styles.toolDonutWrapper} ${darkMode ? styles.toolDonutWrapperDark : ''}`;
From 246a3216bae802879b8c3da14d5727d6fe984604 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 16:38:02 -0800
Subject: [PATCH 09/29] feat: make Tools and Equipment Tracking section
responsive for mobile and tablet
- Add responsive grid layout: 1 column mobile, 2 columns tablet, 4 columns desktop
- Ensure cards span full width on mobile/tablet to prevent squishing
- Reduce spacing and padding on mobile to eliminate empty space
- Add width constraints (100%, max-width: 100%, box-sizing: border-box) to cards
- Ensure filter controls don't overflow on mobile screens
---
.../WeeklyProjectSummary.module.css | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
index 36de700a50..c51290dcdf 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
@@ -702,6 +702,19 @@
.weeklyProjectSummaryDashboardGrid {
grid-template-columns: repeat(2, 1fr);
}
+
+ /* Tools and Equipment Tracking - 2 column layout on tablet */
+ .weeklyProjectSummaryDashboardCategoryContent {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 15px;
+ padding: 12px;
+ }
+
+ /* Cards span full width on tablet */
+ .weeklyProjectSummaryDashboardCategoryContent .wideCard,
+ .weeklyProjectSummaryDashboardCategoryContent .normalCard {
+ grid-column: span 2;
+ }
}
/* Small Screens */
From 2340a6ed916c29f7909c742df9ecc9e86a6e31ce Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 16:38:12 -0800
Subject: [PATCH 10/29] feat: add responsive sizing to ToolsHorizontalBarChart
for mobile/tablet
- Add responsive margin calculation (mobile: {5,5,15,5}, tablet: {8,15,25,8}, desktop: {10,30,40,10})
- Add responsive Y-axis width (mobile: 20px, tablet: 28px, desktop: 35px)
- Add responsive font sizes for Y-axis ticks (mobile: 10px, tablet: 11px, desktop: 12px)
- Add width constraints to chart content container (100%, max-width: 100%)
- Add box-sizing: border-box to card container to prevent overflow
- Reduce padding and margins on mobile for better fit
---
.../Tools/ToolsHorizontalBarChart.jsx | 58 ++++++++++++++++++-
.../Tools/ToolsHorizontalBarChart.module.css | 31 +++++++++-
2 files changed, 84 insertions(+), 5 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.jsx
index 25d3101279..d80fb2b058 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.jsx
@@ -71,6 +71,7 @@ function ToolsHorizontalBarChart({ darkMode: darkModeProp }) {
const [allTools, setAllTools] = useState([]);
const [selectedTools, setSelectedTools] = useState([]);
const [isPreviewHovering, setIsPreviewHovering] = useState(false);
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const currentDate = new Date();
const startDate12MonthsAgo = new Date(currentDate.getFullYear() - 1, currentDate.getMonth(), 1);
@@ -79,6 +80,56 @@ function ToolsHorizontalBarChart({ darkMode: darkModeProp }) {
const [startDate, setStartDate] = useState(startDate12MonthsAgo.toISOString().split('T')[0]);
const [endDate, setEndDate] = useState(endOfCurrentMonth.toISOString().split('T')[0]);
+ // Responsive height calculation
+ useEffect(() => {
+ const handleResize = () => setWindowWidth(window.innerWidth);
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // Calculate responsive chart height: 240px mobile, 260px tablet, 280px desktop
+ const getChartHeight = () => {
+ if (windowWidth <= 768) {
+ return 240; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 260; // Tablet
+ }
+ return 280; // Desktop
+ };
+
+ // Calculate responsive margins: mobile {5,5,15,5}, tablet {8,15,25,8}, desktop {10,30,40,10}
+ const getChartMargins = () => {
+ if (windowWidth <= 768) {
+ return { top: 5, right: 5, left: 15, bottom: 5 }; // Mobile
+ } else if (windowWidth <= 1024) {
+ return { top: 8, right: 15, left: 25, bottom: 8 }; // Tablet
+ }
+ return { top: 10, right: 30, left: 40, bottom: 10 }; // Desktop
+ };
+
+ // Calculate responsive Y-axis width: mobile 20, tablet 28, desktop 35
+ const getYAxisWidth = () => {
+ if (windowWidth <= 768) {
+ return 20; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 28; // Tablet
+ }
+ return 35; // Desktop
+ };
+
+ // Calculate responsive font size: mobile 10, tablet 11, desktop 12
+ const getYAxisFontSize = () => {
+ if (windowWidth <= 768) {
+ return 10; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 11; // Tablet
+ }
+ return 12; // Desktop
+ };
+
+ // Date range logging removed for production
+
+ // Fetch projects list
useEffect(() => {
const fetchProjects = async () => {
try {
@@ -495,8 +546,11 @@ function ToolsHorizontalBarChart({ darkMode: darkModeProp }) {
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
index 46b4ecbfe9..c63d101415 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsHorizontalBarChart.module.css
@@ -11,6 +11,9 @@
flex-direction: column;
transition: transform 0.2s ease;
min-height: 280px; /* Keep min-height for empty/loading states */
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
}
.tools-horizontal-bar-chart-card:hover {
@@ -42,6 +45,8 @@
display: flex;
flex-direction: column;
min-height: 0;
+ width: 100%;
+ max-width: 100%;
}
.tools-horizontal-bar-chart-loading {
@@ -228,13 +233,19 @@
/* Responsive adjustments */
@media (width <= 768px) {
.tools-horizontal-bar-chart-card {
- padding: 12px;
- min-height: 250px;
+ padding: 10px;
+ min-height: auto; /* Remove fixed min-height to prevent empty space */
+ max-height: none; /* Allow content to determine height */
}
.tools-horizontal-bar-chart-title {
font-size: 14px;
- margin-bottom: 12px;
+ margin-bottom: 10px;
+ }
+
+ .tools-horizontal-bar-chart-filters {
+ padding: 8px 0;
+ margin-bottom: 10px;
}
.tools-horizontal-bar-chart-date-picker-group {
@@ -248,3 +259,17 @@
width: 100%;
}
}
+
+ .tools-horizontal-bar-chart-loading,
+ .tools-horizontal-bar-chart-error,
+ .tools-horizontal-bar-chart-empty {
+ height: 180px; /* Reduce height on mobile */
+ }
+
+/* Tablet adjustments */
+@media (min-width: 769px) and (max-width: 1024px) {
+ .tools-horizontal-bar-chart-card {
+ padding: 12px;
+ min-height: auto;
+ }
+}
From 7cef2c54e3d5a39ebd7eb27a2d6ad986caa0536d Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 16:38:30 -0800
Subject: [PATCH 11/29] feat: add responsive sizing to
ToolsStoppageHorizontalBarChart for mobile/tablet
- Add responsive Chart.js options: maxBarThickness, categoryPercentage, barPercentage
- Add responsive font sizes for scales, legend, and datalabels based on screen width
- Wrap Chart.js Bar component in constrained container div to prevent overflow
- Add Bootstrap Row/Col responsive fixes for mobile (full width, proper padding)
- Add width constraints (100%, max-width: 100%) to chart container
- Reduce title font size on mobile for better fit
- Ensure filter controls stack vertically and don't overflow on mobile
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 111 ++++++++++++++++--
...ToolsStoppageHorizontalBarChart.module.css | 64 ++++++++--
2 files changed, 153 insertions(+), 22 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index d32929c607..9208b51ff4 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -40,8 +40,76 @@ export default function ToolsStoppageHorizontalBarChart() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState([]);
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const emptyData = [];
+ // Responsive height calculation
+ useEffect(() => {
+ const handleResize = () => setWindowWidth(window.innerWidth);
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // Calculate responsive chart height: 240px mobile, 280px tablet, 300px desktop
+ const getChartHeight = () => {
+ if (windowWidth <= 768) {
+ return 240; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 280; // Tablet
+ }
+ return 300; // Desktop
+ };
+
+ // Calculate responsive maxBarThickness: mobile 20, tablet 22, desktop 25
+ const getMaxBarThickness = () => {
+ if (windowWidth <= 768) {
+ return 20; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 22; // Tablet
+ }
+ return 25; // Desktop
+ };
+
+ // Calculate responsive categoryPercentage: mobile 0.5, tablet 0.55, desktop 0.6
+ const getCategoryPercentage = () => {
+ if (windowWidth <= 768) {
+ return 0.5; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 0.55; // Tablet
+ }
+ return 0.6; // Desktop
+ };
+
+ // Calculate responsive barPercentage: mobile 0.85, tablet 0.87, desktop 0.9
+ const getBarPercentage = () => {
+ if (windowWidth <= 768) {
+ return 0.85; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 0.87; // Tablet
+ }
+ return 0.9; // Desktop
+ };
+
+ // Calculate responsive font size: mobile 10, tablet 11, desktop 12
+ const getFontSize = () => {
+ if (windowWidth <= 768) {
+ return 10; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 11; // Tablet
+ }
+ return 12; // Desktop
+ };
+
+ // Calculate responsive title font size: mobile 11, tablet 12, desktop 14
+ const getTitleFontSize = () => {
+ if (windowWidth <= 768) {
+ return 11; // Mobile
+ } else if (windowWidth <= 1024) {
+ return 12; // Tablet
+ }
+ return 14; // Desktop
+ };
+
useEffect(() => {
const fetchProjects = async () => {
setLoading(true);
@@ -203,7 +271,7 @@ export default function ToolsStoppageHorizontalBarChart() {
}),
};
- // ✅ Prepare Chart.js data
+ // Prepare Chart.js data with responsive bar thickness
const chartData = {
labels: data.map(item =>
item.name.length > 20 ? `${item.name.substring(0, 18)}...` : item.name,
@@ -213,24 +281,24 @@ export default function ToolsStoppageHorizontalBarChart() {
label: 'Used its lifetime',
data: data.map(item => item.usedForLifetime || 0),
backgroundColor: '#4589FF',
- maxBarThickness: 25, // Max thickness, allows scaling
+ maxBarThickness: getMaxBarThickness(),
},
{
label: 'Damaged',
data: data.map(item => item.damaged || 0),
backgroundColor: '#FF0000',
- maxBarThickness: 25,
+ maxBarThickness: getMaxBarThickness(),
},
{
label: 'Lost',
data: data.map(item => item.lost || 0),
backgroundColor: '#FFB800',
- maxBarThickness: 25,
+ maxBarThickness: getMaxBarThickness(),
},
],
};
- // ✅ Chart.js options for horizontal stacked bars
+ // Chart.js options for horizontal stacked bars with responsive settings
const chartOptions = {
indexAxis: 'y',
maintainAspectRatio: false,
@@ -238,28 +306,43 @@ export default function ToolsStoppageHorizontalBarChart() {
plugins: {
legend: {
position: 'top',
- labels: { color: darkMode ? '#e0e0e0' : '#000' },
+ labels: {
+ color: darkMode ? '#e0e0e0' : '#000',
+ font: { size: getFontSize() },
+ },
},
tooltip: { enabled: true, color: darkMode ? '#FFFFFF' : '#000000' },
datalabels: {
display: true,
color: '#fff',
- font: { weight: 'bold' },
+ font: { weight: 'bold', size: getFontSize() },
},
},
scales: {
x: {
stacked: true,
grid: { color: darkMode ? '#364156' : '#e0e0e0' },
- ticks: { color: darkMode ? '#e0e0e0' : '#000' },
+ ticks: {
+ color: darkMode ? '#e0e0e0' : '#000',
+ font: { size: getFontSize() },
+ maxRotation: 0,
+ },
},
y: {
- title: { display: true, text: 'Tools', color: darkMode ? '#FFFFFF' : '#000000' },
+ title: {
+ display: true,
+ text: 'Tools',
+ color: darkMode ? '#FFFFFF' : '#000000',
+ font: { size: getTitleFontSize() },
+ },
stacked: true,
grid: { display: false },
- ticks: { color: darkMode ? '#e0e0e0' : '#000' },
- categoryPercentage: 0.6, // Reduce spacing between categories (default 0.8)
- barPercentage: 0.9, // Increase bar width within category (default 0.9)
+ ticks: {
+ color: darkMode ? '#e0e0e0' : '#000',
+ font: { size: getFontSize() },
+ },
+ categoryPercentage: getCategoryPercentage(),
+ barPercentage: getBarPercentage(),
},
},
};
@@ -320,7 +403,9 @@ export default function ToolsStoppageHorizontalBarChart() {
{loading && Loading tool availability data...
}
{!loading && selectedProject && data.length > 0 && (
-
+
+
+
)}
{!loading && selectedProject && data.length === 0 && (
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
index 0a56bcfa11..875c71c31b 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
@@ -144,27 +144,60 @@
-/* Override min-height for tools-availability-page when used in card context */
-:global(.tools-availability-page) {
- min-height: auto !important; /* Override 100vh from ToolsAvailabilityPage.css */
- height: auto; /* Let content determine height */
- padding: 8px; /* Remove excessive padding (24px) since card already has padding */
-}
-
/* Chart container - prevent extra vertical space */
:global(.tools-horizontal-chart-container) {
width: 100%;
+ max-width: 100%;
height: auto; /* Ensure container doesn't force height */
display: flex;
flex-direction: column;
+ box-sizing: border-box;
+}
+
+/* Override min-height for tools-availability-page when used in card context */
+:global(.tools-availability-page) {
+ min-height: auto !important; /* Override 100vh from ToolsAvailabilityPage.css */
+ height: auto; /* Let content determine height */
+ padding: 8px; /* Remove excessive padding (24px) since card already has padding */
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
}
/* Filter controls layout for small screens */
-@media (width <= 768px) {
+@media (max-width: 768px) {
+ :global(.tools-availability-page) {
+ padding: 6px; /* Further reduce padding on mobile */
+ }
+
+ :global(.tools-chart-title) {
+ font-size: 0.9rem;
+ margin-bottom: 0.8rem;
+ }
+
+ /* Bootstrap Row/Col fixes for mobile */
+ :global(.tools-availability-page .row) {
+ width: 100%;
+ max-width: 100%;
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ :global(.tools-availability-page .col),
+ :global(.tools-availability-page [class*='col-']) {
+ width: 100%;
+ max-width: 100%;
+ padding-left: 8px;
+ padding-right: 8px;
+ margin-bottom: 8px;
+ }
+
:global(.datepickerWrapper) {
display: flex;
flex-direction: column;
align-items: stretch;
+ width: 100%;
+ max-width: 100%;
}
:global(.datepickerWrapper .btn) {
@@ -173,10 +206,23 @@
}
:global(.customSelect__control) {
- font-size: 1rem;
+ font-size: 10px;
+ width: 100%;
+ max-width: 100%;
}
:global(.customSelect__multi-value) {
font-size: 1rem;
}
}
+
+ :global(.tools-horizontal-chart-container) {
+ padding: 0;
+ }
+
+/* Tablet adjustments */
+@media (min-width: 769px) and (max-width: 1024px) {
+ :global(.tools-availability-page) {
+ padding: 10px;
+ }
+}
From 5a53b08f702bbfbc09f6dcb092d36a4a457c0e5c Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Tue, 11 Nov 2025 16:39:17 -0800
Subject: [PATCH 12/29] feat: add responsive sizing to ToolStatusDonutChart for
mobile/tablet
- Add responsive margin calculation (mobile: {20,20,20,20}, tablet: {25,25,30,30}, desktop: {30,30,40,40})
- Add responsive font size for center text (mobile: 10px, tablet: 12px, desktop: 14px)
- Remove max-width: 400px constraint and add width: 100%, max-width: 100%
- Add box-sizing: border-box to wrapper for proper sizing
- Reduce spacing and font sizes on mobile for better readability
- Ensure chart fits within viewport without overflow
---
.../ToolStatusDonutChart.css | 49 ++++++++++++++++++-
.../ToolStatusDonutChart.jsx | 5 +-
2 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
index cd93683818..92591361b7 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
@@ -2,12 +2,13 @@
--donut-text-color: #000;
--legend-text-color: #000;
width: 100%;
- max-width: 400px;
+ max-width: 100%;
margin: 0 auto;
text-align: center;
height: auto; /* Ensure wrapper doesn't stretch */
display: flex;
flex-direction: column; /* Stack content vertically */
+ box-sizing: border-box;
}
.dark-mode.tool-donut-wrapper {
@@ -111,4 +112,50 @@
flex: 1 1 100%;
max-width: 100%;
}
+
+ /* Mobile adjustments for donut chart */
+ .tool-donut-wrapper {
+ max-width: 100%;
+ padding: 0;
+ }
+
+ .tool-donut-title {
+ font-size: 1rem;
+ margin-bottom: 0.8rem;
+ }
+
+ .tool-donut-filters {
+ gap: 1.5rem;
+ margin-bottom: 1rem;
+ }
+
+ .filter-item {
+ font-size: 12px;
+ }
+
+ .filter-label {
+ font-size: 12px;
+ }
+
+ .tool-donut-legend {
+ margin-top: 0.8rem;
+ gap: 0.4rem;
+ }
+
+ .tool-donut-legend-item {
+ font-size: 12px;
+ padding: 5px 12px;
+ min-width: 120px;
+ }
+}
+
+/* Tablet adjustments */
+@media (min-width: 769px) and (max-width: 1024px) {
+ .tool-donut-wrapper {
+ max-width: 100%;
+ }
+
+ .tool-donut-filters {
+ gap: 2rem;
+ }
}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
index 9089bca3ac..251603feb0 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
@@ -13,8 +13,8 @@ const COLORS = {
const RADIAN = Math.PI / 180;
const renderCustomizedLabel = ({ cx, cy, midAngle, outerRadius, percent, width }) => {
- const isSmall = width <= 768;
- if (isSmall) return null;
+ // Hide labels on mobile/tablet for better readability
+ if (width <= 1024) return null;
const radius = outerRadius + 20;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
@@ -162,7 +162,6 @@ export default function ToolStatusDonutChart() {
return () => window.removeEventListener('resize', handleResize);
}, []);
- const isXS = windowWidth <= 480;
const chartData = availabilityData?.data || [];
const total = availabilityData?.total || 0;
From 781af06fafa989c46be2c3060b333921009f16e3 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Fri, 5 Dec 2025 13:55:05 -0800
Subject: [PATCH 13/29] fix: improve ToolsStoppageHorizontalBarChart background
and axis visibility
- Fix grey background in light mode: Add white background (#ffffff) to tools-availability-page and chart containers to match other charts in Tools and Equipment Tracking section
- Fix axis visibility in dark mode: Change axis border and tick colors from #e0e0e0 to #ffffff for better contrast and visibility
- Add !important flags to ensure background colors override parent card styles
- Add background styling to chart container wrapper divs to cover entire chart area including legend
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 23 ++++++++++++++++---
...ToolsStoppageHorizontalBarChart.module.css | 21 +++++++++++++++++
2 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index 9208b51ff4..bcacdf538d 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -322,8 +322,12 @@ export default function ToolsStoppageHorizontalBarChart() {
x: {
stacked: true,
grid: { color: darkMode ? '#364156' : '#e0e0e0' },
+ border: {
+ color: darkMode ? '#ffffff' : '#000000', // Make axis border visible in dark mode
+ width: 1,
+ },
ticks: {
- color: darkMode ? '#e0e0e0' : '#000',
+ color: darkMode ? '#ffffff' : '#000', // Brighter color in dark mode for better visibility
font: { size: getFontSize() },
maxRotation: 0,
},
@@ -337,8 +341,12 @@ export default function ToolsStoppageHorizontalBarChart() {
},
stacked: true,
grid: { display: false },
+ border: {
+ color: darkMode ? '#ffffff' : '#000000', // Make axis border visible in dark mode
+ width: 1,
+ },
ticks: {
- color: darkMode ? '#e0e0e0' : '#000',
+ color: darkMode ? '#ffffff' : '#000', // Brighter color in dark mode for better visibility
font: { size: getFontSize() },
},
categoryPercentage: getCategoryPercentage(),
@@ -403,7 +411,16 @@ export default function ToolsStoppageHorizontalBarChart() {
{loading && Loading tool availability data...
}
{!loading && selectedProject && data.length > 0 && (
-
+
)}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
index 875c71c31b..24c937b71f 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/Tools/ToolsStoppageHorizontalBarChart/ToolsStoppageHorizontalBarChart.module.css
@@ -152,6 +152,21 @@
display: flex;
flex-direction: column;
box-sizing: border-box;
+ background-color: #ffffff !important; /* White background to show through transparent canvas */
+}
+
+/* Dark mode for chart container */
+:global(.tools-availability-page.dark-mode .tools-horizontal-chart-container) {
+ background-color: #2c3344 !important;
+}
+
+/* Ensure Chart.js canvas wrapper has background */
+:global(.tools-horizontal-chart-container > div) {
+ background-color: #ffffff !important;
+}
+
+:global(.tools-availability-page.dark-mode .tools-horizontal-chart-container > div) {
+ background-color: #2c3344 !important;
}
/* Override min-height for tools-availability-page when used in card context */
@@ -162,6 +177,12 @@
width: 100%;
max-width: 100%;
box-sizing: border-box;
+ background-color: #ffffff !important; /* White background in light mode to prevent grey parent card background from showing through - !important to override any conflicting styles */
+}
+
+/* Dark mode background for tools-availability-page */
+:global(.tools-availability-page.dark-mode) {
+ background-color: #2c3344 !important; /* Dark background in dark mode */
}
/* Filter controls layout for small screens */
From b35adefbc7062abd779e55f446d9d4e8c9f22739 Mon Sep 17 00:00:00 2001
From: Aditya Gambhir <67105262+Aditya-gam@users.noreply.github.com>
Date: Sat, 6 Dec 2025 12:23:05 -0800
Subject: [PATCH 14/29] fix(tools-charts): improve responsive design and height
matching in Tools section
- Add responsive breakpoint at 1366px for better chart visibility on medium screens
- Standardize chart heights across all three Tools charts (240px/280px/300px)
- Fix height matching so donut chart determines section height, other charts adapt
- Remove conflicting dark mode CSS overrides for react-select dropdowns
- Standardize all dropdowns to use consistent react-select dark mode styles
- Convert ToolStatusDonutChart native selects to react-select with proper theming
- Fix ToolStatusDonutChart to use unfiltered toolslist for options to prevent disappearing selections
- Ensure all charts stretch or shrink to match donut chart's natural height
- Remove min-height constraints that prevented proper height matching
---
.../Tools/ToolsStoppageHorizontalBarChart.jsx | 84 ++++++-
.../ToolStatusDonutChart.css | 19 ++
.../ToolStatusDonutChart.jsx | 226 +++++++++++++-----
.../Tools/ToolsHorizontalBarChart.jsx | 7 +-
.../Tools/ToolsHorizontalBarChart.module.css | 4 +-
...ToolsStoppageHorizontalBarChart.module.css | 51 ++--
.../WeeklyProjectSummary.module.css | 49 +++-
7 files changed, 349 insertions(+), 91 deletions(-)
diff --git a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
index bcacdf538d..56b392c9b8 100644
--- a/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
+++ b/src/components/BMDashboard/Tools/ToolsStoppageHorizontalBarChart.jsx
@@ -246,28 +246,83 @@ export default function ToolsStoppageHorizontalBarChart() {
const dateRangeLabel =
startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : '';
- const selectDarkStyles = {
+ // Consistent react-select styles matching paid-labor-cost pattern
+ const selectStyles = {
control: base => ({
...base,
- backgroundColor: '#2c3344',
- borderColor: '#364156',
+ minHeight: '38px',
+ fontSize: '12px',
+ backgroundColor: darkMode ? '#253342' : '#fff',
+ borderColor: darkMode ? '#2d4059' : '#ccc',
+ color: darkMode ? '#ffffff' : '#000',
+ boxShadow: 'none',
+ borderRadius: '6px',
+ '&:hover': {
+ borderColor: darkMode ? '#2d4059' : '#999',
+ },
+ }),
+ valueContainer: base => ({
+ ...base,
+ padding: '2px 8px',
+ color: darkMode ? '#ffffff' : '#000',
+ }),
+ input: base => ({
+ ...base,
+ margin: '0px',
+ padding: '0px',
+ color: darkMode ? '#ffffff' : '#000',
+ }),
+ indicatorsContainer: base => ({
+ ...base,
+ padding: '0 4px',
}),
menu: base => ({
...base,
- backgroundColor: '#2c3344',
+ backgroundColor: darkMode ? '#253342' : '#fff',
+ fontSize: '12px',
}),
option: (base, state) => ({
...base,
- backgroundColor: state.isFocused ? '#364156' : '#2c3344',
- color: '#e0e0e0',
+ backgroundColor: state.isSelected
+ ? darkMode
+ ? '#e8a71c'
+ : '#0d55b3'
+ : state.isFocused
+ ? darkMode
+ ? '#3a506b'
+ : '#f0f0f0'
+ : darkMode
+ ? '#253342'
+ : '#fff',
+ color: state.isSelected ? (darkMode ? '#000' : '#fff') : darkMode ? '#ffffff' : '#000',
+ cursor: 'pointer',
+ padding: '8px 12px',
+ fontSize: '12px',
+ ':active': {
+ backgroundColor: darkMode ? '#3a506b' : '#e0e0e0',
+ },
}),
singleValue: base => ({
...base,
- color: '#e0e0e0',
+ color: darkMode ? '#ffffff' : '#000',
+ fontSize: '12px',
}),
placeholder: base => ({
...base,
- color: '#aaaaaa',
+ color: darkMode ? '#aaaaaa' : '#666',
+ fontSize: '12px',
+ }),
+ indicatorSeparator: base => ({
+ ...base,
+ backgroundColor: darkMode ? '#2d4059' : '#ccc',
+ }),
+ dropdownIndicator: base => ({
+ ...base,
+ color: darkMode ? '#ffffff' : '#999',
+ padding: '4px',
+ ':hover': {
+ color: darkMode ? '#ffffff' : '#666',
+ },
}),
};
@@ -389,7 +444,7 @@ export default function ToolsStoppageHorizontalBarChart() {
placeholder="Select a project ID to view data"
isClearable={false}
isDisabled={projects.length === 0}
- styles={darkMode ? selectDarkStyles : {}}
+ styles={selectStyles}
/>
@@ -415,13 +470,22 @@ export default function ToolsStoppageHorizontalBarChart() {
style={{
width: '100%',
maxWidth: '100%',
+ flex: 1,
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'stretch',
position: 'relative',
backgroundColor: darkMode ? '#2c3344' : '#ffffff',
borderRadius: '4px',
padding: '8px',
+ /* Allow container to shrink to match donut chart height */
+ minHeight: 0,
}}
>
-
+
+
+
)}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
index 92591361b7..528fdf13a0 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.css
@@ -22,6 +22,7 @@
margin-bottom: 1.2rem;
font-size: 1.1rem;
color: var(--donut-text-color);
+ flex-shrink: 0;
}
.tool-donut-filters {
@@ -30,6 +31,7 @@
flex-wrap: wrap;
gap: 3rem;
margin-bottom: 1.5rem;
+ flex-shrink: 0;
}
.filter-item {
@@ -38,12 +40,28 @@
align-items: center;
font-size: 14px;
text-align: center;
+ min-width: 200px;
+ width: 100%;
+ max-width: 100%;
}
.filter-label {
font-weight: 600;
font-size: 14px;
color: var(--donut-text-color);
+ margin-bottom: 4px;
+ width: 100%;
+}
+
+.tool-donut-select {
+ width: 100%;
+ max-width: 100%;
+}
+
+.tool-donut-chart-container {
+ flex-shrink: 0;
+ width: 100%;
+ margin: 0 auto;
}
.filter-value {
@@ -61,6 +79,7 @@
margin-top: 1rem;
gap: 0.5rem;
width: 100%;
+ flex-shrink: 0;
}
.tool-donut-legend-item {
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
index 251603feb0..1c0f38b70a 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/ToolStatusDonutChart/ToolStatusDonutChart.jsx
@@ -1,6 +1,7 @@
-import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
+import { useState, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import Select from 'react-select';
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts';
import { fetchToolAvailability, fetchTools } from '../../../../actions/bmdashboard/toolActions';
import styles from './ToolStatusDonutChart.module.css';
@@ -165,54 +166,157 @@ export default function ToolStatusDonutChart() {
const chartData = availabilityData?.data || [];
const total = availabilityData?.total || 0;
- // Check if we have no data for the selected combination
- const hasNoData = (toolId || projectId) && chartData.length === 0 && total === 0;
- const hasNoToolsMatch = total === 0;
-
- // Use the stored initial data for dropdowns, or fall back to current data
- const dropdownData = allToolsData || availabilityData;
- const toolsFromDropdown = dropdownData?.tools || [];
- const allAvailableTools =
- Array.isArray(toolsFromDropdown) && toolsFromDropdown.length
- ? toolsFromDropdown
- : toolslist || [];
-
- // Get all unique projects from the combined data
- const uniqueProjects = Array.from(
- new Map(
- allAvailableTools
- .filter(t => t?.projectId)
- .map(t => [t.projectId, { id: t.projectId, name: t.projectName || 'Unnamed Project' }]),
- ).values(),
+ // Use unfiltered toolslist for options to prevent options disappearing after selection
+ // availabilityData is only used for chart data, not for building dropdown options
+ const uniqueProjects = useMemo(
+ () =>
+ Array.from(
+ new Map(
+ toolslist
+ .filter(t => t?.projectId)
+ .map(t => [t.projectId, { id: t.projectId, name: t.projectName || 'Unnamed Project' }]),
+ ).values(),
+ ),
+ [toolslist],
);
- // Get all unique tools from the combined data
- const uniqueTools = Array.from(
- new Map(
- allAvailableTools
- .filter(t => t?.toolId)
- .map(t => [t.toolId, { id: t.toolId, name: t.name || 'Unnamed Tool' }]),
- ).values(),
+ const uniqueTools = useMemo(
+ () =>
+ Array.from(
+ new Map(
+ toolslist
+ .filter(t => t?.toolId)
+ .map(t => [t.toolId, { id: t.toolId, name: t.name || 'Unnamed Tool' }]),
+ ).values(),
+ ),
+ [toolslist],
);
- // Get the selected tool name
- const selectedTool = uniqueTools.find(tool => tool.id === toolId);
- const toolName = selectedTool ? selectedTool.name : null;
+ // Build react-select option lists
+ const projectOptions = useMemo(
+ () => [
+ { label: 'All', value: '' },
+ ...uniqueProjects.map(project => ({
+ label: project.name,
+ value: project.id,
+ })),
+ ],
+ [uniqueProjects],
+ );
+
+ const toolOptions = useMemo(
+ () => [
+ { label: 'All', value: '' },
+ ...uniqueTools.map(tool => ({
+ label: tool.name,
+ value: tool.id,
+ })),
+ ],
+ [uniqueTools],
+ );
+ // Consistent react-select styles matching paid-labor-cost pattern
+ const selectStyles = useMemo(
+ () => ({
+ control: base => ({
+ ...base,
+ minHeight: '38px',
+ fontSize: '12px',
+ backgroundColor: darkMode ? '#253342' : '#fff',
+ borderColor: darkMode ? '#2d4059' : '#ccc',
+ color: darkMode ? '#ffffff' : '#000',
+ boxShadow: 'none',
+ borderRadius: '6px',
+ '&:hover': {
+ borderColor: darkMode ? '#2d4059' : '#999',
+ },
+ }),
+ valueContainer: base => ({
+ ...base,
+ padding: '2px 8px',
+ color: darkMode ? '#ffffff' : '#000',
+ }),
+ input: base => ({
+ ...base,
+ margin: '0px',
+ padding: '0px',
+ color: darkMode ? '#ffffff' : '#000',
+ }),
+ indicatorsContainer: base => ({
+ ...base,
+ padding: '0 4px',
+ }),
+ menu: base => ({
+ ...base,
+ backgroundColor: darkMode ? '#253342' : '#fff',
+ fontSize: '12px',
+ }),
+ option: (base, state) => ({
+ ...base,
+ backgroundColor: state.isSelected
+ ? darkMode
+ ? '#e8a71c'
+ : '#0d55b3'
+ : state.isFocused
+ ? darkMode
+ ? '#3a506b'
+ : '#f0f0f0'
+ : darkMode
+ ? '#253342'
+ : '#fff',
+ color: state.isSelected ? (darkMode ? '#000' : '#fff') : darkMode ? '#ffffff' : '#000',
+ cursor: 'pointer',
+ padding: '8px 12px',
+ fontSize: '12px',
+ ':active': {
+ backgroundColor: darkMode ? '#3a506b' : '#e0e0e0',
+ },
+ }),
+ singleValue: base => ({
+ ...base,
+ color: darkMode ? '#ffffff' : '#000',
+ fontSize: '12px',
+ }),
+ placeholder: base => ({
+ ...base,
+ color: darkMode ? '#aaaaaa' : '#666',
+ fontSize: '12px',
+ }),
+ indicatorSeparator: base => ({
+ ...base,
+ backgroundColor: darkMode ? '#2d4059' : '#ccc',
+ }),
+ dropdownIndicator: base => ({
+ ...base,
+ color: darkMode ? '#ffffff' : '#999',
+ padding: '4px',
+ ':hover': {
+ color: darkMode ? '#ffffff' : '#666',
+ },
+ }),
+ }),
+ [darkMode],
+ );
+
+ // Responsive sizing standardized to match tallest chart: 240px mobile, 280px tablet, 300px desktop
let innerRadius;
let outerRadius;
let chartHeight;
- if (isXS) {
- innerRadius = 25;
- outerRadius = 40;
- chartHeight = 180;
- } else if (windowWidth <= 768) {
- innerRadius = 30;
- outerRadius = 50;
- chartHeight = 200;
+ const isSmall = windowWidth <= 768;
+ if (windowWidth <= 768) {
+ // Mobile
+ innerRadius = 40;
+ outerRadius = 65;
+ chartHeight = 240;
+ } else if (windowWidth <= 1024) {
+ // Tablet
+ innerRadius = 50;
+ outerRadius = 80;
+ chartHeight = 280;
} else {
- innerRadius = 35;
- outerRadius = 60;
+ // Desktop
+ innerRadius = 70;
+ outerRadius = 100;
chartHeight = 300;
}
@@ -226,32 +330,34 @@ export default function ToolStatusDonutChart() {
-
+