Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/actions/bmdashboard/injuryActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const RESET_BM_INJURY_DATA = 'RESET_BM_INJURY_DATA';
export const FETCH_BM_INJURY_SEVERITIES = 'FETCH_BM_INJURY_SEVERITIES';
export const FETCH_BM_INJURY_TYPES = 'FETCH_BM_INJURY_TYPES';
export const FETCH_BM_INJURY_PROJECTS = 'FETCH_BM_INJURY_PROJECTS';
export const FETCH_BM_INJURY_TREND_SUCCESS = 'FETCH_BM_INJURY_TREND_SUCCESS';
export const CREATE_BM_INJURY_SUCCESS = 'CREATE_BM_INJURY_SUCCESS';

// Helpers
const cleanParams = (obj = {}) => {
Expand Down Expand Up @@ -38,6 +40,8 @@ const setInjuryDataError = payload => ({ type: FETCH_BM_INJURY_DATA_FAILURE, pay
const setInjurySeverities = payload => ({ type: FETCH_BM_INJURY_SEVERITIES, payload });
const setInjuryTypes = payload => ({ type: FETCH_BM_INJURY_TYPES, payload });
const setInjuryProjects = payload => ({ type: FETCH_BM_INJURY_PROJECTS, payload });
export const setInjuryTrendSuccess = payload => ({ type: FETCH_BM_INJURY_TREND_SUCCESS, payload });
export const setCreateInjurySuccess = payload => ({ type: CREATE_BM_INJURY_SUCCESS, payload });

// Thunks
export const fetchInjuryData = (filters) => async dispatch => {
Expand Down Expand Up @@ -80,3 +84,30 @@ export const fetchInjuryProjects = (filters) => async dispatch => {
}
};

// Trend data: { months:[], serious:[], medium:[], low:[] }
export const fetchInjuryTrend = (filters) => async dispatch => {
dispatch(setInjuryDataLoading());
try {
const params = cleanParams(filters);
const res = await axios.get(ENDPOINTS.BM_INJURY_TREND, { params, paramsSerializer });
const data = res?.data && typeof res.data === 'object' ? res.data : { months: [], serious: [], medium: [], low: [] };
dispatch(setInjuryTrendSuccess(data));
} catch (error) {
const msg = error?.response?.data?.error || error?.message || 'Failed to fetch injury trend';
dispatch(setInjuryDataError(msg));
}
};

// Create injuries via API
export const createInjuries = (payload, { useDevSeed = false } = {}) => async dispatch => {
try {
const url = useDevSeed ? ENDPOINTS.BM_INJURY_DEV_SEED : ENDPOINTS.BM_INJURY_CREATE;
const res = await axios.post(url, payload);
dispatch(setCreateInjurySuccess(res?.data));
return res?.data;
} catch (error) {
const msg = error?.response?.data?.error || error?.message || 'Failed to create injuries';
throw new Error(msg);
}
};

122 changes: 122 additions & 0 deletions src/components/BMDashboard/InjuryTrendChart/InjuryTrendChart.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
.injury-dark-body {
background-color: #1B2A41 !important;
}

.injury-trend-container {
width: 100%;
height: 100%;
}

.injury-trend-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 12px;
}

.injury-trend-title {
margin: 10px;
font-size: 20px;
}

.injury-trend-filters {
display: grid;
grid-template-columns: repeat(2, minmax(240px, 1fr));
gap: 12px;
align-items: end;
}

.injury-trend-filters .filter-item {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1 1 0%;
}

/* Scoped input/select overrides ONLY for injury chart */
.injury-trend-container .filter-item input,
.injury-trend-container .filter-item select {
padding: 0 12px;
border: 1px solid #ddd;
border-radius: 8px;
width: 100%;
font-size: 1rem;
background: #ffffff;
transition: all 0.3s ease;
}

.injury-trend-chart-wrapper {
width: 100%;
height: 400px;
}

/* (Reverted) legend chip styles removed */

.injury-trend-container.darkMode .injury-select__control {
background-color: #222;
color: #ddd;
border-color: #2d3a4d;
min-height: 38px;
}

.injury-trend-container.darkMode .injury-select__single-value {
color: #ddd;
}

.injury-trend-container.darkMode .injury-select__menu {
background-color: #222;
color: #ddd;
}

/* dark page background for entire container */
.injury-trend-container.darkMode {
background-color: #1B2A41;
color: #cfd7e3;
}

.injury-trend-container.darkMode .injury-trend-title { color: #cfd7e3; }

/* Ensure project select and datepicker are equal width */
.injury-select__control,
.injury-datepicker {
width: 100%;
min-height: 38px;
}

/* Dark mode styling for the native datepicker input */
.injury-trend-container.darkMode .injury-datepicker {
background-color: #222 !important;
color: #ddd !important;
border: 1px solid #2d3a4d !important;
}

.injury-trend-container.darkMode .injury-datepicker::placeholder {
color: #bbb !important;
}

/* Dark mode version of scoped input/select overrides */
.injury-trend-container.darkMode .filter-item input,
.injury-trend-container.darkMode .filter-item select {
background: #222;
color: #ddd;
border-color: #2d3a4d;
}

/* react-datepicker popup in dark mode */
.injury-trend-container.darkMode .react-datepicker {
background-color: #222;
color: #ddd;
border: 1px solid #2d3a4d;
}
.injury-trend-container.darkMode .react-datepicker__header {
background-color: #2a3446;
border-bottom: 1px solid #2d3a4d;
}
.injury-trend-container.darkMode .react-datepicker__day-name,
.injury-trend-container.darkMode .react-datepicker__day,
.injury-trend-container.darkMode .react-datepicker__current-month {
color: #ddd;
}


Loading
Loading