Skip to content

Commit 6194b88

Browse files
author
Ayoub Baàli
committed
improved the date range selector and fixed #54 #60
1 parent 2c81645 commit 6194b88

File tree

3 files changed

+140
-101
lines changed

3 files changed

+140
-101
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React, { useState, useEffect } from "react";
2+
import moment from "moment";
3+
import { useSelector } from "react-redux";
4+
import DatePicker from "react-datepicker";
5+
import {
6+
readableRange,
7+
fromConverter,
8+
untilConverter,
9+
} from "../util/formatDate";
10+
11+
function CustomDatePicker({ setRange, dispatch, setDateRange }) {
12+
const from = useSelector((state) => state.from);
13+
const until = useSelector((state) => state.until);
14+
const [selectedDate, setSelectedDate] = useState({
15+
from: fromConverter(from).from,
16+
until: untilConverter(until).until,
17+
});
18+
const [warning, setWarning] = useState(false);
19+
const updateDateRange = () => {
20+
if (moment(selectedDate.from).isSameOrAfter(selectedDate.until)) {
21+
return setWarning(true);
22+
}
23+
dispatch(
24+
setDateRange(
25+
Math.round(selectedDate.from / 1000),
26+
Math.round(selectedDate.until / 1000)
27+
)
28+
);
29+
return setWarning(false);
30+
};
31+
32+
useEffect(() => {
33+
setSelectedDate({
34+
...selectedDate,
35+
from: fromConverter(from).from,
36+
until: untilConverter(until).until,
37+
});
38+
setRange(readableRange(from, until));
39+
}, [from, until]);
40+
return (
41+
<div>
42+
<h4>Custom Date Range</h4>
43+
<div className="form">
44+
<p style={{ marginBottom: "10px", color: "white" }}>From: </p>
45+
<DatePicker
46+
selected={selectedDate.from}
47+
onChange={(date) => {
48+
setSelectedDate({ ...selectedDate, from: date });
49+
}}
50+
selectsStart
51+
showTimeSelect
52+
startDate={selectedDate.from}
53+
endDate={selectedDate.until}
54+
dateFormat="yyyy-dd-MM hh:mm aa"
55+
/>
56+
</div>
57+
<div className="until">
58+
<p style={{ marginBottom: "10px", color: "white" }}>Until: </p>
59+
<DatePicker
60+
selected={selectedDate.until}
61+
onChange={(date) => {
62+
setSelectedDate({ ...selectedDate, until: date });
63+
}}
64+
selectsEnd
65+
showTimeSelect
66+
startDate={selectedDate.from}
67+
endDate={selectedDate.until}
68+
minDate={selectedDate.from}
69+
dateFormat="yyyy-dd-MM hh:mm aa"
70+
/>
71+
</div>
72+
{warning && <p style={{ color: "red" }}>Warning: invalid date Range</p>}
73+
74+
<button
75+
style={{
76+
marginTop: "20px",
77+
backgroundColor: "#2ECC40",
78+
color: "white",
79+
}}
80+
type="submit"
81+
className="btn"
82+
onClick={() => updateDateRange()}
83+
>
84+
Apply range
85+
</button>
86+
</div>
87+
);
88+
}
89+
90+
export default CustomDatePicker;

webapp/javascript/components/DateRangePicker.jsx

Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import React, { useState } from "react";
2-
import { useDispatch, useSelector } from "react-redux";
2+
import { useDispatch } from "react-redux";
33

44
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
55
import { faClock } from "@fortawesome/free-solid-svg-icons";
6-
import DatePicker from "react-datepicker";
76
import OutsideClickHandler from "react-outside-click-handler";
7+
import CustomDatePicker from "./CustomDatePicker";
88
import { setDateRange } from "../redux/actions";
9-
import humanReadableRange from "../util/formatDate";
109

1110
const defaultPresets = [
1211
[
@@ -30,24 +29,12 @@ const defaultPresets = [
3029
{ label: "Last 5 years", from: "now-5y", until: "now" },
3130
],
3231
];
32+
3333
function DateRangePicker() {
3434
const dispatch = useDispatch();
35-
const from = useSelector((state) => state.from);
36-
const until = useSelector((state) => state.until);
37-
const [opened, setOpened] = useState(false);
38-
const readableDateForm = humanReadableRange(until, from);
39-
40-
const updateFrom = (from) => {
41-
dispatch(setDateRange(from, until));
42-
};
43-
44-
const updateUntil = (until) => {
45-
dispatch(setDateRange(from, until));
46-
};
4735

48-
const updateDateRange = () => {
49-
dispatch(setDateRange(from, until));
50-
};
36+
const [opened, setOpened] = useState(false);
37+
const [range, setRange] = useState();
5138

5239
const toggleDropdown = () => {
5340
setOpened(!opened);
@@ -56,7 +43,6 @@ function DateRangePicker() {
5643
const hideDropdown = () => {
5744
setOpened(false);
5845
};
59-
6046
const selectPreset = ({ from, until }) => {
6147
dispatch(setDateRange(from, until));
6248
setOpened(false);
@@ -71,7 +57,7 @@ function DateRangePicker() {
7157
onClick={toggleDropdown}
7258
>
7359
<FontAwesomeIcon icon={faClock} />
74-
<span>{readableDateForm.range}</span>
60+
<span>{range}</span>
7561
</button>
7662
<div className="drp-dropdown">
7763
<h4>Quick Presets</h4>
@@ -82,7 +68,7 @@ function DateRangePicker() {
8268
<button
8369
type="button"
8470
className={`drp-preset ${
85-
x.label === readableDateForm.range ? "active" : ""
71+
x.label === range ? "active" : ""
8672
}`}
8773
key={x.label}
8874
onClick={() => selectPreset(x)}
@@ -93,43 +79,11 @@ function DateRangePicker() {
9379
</div>
9480
))}
9581
</div>
96-
<h4>Custom Date Range</h4>
97-
<div className="drp-calendar-input-group">
98-
<DatePicker
99-
className="followed-by-btn"
100-
showTimeSelect
101-
dateFormat="MMM d, yyyy h:mm aa"
102-
onChange={(date) => updateFrom(date / 1000)}
103-
onBlur={() => updateDateRange()}
104-
selected={readableDateForm.from}
105-
/>
106-
<button
107-
type="button"
108-
className="drp-calendar-btn btn"
109-
onClick={updateDateRange}
110-
>
111-
<FontAwesomeIcon icon={faClock} />
112-
Update
113-
</button>
114-
</div>
115-
<div className="drp-calendar-input-group">
116-
<DatePicker
117-
className="followed-by-btn"
118-
showTimeSelect
119-
dateFormat="MMM d, yyyy h:mm aa"
120-
onChange={(date) => updateUntil(date / 1000)}
121-
onBlur={() => updateDateRange()}
122-
selected={readableDateForm.until}
123-
/>
124-
<button
125-
type="button"
126-
className="drp-calendar-btn btn"
127-
onClick={updateDateRange}
128-
>
129-
<FontAwesomeIcon icon={faClock} />
130-
Update
131-
</button>
132-
</div>
82+
<CustomDatePicker
83+
setRange={setRange}
84+
dispatch={dispatch}
85+
setDateRange={setDateRange}
86+
/>
13387
</div>
13488
</OutsideClickHandler>
13589
</div>

webapp/javascript/util/formatDate.js

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,65 +11,60 @@ const multiplierMapping = {
1111
y: "year",
1212
};
1313

14-
function convertPresetsToDate(from) {
14+
export function convertPresetsToDate(from) {
1515
const { groups } = from.match(/^now-(?<number>\d+)(?<multiplier>\D+)$/);
1616
const { number, multiplier } = groups;
1717
let _multiplier = multiplierMapping[multiplier];
1818
if (number > 1) {
1919
_multiplier += "s";
2020
}
21-
// convert preset to a Date object
22-
const fromObjectForm = moment().add(-number, _multiplier).toDate();
21+
const _from = moment().add(-number, _multiplier).toDate() / 1000;
2322

24-
return { fromObjectForm, number, _multiplier };
23+
return { _from, number, _multiplier };
2524
}
2625

27-
function formateDate(from, until, preset = true) {
28-
const { fromObjectForm, number, _multiplier } = preset
29-
? convertPresetsToDate(from)
30-
: {};
31-
32-
/**
33-
* this default values are assinged depending on the state of the parameters from and until
34-
*/
35-
36-
const _from = Math.round(moment(fromObjectForm || from * 1000));
37-
const _until =
38-
until !== "now" ? Math.round(moment(until * 1000)) : Math.round(moment());
39-
40-
let readableDateForm = {
41-
range: `Last ${number} ${_multiplier}`,
42-
from: _from,
43-
until: _until,
44-
};
26+
export function readableRange(from, until) {
27+
const dateFormat = "YYYY-DD-MM hh:mm A";
28+
if (/^now-/.test(from) && until === "now") {
29+
const { number, _multiplier } = convertPresetsToDate(from);
30+
return `Last ${number} ${_multiplier}`;
31+
}
4532

46-
if (until === "now") {
47-
// check if from is not a preset
48-
if (!/^now-/.test(from)) {
49-
readableDateForm = {
50-
...readableDateForm,
51-
from: _from,
52-
range: `${moment(_from).format("lll")} - now`,
53-
};
54-
return readableDateForm;
55-
}
56-
return readableDateForm;
33+
if (until === "now" && !/^now-/.test(from)) {
34+
return `${moment(Math.round(from * 1000)).format(dateFormat)} - now`;
5735
}
5836

59-
readableDateForm = {
60-
...readableDateForm,
61-
range: `${moment(_from).format("lll")} - ${moment(_until).format("llll")}`,
62-
until: _until,
63-
};
37+
if (until !== "now" && /^now-/.test(from)) {
38+
const { _from } = convertPresetsToDate(from);
39+
return `${moment(Math.round(_from * 1000)).format(dateFormat)} - ${moment(
40+
Math.round(until * 1000)
41+
).format(dateFormat)}`;
42+
}
6443

65-
return readableDateForm;
44+
return `${moment(Math.round(from * 1000)).format(dateFormat)} - ${moment(
45+
Math.round(until * 1000)
46+
).format(dateFormat)}`;
6647
}
6748

68-
export default function humanReadableRange(until, from) {
49+
/**
50+
* taking state.from and state.until values
51+
* and converting it to a date object
52+
* this is neccessary because the DatePicker component
53+
* from react-datepicker package has a property (selected)
54+
* that requires an input of type Date if we passed another
55+
* type the Component will give back an error and the app will crash
56+
*/
57+
export function fromConverter(from) {
6958
if (/^now-/.test(from)) {
70-
return formateDate(from, until);
59+
const { _from } = convertPresetsToDate(from);
60+
return { from: _from * 1000 };
7161
}
62+
return { from: moment(from * 1000).toDate() };
63+
}
7264

73-
return formateDate(from, until, false);
74-
// return from + " to " +until;
65+
export function untilConverter(until) {
66+
if (until === "now") {
67+
return { until: moment().toDate() };
68+
}
69+
return { until: moment(until * 1000).toDate() };
7570
}

0 commit comments

Comments
 (0)