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
15 changes: 0 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions src/actions/jobAnalytics/JobsHitsApplicationsActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { JOBS_APPS_HITS_REQUEST, JOBS_APPS_HITS_REQUEST_SUCCESS, JOBS_APPS_HITS_REQUEST_FAILURE } from '../../constants/jobAnalytics/JobsApplicationsHitsConstants';
import { ENDPOINTS } from '../../utils/URL';

export const fetchJobsHitsApplications = (queryParams, token) => async (dispatch) => {
dispatch({ type: JOBS_APPS_HITS_REQUEST });

try {
const response = await fetch(`${ENDPOINTS.JOB_HITS_AND_APPLICATIONS}?${queryParams}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': token
}
});

const data = await response.json();

if(!response.ok) {
throw new Error(data.error || 'Failed to fetch data');
}

dispatch({ type: JOBS_APPS_HITS_REQUEST_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: JOBS_APPS_HITS_REQUEST_FAILURE, payload: error.message });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { Fragment, useEffect, useState } from 'react';
import { fetchJobsHitsApplications } from '../../../actions/jobAnalytics/JobsHitsApplicationsActions';
import { useDispatch, useSelector } from 'react-redux';
import { BarChart, XAxis, YAxis, Tooltip, Legend, Bar, ResponsiveContainer, Brush } from 'recharts';
import styles from './JobsHitsApplicationsChart.module.css';
import Select from 'react-select';
import DatePicker from 'react-datepicker';

export const JobsHitsApplicationsChart = () => {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [selectedRoles, setSelectedRoles] = useState([]);
const [roleOptions, setRoleOptions] = useState([]);
const [roleAssigned, setRoleAssigned] = useState(false);

const { loading, data, error } = useSelector(state => state.jobsHitsApplications);
const darkMode = useSelector(state => state.theme.darkMode);
const dispatch = useDispatch();
const token = localStorage.getItem('token');

useEffect(() => {
const queryParams = new URLSearchParams();
if (startDate) queryParams.append('startDate', startDate.toISOString());
if (endDate) queryParams.append('endDate', endDate.toISOString());
if (selectedRoles.length > 0) {
const roles = selectedRoles.map(role => role.value).join(',');
queryParams.append('roles', roles);
}
dispatch(fetchJobsHitsApplications(queryParams.toString(), token));
}, [startDate, endDate, selectedRoles, dispatch, token]);

useEffect(() => {
if (data && data.length > 0 && !roleAssigned) {
setRoleOptions(
data.map(item => {
return {
value: item.role,
label: item.role,
};
}, setRoleAssigned(true)),
);
}
}, [loading, error, data]);

const CustomYAxisNames = ({ x, y, payload }) => {

Check warning on line 45 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move this component definition out of the parent component and pass data as props.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxXB&open=AZzmM4xZG01ghDwVIxXB&pullRequest=4325

Check warning on line 45 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'payload' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxXA&open=AZzmM4xZG01ghDwVIxXA&pullRequest=4325

Check warning on line 45 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'y' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxW_&open=AZzmM4xZG01ghDwVIxW_&pullRequest=4325

Check warning on line 45 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'x' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxW-&open=AZzmM4xZG01ghDwVIxW-&pullRequest=4325
const text = payload.value;

Check warning on line 46 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'payload.value' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxXC&open=AZzmM4xZG01ghDwVIxXC&pullRequest=4325
const extractedRole = text
.split('-')

Check warning on line 48 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'payload.value.split' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxXD&open=AZzmM4xZG01ghDwVIxXD&pullRequest=4325
.slice(0, 1)[0]
.trim();
return (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} textAnchor="end" fill="#666" fontSize={12}>
<title>{text}</title>
{extractedRole.length > 35 ? `${extractedRole.slice(0, 32)}...` : extractedRole}
</text>
</g>
);
};

const handleStartDateChange = date => {
if (endDate && date > endDate) {
setEndDate(date);
}
setStartDate(date);
};

const handleEndDateChange = date => {
if (startDate && date < startDate) {
setStartDate(date);
}
setEndDate(date);
};

const handleResetDates = () => {
setStartDate(null);
setEndDate(null);
};

return (
<Fragment>
<div className={`${styles.mainContainer} ${darkMode ? styles.bgOxfordBlue : ''}`}>
<h4 className={darkMode ? styles.colorWhite : ''}>Role-wise Hits and Applications</h4>
<div className={styles.filterContainer}>
<div className={styles.dateFilter}>
<div className={styles.dateReset}>
{startDate || endDate ? (
<button
onClick={handleResetDates}
className={`${styles.resetBtn} ${darkMode ? styles.resetBtnDark : ''}`}
>
Reset Dates
</button>
) : (
<button
className={`${styles.resetBtn} ${darkMode ? styles.resetBtnDark : ''}`}
disabled
>
Reset Dates
</button>
)}
</div>
<div className={styles.startDate}>
<label
htmlFor="start-date"
className={`${styles.dateName} ${darkMode ? styles.colorWhite : ''}`}
>
Start Date:
</label>
<DatePicker
id="start-date"
selected={startDate}
onChange={handleStartDateChange}
selectsStart
startDate={startDate}
endDate={endDate}
placeholderText="Start Date"
className={`${styles.datePicker} ${darkMode ? styles.bgYinmnBlue : ''}`}
/>
</div>
<div className={styles.endDate}>
<label
htmlFor="end-date"
className={`${styles.dateName} ${darkMode ? styles.colorWhite : ''}`}
>
End Date:
</label>
<DatePicker
id="end-date"
selected={endDate}
onChange={handleEndDateChange}
selectsEnd
startDate={startDate}
endDate={endDate}
placeholderText="End Date"
className={styles.datePicker}
/>
</div>
</div>

<div className={`${styles.roleFilter}`}>
<div className={styles.roleFilterContainer}>
<label htmlFor="role-select" className={darkMode ? styles.colorWhite : ''}>
Roles:
</label>
<Select
id="role-select"
isMulti
options={roleOptions}
onChange={setSelectedRoles}
placeholder="Select Roles"
className={styles.roleSelector}
/>
</div>
</div>
</div>
<div className={styles.chartContainer}>
{loading && <div className={`${styles.spinner}`}>Loading...</div>}
{error && <div className={`${styles.errorMessage}`}>Issue getting the data</div>}
{!loading && !error && data.length === 0 && (
<div className={`${styles.emptyMessage}`}>
No data available for the selected filters.
</div>
)}
{!loading && !error && data.length > 0 && (
<ResponsiveContainer className={styles.chart} width="100%" height="100%">
<BarChart
layout="vertical"
data={data}
barCategoryGap="40%"
barGap={4}
margin={{ top: 20, right: 30, left: 65, bottom: 20 }}
>
<Bar
dataKey="hits"
fill="#8884d8"
activeBar={false}
isAnimationActive={false}
barSize={14}
/>
<Bar
dataKey="applications"
fill="#82ca9d"
activeBar={false}
isAnimationActive={false}
barSize={14}
/>
<XAxis
type="number"
label={{
value: 'Number of Hits/Applications',
position: 'bottom',
style: {
fill: darkMode ? styles.colorWhite : '',
},
}}
/>
<YAxis
type="category"
dataKey="role"
label={{
value: 'Roles',
position: 'bottom',
style: {
fill: darkMode ? styles.colorWhite : '',
},
}}
width={200}
tick={<CustomYAxisNames />}
/>
<Tooltip cursor={{ fill: 'transparent' }} />
<Legend verticalAlign="top" align="center" />

{data.length > 7 && (
<Brush
dataKey="role"
height={20}
stroke="#8884d8"
startIndex={0}
endIndex={7}
y={480}
/>
)}
</BarChart>
</ResponsiveContainer>
)}
</div>
</div>
</Fragment>

Check warning on line 229 in src/components/JobAnalytics/JobsHitsApplicationsChart/JobsHitsApplicationsChart.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

A fragment with only one child is redundant.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZzmM4xZG01ghDwVIxXE&open=AZzmM4xZG01ghDwVIxXE&pullRequest=4325
);
};
Loading
Loading