From 0e966b7b5d8634152cd5ba8747f7b1d73b740228 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 28 Mar 2017 16:20:34 -0700 Subject: [PATCH 01/15] WIP Introduce date time picker --- ui/package.json | 1 + ui/src/alerts/containers/AlertsApp.js | 4 +- ui/src/shared/components/CustomTimeRange.js | 97 +++++++++++++++++++ ui/src/style/chronograf.scss | 3 +- .../style/components/custom-time-range.scss | 5 + ui/yarn.lock | 52 +++++++++- 6 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 ui/src/shared/components/CustomTimeRange.js create mode 100644 ui/src/style/components/custom-time-range.scss diff --git a/ui/package.json b/ui/package.json index a969d82787..ddd544affc 100644 --- a/ui/package.json +++ b/ui/package.json @@ -111,6 +111,7 @@ "react-tooltip": "^3.2.1", "redux": "^3.3.1", "redux-thunk": "^1.0.3", + "rome": "^2.1.22", "updeep": "^0.13.0" } } diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index d9bb1fbe10..bccc420055 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -5,6 +5,7 @@ import {getAlerts} from '../apis' import AJAX from 'utils/ajax' import _ from 'lodash' import NoKapacitorError from '../../shared/components/NoKapacitorError' +import CustomTimeRange from '../../shared/components/CustomTimeRange' const AlertsApp = React.createClass({ propTypes: { @@ -92,8 +93,6 @@ const AlertsApp = React.createClass({ render() { const {source} = this.props return ( - // I stole this from the Hosts page. - // Perhaps we should create an abstraction?
@@ -104,6 +103,7 @@ const AlertsApp = React.createClass({
+ {}} timeRange={{upper: null, lower: null}}/>
diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js new file mode 100644 index 0000000000..a65c09dc51 --- /dev/null +++ b/ui/src/shared/components/CustomTimeRange.js @@ -0,0 +1,97 @@ +import React, {PropTypes, Component} from 'react' +import rome from 'rome' +import moment from 'moment' +import classNames from 'classnames' +import OnClickOutside from 'react-onclickoutside' + +class CustomTimeRange extends Component { + constructor(props) { + super(props) + this.state = { + isVisible: false, + } + } + + handleClickOutside() { + this.setState({isVisible: false}) + } + + componentDidMount() { + const lower = rome(this.timeLower, { + initialValue: this._formatTimeRange(this.props.timeLower), + }) + const upper = rome(this.timeUpper, { + initialValue: this._formatTimeRange(this.props.timeUpper), + }) + + this.lowerCal = lower + this.upperCal = upper + } + + // If there is an upper or lower time range set, set the corresponding calendar's value. + componentWillReceiveProps(nextProps) { + const {lower, upper} = nextProps.timeRange + if (lower) { + this.lowerCal.setValue(this._formatTimeRange(lower)) + } + + if (upper) { + this.upperCal.setValue(this._formatTimeRange(upper)) + } + } + + render() { + return ( +
+
+
this.timeLower = r} /> +
this.timeUpper = r} /> +
Apply
+
+
+ ); + } + + handleClick() { + const lower = this.lowerCal.getDate().toISOString() + const upper = this.upperCal.getDate().toISOString() + + this.props.onApplyTimeRange({ + lower: `'${lower}'`, + upper: `'${upper}'`, + }) + } + /* + * Upper and lower time ranges are passed in with single quotes as part of + * the string literal, i.e. "'2015-09-23T18:00:00.000Z'". Remove them + * before passing the string to be parsed. + */ + _formatTimeRange(timeRange) { + if (!timeRange) { + return ''; + } + + // If the given time range is relative, create a fixed timestamp based on its value + if (timeRange.match(/^now/)) { + const match = timeRange.match(/\d+\w/)[0]; + const duration = match.slice(0, match.length - 1); + const unitOfTime = match[match.length - 1]; + return moment().subtract(duration, unitOfTime); + } + + return moment(timeRange.replace(/\'/g, '')).format('YYYY-MM-DD HH:mm'); + } +} + +const { + func, + string, +} = PropTypes + +CustomTimeRange.propTypes = { + onApplyTimeRange: func.isRequired, + timeLower: string, + timeUpper: string, +} + +export default OnClickOutside(CustomTimeRange) diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index 60d373e726..474b1fa1ed 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -40,6 +40,7 @@ @import 'components/resizer'; @import 'components/source-indicator'; @import 'components/confirm-buttons'; +@import 'components/custom-time-range'; // Pages @import 'pages/alerts'; @@ -52,4 +53,4 @@ @import 'pages/admin'; // TODO -@import 'unsorted'; \ No newline at end of file +@import 'unsorted'; diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss new file mode 100644 index 0000000000..46ce2946e3 --- /dev/null +++ b/ui/src/style/components/custom-time-range.scss @@ -0,0 +1,5 @@ +.custom-time-container { + background: red; + z-index: 1000; + display: flex; +} diff --git a/ui/yarn.lock b/ui/yarn.lock index fb20ae9dd0..61048a0e0b 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -352,6 +352,10 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atoa@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atoa/-/atoa-1.0.0.tgz#0cc0e91a480e738f923ebc103676471779b34a49" + atob@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" @@ -1540,6 +1544,14 @@ builtin-status-codes@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz#6f22003baacf003ccd287afe6872151fddc58579" +bullseye@1.4.6: + version "1.4.6" + resolved "https://registry.yarnpkg.com/bullseye/-/bullseye-1.4.6.tgz#b73f606f7b4273be80ac65acd75295d62606fe24" + dependencies: + crossvent "^1.3.1" + seleccion "2.0.0" + sell "^1.0.0" + bytes@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" @@ -1964,6 +1976,13 @@ content-type@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" +contra@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/contra/-/contra-1.9.1.tgz#60e498274b3d2d332896d60f82900aefa2ecac8c" + dependencies: + atoa "1.0.0" + ticky "1.0.0" + convert-source-map@^0.3.3: version "0.3.5" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" @@ -2018,6 +2037,12 @@ cross-spawn@^5.0.0: shebang-command "^1.2.0" which "^1.2.9" +crossvent@1.5.0, crossvent@^1.3.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/crossvent/-/crossvent-1.5.0.tgz#3779c1242699e19417f0414e61b144753a52fd6d" + dependencies: + custom-event "1.0.0" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -2200,6 +2225,10 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +custom-event@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.0.tgz#2e4628be19dc4b214b5c02630c5971e811618062" + custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" @@ -4596,7 +4625,7 @@ mocha@^2.4.5: supports-color "1.2.0" to-iso-string "0.0.2" -moment@^2.13.0: +moment@^2.13.0, moment@^2.8.2: version "2.17.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" @@ -6203,6 +6232,15 @@ ripemd160@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" +rome@^2.1.22: + version "2.1.22" + resolved "https://registry.yarnpkg.com/rome/-/rome-2.1.22.tgz#4bf25318cc0522ae92dd090472ce7a6e0b1f5e02" + dependencies: + bullseye "1.4.6" + contra "1.9.1" + crossvent "1.5.0" + moment "^2.8.2" + run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -6243,6 +6281,14 @@ script-loader@~0.6.0: dependencies: raw-loader "~0.5.1" +seleccion@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/seleccion/-/seleccion-2.0.0.tgz#0984ac1e8df513e38b41a608e65042e8381e0a73" + +sell@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sell/-/sell-1.0.0.tgz#3baca7e51f78ddee9e22eea1ac747a6368bd1630" + "semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -6842,6 +6888,10 @@ through@^2.3.6, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +ticky@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ticky/-/ticky-1.0.0.tgz#e87f38ee0491ea32f62e8f0567ba9638b29f049c" + timers-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" From b046e1334010ef20a35a3f4bba230470884ae73d Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 28 Mar 2017 16:44:39 -0700 Subject: [PATCH 02/15] Collect selected time ranges --- ui/src/alerts/containers/AlertsApp.js | 2 +- ui/src/shared/components/CustomTimeRange.js | 2 ++ ui/src/style/components/custom-time-range.scss | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index bccc420055..a8a3f1875b 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -103,7 +103,7 @@ const AlertsApp = React.createClass({
- {}} timeRange={{upper: null, lower: null}}/> + console.log(timeRange)} timeRange={{upper: null, lower: null}}/>
diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index a65c09dc51..62967bc143 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -10,6 +10,8 @@ class CustomTimeRange extends Component { this.state = { isVisible: false, } + + this.handleClick = ::this.handleClick } handleClickOutside() { diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss index 46ce2946e3..43de9974a5 100644 --- a/ui/src/style/components/custom-time-range.scss +++ b/ui/src/style/components/custom-time-range.scss @@ -2,4 +2,9 @@ background: red; z-index: 1000; display: flex; + transform: translateY(102px); +} + +.rd-day-selected { + background: blue; } From 20f83ba1b69715ddea0be0620d8689c6e392c16b Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 28 Mar 2017 17:21:25 -0700 Subject: [PATCH 03/15] Make custom time range's visibility toggleable --- ui/src/alerts/containers/AlertsApp.js | 17 ++++++++++++++++- ui/src/shared/components/CustomTimeRange.js | 19 ++++++++++++++----- .../style/components/custom-time-range.scss | 17 +++++++++++++++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index a8a3f1875b..02f79901a2 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -25,6 +25,7 @@ const AlertsApp = React.createClass({ loading: true, hasKapacitor: false, alerts: [], + isTimeOpen: false, } }, // TODO: show a loading screen until we figure out if there is a kapacitor and fetch the alerts @@ -90,6 +91,14 @@ const AlertsApp = React.createClass({ return component }, + handleToggleTime() { + this.setState({isTimeOpen: !this.state.isTimeOpen}) + }, + + handleCloseTime() { + this.setState({isTimeOpen: false}) + }, + render() { const {source} = this.props return ( @@ -103,7 +112,13 @@ const AlertsApp = React.createClass({
- console.log(timeRange)} timeRange={{upper: null, lower: null}}/> + console.log(timeRange)} + timeRange={{upper: null, lower: null}} + />
diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 62967bc143..46a1973029 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -7,15 +7,12 @@ import OnClickOutside from 'react-onclickoutside' class CustomTimeRange extends Component { constructor(props) { super(props) - this.state = { - isVisible: false, - } this.handleClick = ::this.handleClick } handleClickOutside() { - this.setState({isVisible: false}) + this.props.onClose() } componentDidMount() { @@ -43,8 +40,15 @@ class CustomTimeRange extends Component { } render() { + const {isVisible, onToggle} = this.props + return ( -
+
+
this.timeLower = r} />
this.timeUpper = r} /> @@ -62,6 +66,7 @@ class CustomTimeRange extends Component { lower: `'${lower}'`, upper: `'${upper}'`, }) + this.props.onClose() } /* * Upper and lower time ranges are passed in with single quotes as part of @@ -86,6 +91,7 @@ class CustomTimeRange extends Component { } const { + bool, func, string, } = PropTypes @@ -94,6 +100,9 @@ CustomTimeRange.propTypes = { onApplyTimeRange: func.isRequired, timeLower: string, timeUpper: string, + isVisible: bool.isRequired, + onToggle: func.isRequired, + onClose: func.isRequired, } export default OnClickOutside(CustomTimeRange) diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss index 43de9974a5..28c2a9391f 100644 --- a/ui/src/style/components/custom-time-range.scss +++ b/ui/src/style/components/custom-time-range.scss @@ -1,8 +1,21 @@ +/* + Custom Time Range Dropdown + ------------------------------------------------------ +*/ + +.custom-time-range { + position: relative; +} .custom-time-container { + display: none; + position: absolute; + top: 40px; + right: 0; background: red; z-index: 1000; - display: flex; - transform: translateY(102px); +} +.custom-time-range.show .custom-time-container { + display: block; } .rd-day-selected { From 3cecd6d49b67013aed4c9eea0a46847e2e41d07b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 28 Mar 2017 17:57:44 -0700 Subject: [PATCH 04/15] Set default time for alerts page --- ui/src/alerts/apis/index.js | 6 +++--- ui/src/alerts/containers/AlertsApp.js | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ui/src/alerts/apis/index.js b/ui/src/alerts/apis/index.js index 91cb0e2833..5e7321f1d7 100644 --- a/ui/src/alerts/apis/index.js +++ b/ui/src/alerts/apis/index.js @@ -1,9 +1,9 @@ import {proxy} from 'utils/queryUrlGenerator' -export function getAlerts(proxyLink) { +export function getAlerts(source, timeRange) { return proxy({ - source: proxyLink, - query: "select host, value, level, alertName from alerts order by time desc", + source, + query: `SELECT host, value, level, alertName FROM alerts WHERE time >= '${timeRange.lower}' AND time <= '${timeRange.upper}' ORDER BY time desc`, db: "chronograf", }) } diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index 02f79901a2..546c35eb3a 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -1,12 +1,15 @@ import React, {PropTypes} from 'react' -import AlertsTable from '../components/AlertsTable' import SourceIndicator from '../../shared/components/SourceIndicator' -import {getAlerts} from '../apis' -import AJAX from 'utils/ajax' -import _ from 'lodash' +import AlertsTable from '../components/AlertsTable' import NoKapacitorError from '../../shared/components/NoKapacitorError' import CustomTimeRange from '../../shared/components/CustomTimeRange' +import {getAlerts} from '../apis' +import AJAX from 'utils/ajax' + +import _ from 'lodash' +import moment from 'moment' + const AlertsApp = React.createClass({ propTypes: { source: PropTypes.shape({ @@ -26,6 +29,10 @@ const AlertsApp = React.createClass({ hasKapacitor: false, alerts: [], isTimeOpen: false, + timeRange: { + upper: moment().format(), + lower: moment().subtract(1, 'h').format(), + }, } }, // TODO: show a loading screen until we figure out if there is a kapacitor and fetch the alerts @@ -46,9 +53,8 @@ const AlertsApp = React.createClass({ }, fetchAlerts() { - getAlerts(this.props.source.links.proxy).then((resp) => { - const results = [] - + getAlerts(this.props.source.links.proxy, this.state.timeRange).then((resp) => { + const results = []; const alertSeries = _.get(resp, ['data', 'results', '0', 'series'], []) if (alertSeries.length === 0) { this.setState({loading: false, alerts: []}) From e3282bc50529616770216fb4d99d8ccddea53e1a Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 09:29:55 -0700 Subject: [PATCH 05/15] Enable date picker for alerts page --- ui/src/alerts/components/AlertsTable.js | 11 ++++++----- ui/src/alerts/containers/AlertsApp.js | 18 ++++++++++++++---- ui/src/shared/components/CustomTimeRange.js | 5 +---- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ui/src/alerts/components/AlertsTable.js b/ui/src/alerts/components/AlertsTable.js index 124591e577..ef797e854b 100644 --- a/ui/src/alerts/components/AlertsTable.js +++ b/ui/src/alerts/components/AlertsTable.js @@ -27,18 +27,19 @@ const AlertsTable = React.createClass({ }, componentWillReceiveProps(newProps) { - this.filterAlerts(newProps.alerts, this.state.searchTerm) + this.filterAlerts(this.state.searchTerm, newProps.alerts) }, - filterAlerts(searchTerm) { - const filteredAlerts = this.props.alerts.filter((h) => { + filterAlerts(searchTerm, newAlerts) { + const alerts = newAlerts || this.props.alerts + const filteredAlerts = alerts.filter((h) => { if (h.host === null || h.name === null || h.level === null) { return false } return h.name.toLowerCase().search((searchTerm).toLowerCase()) !== -1 || - h.host.toLowerCase().search((searchTerm).toLowerCase()) !== -1 || - h.level.toLowerCase().search((searchTerm).toLowerCase()) !== -1 + h.host.toLowerCase().search((searchTerm).toLowerCase()) !== -1 || + h.level.toLowerCase().search((searchTerm).toLowerCase()) !== -1 }) this.setState({searchTerm, filteredAlerts}) }, diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index 546c35eb3a..97643b7f5f 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -52,6 +52,12 @@ const AlertsApp = React.createClass({ }) }, + componentDidUpdate(prevProps, prevState) { + if (!_.isEqual(prevState.timeRange, this.state.timeRange)) { + this.fetchAlerts() + } + }, + fetchAlerts() { getAlerts(this.props.source.links.proxy, this.state.timeRange).then((resp) => { const results = []; @@ -105,8 +111,13 @@ const AlertsApp = React.createClass({ this.setState({isTimeOpen: false}) }, + handleApplyTime(timeRange) { + this.setState({timeRange}) + }, + render() { const {source} = this.props + const {timeRange} = this.state return (
@@ -122,8 +133,8 @@ const AlertsApp = React.createClass({ isVisible={this.state.isTimeOpen} onToggle={this.handleToggleTime} onClose={this.handleCloseTime} - onApplyTimeRange={(timeRange) => console.log(timeRange)} - timeRange={{upper: null, lower: null}} + onApplyTimeRange={this.handleApplyTime} + timeRange={timeRange} />
@@ -132,7 +143,7 @@ const AlertsApp = React.createClass({
- { this.renderSubComponents() } + {this.renderSubComponents()}
@@ -140,7 +151,6 @@ const AlertsApp = React.createClass({
) }, - }) export default AlertsApp diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 46a1973029..6358164e57 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -62,10 +62,7 @@ class CustomTimeRange extends Component { const lower = this.lowerCal.getDate().toISOString() const upper = this.upperCal.getDate().toISOString() - this.props.onApplyTimeRange({ - lower: `'${lower}'`, - upper: `'${upper}'`, - }) + this.props.onApplyTimeRange({lower, upper}) this.props.onClose() } /* From adbaec0089d66a585b5e93a51ff7b588a9d30b3a Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 09:58:29 -0700 Subject: [PATCH 06/15] Format time display --- ui/src/shared/components/CustomTimeRange.js | 25 ++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 6358164e57..7a26a985a3 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -16,11 +16,13 @@ class CustomTimeRange extends Component { } componentDidMount() { - const lower = rome(this.timeLower, { - initialValue: this._formatTimeRange(this.props.timeLower), + const {timeRange} = this.props + + const lower = rome(this.lower, { + initialValue: this._formatTimeRange(timeRange.lower), }) - const upper = rome(this.timeUpper, { - initialValue: this._formatTimeRange(this.props.timeUpper), + const upper = rome(this.upper, { + initialValue: this._formatTimeRange(timeRange.upper), }) this.lowerCal = lower @@ -40,18 +42,18 @@ class CustomTimeRange extends Component { } render() { - const {isVisible, onToggle} = this.props + const {isVisible, onToggle, timeRange: {upper, lower}} = this.props return (
-
this.timeLower = r} /> -
this.timeUpper = r} /> +
this.lower = r} /> +
this.upper = r} />
Apply
@@ -90,13 +92,16 @@ class CustomTimeRange extends Component { const { bool, func, + shape, string, } = PropTypes CustomTimeRange.propTypes = { onApplyTimeRange: func.isRequired, - timeLower: string, - timeUpper: string, + timeRange: shape({ + lower: string.isRequired, + upper: string.isRequired, + }).isRequired, isVisible: bool.isRequired, onToggle: func.isRequired, onClose: func.isRequired, From a674bc0196a9cc02721d259ac1a68062fec79f7b Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 10:49:01 -0700 Subject: [PATCH 07/15] Update LICENSE_OF_DEPENDENCIES.md --- LICENSE_OF_DEPENDENCIES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE_OF_DEPENDENCIES.md b/LICENSE_OF_DEPENDENCIES.md index b9ed04af0e..03e0778bd5 100644 --- a/LICENSE_OF_DEPENDENCIES.md +++ b/LICENSE_OF_DEPENDENCIES.md @@ -891,6 +891,7 @@ * rimraf 2.5.3 [ISC](http://github.com/isaacs/rimraf) * rimraf 2.5.4 [ISC](http://github.com/isaacs/rimraf) * ripemd160 0.2.0 [Unknown](https://github.com/cryptocoinjs/ripemd160) +* rome 2.1.22 [MIT](https://github.com/bevacqua/rome) * run-async 0.1.0 [MIT](http://github.com/SBoudrias/run-async) * rx-lite 3.1.2 [Apache License](https://github.com/Reactive-Extensions/RxJS) * samsam 1.1.2 [BSD](https://github.com/busterjs/samsam) From 034e2d611d4b5ad1de9aa73f9bd0863aea9e1ccb Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 29 Mar 2017 11:03:14 -0700 Subject: [PATCH 08/15] Resolve conflict --- ui/src/shared/components/CustomTimeRange.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 7a26a985a3..5f5d0e70d2 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -46,7 +46,7 @@ class CustomTimeRange extends Component { return (
-
From ac7559c4a915d23d55bb50600f9a33266211288f Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 29 Mar 2017 11:09:40 -0700 Subject: [PATCH 10/15] Give time range caret more breathing room --- .../style/components/custom-time-range.scss | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss index 28c2a9391f..b7490e6e42 100644 --- a/ui/src/style/components/custom-time-range.scss +++ b/ui/src/style/components/custom-time-range.scss @@ -6,6 +6,16 @@ .custom-time-range { position: relative; } +.btn.btn-sm.btn-info.custom-time-range--btn { + padding: 0 30px 0 9px !important; + + .caret { + position: absolute; + right: 9px; + top: 50%; + transform: translateY(-50%); + } +} .custom-time-container { display: none; position: absolute; @@ -14,8 +24,14 @@ background: red; z-index: 1000; } -.custom-time-range.show .custom-time-container { - display: block; +.custom-time-range.show { + .custom-time-container { + display: block; + } + .custom-time-range--btn { + color: $g20-white; + background-color: $g6-smoke; + } } .rd-day-selected { From 71762c8b30ba1e3559db107301b55143ad6010d2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 11:13:22 -0700 Subject: [PATCH 11/15] Refactor to js class --- ui/src/alerts/containers/AlertsApp.js | 71 ++++++++++++++++----------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/ui/src/alerts/containers/AlertsApp.js b/ui/src/alerts/containers/AlertsApp.js index 97643b7f5f..e6c1617481 100644 --- a/ui/src/alerts/containers/AlertsApp.js +++ b/ui/src/alerts/containers/AlertsApp.js @@ -1,4 +1,4 @@ -import React, {PropTypes} from 'react' +import React, {PropTypes, Component} from 'react' import SourceIndicator from '../../shared/components/SourceIndicator' import AlertsTable from '../components/AlertsTable' import NoKapacitorError from '../../shared/components/NoKapacitorError' @@ -10,31 +10,27 @@ import AJAX from 'utils/ajax' import _ from 'lodash' import moment from 'moment' -const AlertsApp = React.createClass({ - propTypes: { - source: PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - type: PropTypes.string, // 'influx-enterprise' - links: PropTypes.shape({ - proxy: PropTypes.string.isRequired, - }).isRequired, - }), // .isRequired, - addFlashMessage: PropTypes.func, // .isRequired, - }, - - getInitialState() { - return { +class AlertsApp extends Component { + constructor(props) { + super(props) + this.state = { loading: true, hasKapacitor: false, alerts: [], isTimeOpen: false, timeRange: { upper: moment().format(), - lower: moment().subtract(1, 'h').format(), + lower: moment().subtract(1, 'd').format(), }, } - }, + + this.fetchAlerts = ::this.fetchAlerts + this.renderSubComponents = ::this.renderSubComponents + this.handleToggleTime = ::this.handleToggleTime + this.handleCloseTime = ::this.handleCloseTime + this.handleApplyTime = ::this.handleApplyTime + } + // TODO: show a loading screen until we figure out if there is a kapacitor and fetch the alerts componentDidMount() { const {source} = this.props @@ -50,17 +46,18 @@ const AlertsApp = React.createClass({ this.setState({loading: false}) } }) - }, + } componentDidUpdate(prevProps, prevState) { if (!_.isEqual(prevState.timeRange, this.state.timeRange)) { this.fetchAlerts() } - }, + } fetchAlerts() { getAlerts(this.props.source.links.proxy, this.state.timeRange).then((resp) => { - const results = []; + const results = [] + const alertSeries = _.get(resp, ['data', 'results', '0', 'series'], []) if (alertSeries.length === 0) { this.setState({loading: false, alerts: []}) @@ -84,7 +81,7 @@ const AlertsApp = React.createClass({ }) this.setState({loading: false, alerts: results}) }) - }, + } renderSubComponents() { let component @@ -101,19 +98,19 @@ const AlertsApp = React.createClass({ } } return component - }, + } handleToggleTime() { this.setState({isTimeOpen: !this.state.isTimeOpen}) - }, + } handleCloseTime() { this.setState({isTimeOpen: false}) - }, + } handleApplyTime(timeRange) { this.setState({timeRange}) - }, + } render() { const {source} = this.props @@ -150,7 +147,25 @@ const AlertsApp = React.createClass({
) - }, -}) + } +} + +const { + func, + shape, + string, +} = PropTypes + +AlertsApp.propTypes = { + source: shape({ + id: string.isRequired, + name: string.isRequired, + type: string, // 'influx-enterprise' + links: shape({ + proxy: string.isRequired, + }).isRequired, + }), + addFlashMessage: func, +} export default AlertsApp From 6783248eb07b2cf0c721f61f81290d74c90cee43 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 11:14:50 -0700 Subject: [PATCH 12/15] Semicolon destruction --- ui/src/shared/components/CustomTimeRange.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 212178be8d..341b92d7b0 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -57,7 +57,7 @@ class CustomTimeRange extends Component {
Apply
- ); + ) } handleClick() { @@ -74,18 +74,18 @@ class CustomTimeRange extends Component { */ _formatTimeRange(timeRange) { if (!timeRange) { - return ''; + return '' } // If the given time range is relative, create a fixed timestamp based on its value if (timeRange.match(/^now/)) { - const match = timeRange.match(/\d+\w/)[0]; - const duration = match.slice(0, match.length - 1); - const unitOfTime = match[match.length - 1]; - return moment().subtract(duration, unitOfTime); + const match = timeRange.match(/\d+\w/)[0] + const duration = match.slice(0, match.length - 1) + const unitOfTime = match[match.length - 1] + return moment().subtract(duration, unitOfTime) } - return moment(timeRange.replace(/\'/g, '')).format('YYYY-MM-DD HH:mm'); + return moment(timeRange.replace(/\'/g, '')).format('YYYY-MM-DD HH:mm') } } From ec6bc815a78f4949fb51570df06cf9920da427f8 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 29 Mar 2017 16:02:38 -0700 Subject: [PATCH 13/15] Add 2nd kind of scrollbar mixin Square corners vs round, good for dropdowns --- ui/src/style/mixins/mixins.scss | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ui/src/style/mixins/mixins.scss b/ui/src/style/mixins/mixins.scss index 4d3646f72d..5bc20a2681 100644 --- a/ui/src/style/mixins/mixins.scss +++ b/ui/src/style/mixins/mixins.scss @@ -41,6 +41,35 @@ $scrollbar-offset: 3px; @mixin custom-scrollbar($trackColor, $handleColor) { &::-webkit-scrollbar { width: $scrollbar-width; + + &-button { + background-color: $trackColor; + } + &-track { + background-color: $trackColor; + } + &-track-piece { + background-color: $trackColor; + border: $scrollbar-offset solid $trackColor; + border-radius: ($scrollbar-width / 2); + } + &-thumb { + background-color: $handleColor; + border: $scrollbar-offset solid $trackColor; + border-radius: ($scrollbar-width / 2); + } + &-corner { + background-color: $trackColor; + } + } + &::-webkit-resizer { + background-color: $trackColor; + } +} +@mixin custom-scrollbar-round($trackColor, $handleColor) { + &::-webkit-scrollbar { + width: $scrollbar-width; + border-top-right-radius: $radius; border-bottom-right-radius: $radius; &-button { @@ -48,6 +77,7 @@ $scrollbar-offset: 3px; } &-track { background-color: $trackColor; + border-top-right-radius: $radius; border-bottom-right-radius: $radius; } &-track-piece { From 18a04d6964c8acd13834e4b1d6f5bab6394388c7 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 29 Mar 2017 16:03:22 -0700 Subject: [PATCH 14/15] Change dropdown scrollbar type to "Round" --- ui/src/style/theme/theme-dark.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/theme/theme-dark.scss b/ui/src/style/theme/theme-dark.scss index 21d869ed0f..89ee45fe7a 100644 --- a/ui/src/style/theme/theme-dark.scss +++ b/ui/src/style/theme/theme-dark.scss @@ -267,7 +267,7 @@ input { padding: 0 !important; max-height: 290px; overflow: auto; - @include custom-scrollbar($c-pool, $c-laser); + @include custom-scrollbar-round($c-pool, $c-laser); @include gradient-h($c-ocean, $c-pool); box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.8); From 30514a3d323429cb06ec751923cb2443778bc9ac Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 29 Mar 2017 16:03:32 -0700 Subject: [PATCH 15/15] Style custom time range component --- ui/src/shared/components/CustomTimeRange.js | 10 +- .../style/components/custom-time-range.scss | 237 +++++++++++++++++- 2 files changed, 232 insertions(+), 15 deletions(-) diff --git a/ui/src/shared/components/CustomTimeRange.js b/ui/src/shared/components/CustomTimeRange.js index 341b92d7b0..9da0bdf377 100644 --- a/ui/src/shared/components/CustomTimeRange.js +++ b/ui/src/shared/components/CustomTimeRange.js @@ -51,10 +51,12 @@ class CustomTimeRange extends Component { {`${moment(lower).format('MMM Do HH:mm')} — ${moment(upper).format('MMM Do HH:mm')}`} -
-
this.lower = r} /> -
this.upper = r} /> -
Apply
+
+
+
this.lower = r} /> +
this.upper = r} /> +
+
Apply
) diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss index b7490e6e42..ab701d4257 100644 --- a/ui/src/style/components/custom-time-range.scss +++ b/ui/src/style/components/custom-time-range.scss @@ -12,28 +12,243 @@ .caret { position: absolute; right: 9px; - top: 50%; + top: calc(50% + 1px); transform: translateY(-50%); } } -.custom-time-container { +.custom-time--container { display: none; position: absolute; - top: 40px; + flex-direction: column; + align-items: center; + top: 35px; right: 0; - background: red; + background: $g5-pepper; + border-radius: $radius; + padding: 8px; z-index: 1000; + box-shadow: 0 2px 5px 0.6px rgba(15, 14, 21, 0.2); } -.custom-time-range.show { - .custom-time-container { - display: block; +.custom-time--dates { + display: flex; + align-items: flex-start; + justify-content: space-between; +} +.custom-time--lower { + margin-right: 4px; +} +.custom-time--upper { + margin-left: 4px; +} + +$custom-time-arrow: 28px; +$rd-cell-size: 30px; + +.rd-container { + display: flex !important; + flex-direction: column; + align-items: center; +} +.rd-date { + position: relative; +} +.rd-back, +.rd-next, +.rd-month-label { + position: absolute; + top: 0; + height: $custom-time-arrow; + line-height: $custom-time-arrow; +} +.rd-back, +.rd-next { + outline: none; + width: $custom-time-arrow; + border: 0; + background-color: transparent; + border-radius: 50%; + color: $g15-platinum; + transition: + background-color 0.25s ease, + color 0.25s ease; + + &:after { + font-family: 'icomoon' !important; + font-style: normal; + font-weight: normal; + font-variant: normal; + color: inherit; + position: absolute; + top: 50%; + transform: translate(-50%,-50%); + font-size: 16px; } - .custom-time-range--btn { - color: $g20-white; + &:hover { background-color: $g6-smoke; + color: $g20-white; + } +} +.rd-back { + left: 0; + &:after { + left: calc(50% - 1px); + content: "\e90c"; + } +} +.rd-next { + left: calc(100% - #{$custom-time-arrow}); + &:after { + left: calc(50% + 1px); + content: "\e911"; + } +} +.rd-month-label { + font-weight: 600; + color: $g15-platinum; + left: $custom-time-arrow; + text-align: center; + @include no-user-select(); + width: calc(100% - #{($custom-time-arrow * 2)}); +} +.rd-days { + margin-top: ($custom-time-arrow + 8px); + background-color: transparent; + border-radius: $radius-small; + + /* Cancel out default table styles */ + tr:hover { + background-color: transparent !important; + color: inherit !important; + } + + thead.rd-days-head th.rd-day-head, + tbody.rd-days-body td.rd-day-body { + padding: 0 !important; + min-height: $rd-cell-size !important; + height: $rd-cell-size !important; + max-height: $rd-cell-size !important; + min-width: $rd-cell-size !important; + width: $rd-cell-size !important; + max-width: $rd-cell-size !important; + vertical-align: middle; + text-align: center; + border: 2px solid $g5-pepper !important; + } + thead.rd-days-head th.rd-day-head { + color: $g15-platinum !important; + background-color: $g5-pepper !important; + } + tbody.rd-days-body td.rd-day-body { + @include no-user-select(); + letter-spacing: -1px; + font-family: $code-font; + transition: + background-color 0.25s ease, + color 0.25s ease; + color: $g13-mist !important; + background-color: $g3-castle; + border-radius: 5px; + + &:hover { + cursor: $cc-pointer; + color: $g20-white !important; + background-color: $g6-smoke; + } + &.rd-day-next-month, + &.rd-day-prev-month { + cursor: $cc-default; + color: $g8-storm !important; + background-color: $g5-pepper !important; + } + &.rd-day-selected { + background-color: $c-pool !important; + color: $g20-white !important; + } } } -.rd-day-selected { - background: blue; + +.rd-time { + margin: 0 2px; + width: calc(100% - 4px); + height: 30px; + display: flex; + align-items: center; + justify-content: center; + position: relative; } +.rd-time-selected { + @include no-user-select(); + height: 28px; + line-height: 28px; + background-color: $g3-castle; + border-radius: $radius-small; + width: 100%; + letter-spacing: -1px; + font-family: $code-font; + color: $g13-mist; + display: inline-block; + transition: + color 0.25s ease, + background-color 0.25s ease; + text-align: center; + + &:hover { + color: $g20-white; + background-color: $g6-smoke; + cursor: $cc-pointer; + } +} +.rd-time-list { + position: absolute; + top: 50%; + left: 50%; + width: 120px; + height: 200px; + transform: translate(-50%,-50%); + overflow: auto; + overflow-x: hidden; + overflow-y: scroll; + @include custom-scrollbar-round($c-pool, $c-laser); + @include gradient-h($c-ocean, $c-pool); + border-radius: $radius; + box-shadow: 0 2px 5px 0.6px rgba(15, 14, 21, 0.2); +} + +.rd-time-option { + width: 100%; + height: 24px; + line-height: 24px; + padding-left: $scrollbar-width; + text-align: center; + @include no-user-select(); + font-family: $code-font; + color: $c-yeti; + letter-spacing: -1px; + + &:hover, + &:active, + &:focus { + color: $g20-white; + cursor: $cc-pointer; + outline: none; + @include gradient-h($c-laser, $c-pool); + } +} + +.custom-time--apply { + margin-top: 8px; + width: 120px; +} + + +/* Show State */ +.custom-time-range.show { + .custom-time--container { + display: flex; + } + .custom-time-range--btn { + color: $g20-white !important; + background-color: $g6-smoke; + } +} \ No newline at end of file