Merge pull request #46 from prymitive/silence-form-tweaks

refactor(ui): refactor silence form
This commit is contained in:
Łukasz Mierzwa
2018-08-15 11:46:29 +01:00
committed by GitHub
11 changed files with 921 additions and 293 deletions

163
ui/package-lock.json generated
View File

@@ -202,14 +202,6 @@
}
}
},
"add-dom-event-listener": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.0.2.tgz",
"integrity": "sha1-j67SxBAIchzxEdodMNmVuFvkK+0=",
"requires": {
"object-assign": "4.1.1"
}
},
"address": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
@@ -2322,24 +2314,11 @@
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.0.tgz",
"integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ=="
},
"component-classes": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz",
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
"requires": {
"component-indexof": "0.0.3"
}
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-indexof": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
},
"compressible": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz",
@@ -2625,15 +2604,6 @@
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
},
"css-animation": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.4.1.tgz",
"integrity": "sha1-W4gTEl3g+7uwu+G0cq6EIhRpt6g=",
"requires": {
"babel-runtime": "6.26.0",
"component-classes": "1.2.6"
}
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@@ -3104,11 +3074,6 @@
"esutils": "2.0.2"
}
},
"dom-align": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.8.0.tgz",
"integrity": "sha512-B85D4ef2Gj5lw0rK0KM2+D5/pH7yqNxg2mB+E8uzFaolpm7RQmsxEfjyEuNiF8UBBkffumYDeKRzTzc3LePP+w=="
},
"dom-converter": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz",
@@ -6949,11 +6914,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -7002,11 +6962,6 @@
"integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=",
"dev": true
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
},
"lodash.isarray": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-4.0.0.tgz",
@@ -7022,23 +6977,6 @@
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
"integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"requires": {
"lodash._getnative": "3.9.1",
"lodash.isarguments": "3.1.0",
"lodash.isarray": "3.0.4"
},
"dependencies": {
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
}
}
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -9811,77 +9749,6 @@
}
}
},
"rc-align": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.3.tgz",
"integrity": "sha512-h5KgyB5IXYR7iKpYFcMr54cuQ2eozPCZ11kbXPG5+6CWvmyJ+c0R/yjndVndiNk2G3MKcTMbJNdDv5DIckLAxQ==",
"requires": {
"babel-runtime": "6.26.0",
"dom-align": "1.8.0",
"prop-types": "15.6.2",
"rc-util": "4.5.1"
}
},
"rc-animate": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.4.4.tgz",
"integrity": "sha512-DjJLTUQj7XKKcuS8cczN0uOLfuSmgrVXFGieP1SZc87xUUTFGh8B/KjNmEtlfvxkSrSuVfb2rrEPER4SqKUtEA==",
"requires": {
"babel-runtime": "6.26.0",
"css-animation": "1.4.1",
"prop-types": "15.6.2"
}
},
"rc-calendar": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.7.0.tgz",
"integrity": "sha512-067i3TC0H/6N6ZIhtbe9o/+F955n9nujNxn6Hoi6dJNr0LXc6F7305EI8N9k3zycx9JAliFQ1LF+MeFQwWRwpw==",
"requires": {
"babel-runtime": "6.26.0",
"classnames": "2.2.6",
"create-react-class": "15.6.3",
"moment": "2.22.2",
"prop-types": "15.6.2",
"rc-trigger": "2.5.4",
"rc-util": "4.5.1"
}
},
"rc-time-picker": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.4.0.tgz",
"integrity": "sha512-BgF0Fu/d36AK0h8jYBa01VWCm5vHWtYCh4DXBQhNazPLSH9hMP6JHLMJPSYMJ9jKttdE18O+F3j0mVQCL8JpDg==",
"requires": {
"babel-runtime": "6.26.0",
"classnames": "2.2.6",
"moment": "2.22.2",
"prop-types": "15.6.2",
"rc-trigger": "2.5.4"
}
},
"rc-trigger": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.5.4.tgz",
"integrity": "sha512-clgXOdazDW2qg4vTZSAExpvOuojPNuMoamG+SxAm5Ih+rpVcrtEiDlDZWY4yUHyfEWJZBzgbrr4np/z2FK6RfA==",
"requires": {
"babel-runtime": "6.26.0",
"classnames": "2.2.6",
"prop-types": "15.6.2",
"rc-align": "2.4.3",
"rc-animate": "2.4.4",
"rc-util": "4.5.1"
}
},
"rc-util": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.5.1.tgz",
"integrity": "sha512-PdCmHyBBodZdw6Oaikt0l+/R79IcRXpYkTrqD/Rbl4ZdoOi61t5TtEe40Q+A7rkWG5U1xjcN+h8j9H6GdtnICw==",
"requires": {
"add-dom-event-listener": "1.0.2",
"babel-runtime": "6.26.0",
"prop-types": "15.6.2",
"shallowequal": "0.2.2"
}
},
"react": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
@@ -9913,6 +9780,28 @@
"section-iterator": "2.0.0"
}
},
"react-datepicker": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-1.6.0.tgz",
"integrity": "sha512-wfFfw3Tn+CCTtP7PPPhSAfkfVT3UTifFb8BmuhtwWKK3g4S83EEPRQE18XFtxd22dKGqp336NVtDTTyUJ1p9+g==",
"requires": {
"classnames": "2.2.6",
"prop-types": "15.6.2",
"react-onclickoutside": "6.7.1",
"react-popper": "0.9.5"
},
"dependencies": {
"react-popper": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.9.5.tgz",
"integrity": "sha1-AqJO8+7DOvnlToNYq3DrDjMe3QU=",
"requires": {
"popper.js": "1.14.4",
"prop-types": "15.6.2"
}
}
}
},
"react-dev-utils": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz",
@@ -11006,14 +10895,6 @@
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.0.0.tgz",
"integrity": "sha1-UI0YOLPeWQq4dXsBGyXkMJAJRfc="
},
"shallowequal": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=",
"requires": {
"lodash.keys": "3.1.2"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",

View File

@@ -25,10 +25,9 @@
"prop-types": "15.6.2",
"qs": "6.5.2",
"raven-js": "3.26.4",
"rc-calendar": "9.7.0",
"rc-time-picker": "3.4.0",
"react": "16.4.2",
"react-autosuggest": "9.3.4",
"react-datepicker": "1.6.0",
"react-dom": "16.4.2",
"react-highlighter": "0.4.2",
"react-input-range": "1.3.0",

View File

@@ -0,0 +1,66 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleUp } from "@fortawesome/free-solid-svg-icons/faAngleUp";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons/faAngleDown";
const Duration = observer(
class Duration extends Component {
static propTypes = {
value: PropTypes.number.isRequired,
label: PropTypes.string,
onInc: PropTypes.func.isRequired,
onDec: PropTypes.func.isRequired
};
render() {
const { value, label, onInc, onDec } = this.props;
return (
<table>
<tbody>
<tr>
<td className="text-center">
<span onClick={onInc}>
<FontAwesomeIcon
icon={faAngleUp}
size="2x"
className="text-muted cursor-pointer"
/>
</span>
</td>
<td width="50%" />
</tr>
<tr>
<td className="text-center">
<h2>{value}</h2>
</td>
<td width="50%">
{label ? (
<span className="text-muted ml-2">{label}</span>
) : null}
</td>
</tr>
<tr>
<td className="text-center">
<span onClick={onDec}>
<FontAwesomeIcon
icon={faAngleDown}
size="2x"
className="text-muted cursor-pointer"
/>
</span>
</td>
<td width="50%" />
</tr>
</tbody>
</table>
);
}
}
);
export { Duration };

View File

@@ -0,0 +1,81 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { observer } from "mobx-react";
import moment from "moment";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleUp } from "@fortawesome/free-solid-svg-icons/faAngleUp";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons/faAngleDown";
const IconTd = ({ icon, onClick }) => (
<td>
<span onClick={onClick}>
<FontAwesomeIcon
icon={icon}
size="2x"
className="text-muted cursor-pointer"
/>
</span>
</td>
);
IconTd.propTypes = {
icon: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired
};
const HourMinute = observer(
class HourMinute extends Component {
static propTypes = {
dateValue: PropTypes.instanceOf(moment).isRequired,
onHourInc: PropTypes.func.isRequired,
onHourDec: PropTypes.func.isRequired,
onMinuteInc: PropTypes.func.isRequired,
onMinuteDec: PropTypes.func.isRequired
};
render() {
const {
dateValue,
onHourInc,
onHourDec,
onMinuteInc,
onMinuteDec
} = this.props;
const hour = dateValue.hour();
const minute = dateValue.minute();
return (
<table className="text-center border-0 my-auto">
<tbody>
<tr>
<IconTd icon={faAngleUp} onClick={onHourInc} />
<td />
<IconTd icon={faAngleUp} onClick={onMinuteInc} />
</tr>
<tr>
<td>
<h2>{hour > 9 ? hour : `0${hour}`}</h2>
</td>
<td>
<h2 className="mx-2">:</h2>
</td>
<td>
<h2>{minute > 9 ? minute : `0${minute}`}</h2>
</td>
</tr>
<tr>
<IconTd icon={faAngleDown} onClick={onHourDec} />
<td />
<IconTd icon={faAngleDown} onClick={onMinuteDec} />
</tr>
</tbody>
</table>
);
}
}
);
export { HourMinute };

View File

@@ -0,0 +1,484 @@
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
margin-left: -8px;
position: absolute; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow, .react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
box-sizing: content-box;
position: absolute;
border: 8px solid transparent;
height: 0;
width: 1px; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
content: "";
z-index: -1;
border-width: 8px;
left: -8px;
border-bottom-color: #dee2e6; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle {
top: 0;
margin-top: -8px; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before {
border-top: none;
border-bottom-color: #fff; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before {
top: -1px;
border-bottom-color: #dee2e6; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
bottom: 0;
margin-bottom: -8px; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
border-bottom: none;
border-top-color: #fff; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
bottom: -1px;
border-top-color: #dee2e6; }
.react-datepicker-wrapper {
display: inline-block; }
.react-datepicker {
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 0.9375rem;
background-color: #fff;
color: #000;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
display: inline-block;
position: relative; }
.react-datepicker--time-only .react-datepicker__triangle {
left: 35px; }
.react-datepicker--time-only .react-datepicker__time-container {
border-left: 0; }
.react-datepicker--time-only .react-datepicker__time {
border-radius: 0.3rem; }
.react-datepicker--time-only .react-datepicker__time-box {
border-radius: 0.3rem; }
.react-datepicker__triangle {
position: absolute;
left: 50px; }
.react-datepicker-popper {
z-index: 1; }
.react-datepicker-popper[data-placement^="bottom"] {
margin-top: 10px; }
.react-datepicker-popper[data-placement^="top"] {
margin-bottom: 10px; }
.react-datepicker-popper[data-placement^="right"] {
margin-left: 8px; }
.react-datepicker-popper[data-placement^="right"] .react-datepicker__triangle {
left: auto;
right: 42px; }
.react-datepicker-popper[data-placement^="left"] {
margin-right: 8px; }
.react-datepicker-popper[data-placement^="left"] .react-datepicker__triangle {
left: 42px;
right: auto; }
.react-datepicker__header {
text-align: center;
background-color: #fff;
border-bottom: 1px solid #dee2e6;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
padding-top: 8px;
position: relative; }
.react-datepicker__header--time {
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px; }
.react-datepicker__year-dropdown-container--select,
.react-datepicker__month-dropdown-container--select,
.react-datepicker__month-year-dropdown-container--select,
.react-datepicker__year-dropdown-container--scroll,
.react-datepicker__month-dropdown-container--scroll,
.react-datepicker__month-year-dropdown-container--scroll {
display: inline-block;
margin: 0 2px; }
.react-datepicker__current-month,
.react-datepicker-time__header {
margin-top: 0;
color: #000;
font-weight: bold;
font-size: 1.10625rem; }
.react-datepicker-time__header {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden; }
.react-datepicker__navigation {
background: none;
line-height: 1.7rem;
text-align: center;
cursor: pointer;
position: absolute;
top: 10px;
width: 0;
padding: 0;
border: 0.45rem solid transparent;
z-index: 1;
height: 10px;
width: 10px;
text-indent: -999em;
overflow: hidden; }
.react-datepicker__navigation--previous {
left: 10px;
border-right-color: #95a5a6; }
.react-datepicker__navigation--previous:hover {
border-right-color: #798d8f; }
.react-datepicker__navigation--previous--disabled, .react-datepicker__navigation--previous--disabled:hover {
border-right-color: #95a5a6;
cursor: default; }
.react-datepicker__navigation--next {
right: 10px;
border-left-color: #95a5a6; }
.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) {
right: 80px; }
.react-datepicker__navigation--next:hover {
border-left-color: #798d8f; }
.react-datepicker__navigation--next--disabled, .react-datepicker__navigation--next--disabled:hover {
border-left-color: #95a5a6;
cursor: default; }
.react-datepicker__navigation--years {
position: relative;
top: 0;
display: block;
margin-left: auto;
margin-right: auto; }
.react-datepicker__navigation--years-previous {
top: 4px;
border-top-color: #95a5a6; }
.react-datepicker__navigation--years-previous:hover {
border-top-color: #798d8f; }
.react-datepicker__navigation--years-upcoming {
top: -4px;
border-bottom-color: #95a5a6; }
.react-datepicker__navigation--years-upcoming:hover {
border-bottom-color: #798d8f; }
.react-datepicker__month-container {
float: left; }
.react-datepicker__month {
margin: 0.4rem;
text-align: center; }
.react-datepicker__time-container {
float: right;
border-left: 1px solid #dee2e6;
width: 70px; }
.react-datepicker__time-container--with-today-button {
display: inline;
border: 1px solid #aeaeae;
border-radius: 0.3rem;
position: absolute;
right: -72px;
top: 0; }
.react-datepicker__time-container .react-datepicker__time {
position: relative;
background: white; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box {
width: 70px;
overflow-x: hidden;
margin: 0 auto;
text-align: center; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list {
list-style: none;
margin: 0;
height: calc(195px + (1.7rem / 2));
overflow-y: scroll;
padding-right: 30px;
width: 100%;
box-sizing: content-box; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item {
padding: 5px 10px; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover {
cursor: pointer;
background-color: #fff; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected {
background-color: #2C3E50;
color: white;
font-weight: bold; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover {
background-color: #2C3E50; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled {
color: #95a5a6; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover {
cursor: default;
background-color: transparent; }
.react-datepicker__week-number {
color: #95a5a6;
display: inline-block;
width: 1.7rem;
line-height: 1.7rem;
text-align: center;
margin: 0.166rem; }
.react-datepicker__week-number.react-datepicker__week-number--clickable {
cursor: pointer; }
.react-datepicker__week-number.react-datepicker__week-number--clickable:hover {
border-radius: 0.25rem;
background-color: #fff; }
.react-datepicker__day-names,
.react-datepicker__week {
white-space: nowrap; }
.react-datepicker__day-name,
.react-datepicker__day,
.react-datepicker__time-name {
color: #000;
display: inline-block;
width: 1.7rem;
line-height: 1.7rem;
text-align: center;
margin: 0.166rem; }
.react-datepicker__day {
cursor: pointer; }
.react-datepicker__day:hover {
border-radius: 0.25rem;
background-color: #fff; }
.react-datepicker__day--today {
font-weight: bold; }
.react-datepicker__day--highlighted {
border-radius: 0.25rem;
background-color: #95a5a6;
color: #fff; }
.react-datepicker__day--highlighted:hover {
background-color: #87999a; }
.react-datepicker__day--highlighted-custom-1 {
color: magenta; }
.react-datepicker__day--highlighted-custom-2 {
color: green; }
.react-datepicker__day--selected, .react-datepicker__day--in-selecting-range, .react-datepicker__day--in-range {
border-radius: 0.25rem;
background-color: #2C3E50;
color: #fff; }
.react-datepicker__day--selected:hover, .react-datepicker__day--in-selecting-range:hover, .react-datepicker__day--in-range:hover {
background-color: #233140; }
.react-datepicker__day--keyboard-selected {
border-radius: 0.25rem;
background-color: #3e5871;
color: #fff; }
.react-datepicker__day--keyboard-selected:hover {
background-color: #233140; }
.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range) {
background-color: rgba(44, 62, 80, 0.5); }
.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range) {
background-color: #fff;
color: #000; }
.react-datepicker__day--disabled {
cursor: default;
color: #95a5a6; }
.react-datepicker__day--disabled:hover {
background-color: transparent; }
.react-datepicker__input-container {
position: relative;
display: inline-block; }
.react-datepicker__year-read-view,
.react-datepicker__month-read-view,
.react-datepicker__month-year-read-view {
border: 1px solid transparent;
border-radius: 0.25rem; }
.react-datepicker__year-read-view:hover,
.react-datepicker__month-read-view:hover,
.react-datepicker__month-year-read-view:hover {
cursor: pointer; }
.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow {
border-top-color: #798d8f; }
.react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
border-top-color: #95a5a6;
float: right;
margin-left: 20px;
top: 8px;
position: relative;
border-width: 0.45rem; }
.react-datepicker__year-dropdown,
.react-datepicker__month-dropdown,
.react-datepicker__month-year-dropdown {
background-color: #fff;
position: absolute;
width: 50%;
left: 25%;
top: 30px;
z-index: 1;
text-align: center;
border-radius: 0.25rem;
border: 1px solid #dee2e6; }
.react-datepicker__year-dropdown:hover,
.react-datepicker__month-dropdown:hover,
.react-datepicker__month-year-dropdown:hover {
cursor: pointer; }
.react-datepicker__year-dropdown--scrollable,
.react-datepicker__month-dropdown--scrollable,
.react-datepicker__month-year-dropdown--scrollable {
height: 150px;
overflow-y: scroll; }
.react-datepicker__year-option,
.react-datepicker__month-option,
.react-datepicker__month-year-option {
line-height: 20px;
width: 100%;
display: block;
margin-left: auto;
margin-right: auto; }
.react-datepicker__year-option:first-of-type,
.react-datepicker__month-option:first-of-type,
.react-datepicker__month-year-option:first-of-type {
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem; }
.react-datepicker__year-option:last-of-type,
.react-datepicker__month-option:last-of-type,
.react-datepicker__month-year-option:last-of-type {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem; }
.react-datepicker__year-option:hover,
.react-datepicker__month-option:hover,
.react-datepicker__month-year-option:hover {
background-color: #95a5a6; }
.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming,
.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,
.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming {
border-bottom-color: #798d8f; }
.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous,
.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,
.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous {
border-top-color: #798d8f; }
.react-datepicker__year-option--selected,
.react-datepicker__month-option--selected,
.react-datepicker__month-year-option--selected {
position: absolute;
left: 15px; }
.react-datepicker__close-icon {
background-color: transparent;
border: 0;
cursor: pointer;
outline: 0;
padding: 0;
vertical-align: middle;
position: absolute;
height: 16px;
width: 16px;
top: 25%;
right: 7px; }
.react-datepicker__close-icon::after {
background-color: #2C3E50;
border-radius: 50%;
bottom: 0;
box-sizing: border-box;
color: #fff;
content: "\00d7";
cursor: pointer;
font-size: 12px;
height: 16px;
width: 16px;
line-height: 1;
margin: -8px auto 0;
padding: 2px;
position: absolute;
right: 0px;
text-align: center; }
.react-datepicker__today-button {
background: #fff;
border-top: 1px solid #dee2e6;
cursor: pointer;
text-align: center;
font-weight: bold;
padding: 5px 0;
clear: left; }
.react-datepicker__portal {
position: fixed;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
left: 0;
top: 0;
justify-content: center;
align-items: center;
display: flex;
z-index: 2147483647; }
.react-datepicker__portal .react-datepicker__day-name,
.react-datepicker__portal .react-datepicker__day,
.react-datepicker__portal .react-datepicker__time-name {
width: 3rem;
line-height: 3rem; }
@media (max-width: 400px), (max-height: 550px) {
.react-datepicker__portal .react-datepicker__day-name,
.react-datepicker__portal .react-datepicker__day,
.react-datepicker__portal .react-datepicker__time-name {
width: 2rem;
line-height: 2rem; } }
.react-datepicker__portal .react-datepicker__current-month,
.react-datepicker__portal .react-datepicker-time__header {
font-size: 1.6875rem; }
.react-datepicker__portal .react-datepicker__navigation {
border: 0.81rem solid transparent; }
.react-datepicker__portal .react-datepicker__navigation--previous {
border-right-color: #95a5a6; }
.react-datepicker__portal .react-datepicker__navigation--previous:hover {
border-right-color: #798d8f; }
.react-datepicker__portal .react-datepicker__navigation--previous--disabled, .react-datepicker__portal .react-datepicker__navigation--previous--disabled:hover {
border-right-color: #95a5a6;
cursor: default; }
.react-datepicker__portal .react-datepicker__navigation--next {
border-left-color: #95a5a6; }
.react-datepicker__portal .react-datepicker__navigation--next:hover {
border-left-color: #798d8f; }
.react-datepicker__portal .react-datepicker__navigation--next--disabled, .react-datepicker__portal .react-datepicker__navigation--next--disabled:hover {
border-left-color: #95a5a6;
cursor: default; }
.react-datepicker__day:not(.react-datepicker__day--disabled) {
border-width: 1px;
border-style: solid;
border-color: transparent; }
.react-datepicker__day:hover:not(.react-datepicker__day--disabled) {
border-color: #dee2e6; }
.react-datepicker__today-button {
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem; }

View File

@@ -0,0 +1,181 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { observable, action } from "mobx";
import { observer } from "mobx-react";
import moment from "moment";
import DatePicker from "react-datepicker";
import { Duration } from "./Duration";
import { HourMinute } from "./HourMinute";
import "./index.css";
const Tab = ({ title, active, onClick }) => (
<li className="nav-item">
<a
className={`nav-link cursor-pointer ${
active ? "active text-white" : "text-primary"
}`}
onClick={onClick}
>
{title}
</a>
</li>
);
Tab.propTypes = {
title: PropTypes.node.isRequired,
active: PropTypes.bool,
onClick: PropTypes.func.isRequired
};
const TabNames = Object.freeze({
Start: "start",
End: "end",
Duration: "duration"
});
const TabContentStart = observer(({ silenceFormStore }) => {
return (
<div className="d-flex flex-sm-row flex-column justify-content-around mx-3 mt-2 ">
<DatePicker
inline
todayButton={"Now"}
minDate={moment()}
selected={silenceFormStore.data.startsAt}
onChange={val => {
silenceFormStore.data.startsAt = moment(val);
silenceFormStore.data.verifyStarEnd();
}}
/>
<HourMinute
dateValue={silenceFormStore.data.startsAt}
onHourInc={() => silenceFormStore.data.incStart(60)}
onHourDec={() => silenceFormStore.data.decStart(60)}
onMinuteInc={() => silenceFormStore.data.incStart(1)}
onMinuteDec={() => silenceFormStore.data.decStart(1)}
/>
</div>
);
});
const TabContentEnd = observer(({ silenceFormStore }) => {
return (
<div className="d-flex flex-sm-row flex-column justify-content-around mx-3 mt-2 ">
<DatePicker
inline
todayButton={"Now"}
minDate={moment()}
selected={silenceFormStore.data.endsAt}
onChange={val => {
silenceFormStore.data.endsAt = moment(val);
silenceFormStore.data.verifyStarEnd();
}}
/>
<HourMinute
dateValue={silenceFormStore.data.endsAt}
onHourInc={() => silenceFormStore.data.incEnd(60)}
onHourDec={() => silenceFormStore.data.decEnd(60)}
onMinuteInc={() => silenceFormStore.data.incEnd(1)}
onMinuteDec={() => silenceFormStore.data.decEnd(1)}
/>
</div>
);
});
const TabContentDuration = observer(({ silenceFormStore }) => {
return (
<div className="d-flex flex-sm-row flex-column justify-content-around mt-2 mx-3">
<Duration
label="days"
value={silenceFormStore.data.toDuration.days}
onInc={() => silenceFormStore.data.incDuration(60 * 24)}
onDec={() => silenceFormStore.data.decDuration(60 * 24)}
/>
<Duration
label="hours"
value={silenceFormStore.data.toDuration.hours}
onInc={() => silenceFormStore.data.incDuration(60)}
onDec={() => silenceFormStore.data.decDuration(60)}
/>
<Duration
label="minutes"
value={silenceFormStore.data.toDuration.minutes}
onInc={() => silenceFormStore.data.incDuration(1)}
onDec={() => silenceFormStore.data.decDuration(1)}
/>
</div>
);
});
TabContentDuration.propTypes = {
silenceFormStore: PropTypes.object.isRequired
};
const DateTimeSelect = observer(
class DateTimeSelect extends Component {
static propTypes = {
silenceFormStore: PropTypes.object.isRequired
};
tab = observable(
{
current: TabNames.Duration,
setStart() {
this.current = TabNames.Start;
},
setEnd() {
this.current = TabNames.End;
},
setDuration() {
this.current = TabNames.Duration;
}
},
{
setStart: action.bound,
setEnd: action.bound,
setDuration: action.bound
}
);
render() {
const { silenceFormStore } = this.props;
return (
<React.Fragment>
<ul className="nav nav-tabs nav-fill">
<Tab
title="Start"
active={this.tab.current === TabNames.Start}
onClick={this.tab.setStart}
/>
<Tab
title="End"
active={this.tab.current === TabNames.End}
onClick={this.tab.setEnd}
/>
<Tab
title="Duration"
active={this.tab.current === TabNames.Duration}
onClick={this.tab.setDuration}
/>
</ul>
<div className="tab-content mb-3">
{this.tab.current === TabNames.Duration ? (
<TabContentDuration silenceFormStore={silenceFormStore} />
) : null}
{this.tab.current === TabNames.Start ? (
<TabContentStart silenceFormStore={silenceFormStore} />
) : null}
{this.tab.current === TabNames.End ? (
<TabContentEnd silenceFormStore={silenceFormStore} />
) : null}
</div>
</React.Fragment>
);
}
}
);
export { DateTimeSelect };

View File

@@ -0,0 +1,29 @@
@import "../../../../node_modules/bootswatch/dist/flatly/variables";
$datepicker__background-color: $white;
$datepicker__border-color: $gray-300;
$datepicker__highlighted-color: $secondary;
$datepicker__muted-color: $gray-600;
$datepicker__selected-color: $primary;
$datepicker__text-color: $black;
$datepicker__header-color: $black;
$datepicker__navigation-disabled-color: $gray-600;
$datepicker__font-size: $font-size-base;
$datepicker__font-family: $font-family-sans-serif;
$datepicker__border-radius: 0.25rem;
@import "../../../../node_modules/react-datepicker/src/stylesheets/datepicker.scss";
.react-datepicker__day:not(.react-datepicker__day--disabled) {
border-width: 1px;
border-style: solid;
border-color: transparent;
}
.react-datepicker__day:hover:not(.react-datepicker__day--disabled) {
border-color: $datepicker__border-color;
}
.react-datepicker__today-button {
border-bottom-left-radius: $datepicker__border-radius;
border-bottom-right-radius: $datepicker__border-radius;
}

View File

@@ -14,7 +14,7 @@ import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
import { AlertManagerInput } from "./AlertManagerInput";
import { SilenceMatch } from "./SilenceMatch";
import { SilenceStartEnd } from "./SilenceStartEnd";
import { DateTimeSelect } from "./DateTimeSelect";
import { SilencePreview } from "./SilencePreview";
const IconInput = ({ icon, placeholder, value, onChange }) => (
@@ -124,7 +124,7 @@ const SilenceForm = observer(
>
<FontAwesomeIcon icon={faPlus} />
</button>
<SilenceStartEnd silenceFormStore={silenceFormStore} />
<DateTimeSelect silenceFormStore={silenceFormStore} />
<IconInput
placeholder="Author"
icon={faUser}

View File

@@ -1,18 +0,0 @@
.rc-calendar {
font-family: inherit;
font-size: 1rem;
}
.rc-calendar-picker {
z-index: 2000;
}
.rc-calendar-selected-day > .rc-calendar-date {
border: 0;
color: #fff;
background-color: #455a64;
}
.rc-calendar-today .rc-calendar-date {
border-color: #7b8a8b;
}

View File

@@ -1,129 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { observable, action, toJS } from "mobx";
import { observer } from "mobx-react";
import moment from "moment";
import Picker from "rc-calendar/lib/Picker";
import RangeCalendar from "rc-calendar/lib/RangeCalendar";
import "rc-calendar/assets/index.css";
import TimePickerPanel from "rc-time-picker/lib/Panel";
import "rc-time-picker/assets/index.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendarAlt } from "@fortawesome/free-solid-svg-icons/faCalendarAlt";
import "./SilenceStartEnd.css";
function disabledDate(current) {
if (!current) return false;
const date = moment();
date.hour(0);
date.minute(0);
date.second(0);
return current.isBefore(date);
}
function isValidRange(v) {
return v && v[0] && v[1];
}
const format = "YYYY-MM-DD HH:mm";
function formatTimestamp(v) {
return v ? v.format(format) : "";
}
const timePickerElement = (
<TimePickerPanel
defaultValue={[
moment().second(0),
moment()
.second(0)
.add(1, "minute")
]}
showSecond={false}
/>
);
const SilenceStartEnd = observer(
class SilenceStartEnd extends Component {
static propTypes = {
silenceFormStore: PropTypes.object.isRequired
};
data = observable({
hoverValue: []
});
onChange = action(value => {
const { silenceFormStore } = this.props;
if (value && value[0]) {
silenceFormStore.data.startsAt = value[0];
}
if (value && value[1]) {
silenceFormStore.data.endsAt = value[1];
}
});
onHoverChange = action(value => {
this.data.hoverValue = value;
});
render() {
const { silenceFormStore } = this.props;
const now = moment().second(0);
const calendar = (
<RangeCalendar
hoverValue={toJS(this.data.hoverValue)}
onHoverChange={this.onHoverChange}
showWeekNumber={false}
dateInputPlaceholder={["start", "end"]}
defaultValue={[now, now.clone().add(1, "hour")]}
timePicker={timePickerElement}
disabledDate={disabledDate}
format={format}
/>
);
return (
<Picker
value={[silenceFormStore.data.startsAt, silenceFormStore.data.endsAt]}
onChange={this.onChange}
calendar={calendar}
>
{({ value }) => {
return (
<div className="input-group mb-3">
<div className="input-group-prepend">
<span className="input-group-text">
<FontAwesomeIcon icon={faCalendarAlt} />
</span>
</div>
<input
placeholder="Silence start and end"
className="form-control bg-white"
value={
(isValidRange(value) &&
`${formatTimestamp(value[0])} - ${formatTimestamp(
value[1]
)}`) ||
""
}
readOnly
/>
</div>
);
}}
</Picker>
);
}
}
);
export { SilenceStartEnd };

View File

@@ -50,20 +50,24 @@ class SilenceFormStore {
endsAt: moment().add(1, "hour"),
comment: "",
author: "",
resetProgress() {
this.inProgress = false;
},
// append a new empty matcher to the list
addEmptyMatcher() {
let m = NewEmptyMatcher();
this.matchers.push(m);
},
deleteMatcher(id) {
// only delete matchers if we have more than 1
if (this.matchers.length > 1) {
this.matchers = this.matchers.filter(m => m.id !== id);
}
},
fillMatchersFromGroup(group) {
let matchers = [];
@@ -108,6 +112,40 @@ class SilenceFormStore {
this.matchers = matchers;
},
verifyStarEnd() {
if (this.endsAt.isSameOrBefore(this.startsAt)) {
this.endsAt = moment(this.startsAt).add(1, "minutes");
}
},
incStart(minutes) {
this.startsAt = moment(this.startsAt).add(minutes, "minutes");
this.verifyStarEnd();
},
decStart(minutes) {
this.startsAt = moment(this.startsAt).subtract(minutes, "minutes");
this.verifyStarEnd();
},
incEnd(minutes) {
this.endsAt = moment(this.endsAt).add(minutes, "minutes");
this.verifyStarEnd();
},
decEnd(minutes) {
this.endsAt = moment(this.endsAt).subtract(minutes, "minutes");
this.verifyStarEnd();
},
incDuration(minutes) {
this.endsAt = moment(this.endsAt).add(minutes, "minutes");
},
decDuration(minutes) {
const newEndsAt = moment(this.endsAt).subtract(minutes, "minutes");
if (newEndsAt.isAfter(this.startsAt)) {
this.endsAt = newEndsAt;
}
},
get toAlertmanagerPayload() {
const payload = {
matchers: this.matchers.map(m => ({
@@ -132,6 +170,14 @@ class SilenceFormStore {
comment: this.comment
};
return payload;
},
get toDuration() {
const data = {
days: this.endsAt.diff(this.startsAt, "days"),
hours: this.endsAt.diff(this.startsAt, "hours") % 24,
minutes: this.endsAt.diff(this.startsAt, "minutes") % 60
};
return data;
}
},
{
@@ -139,7 +185,15 @@ class SilenceFormStore {
addEmptyMatcher: action.bound,
deleteMatcher: action.bound,
fillMatchersFromGroup: action.bound,
toAlertmanagerPayload: computed
verifyStarEnd: action.bound,
incStart: action.bound,
decStart: action.bound,
incEnd: action.bound,
decEnd: action.bound,
incDuration: action.bound,
decDuration: action.bound,
toAlertmanagerPayload: computed,
toDuration: computed
},
{ name: "Silence form store" }
);