Skip to content

Commit 30c50e0

Browse files
committed
Added time filter
1 parent 89393d6 commit 30c50e0

9 files changed

Lines changed: 222 additions & 9 deletions

File tree

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
"@testing-library/react": "^10.4.9",
107107
"@testing-library/user-event": "^12.0.14",
108108
"@types/dompurify": "^3.0.5",
109+
"@types/react-datepicker": "^7.0.0",
109110
"@types/react-router-dom": "^5.3.3",
110111
"babel-eslint": "^10.1.0",
111112
"cross-env": "^5.2.1",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
BarChart,
3+
Bar,
4+
XAxis,
5+
YAxis,
6+
CartesianGrid,
7+
Tooltip,
8+
ResponsiveContainer,
9+
LabelList,
10+
} from 'recharts';
11+
12+
function AgeChart({ data, compareLabel }) {
13+
const formatTooltip = (value, name, props) => {
14+
const { change } = props.payload;
15+
let changeText = '';
16+
if (compareLabel && change !== undefined) {
17+
if (change > 0) {
18+
changeText = `${change}% more than ${compareLabel}`;
19+
} else if (change < 0) {
20+
changeText = `${Math.abs(change)}% less than ${compareLabel}`;
21+
} else {
22+
changeText = `No change from ${compareLabel}`;
23+
}
24+
return [`${value} (${changeText})`, 'Applicants'];
25+
}
26+
return [`${value}`, 'Applicants'];
27+
};
28+
29+
return (
30+
<div style={{ width: '800px', height: 500, margin: '0 auto', padding: '20px' }}>
31+
<h2>Applicants grouped by Age</h2>
32+
<ResponsiveContainer width="100%" height="100%">
33+
<BarChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 20 }} barSize={80}>
34+
<CartesianGrid strokeDasharray="3 3" />
35+
<XAxis dataKey="ageGroup" />
36+
<YAxis />
37+
<Tooltip formatter={formatTooltip} />
38+
<Bar dataKey="applicants" fill="#3b82f6">
39+
<LabelList dataKey="applicants" position="top" />
40+
</Bar>
41+
</BarChart>
42+
</ResponsiveContainer>
43+
</div>
44+
);
45+
}
46+
47+
export default AgeChart;

src/components/ApplicantsAgeChart.jsx renamed to src/components/ApplicantsChart/ApplicantsAgeChart.jsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,39 @@ import {
1010
} from 'recharts';
1111

1212
const data = [
13-
{ ageGroup: '18 - 21', applicants: 25 },
14-
{ ageGroup: '21 - 24', applicants: 60 },
15-
{ ageGroup: '24 - 27', applicants: 45 },
16-
{ ageGroup: '27 - 30', applicants: 7 },
17-
{ ageGroup: '30 - 33', applicants: 10 },
13+
{ ageGroup: '18 - 21', applicants: 25, change: 10 },
14+
{ ageGroup: '21 - 24', applicants: 60, change: -5 },
15+
{ ageGroup: '24 - 27', applicants: 45, change: 15 },
16+
{ ageGroup: '27 - 30', applicants: 7, change: -3 },
17+
{ ageGroup: '30 - 33', applicants: 10, change: 0 },
1818
];
1919

2020
function ApplicantsChartPage() {
21+
const formatTooltip = (value, name, props) => {
22+
const { change } = props.payload;
23+
let changeText = '';
24+
if (change > 0) {
25+
changeText = `${change}% more than last week`;
26+
} else if (change < 0) {
27+
changeText = `${Math.abs(change)}% less than last week`;
28+
} else {
29+
changeText = `No change from last week`;
30+
}
31+
return [`${value} (${changeText})`, 'Applicants'];
32+
};
33+
2134
return (
22-
<div style={{ width: '100%', height: 500, padding: '20px' }}>
35+
<div style={{ width: '800px', height: 500, margin: '0 auto', padding: '20px' }}>
2336
<h2>Applicants grouped by Age</h2>
2437
<ResponsiveContainer width="100%" height="100%">
25-
<BarChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 20 }}>
38+
<BarChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 20 }} barSize={80}>
2639
<CartesianGrid strokeDasharray="3 3" />
2740
<XAxis
2841
dataKey="ageGroup"
2942
label={{ value: 'Age Group', position: 'insideBottom', offset: -10 }}
3043
/>
3144
<YAxis label={{ value: 'Applicants', angle: -90, position: 'insideLeft' }} />
32-
<Tooltip />
45+
<Tooltip formatter={formatTooltip} />
3346
<Bar dataKey="applicants" fill="#3b82f6">
3447
<LabelList dataKey="applicants" position="top" />
3548
</Bar>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useState, useEffect } from 'react';
2+
import DatePicker from 'react-datepicker';
3+
import 'react-datepicker/dist/react-datepicker.css';
4+
5+
function TimeFilter({ onFilterChange }) {
6+
const [selectedOption, setSelectedOption] = useState('weekly');
7+
const [startDate, setStartDate] = useState(null);
8+
const [endDate, setEndDate] = useState(null);
9+
10+
useEffect(() => {
11+
onFilterChange({ selectedOption, startDate, endDate });
12+
}, [selectedOption, startDate, endDate]);
13+
14+
return (
15+
<div
16+
style={{
17+
display: 'flex',
18+
justifyContent: 'center',
19+
alignItems: 'center',
20+
gap: '16px',
21+
margin: '20px auto',
22+
flexWrap: 'wrap',
23+
}}
24+
>
25+
<label htmlFor="timeFilterSelect" style={{ fontWeight: 500 }}>
26+
Time Filter:
27+
</label>
28+
<select
29+
id="timeFilterSelect"
30+
value={selectedOption}
31+
onChange={e => setSelectedOption(e.target.value)}
32+
style={{
33+
padding: '6px 12px',
34+
borderRadius: '4px',
35+
border: '1px solid #ccc',
36+
fontSize: '14px',
37+
}}
38+
>
39+
<option value="weekly">Weekly</option>
40+
<option value="monthly">Monthly</option>
41+
<option value="yearly">Yearly</option>
42+
<option value="custom">Custom Dates</option>
43+
</select>
44+
45+
{selectedOption === 'custom' && (
46+
<>
47+
<DatePicker
48+
selected={startDate}
49+
onChange={date => setStartDate(date)}
50+
placeholderText="Start Date"
51+
dateFormat="yyyy/MM/dd"
52+
style={{ marginRight: '10px' }}
53+
/>
54+
<span>to</span>
55+
<DatePicker
56+
selected={endDate}
57+
onChange={date => setEndDate(date)}
58+
placeholderText="End Date"
59+
dateFormat="yyyy/MM/dd"
60+
/>
61+
</>
62+
)}
63+
</div>
64+
);
65+
}
66+
67+
export default TimeFilter;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import sampleData from './data';
2+
3+
const fetchApplicantsData = filter =>
4+
new Promise(resolve => {
5+
setTimeout(() => {
6+
resolve(sampleData[filter.selectedOption]);
7+
}, 500);
8+
});
9+
10+
export default fetchApplicantsData;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const sampleData = {
2+
weekly: [
3+
{ ageGroup: '18 - 21', applicants: 25, change: 10 },
4+
{ ageGroup: '21 - 24', applicants: 60, change: -5 },
5+
{ ageGroup: '24 - 27', applicants: 45, change: 15 },
6+
{ ageGroup: '27 - 30', applicants: 7, change: -3 },
7+
{ ageGroup: '30 - 33', applicants: 10, change: 0 },
8+
],
9+
monthly: [
10+
{ ageGroup: '18 - 21', applicants: 100, change: 20 },
11+
{ ageGroup: '21 - 24', applicants: 150, change: -10 },
12+
{ ageGroup: '24 - 27', applicants: 130, change: 25 },
13+
{ ageGroup: '27 - 30', applicants: 30, change: -12 },
14+
{ ageGroup: '30 - 33', applicants: 40, change: 5 },
15+
],
16+
yearly: [
17+
{ ageGroup: '18 - 21', applicants: 1200, change: 5 },
18+
{ ageGroup: '21 - 24', applicants: 1400, change: 12 },
19+
{ ageGroup: '24 - 27', applicants: 1100, change: 8 },
20+
{ ageGroup: '27 - 30', applicants: 300, change: -10 },
21+
{ ageGroup: '30 - 33', applicants: 450, change: 15 },
22+
],
23+
custom: [
24+
{ ageGroup: '18 - 21', applicants: 200 },
25+
{ ageGroup: '21 - 24', applicants: 300 },
26+
{ ageGroup: '24 - 27', applicants: 250 },
27+
{ ageGroup: '27 - 30', applicants: 70 },
28+
{ ageGroup: '30 - 33', applicants: 100 },
29+
],
30+
};
31+
32+
export default sampleData;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useState, useEffect } from 'react';
2+
import TimeFilter from './TimeFilter';
3+
import AgeChart from './AgeChart';
4+
import fetchApplicantsData from './api';
5+
6+
function ApplicantsChart() {
7+
const [chartData, setChartData] = useState([]);
8+
const [compareLabel, setCompareLabel] = useState('last week');
9+
const [loading, setLoading] = useState(false);
10+
11+
const handleFilterChange = async filter => {
12+
setLoading(true);
13+
const data = await fetchApplicantsData(filter);
14+
setChartData(data);
15+
16+
setCompareLabel(
17+
filter.selectedOption === 'custom' ? null : `last ${filter.selectedOption.slice(0, -2)}`,
18+
);
19+
setLoading(false);
20+
};
21+
22+
useEffect(() => {
23+
handleFilterChange({ selectedOption: 'weekly' });
24+
}, []);
25+
26+
return (
27+
<div>
28+
<TimeFilter onFilterChange={handleFilterChange} />
29+
{loading ? <p>Loading...</p> : <AgeChart data={chartData} compareLabel={compareLabel} />}
30+
</div>
31+
);
32+
}
33+
34+
export default ApplicantsChart;

src/routes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import UnsubscribeForm from './components/EmailSubscribeForm/Unsubscribe';
4343
import NotFoundPage from './components/NotFound/NotFoundPage';
4444
import { EmailSender } from './components/common/EmailSender/EmailSender';
4545
import Collaboration from './components/Collaboration';
46-
import ApplicantsAgeChart from './components/ApplicantsAgeChart';
46+
import ApplicantsAgeChart from './components/ApplicantsChart';
4747

4848
// LB Dashboard
4949
import LBProtectedRoute from './components/common/LBDashboard/LBProtectedRoute';

0 commit comments

Comments
 (0)