diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3114b39af..5d5721604c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ **New features** - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views +- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data +- [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift **Enhancements** - [Issue #1297](https://github.com/grafana/grafana/issues/1297). Graphite: Added cumulative and minimumBelow graphite functions @@ -13,6 +15,9 @@ - [Issue #1298](https://github.com/grafana/grafana/issues/1298). InfluxDB: Fix handling of empty array in templating variable query - [Issue #1309](https://github.com/grafana/grafana/issues/1309). Graph: Fixed issue when using zero as a grid threshold - [Issue #1345](https://github.com/grafana/grafana/issues/1345). UI: Fixed position of confirm modal when scrolled down +- [Issue #1372](https://github.com/grafana/grafana/issues/1372). Graphite: Fix for nested complex queries, where a query references a query that references another query (ie the #[A-Z] syntax) +- [Issue #1363](https://github.com/grafana/grafana/issues/1363). Templating: Fix to allow custom template variables to contain white space, now only splits on ',' +- [Issue #1359](https://github.com/grafana/grafana/issues/1359). Graph: Fix for all series tooltip showing series with all null values when ``Hide Empty`` option is enabled **Tech** - [Issue #1311](https://github.com/grafana/grafana/issues/1311). Tech: Updated Font-Awesome from 3.2 to 4.2 diff --git a/src/app/controllers/all.js b/src/app/controllers/all.js index ef6f473e34e..bf5c8164397 100644 --- a/src/app/controllers/all.js +++ b/src/app/controllers/all.js @@ -7,5 +7,6 @@ define([ './inspectCtrl', './jsonEditorCtrl', './loginCtrl', + './sidemenuCtrl', './errorCtrl', ], function () {}); diff --git a/src/app/controllers/loginCtrl.js b/src/app/controllers/loginCtrl.js index 9cc28d75ddb..45d24987ca1 100644 --- a/src/app/controllers/loginCtrl.js +++ b/src/app/controllers/loginCtrl.js @@ -8,15 +8,27 @@ function (angular, config) { var module = angular.module('grafana.controllers'); module.controller('LoginCtrl', function($scope, backendSrv, $location, $routeParams, alertSrv) { - $scope.loginModel = { + + $scope.formModel = { user: '', - password: '' + email: '', + password: '', }; - $scope.newUser = {}; - $scope.grafana.sidemenu = false; - $scope.mode = 'login'; + $scope.loginMode = true; + $scope.submitBtnClass = 'btn-inverse'; + $scope.submitBtnText = 'Log in'; + $scope.strengthClass = ''; + + $scope.init = function() { + if ($routeParams.logout) { + $scope.logout(); + } + + $scope.$watch("loginMode", $scope.loginModeChanged); + $scope.passwordChanged(); + }; // build info view model $scope.buildInfo = { @@ -26,30 +38,44 @@ function (angular, config) { }; $scope.submit = function() { - if ($scope.mode === 'login') { + if ($scope.loginMode) { $scope.login(); } else { $scope.signUp(); } }; - $scope.init = function() { - if ($routeParams.logout) { - $scope.logout(); - } + $scope.loginModeChanged = function(newValue) { + $scope.submitBtnText = newValue ? 'Log in' : 'Sign up'; }; - $scope.signUp = function() { - if ($scope.mode === 'login') { - $scope.mode = 'signup'; + $scope.passwordChanged = function(newValue) { + if (!newValue) { + $scope.strengthText = ""; + $scope.strengthClass = "hidden"; + return; + } + if (newValue.length < 4) { + $scope.strengthText = "strength: weak sauce."; + $scope.strengthClass = "password-strength-bad"; + return; + } + if (newValue.length <= 6) { + $scope.strengthText = "strength: you can do better."; + $scope.strengthClass = "password-strength-ok"; return; } + $scope.strengthText = "strength: strong like a bull."; + $scope.strengthClass = "password-strength-good"; + }; + + $scope.signUp = function() { if (!$scope.loginForm.$valid) { return; } - backendSrv.put('/api/user/signup', $scope.newUser).then(function() { + backendSrv.post('/api/user/signup', $scope.formModel).then(function() { window.location.href = config.appSubUrl + '/'; }); }; @@ -63,17 +89,13 @@ function (angular, config) { }; $scope.login = function() { - if ($scope.mode === 'signup') { - $scope.mode = 'login'; - return; - } delete $scope.loginError; if (!$scope.loginForm.$valid) { return; } - backendSrv.post('/login', $scope.loginModel).then(function() { + backendSrv.post('/login', $scope.formModel).then(function() { window.location.href = config.appSubUrl + '/'; }); }; diff --git a/src/app/controllers/sidemenuCtrl.js b/src/app/controllers/sidemenuCtrl.js new file mode 100644 index 00000000000..10b73e2458a --- /dev/null +++ b/src/app/controllers/sidemenuCtrl.js @@ -0,0 +1,104 @@ +define([ + 'angular', + 'lodash', + 'jquery', + 'config', +], +function (angular, _, $, config) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('SideMenuCtrl', function($scope, $location) { + + $scope.getUrl = function(url) { + return config.appSubUrl + url; + }; + + $scope.menu = [ + { + text: "Dashbord", + href: $scope.getUrl("/"), + startsWith: config.appSubUrl + '/dashboard/', + icon: "fa fa-th-large", + links: [ + { text: 'Settings', editview: 'settings', icon: "fa fa-cogs" }, + { text: 'Templating', editview: 'templating', icon: "fa fa-cogs" }, + { text: 'Annotations', editview: 'annotations', icon: "fa fa-bolt" }, + { text: 'Export', href:"", icon: "fa fa-bolt" }, + { text: 'JSON', href:"", icon: "fa fa-bolt" }, + ] + }, + { + text: "Account", href: $scope.getUrl("/account"), + icon: "fa fa-shield", + links: [ + { text: 'Info', href: $scope.getUrl("/account"), icon: "fa fa-sitemap" }, + { text: 'Data sources', href: $scope.getUrl("/account/datasources"), icon: "fa fa-sitemap" }, + { text: 'Users', href: $scope.getUrl("/account/users"), icon: "fa fa-users" }, + { text: 'API Keys', href: $scope.getUrl("/account/apikeys"), icon: "fa fa-key" }, + ] + }, + { + text: "Profile", href: $scope.getUrl("/profile"), + icon: "fa fa-user", + links: [ + { text: 'Info', href: $scope.getUrl("/profile"), icon: "fa fa-sitemap" }, + { text: 'Password', href:"", icon: "fa fa-lock" }, + ] + } + ]; + + $scope.onAppEvent('$routeUpdate', function() { + $scope.updateState(); + }); + + $scope.onAppEvent('$routeChangeSuccess', function() { + $scope.updateState(); + }); + + $scope.updateState = function() { + var currentPath = config.appSubUrl + $location.path(); + var search = $location.search(); + + _.each($scope.menu, function(item) { + item.active = false; + + if (item.href === currentPath) { + item.active = true; + } + + if (item.startsWith) { + if (currentPath.indexOf(item.startsWith) === 0) { + item.active = true; + item.href = currentPath; + } + } + + _.each(item.links, function(link) { + link.active = false; + + if (link.editview) { + var params = {}; + _.each(search, function(value, key) { + if (value !== null) { params[key] = value; } + }); + + params.editview = link.editview; + link.href = currentPath + '?' + $.param(params); + } + + if (link.href === currentPath) { + item.active = true; + link.active = true; + } + }); + }); + }; + + $scope.init = function() { + $scope.updateState(); + }; + }); + +}); diff --git a/src/app/directives/dashEditLink.js b/src/app/directives/dashEditLink.js index b0ac97d3423..dbafebae35c 100644 --- a/src/app/directives/dashEditLink.js +++ b/src/app/directives/dashEditLink.js @@ -5,6 +5,12 @@ define([ function (angular, $) { 'use strict'; + var editViewMap = { + 'settings': 'app/partials/dasheditor.html', + 'annotations': 'app/features/annotations/partials/editor.html', + 'templating': 'app/partials/templating_editor.html', + }; + angular .module('grafana.directives') .directive('dashEditorLink', function($timeout) { @@ -25,7 +31,7 @@ function (angular, $) { angular .module('grafana.directives') - .directive('dashEditorView', function($compile) { + .directive('dashEditorView', function($compile, $location) { return { restrict: 'A', link: function(scope, elem) { @@ -48,10 +54,9 @@ function (angular, $) { if (editorScope) { editorScope.dismiss(); } } - scope.onAppEvent("dashboard-loaded", hideEditorPane); - scope.onAppEvent('hide-dash-editor', hideEditorPane); + function showEditorPane(evt, payload, editview) { + if (editview) { payload.src = editViewMap[editview]; } - scope.onAppEvent('show-dash-editor', function(evt, payload) { if (lastEditor === payload.src) { hideEditorPane(); return; @@ -70,6 +75,14 @@ function (angular, $) { lastEditor = null; editorScope = null; hideScrollbars(false); + + if (editview) { + var urlParams = $location.search(); + if (editview === urlParams.editview) { + delete urlParams.editview; + $location.search(urlParams); + } + } }; // hide page scrollbars while edit pane is visible @@ -79,8 +92,18 @@ function (angular, $) { var view = $('
'); elem.append(view); $compile(elem.contents())(editorScope); + } + + scope.$watch("dashboardViewState.state.editview", function(newValue, oldValue) { + if (newValue) { + showEditorPane(null, {}, newValue); + } else if (oldValue) { + hideEditorPane(); + } }); + scope.onAppEvent('hide-dash-editor', hideEditorPane); + scope.onAppEvent('show-dash-editor', showEditorPane); } }; }); diff --git a/src/app/directives/tip.js b/src/app/directives/tip.js index 895015cb671..a0aac161bb2 100644 --- a/src/app/directives/tip.js +++ b/src/app/directives/tip.js @@ -18,6 +18,21 @@ function (angular, kbn) { }; }); + angular + .module('grafana.directives') + .directive('watchChange', function() { + return { + scope: { onchange: '&watchChange' }, + link: function(scope, element) { + element.on('input', function() { + scope.$apply(function () { + scope.onchange({ inputValue: element.val() }); + }); + }); + } + }; + }); + angular .module('grafana.directives') .directive('editorOptBool', function($compile) { diff --git a/src/app/features/account/accountCtrl.js b/src/app/features/account/accountCtrl.js new file mode 100644 index 00000000000..8d4130bda03 --- /dev/null +++ b/src/app/features/account/accountCtrl.js @@ -0,0 +1,29 @@ +define([ + 'angular', +], +function (angular) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('AccountCtrl', function($scope, $http, backendSrv) { + + $scope.init = function() { + $scope.getAccount(); + }; + + $scope.getAccount = function() { + backendSrv.get('/api/account').then(function(account) { + $scope.account = account; + }); + }; + + $scope.update = function() { + if (!$scope.accountForm.$valid) { return; } + backendSrv.put('/api/account', $scope.account).then($scope.getAccount); + }; + + $scope.init(); + + }); +}); diff --git a/src/app/features/account/accountUsersCtrl.js b/src/app/features/account/accountUsersCtrl.js index 560b8ff4bc6..65ad8bcf847 100644 --- a/src/app/features/account/accountUsersCtrl.js +++ b/src/app/features/account/accountUsersCtrl.js @@ -24,22 +24,12 @@ function (angular) { }; $scope.removeUser = function(user) { - backendSrv.request({ - method: 'DELETE', - url: '/api/account/users/' + user.userId - }).then($scope.get); + backendSrv.delete('/api/account/users/' + user.userId).then($scope.get); }; $scope.addUser = function() { - if (!$scope.form.$valid) { - return; - } - - backendSrv.request({ - method: 'PUT', - url: '/api/account/users', - data: $scope.user, - }).then($scope.get); + if (!$scope.form.$valid) { return; } + backendSrv.post('/api/account/users', $scope.user).then($scope.get); }; $scope.init(); diff --git a/src/app/features/account/apiKeysCtrl.js b/src/app/features/account/apiKeysCtrl.js index 8878edc3506..64df055dd90 100644 --- a/src/app/features/account/apiKeysCtrl.js +++ b/src/app/features/account/apiKeysCtrl.js @@ -26,12 +26,7 @@ function (angular) { }; $scope.addToken = function() { - backendSrv.request({ - method: 'PUT', - url: '/api/tokens', - data: $scope.token, - desc: 'Add token' - }).then($scope.getTokens); + backendSrv.post('/api/tokens', $scope.token).then($scope.getTokens); }; $scope.init(); diff --git a/src/app/features/account/partials/account.html b/src/app/features/account/partials/account.html new file mode 100644 index 00000000000..5465d0c7094 --- /dev/null +++ b/src/app/features/account/partials/account.html @@ -0,0 +1,37 @@ +
+ +
+ +
+
+ + Account information +
+
+ +
+
+
+
+
+
+
    +
  • + Account name +
  • +
  • + +
  • +
+
+
+
+
+ +
+
+
+
+
+ + diff --git a/src/app/features/account/partials/apikeys.html b/src/app/features/account/partials/apikeys.html index f286360a811..489cd85c743 100644 --- a/src/app/features/account/partials/apikeys.html +++ b/src/app/features/account/partials/apikeys.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/features/account/partials/datasources.html b/src/app/features/account/partials/datasources.html index 461537ba626..b715619262e 100644 --- a/src/app/features/account/partials/datasources.html +++ b/src/app/features/account/partials/datasources.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/features/account/partials/users.html b/src/app/features/account/partials/users.html index dc6becf45b6..819e5ef76ba 100644 --- a/src/app/features/account/partials/users.html +++ b/src/app/features/account/partials/users.html @@ -1,11 +1,11 @@ -
+
- Account users + Users
diff --git a/src/app/features/all.js b/src/app/features/all.js index 54946bb0fc2..de8e1ac3a32 100644 --- a/src/app/features/all.js +++ b/src/app/features/all.js @@ -12,6 +12,7 @@ define([ './account/datasourcesCtrl', './account/apiKeysCtrl', './account/importCtrl', + './account/accountCtrl', './admin/adminUsersCtrl', './grafanaDatasource/datasource', ], function () {}); diff --git a/src/app/features/dashboard/partials/panelTime.html b/src/app/features/dashboard/partials/panelTime.html new file mode 100644 index 00000000000..7b8b507a2ae --- /dev/null +++ b/src/app/features/dashboard/partials/panelTime.html @@ -0,0 +1,43 @@ +
+
+
+
    +
  • + +
  • +
  • + Override relative time +
  • +
  • + Last +
  • +
  • + +
  • +
+
+
+
+
    +
  • + +
  • +
  • + Add time shift +
  • +
  • + Amount +
  • +
  • + +
  • +
+
+
+
+
+ diff --git a/src/app/features/dashboard/timeSrv.js b/src/app/features/dashboard/timeSrv.js index 564f30de7fd..0d8dc31567b 100644 --- a/src/app/features/dashboard/timeSrv.js +++ b/src/app/features/dashboard/timeSrv.js @@ -19,12 +19,23 @@ define([ this.time = dashboard.time; this._initTimeFromUrl(); + this._parseTime(); if(this.dashboard.refresh) { this.set_interval(this.dashboard.refresh); } }; + this._parseTime = function() { + // when absolute time is saved in json it is turned to a string + if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) { + this.time.from = new Date(this.time.from); + } + if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) { + this.time.to = new Date(this.time.to); + } + }; + this._parseUrlParam = function(value) { if (value.indexOf('now') !== -1) { return value; @@ -109,9 +120,7 @@ define([ this.timeRange = function(parse) { var _t = this.time; - if(_.isUndefined(_t) || _.isUndefined(_t.from)) { - return false; - } + if(parse === false) { return { from: _t.from, diff --git a/src/app/features/dashboard/viewStateSrv.js b/src/app/features/dashboard/viewStateSrv.js index c08d3cdba6b..6ebb5d05fab 100644 --- a/src/app/features/dashboard/viewStateSrv.js +++ b/src/app/features/dashboard/viewStateSrv.js @@ -53,12 +53,13 @@ function (angular, _, $) { state.panelId = parseInt(state.panelId) || null; state.fullscreen = state.fullscreen ? true : null; state.edit = (state.edit === "true" || state.edit === true) || null; + state.editview = state.editview || null; return state; }; DashboardViewState.prototype.serializeToUrl = function() { var urlState = _.clone(this.state); - urlState.fullscreen = this.state.fullscreen ? true : null, + urlState.fullscreen = this.state.fullscreen ? true : null; urlState.edit = this.state.edit ? true : null; return urlState; }; diff --git a/src/app/features/graphite/datasource.js b/src/app/features/graphite/datasource.js index 639e150a320..a486250ac74 100644 --- a/src/app/features/graphite/datasource.js +++ b/src/app/features/graphite/datasource.js @@ -276,6 +276,7 @@ function (angular, _, $, config, kbn, moment) { targetValue = targets[this._seriesRefLetters[i]]; targetValue = targetValue.replace(regex, nestedSeriesRegexReplacer); + targets[this._seriesRefLetters[i]] = targetValue; clean_options.push("target=" + encodeURIComponent(targetValue)); } diff --git a/src/app/features/graphite/queryCtrl.js b/src/app/features/graphite/queryCtrl.js index 0654c474af5..d878386d461 100644 --- a/src/app/features/graphite/queryCtrl.js +++ b/src/app/features/graphite/queryCtrl.js @@ -293,7 +293,7 @@ function (angular, _, config, gfunc, Parser) { function MetricSegment(options) { if (options === '*' || options.value === '*') { this.value = '*'; - this.html = $sce.trustAsHtml(''); + this.html = $sce.trustAsHtml(''); this.expandable = true; return; } diff --git a/src/app/features/influxdb/datasource.js b/src/app/features/influxdb/datasource.js index 692fdbdbbf1..c867fb2a4a5 100644 --- a/src/app/features/influxdb/datasource.js +++ b/src/app/features/influxdb/datasource.js @@ -374,7 +374,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { var fromIsAbsolute = from[from.length-1] === 's'; if (until === 'now()' && !fromIsAbsolute) { - return 'time > now() - ' + from; + return 'time > ' + from; } return 'time > ' + from + ' and time < ' + until; @@ -382,14 +382,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { function getInfluxTime(date) { if (_.isString(date)) { - if (date === 'now') { - return 'now()'; - } - else if (date.indexOf('now') >= 0) { - return date.substring(4); - } - - date = kbn.parseDate(date); + return date.replace('now', 'now()'); } return to_utc_epoch_seconds(date); diff --git a/src/app/features/profile/partials/profile.html b/src/app/features/profile/partials/profile.html index 2f5df6146ed..800062c8453 100644 --- a/src/app/features/profile/partials/profile.html +++ b/src/app/features/profile/partials/profile.html @@ -7,51 +7,53 @@
- Your info + Personal information
+
+
+
+
+
    +
  • + Name +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Email +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Username +
  • +
  • + +
  • +
+
+
+
- - -
-
    -
  • - Name -
  • -
  • - -
  • -
-
-
-
-
    -
  • - Email -
  • -
  • - -
  • -
-
-
-
-
    -
  • - Username -
  • -
  • - -
  • -
-
-
- -
- -
+
+ + +
@@ -59,7 +61,7 @@
- Your accounts + Your accounts

@@ -87,7 +89,7 @@
- Add account + Add account

@@ -111,5 +113,8 @@
+ + + diff --git a/src/app/features/profile/profileCtrl.js b/src/app/features/profile/profileCtrl.js index 0a2ea70efe7..b028e48be1d 100644 --- a/src/app/features/profile/profileCtrl.js +++ b/src/app/features/profile/profileCtrl.js @@ -33,12 +33,11 @@ function (angular) { $scope.update = function() { if (!$scope.userForm.$valid) { return; } - - backendSrv.post('/api/user/', $scope.user); + backendSrv.put('/api/user/', $scope.user); }; $scope.createAccount = function() { - backendSrv.put('/api/account/', $scope.newAccount).then($scope.getUserAccounts); + backendSrv.post('/api/account/', $scope.newAccount).then($scope.getUserAccounts); }; $scope.init(); diff --git a/src/app/features/templating/templateValuesSrv.js b/src/app/features/templating/templateValuesSrv.js index 51b11dfb3fa..baff0a840b8 100644 --- a/src/app/features/templating/templateValuesSrv.js +++ b/src/app/features/templating/templateValuesSrv.js @@ -81,8 +81,8 @@ function (angular, _, kbn) { this._updateNonQueryVariable = function(variable) { // extract options in comma seperated string - variable.options = _.map(variable.query.split(/[\s,]+/), function(text) { - return { text: text, value: text }; + variable.options = _.map(variable.query.split(/[,]+/), function(text) { + return { text: text.trim(), value: text.trim() }; }); if (variable.type === 'interval') { diff --git a/src/app/panels/graph/graph.tooltip.js b/src/app/panels/graph/graph.tooltip.js index 9e750cb6a9c..9e0ff0b8c7b 100644 --- a/src/app/panels/graph/graph.tooltip.js +++ b/src/app/panels/graph/graph.tooltip.js @@ -71,7 +71,7 @@ function ($) { for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; - if (!series.data.length) { + if (!series.data.length || (scope.panel.legend.hideEmpty && series.allIsNull)) { results.push({ hidden: true }); continue; } @@ -163,7 +163,7 @@ function ($) { value = series.formatValue(hoverInfo.value); seriesHtml += '
'; - seriesHtml += ' ' + series.label + ':
'; + seriesHtml += ' ' + series.label + ':
'; seriesHtml += '
' + value + '
'; plot.highlight(i, hoverInfo.hoverIndex); } @@ -174,7 +174,7 @@ function ($) { else if (item) { series = seriesList[item.seriesIndex]; group = '
'; - group += ' ' + series.label + ':
'; + group += ' ' + series.label + ':
'; if (scope.panel.stack && scope.panel.tooltip.value_type === 'individual') { value = item.datapoint[1] - item.datapoint[2]; diff --git a/src/app/panels/graph/module.html b/src/app/panels/graph/module.html index fa205e488a6..20896fc5c83 100644 --- a/src/app/panels/graph/module.html +++ b/src/app/panels/graph/module.html @@ -3,6 +3,10 @@
+ + {{panelMeta.timeInfo}} + +
No datapoints No datapoints returned from metric query diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js index 7fde36b475f..56a15a7b72e 100644 --- a/src/app/panels/graph/module.js +++ b/src/app/panels/graph/module.js @@ -26,6 +26,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { $scope.panelMeta.addEditorTab('Axes & Grid', 'app/panels/graph/axisEditor.html'); $scope.panelMeta.addEditorTab('Display Styles', 'app/panels/graph/styleEditor.html'); + $scope.panelMeta.addEditorTab('Time range', 'app/features/dashboard/partials/panelTime.html'); $scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()'); $scope.panelMeta.addExtendedMenuItem('Toggle legend', '', 'toggleLegend()'); @@ -88,6 +89,9 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { value_type: 'cumulative', shared: false, }, + // time overrides + timeFrom: null, + timeShift: null, // metric queries targets: [{}], // series color overrides @@ -114,6 +118,26 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) { $scope.updateTimeRange = function () { $scope.range = timeSrv.timeRange(); $scope.rangeUnparsed = timeSrv.timeRange(false); + + $scope.panelMeta.timeInfo = ""; + + // check panel time overrrides + if ($scope.panel.timeFrom) { + if (_.isString($scope.rangeUnparsed.from)) { + $scope.panelMeta.timeInfo = "last " + $scope.panel.timeFrom; + $scope.rangeUnparsed.from = 'now-' + $scope.panel.timeFrom; + $scope.range.from = kbn.parseDate($scope.rangeUnparsed.from); + } + } + + if ($scope.panel.timeShift) { + var timeShift = '-' + $scope.panel.timeShift; + $scope.panelMeta.timeInfo += ' timeshift ' + timeShift; + $scope.range.from = kbn.parseDateMath(timeShift, $scope.range.from); + $scope.range.to = kbn.parseDateMath(timeShift, $scope.range.to); + $scope.rangeUnparsed = $scope.range; + } + if ($scope.panel.maxDataPoints) { $scope.resolution = $scope.panel.maxDataPoints; } diff --git a/src/app/panels/timepicker/editor.html b/src/app/panels/timepicker/editor.html index fc51d35ad9f..8771bcadb20 100644 --- a/src/app/panels/timepicker/editor.html +++ b/src/app/panels/timepicker/editor.html @@ -1,18 +1,44 @@
-
-
- - -
-
- - -
+
+
+
    +
  • + Relative time options +
  • +
  • + +
  • +
  • + Until +
  • +
  • + now- +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Auto-refresh options +
  • +
  • + +
  • +
+
+
-

-
- - For these changes to fully take effect save and reload the dashboard. -
-

-
+

+
+ + For these changes to fully take effect save and reload the dashboard. +
+

+
diff --git a/src/app/panels/timepicker/module.js b/src/app/panels/timepicker/module.js index eca4936ad00..2f0d9bd75b8 100644 --- a/src/app/panels/timepicker/module.js +++ b/src/app/panels/timepicker/module.js @@ -58,10 +58,14 @@ function (angular, app, _, moment, kbn) { $scope.init = function() { var time = timeSrv.timeRange(true); - if(time) { - $scope.panel.now = timeSrv.timeRange(false).to === "now" ? true : false; - $scope.time = getScopeTimeObj(time.from,time.to); + $scope.panel.now = false; + + var unparsed = timeSrv.timeRange(false); + if (_.isString(unparsed.to) && unparsed.to.indexOf('now') === 0) { + $scope.panel.now = true; } + + $scope.time = getScopeTimeObj(time.from, time.to); }; $scope.customTime = function() { @@ -142,6 +146,10 @@ function (angular, app, _, moment, kbn) { to: "now" }; + if ($scope.panel.nowDelay) { + _filter.to = 'now-' + $scope.panel.nowDelay; + } + timeSrv.setTime(_filter); $scope.time = getScopeTimeObj(kbn.parseDate(_filter.from),new Date()); diff --git a/src/app/partials/dashboard.html b/src/app/partials/dashboard.html index 5795c6e2c8f..d5118a3ee96 100644 --- a/src/app/partials/dashboard.html +++ b/src/app/partials/dashboard.html @@ -104,7 +104,7 @@
- + ADD ROW
diff --git a/src/app/partials/dashboard_topnav.html b/src/app/partials/dashboard_topnav.html index 0d3a8f30ee1..98261b1a94f 100644 --- a/src/app/partials/dashboard_topnav.html +++ b/src/app/partials/dashboard_topnav.html @@ -1,15 +1,22 @@ -