Skip to content
131 changes: 101 additions & 30 deletions src/actions/bmdashboard/injuryActions.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import axios from 'axios';
import {
FETCH_INJURIES_REQUEST,
FETCH_INJURIES_SUCCESS,
FETCH_INJURIES_FAILURE
} from './types';
import { FETCH_INJURIES_REQUEST, FETCH_INJURIES_SUCCESS, FETCH_INJURIES_FAILURE } from './types';
import { ENDPOINTS } from '../../utils/URL';

export const FETCH_BM_INJURY_DATA_REQUEST = 'FETCH_BM_INJURY_DATA_REQUEST';
Expand Down Expand Up @@ -38,6 +34,98 @@
return usp.toString();
};
const safeData = res => (Array.isArray(res?.data) ? res.data : res?.data?.data ?? []);
const normalizeSeverityBucket = category => {
const normalizedCategory = String(category || '').trim().toLowerCase();

if (['critical', 'severe', 'serious', 'major'].includes(normalizedCategory)) {
return 'serious';
}

if (['moderate', 'medium'].includes(normalizedCategory)) {
return 'medium';
}

if (['minor', 'low'].includes(normalizedCategory)) {
return 'low';
}

return null;
};

const getMonthlyRanges = (startDate, endDate) => {
const ranges = [];
const firstMonth = new Date(`${startDate}T00:00:00.000Z`);
const lastMonth = new Date(`${endDate}T00:00:00.000Z`);

firstMonth.setUTCDate(1);
lastMonth.setUTCDate(1);

let currentMonth = new Date(firstMonth);
while (currentMonth <= lastMonth) {

Check failure on line 64 in src/actions/bmdashboard/injuryActions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'lastMonth' is not modified in this loop.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ2abPdQ0EzrWDwS6W-p&open=AZ2abPdQ0EzrWDwS6W-p&pullRequest=5064

Check failure on line 64 in src/actions/bmdashboard/injuryActions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'currentMonth' is not modified in this loop.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ2abPdQ0EzrWDwS6W-o&open=AZ2abPdQ0EzrWDwS6W-o&pullRequest=5064
const monthStart = new Date(currentMonth);
const monthEnd = new Date(Date.UTC(monthStart.getUTCFullYear(), monthStart.getUTCMonth() + 1, 0));
const monthStartIso = monthStart.toISOString().slice(0, 10);
const monthEndIso = monthEnd.toISOString().slice(0, 10);

const rangeStart = startDate > monthStartIso ? startDate : monthStartIso;

Check warning on line 70 in src/actions/bmdashboard/injuryActions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `Math.max()` to simplify ternary expressions.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ2abPdQ0EzrWDwS6W-q&open=AZ2abPdQ0EzrWDwS6W-q&pullRequest=5064
const rangeEnd = endDate < monthEndIso ? endDate : monthEndIso;

Check warning on line 71 in src/actions/bmdashboard/injuryActions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `Math.min()` to simplify ternary expressions.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ2abPdQ0EzrWDwS6W-r&open=AZ2abPdQ0EzrWDwS6W-r&pullRequest=5064

ranges.push({
label: monthStart.toLocaleString('en-US', { month: 'short', year: 'numeric', timeZone: 'UTC' }),
startDate: rangeStart,
endDate: rangeEnd,
});

currentMonth.setUTCMonth(currentMonth.getUTCMonth() + 1);
}

return ranges;
};

const aggregateMonthlySeveritySeries = async (projectId, startDate, endDate) => {
const monthRanges = getMonthlyRanges(startDate, endDate);
const requests = monthRanges.map(({ startDate: monthStart, endDate: monthEnd }) => {
const params = {
startDate: monthStart,
endDate: monthEnd,
groupBy: 'severity',
};

if (projectId && projectId !== 'all') {
params.projectId = projectId;
}

return axios.get(`${ENDPOINTS.INJURIES}/distribution`, { params });
});

const responses = await Promise.all(requests);

return monthRanges.reduce(
(series, range, index) => {
const distribution = Array.isArray(responses[index]?.data?.distribution)
? responses[index].data.distribution
: [];

const counts = distribution.reduce(
(accumulator, item) => {
const bucket = normalizeSeverityBucket(item?.category);
if (bucket) {
accumulator[bucket] += Number(item?.count) || 0;
}
return accumulator;
},
{ serious: 0, medium: 0, low: 0 },
);

series.months.push(range.label);
series.serious.push(counts.serious);
series.medium.push(counts.medium);
series.low.push(counts.low);
return series;
},
{ months: [], serious: [], medium: [], low: [] },
);
};

// Action creators
export const resetInjuryData = () => ({ type: RESET_BM_INJURY_DATA });
Expand Down Expand Up @@ -133,30 +221,21 @@
dispatch({ type: FETCH_INJURIES_REQUEST });

try {
// Build query parameters
const params = {};
if (projectId && projectId !== 'all') {
params.projectId = projectId;
}
if (startDate) params.startDate = startDate;
if (endDate) params.endDate = endDate;

// API call
const response = await axios.get(ENDPOINTS.INJURIES, { params });
const data = await getInjuryData(projectId, startDate, endDate);

dispatch({
type: FETCH_INJURIES_SUCCESS,
payload: response.data
payload: data,
});

return response;
return data;
} catch (error) {
dispatch({
type: FETCH_INJURIES_FAILURE,
payload: {
message: error.response?.data?.message || 'Failed to fetch injury data',
status: error.response?.status
}
status: error.response?.status,
},
});

throw error;
Expand All @@ -165,17 +244,9 @@

// Function to get injury data (non-Redux version for direct component use)
export const getInjuryData = async (projectId, startDate, endDate) => {
// Build query parameters
const params = {};
if (projectId && projectId !== 'all') {
params.projectId = projectId;
if (!startDate || !endDate) {
return { months: [], serious: [], medium: [], low: [] };
}
if (startDate) params.startDate = startDate;
if (endDate) params.endDate = endDate;

// API call
const response = await axios.get(ENDPOINTS.INJURIES, { params });

// Return the data directly
return response.data;
return aggregateMonthlySeveritySeries(projectId, startDate, endDate);
};
14 changes: 9 additions & 5 deletions src/components/BMDashboard/InjuryChart/InjuryChartForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,13 @@ function InjuryChartForm({ dark }) {
{/* Filter Form */}
<div
className={`${styles.filterForm} mb-4 p-3 ${
dark ? styles.wrapperDark : 'bg-white'
dark ? styles.wrapperDark : styles.lightBackground
} rounded shadow-sm`}
>
<div className="row g-3">
<div className="col-md-4">
<FormGroup>
<Label for="project" className={dark ? styles.wrapperDark : ''}>
<Label for="project" htmlFor="project" className={dark ? styles.wrapperDark : ''}>
Project
</Label>
<Input id="project" type="select" value={projectId} onChange={handleProjectChange}>
Expand All @@ -140,7 +140,7 @@ function InjuryChartForm({ dark }) {
</FormGroup>
</div>

<div className="ol-md-4">
<div className="col-md-4">
<FormGroup>
<Label className={dark ? styles.wrapperDark : ''}>Start Date</Label>
<DatePicker
Expand Down Expand Up @@ -184,7 +184,7 @@ function InjuryChartForm({ dark }) {
{!error && chartData && chartData.length > 0 && (
<div
className={`${styles.injuryChartContainer} ${
dark ? styles.wrapperDark : 'bg-white'
dark ? styles.wrapperDark : styles.lightBackground
} p-4 rounded shadow-sm`}
>
<div className="d-flex justify-content-end mb-2">
Expand Down Expand Up @@ -276,7 +276,11 @@ function InjuryChartForm({ dark }) {

{/* No Data Display */}
{!error && !loading && (!chartData || chartData.length === 0) && (
<div className="text-center p-5 bg-white rounded shadow-sm">
<div
className={`text-center p-5 rounded shadow-sm ${
dark ? styles.wrapperDark : styles.lightBackground
}`}
>
<p className="text-muted">No injury data available for the selected criteria.</p>
</div>
)}
Expand Down
Loading