Skip to content

Commit 7246d32

Browse files
author
Ayoub Baàli
committed
Added a date and time picker
1 parent 171f921 commit 7246d32

File tree

5 files changed

+152
-56
lines changed

5 files changed

+152
-56
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"moment": "^2.27.0",
7272
"normalize.css": "^8.0.1",
7373
"react": "16.12.0",
74+
"react-datepicker": "^3.4.1",
7475
"react-dom": "^16.13.1",
7576
"react-flot": "^1.3.0",
7677
"react-keybind": "^0.8.1",

webapp/javascript/components/DateRangePicker.jsx

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { useDispatch, useSelector } from "react-redux";
33

44
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
55
import { faClock } from "@fortawesome/free-solid-svg-icons";
6-
6+
import DatePicker from "react-datepicker";
77
import OutsideClickHandler from "react-outside-click-handler";
8-
import moment from "moment";
98
import { setDateRange } from "../redux/actions";
9+
import humanReadableRange from "../util/formatDate";
1010

1111
const defaultPresets = [
1212
[
@@ -30,24 +30,12 @@ const defaultPresets = [
3030
{ label: "Last 5 years", from: "now-5y", until: "now" },
3131
],
3232
];
33-
34-
const multiplierMapping = {
35-
s: "second",
36-
m: "minute",
37-
h: "hour",
38-
d: "day",
39-
w: "week",
40-
M: "month",
41-
y: "year",
42-
};
43-
4433
function DateRangePicker() {
4534
const dispatch = useDispatch();
4635
const from = useSelector((state) => state.from);
4736
const until = useSelector((state) => state.until);
48-
4937
const [opened, setOpened] = useState(false);
50-
const [presets, setPresets] = useState(defaultPresets);
38+
const readableDateForm = humanReadableRange(until, from);
5139

5240
const updateFrom = (from) => {
5341
dispatch(setDateRange(from, until));
@@ -61,23 +49,6 @@ function DateRangePicker() {
6149
dispatch(setDateRange(from, until));
6250
};
6351

64-
const humanReadableRange = () => {
65-
if (until === "now") {
66-
const m = from.match(/^now-(?<number>\d+)(?<multiplier>\D+)$/);
67-
if (m && multiplierMapping[m.groups.multiplier]) {
68-
let multiplier = multiplierMapping[m.groups.multiplier];
69-
if (m.groups.number > 1) {
70-
multiplier += "s";
71-
}
72-
return `Last ${m.groups.number} ${multiplier}`;
73-
}
74-
}
75-
return `${moment(from * 1000).format("lll")}${moment(
76-
until * 1000
77-
).format("lll")}`;
78-
// return from + " to " +until;
79-
};
80-
8152
const toggleDropdown = () => {
8253
setOpened(!opened);
8354
};
@@ -94,19 +65,24 @@ function DateRangePicker() {
9465
return (
9566
<div className={opened ? "drp-container opened" : "drp-container"}>
9667
<OutsideClickHandler onOutsideClick={hideDropdown}>
97-
<button className="btn drp-button" onClick={toggleDropdown}>
68+
<button
69+
type="button"
70+
className="btn drp-button"
71+
onClick={toggleDropdown}
72+
>
9873
<FontAwesomeIcon icon={faClock} />
99-
<span>{humanReadableRange()}</span>
74+
<span>{readableDateForm.range}</span>
10075
</button>
10176
<div className="drp-dropdown">
10277
<h4>Quick Presets</h4>
10378
<div className="drp-presets">
104-
{presets.map((arr, i) => (
79+
{defaultPresets.map((arr, i) => (
10580
<div key={`preset-${i + 1}`} className="drp-preset-column">
10681
{arr.map((x) => (
10782
<button
83+
type="button"
10884
className={`drp-preset ${
109-
x.label === humanReadableRange() ? "active" : ""
85+
x.label === readableDateForm.range ? "active" : ""
11086
}`}
11187
key={x.label}
11288
onClick={() => selectPreset(x)}
@@ -119,25 +95,37 @@ function DateRangePicker() {
11995
</div>
12096
<h4>Custom Date Range</h4>
12197
<div className="drp-calendar-input-group">
122-
<input
98+
<DatePicker
12399
className="followed-by-btn"
124-
onChange={(e) => updateFrom(e.target.value)}
125-
onBlur={updateDateRange}
126-
value={from}
100+
showTimeSelect
101+
dateFormat="MMM d, yyyy h:mm aa"
102+
onChange={(date) => updateFrom(date / 1000)}
103+
onBlur={() => updateDateRange()}
104+
selected={readableDateForm.from}
127105
/>
128-
<button className="drp-calendar-btn btn" onClick={updateDateRange}>
106+
<button
107+
type="button"
108+
className="drp-calendar-btn btn"
109+
onClick={updateDateRange}
110+
>
129111
<FontAwesomeIcon icon={faClock} />
130112
Update
131113
</button>
132114
</div>
133115
<div className="drp-calendar-input-group">
134-
<input
116+
<DatePicker
135117
className="followed-by-btn"
136-
onChange={(e) => updateUntil(e.target.value)}
137-
onBlur={updateDateRange}
138-
value={until}
118+
showTimeSelect
119+
dateFormat="MMM d, yyyy h:mm aa"
120+
onChange={(date) => updateUntil(date / 1000)}
121+
onBlur={() => updateDateRange()}
122+
selected={readableDateForm.until}
139123
/>
140-
<button className="drp-calendar-btn btn" onClick={updateDateRange}>
124+
<button
125+
type="button"
126+
className="drp-calendar-btn btn"
127+
onClick={updateDateRange}
128+
>
141129
<FontAwesomeIcon icon={faClock} />
142130
Update
143131
</button>

webapp/javascript/util/formatDate.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import moment from "moment";
2+
3+
const multiplierMapping = {
4+
s: "second",
5+
m: "minute",
6+
h: "hour",
7+
d: "day",
8+
w: "week",
9+
M: "month",
10+
y: "year",
11+
};
12+
13+
export default function humanReadableRange(until, from) {
14+
if (until === "now" && typeof from === "string") {
15+
const m = from.match(/^now-(?<number>\d+)(?<multiplier>\D+)$/);
16+
if (m && multiplierMapping[m.groups.multiplier]) {
17+
let multiplier = multiplierMapping[m.groups.multiplier];
18+
if (m.groups.number > 1) {
19+
multiplier += "s";
20+
}
21+
const readableDateForm = {
22+
range: `Last ${m.groups.number} ${multiplier}`,
23+
from: moment().add(-m.groups.number, multiplier).toDate(),
24+
until: moment().toDate(),
25+
};
26+
return readableDateForm;
27+
}
28+
}
29+
const readableDateForm = {
30+
range: `${moment(from * 1000).format("lll")}${
31+
until === "now"
32+
? moment().format("lll")
33+
: moment(until * 1000).format("lll")
34+
}`,
35+
from: moment(from * 1000).toDate(),
36+
until: until === "now" ? moment().toDate() : moment(until * 1000).toDate(),
37+
};
38+
39+
return readableDateForm;
40+
// return from + " to " +until;
41+
}

webapp/sass/components/daterangepicker.scss

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
@use "../variables" as *;
22
@use "../mixins/outline" as *;
3+
/**
4+
* DatePicker Compenent styles
5+
* TODO: adjust styling
6+
*/
7+
@import "../../node_modules/react-datepicker/dist/react-datepicker.css";
8+
@import "../../node_modules/react-datepicker/dist/react-datepicker-cssmodules.css";
39

410
.drp-button {
511
white-space: nowrap;
@@ -21,18 +27,20 @@
2127
right: 0px;
2228
width: 400px;
2329
padding: 20px 20px 20px 20px;
24-
border: 1px solid rgb(75, 75, 75);
25-
box-shadow: 0 5px 20px rgba(0,0,0,0.9);
30+
border: 1px solid rgb(75, 75, 75);
31+
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.9);
2632
border-radius: 4px;
2733

2834
z-index: 1;
2935
}
3036

31-
h4:nth-child(1), h5:nth-child(1) {
37+
h4:nth-child(1),
38+
h5:nth-child(1) {
3239
margin-top: 0;
3340
}
3441

35-
h4, h5 {
42+
h4,
43+
h5 {
3644
margin: 10px 0;
3745
}
3846

@@ -53,10 +61,11 @@
5361
border: none;
5462
text-align: left;
5563
padding: 2px 0;
56-
color: rgba(255,255,255,0.66);
64+
color: rgba(255, 255, 255, 0.66);
5765

58-
&:hover, &.active {
59-
color: rgba(255,255,255,1);
66+
&:hover,
67+
&.active {
68+
color: rgba(255, 255, 255, 1);
6069
}
6170
}
6271

yarn.lock

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,14 @@ cosmiconfig@^5.0.0:
31363136
js-yaml "^3.13.1"
31373137
parse-json "^4.0.0"
31383138

3139+
create-react-context@^0.3.0:
3140+
version "0.3.0"
3141+
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c"
3142+
integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==
3143+
dependencies:
3144+
gud "^1.0.0"
3145+
warning "^4.0.3"
3146+
31393147
cross-spawn@^5.0.1:
31403148
version "5.1.0"
31413149
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -3275,6 +3283,11 @@ data-urls@^2.0.0:
32753283
whatwg-mimetype "^2.3.0"
32763284
whatwg-url "^8.0.0"
32773285

3286+
date-fns@^2.0.1:
3287+
version "2.16.1"
3288+
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
3289+
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
3290+
32783291
dateformat@^3.0.0:
32793292
version "3.0.3"
32803293
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@@ -3331,7 +3344,7 @@ decompress-response@^5.0.0:
33313344
dependencies:
33323345
mimic-response "^2.0.0"
33333346

3334-
deep-equal@^1.0.1:
3347+
deep-equal@^1.0.1, deep-equal@^1.1.1:
33353348
version "1.1.1"
33363349
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
33373350
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
@@ -4732,6 +4745,11 @@ growly@^1.3.0:
47324745
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
47334746
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
47344747

4748+
gud@^1.0.0:
4749+
version "1.0.0"
4750+
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
4751+
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
4752+
47354753
gzip-size@^6.0.0:
47364754
version "6.0.0"
47374755
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
@@ -7363,6 +7381,11 @@ pkg-dir@^5.0.0:
73637381
dependencies:
73647382
find-up "^5.0.0"
73657383

7384+
popper.js@^1.14.4:
7385+
version "1.16.1"
7386+
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
7387+
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
7388+
73667389
posix-character-classes@^0.1.0:
73677390
version "0.1.1"
73687391
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
@@ -7536,7 +7559,7 @@ prop-types-exact@^1.2.0:
75367559
object.assign "^4.1.0"
75377560
reflect.ownkeys "^0.2.0"
75387561

7539-
prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
7562+
prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
75407563
version "15.7.2"
75417564
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
75427565
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -7632,6 +7655,17 @@ randombytes@^2.1.0:
76327655
dependencies:
76337656
safe-buffer "^5.1.0"
76347657

7658+
react-datepicker@^3.4.1:
7659+
version "3.4.1"
7660+
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-3.4.1.tgz#3c8e8989f1ab31a767c17170a2d1318aa3c3b9ef"
7661+
integrity sha512-ASyVb7UmVx1vzeITidD7Cr/EXRXhKyjjbSkBndPc1MipYq4rqQ3eMFgvRQzpsXc3JmIMFgICm7nmN6Otc1GE/Q==
7662+
dependencies:
7663+
classnames "^2.2.6"
7664+
date-fns "^2.0.1"
7665+
prop-types "^15.7.2"
7666+
react-onclickoutside "^6.9.0"
7667+
react-popper "^1.3.4"
7668+
76357669
react-dom@^16.13.1:
76367670
version "16.13.1"
76377671
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
@@ -7681,6 +7715,11 @@ react-modal@^3.12.1:
76817715
react-lifecycles-compat "^3.0.0"
76827716
warning "^4.0.3"
76837717

7718+
react-onclickoutside@^6.9.0:
7719+
version "6.10.0"
7720+
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.10.0.tgz#05abb592575b08b4d129003494056b9dff46eeeb"
7721+
integrity sha512-7i2L3ef+0ILXpL6P+Hg304eCQswh4jl3ynwR71BSlMU49PE2uk31k8B2GkP6yE9s2D4jTGKnzuSpzWxu4YxfQQ==
7722+
76847723
react-outside-click-handler@^1.3.0:
76857724
version "1.3.0"
76867725
resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115"
@@ -7692,6 +7731,19 @@ react-outside-click-handler@^1.3.0:
76927731
object.values "^1.1.0"
76937732
prop-types "^15.7.2"
76947733

7734+
react-popper@^1.3.4:
7735+
version "1.3.7"
7736+
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324"
7737+
integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==
7738+
dependencies:
7739+
"@babel/runtime" "^7.1.2"
7740+
create-react-context "^0.3.0"
7741+
deep-equal "^1.1.1"
7742+
popper.js "^1.14.4"
7743+
prop-types "^15.6.1"
7744+
typed-styles "^0.0.7"
7745+
warning "^4.0.2"
7746+
76957747
react-redux@^7.2.1:
76967748
version "7.2.1"
76977749
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
@@ -9190,6 +9242,11 @@ type-fest@^0.8.1:
91909242
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
91919243
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
91929244

9245+
typed-styles@^0.0.7:
9246+
version "0.0.7"
9247+
resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9"
9248+
integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==
9249+
91939250
typedarray-to-buffer@^3.1.5:
91949251
version "3.1.5"
91959252
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
@@ -9360,7 +9417,7 @@ walker@^1.0.7, walker@~1.0.5:
93609417
dependencies:
93619418
makeerror "1.0.x"
93629419

9363-
warning@^4.0.3:
9420+
warning@^4.0.2, warning@^4.0.3:
93649421
version "4.0.3"
93659422
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
93669423
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==

0 commit comments

Comments
 (0)