mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'origin/pro'
This commit is contained in:
commit
861e45aedf
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,7 +1,8 @@
|
||||
node_modules
|
||||
coverage/
|
||||
.aws-config.json
|
||||
dist
|
||||
/dist
|
||||
/tmp
|
||||
|
||||
# locally required config files
|
||||
web.config
|
||||
|
19
Gruntfile.js
19
Gruntfile.js
@ -11,6 +11,22 @@ module.exports = function (grunt) {
|
||||
docsDir: 'docs/'
|
||||
};
|
||||
|
||||
config.mode = grunt.option('mode') || 'standalone';
|
||||
config.modeOptions = {
|
||||
zipSuffix: '',
|
||||
requirejs: {
|
||||
paths: { config: '../config.sample' },
|
||||
excludeConfig: true,
|
||||
}
|
||||
};
|
||||
|
||||
if (config.mode === 'backend') {
|
||||
grunt.log.writeln('Setting backend build mode');
|
||||
config.modeOptions.zipSuffix = '-backend';
|
||||
config.modeOptions.requirejs.paths = {};
|
||||
config.modeOptions.requirejs.excludeConfig = false;
|
||||
}
|
||||
|
||||
// load plugins
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
@ -34,5 +50,4 @@ module.exports = function (grunt) {
|
||||
|
||||
// pass the config to grunt
|
||||
grunt.initConfig(config);
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
"company": "Coding Instinct AB"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "1.9.1",
|
||||
"version": "2.0.0-alpha",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/torkelo/grafana.git"
|
||||
@ -16,7 +16,7 @@
|
||||
"grunt-angular-templates": "^0.5.5",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-compress": "~0.13.0",
|
||||
"grunt-contrib-concat": "^0.4.0",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.5.0",
|
||||
@ -33,7 +33,7 @@
|
||||
"grunt-ng-annotate": "^0.9.2",
|
||||
"grunt-ngmin": "0.0.3",
|
||||
"grunt-string-replace": "~0.2.4",
|
||||
"grunt-usemin": "^2.1.1",
|
||||
"grunt-usemin": "3.0.0",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"karma": "~0.12.21",
|
||||
"karma-chrome-launcher": "~0.1.4",
|
||||
@ -65,6 +65,7 @@
|
||||
"dependencies": {
|
||||
"grunt-jscs": "^0.8.1",
|
||||
"karma-sinon": "^1.0.3",
|
||||
"lodash": "^2.4.1",
|
||||
"sinon": "^1.10.3"
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +49,7 @@ function (angular, $, _, appLevelRequire, config) {
|
||||
return module;
|
||||
};
|
||||
|
||||
app.config(function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
|
||||
$routeProvider.otherwise({ redirectTo: config.default_route });
|
||||
app.config(function($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
|
||||
// this is how the internet told me to dynamically add modules :/
|
||||
register_fns.controller = $controllerProvider.register;
|
||||
register_fns.directive = $compileProvider.directive;
|
||||
@ -68,7 +67,16 @@ function (angular, $, _, appLevelRequire, config) {
|
||||
'pasvaz.bindonce'
|
||||
];
|
||||
|
||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters'];
|
||||
|
||||
if (window.grafanaBackend) {
|
||||
module_types.push('routes');
|
||||
angular.module('grafana.routes.standalone', []);
|
||||
}
|
||||
else {
|
||||
module_types.push('routes.standalone');
|
||||
angular.module('grafana.routes', []);
|
||||
}
|
||||
|
||||
_.each(module_types, function (type) {
|
||||
var module_name = 'grafana.'+type;
|
||||
@ -85,7 +93,8 @@ function (angular, $, _, appLevelRequire, config) {
|
||||
'directives/all',
|
||||
'filters/all',
|
||||
'components/partials',
|
||||
'routes/standalone/default',
|
||||
'routes/standalone/all',
|
||||
'routes/backend/all',
|
||||
];
|
||||
|
||||
_.each(config.plugins.dependencies, function(dep) {
|
||||
|
12
src/app/components/config.js
Normal file
12
src/app/components/config.js
Normal file
@ -0,0 +1,12 @@
|
||||
define([
|
||||
'settings',
|
||||
],
|
||||
function (Settings) {
|
||||
"use strict";
|
||||
|
||||
var bootData = window.grafanaBootData;
|
||||
var options = bootData.settings;
|
||||
|
||||
return new Settings(options);
|
||||
|
||||
});
|
@ -6,6 +6,8 @@ function () {
|
||||
function PanelMeta(options) {
|
||||
this.description = options.description;
|
||||
this.fullscreen = options.fullscreen;
|
||||
this.editIcon = options.editIcon;
|
||||
this.panelName = options.panelName;
|
||||
this.menu = [];
|
||||
this.editorTabs = [];
|
||||
this.extendedMenu = [];
|
||||
|
100
src/app/components/require.backend.js
Normal file
100
src/app/components/require.backend.js
Normal file
@ -0,0 +1,100 @@
|
||||
|
||||
require.config({
|
||||
urlArgs: 'bust=' + (new Date().getTime()),
|
||||
baseUrl: 'public/app',
|
||||
|
||||
paths: {
|
||||
config: 'components/config',
|
||||
settings: 'components/settings',
|
||||
kbn: 'components/kbn',
|
||||
store: 'components/store',
|
||||
|
||||
css: '../vendor/require/css',
|
||||
text: '../vendor/require/text',
|
||||
moment: '../vendor/moment',
|
||||
filesaver: '../vendor/filesaver',
|
||||
angular: '../vendor/angular/angular',
|
||||
'angular-route': '../vendor/angular/angular-route',
|
||||
'angular-sanitize': '../vendor/angular/angular-sanitize',
|
||||
'angular-dragdrop': '../vendor/angular/angular-dragdrop',
|
||||
'angular-strap': '../vendor/angular/angular-strap',
|
||||
timepicker: '../vendor/angular/timepicker',
|
||||
datepicker: '../vendor/angular/datepicker',
|
||||
bindonce: '../vendor/angular/bindonce',
|
||||
crypto: '../vendor/crypto.min',
|
||||
spectrum: '../vendor/spectrum',
|
||||
|
||||
lodash: 'components/lodash.extended',
|
||||
'lodash-src': '../vendor/lodash',
|
||||
bootstrap: '../vendor/bootstrap/bootstrap',
|
||||
|
||||
jquery: '../vendor/jquery/jquery-2.1.1.min',
|
||||
|
||||
'extend-jquery': 'components/extend-jquery',
|
||||
|
||||
'jquery.flot': '../vendor/jquery/jquery.flot',
|
||||
'jquery.flot.pie': '../vendor/jquery/jquery.flot.pie',
|
||||
'jquery.flot.events': '../vendor/jquery/jquery.flot.events',
|
||||
'jquery.flot.selection': '../vendor/jquery/jquery.flot.selection',
|
||||
'jquery.flot.stack': '../vendor/jquery/jquery.flot.stack',
|
||||
'jquery.flot.stackpercent':'../vendor/jquery/jquery.flot.stackpercent',
|
||||
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
|
||||
'jquery.flot.crosshair': '../vendor/jquery/jquery.flot.crosshair',
|
||||
'jquery.flot.fillbelow': '../vendor/jquery/jquery.flot.fillbelow',
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
|
||||
'bootstrap-tagsinput': '../vendor/tagsinput/bootstrap-tagsinput',
|
||||
},
|
||||
shim: {
|
||||
|
||||
spectrum: {
|
||||
deps: ['jquery']
|
||||
},
|
||||
|
||||
crypto: {
|
||||
exports: 'Crypto'
|
||||
},
|
||||
|
||||
angular: {
|
||||
deps: ['jquery','config'],
|
||||
exports: 'angular'
|
||||
},
|
||||
|
||||
bootstrap: {
|
||||
deps: ['jquery']
|
||||
},
|
||||
|
||||
modernizr: {
|
||||
exports: 'Modernizr'
|
||||
},
|
||||
|
||||
jquery: {
|
||||
exports: 'jQuery'
|
||||
},
|
||||
|
||||
// simple dependency declaration
|
||||
//
|
||||
'jquery.flot': ['jquery'],
|
||||
'jquery.flot.pie': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.events': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.selection':['jquery', 'jquery.flot'],
|
||||
'jquery.flot.stack': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.stackpercent':['jquery', 'jquery.flot'],
|
||||
'jquery.flot.time': ['jquery', 'jquery.flot'],
|
||||
'jquery.flot.crosshair':['jquery', 'jquery.flot'],
|
||||
'jquery.flot.fillbelow':['jquery', 'jquery.flot'],
|
||||
'angular-dragdrop': ['jquery', 'angular'],
|
||||
'angular-mocks': ['angular'],
|
||||
'angular-sanitize': ['angular'],
|
||||
'angular-route': ['angular'],
|
||||
'angular-strap': ['angular', 'bootstrap','timepicker', 'datepicker'],
|
||||
'bindonce': ['angular'],
|
||||
|
||||
timepicker: ['jquery', 'bootstrap'],
|
||||
datepicker: ['jquery', 'bootstrap'],
|
||||
|
||||
'bootstrap-tagsinput': ['jquery'],
|
||||
},
|
||||
waitSeconds: 60,
|
||||
});
|
@ -1,9 +1,6 @@
|
||||
/**
|
||||
* Bootstrap require with the needed config, then load the app.js module.
|
||||
*/
|
||||
require.config({
|
||||
baseUrl: 'app',
|
||||
urlArgs: 'bust=' + (new Date().getTime()),
|
||||
baseUrl: 'app',
|
||||
|
||||
paths: {
|
||||
config: ['../config', '../config.sample'],
|
||||
@ -47,7 +44,6 @@ require.config({
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
|
||||
'bootstrap-tagsinput': '../vendor/tagsinput/bootstrap-tagsinput',
|
||||
|
||||
},
|
||||
shim: {
|
||||
|
@ -16,24 +16,22 @@ function (_, crypto) {
|
||||
datasources : {},
|
||||
window_title_prefix : 'Grafana - ',
|
||||
panels : {
|
||||
'graph': { path: 'panels/graph' },
|
||||
'singlestat': { path: 'panels/singlestat' },
|
||||
'text': { path: 'panels/text' }
|
||||
'graph': { path: 'panels/graph', name: 'Graph' },
|
||||
'singlestat': { path: 'panels/singlestat', name: 'Single stat' },
|
||||
'text': { path: 'panels/text', name: 'Text' },
|
||||
'dashlist': { path: 'panels/dashlist', name: 'Dashboard list' },
|
||||
},
|
||||
plugins : {},
|
||||
default_route : '/dashboard/file/default.json',
|
||||
playlist_timespan : "1m",
|
||||
unsaved_changes_warning : true,
|
||||
search : { max_results: 100 },
|
||||
admin : {}
|
||||
new_panel_title: 'no title (click here)',
|
||||
plugins: {},
|
||||
default_route: '/dashboard/file/default.json',
|
||||
playlist_timespan: "1m",
|
||||
unsaved_changes_warning: true,
|
||||
search: { max_results: 100 },
|
||||
admin: {},
|
||||
appSubUrl: ""
|
||||
};
|
||||
|
||||
// This initializes a new hash on purpose, to avoid adding parameters to
|
||||
// config.js without providing sane defaults
|
||||
var settings = {};
|
||||
_.each(defaults, function(value, key) {
|
||||
settings[key] = typeof options[key] !== 'undefined' ? options[key] : defaults[key];
|
||||
});
|
||||
var settings = _.extend({}, defaults, options);
|
||||
|
||||
var parseBasicAuth = function(datasource) {
|
||||
var passwordEnd = datasource.url.indexOf('@');
|
||||
|
@ -8,9 +8,15 @@ define([], function() {
|
||||
set: function(key, value) {
|
||||
window.localStorage[key] = value;
|
||||
},
|
||||
getBool: function(key) {
|
||||
getBool: function(key, def) {
|
||||
if (def !== void 0 && !this.exists(key)) {
|
||||
return def;
|
||||
}
|
||||
return window.localStorage[key] === 'true' ? true : false;
|
||||
},
|
||||
exists: function(key) {
|
||||
return window.localStorage[key] !== void 0;
|
||||
},
|
||||
delete: function(key) {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
|
@ -6,4 +6,7 @@ define([
|
||||
'./graphiteImport',
|
||||
'./inspectCtrl',
|
||||
'./jsonEditorCtrl',
|
||||
'./loginCtrl',
|
||||
'./sidemenuCtrl',
|
||||
'./errorCtrl',
|
||||
], function () {});
|
||||
|
22
src/app/controllers/errorCtrl.js
Normal file
22
src/app/controllers/errorCtrl.js
Normal file
@ -0,0 +1,22 @@
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'lodash'
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ErrorCtrl', function($scope) {
|
||||
|
||||
var showSideMenu = $scope.grafana.sidemenu;
|
||||
$scope.grafana.sidemenu = false;
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
$scope.grafana.sidemenu = showSideMenu;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -10,32 +10,47 @@ function (angular, config, _, $, store) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('GrafanaCtrl', function($scope, alertSrv, utilSrv, grafanaVersion, $rootScope, $controller) {
|
||||
|
||||
$scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion;
|
||||
$scope._ = _;
|
||||
$rootScope.profilingEnabled = store.getBool('profilingEnabled');
|
||||
$rootScope.performance = { loadStart: new Date().getTime() };
|
||||
module.controller('GrafanaCtrl', function($scope, alertSrv, utilSrv, grafanaVersion, $rootScope, $controller, userSrv, $timeout) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.grafana = {};
|
||||
$scope.grafana.version = grafanaVersion;
|
||||
$scope._ = _;
|
||||
|
||||
$rootScope.profilingEnabled = store.getBool('profilingEnabled');
|
||||
$rootScope.performance = { loadStart: new Date().getTime() };
|
||||
$rootScope.appSubUrl = config.appSubUrl;
|
||||
|
||||
if ($rootScope.profilingEnabled) { $scope.initProfiling(); }
|
||||
|
||||
alertSrv.init();
|
||||
utilSrv.init();
|
||||
|
||||
$scope.dashAlerts = alertSrv;
|
||||
$scope.grafana = { style: 'dark' };
|
||||
};
|
||||
$scope.grafana.lightTheme = false;
|
||||
$scope.grafana.user = userSrv.getSignedInUser();
|
||||
$scope.grafana.sidemenu = store.getBool('grafana.sidemenu');
|
||||
$scope.topnav = { title: 'Grafana' };
|
||||
|
||||
$scope.toggleConsole = function() {
|
||||
$scope.consoleEnabled = !$scope.consoleEnabled;
|
||||
store.set('grafanaConsole', $scope.consoleEnabled);
|
||||
$scope.onAppEvent('logged-out', function() {
|
||||
$scope.grafana.sidemenu = false;
|
||||
$scope.grafana.user = {};
|
||||
});
|
||||
};
|
||||
|
||||
$scope.initDashboard = function(dashboardData, viewScope) {
|
||||
$controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
|
||||
};
|
||||
|
||||
$scope.toggleSideMenu = function() {
|
||||
$scope.grafana.sidemenu = !$scope.grafana.sidemenu;
|
||||
store.set('grafana.sidemenu', $scope.grafana.sidemenu);
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$broadcast("render");
|
||||
}, 50);
|
||||
};
|
||||
|
||||
$rootScope.onAppEvent = function(name, callback) {
|
||||
var unbind = $rootScope.$on(name, callback);
|
||||
this.$on('$destroy', unbind);
|
||||
@ -81,7 +96,11 @@ function (angular, config, _, $, store) {
|
||||
$scope.initProfiling = function() {
|
||||
var count = 0;
|
||||
|
||||
$scope.$watch(function digestCounter() { count++; }, function() { });
|
||||
$scope.$watch(function digestCounter() {
|
||||
count++;
|
||||
}, function() {
|
||||
});
|
||||
|
||||
$scope.onAppEvent('dashboard-loaded', function() {
|
||||
count = 0;
|
||||
|
||||
|
103
src/app/controllers/loginCtrl.js
Normal file
103
src/app/controllers/loginCtrl.js
Normal file
@ -0,0 +1,103 @@
|
||||
define([
|
||||
'angular',
|
||||
'config',
|
||||
],
|
||||
function (angular, config) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('LoginCtrl', function($scope, backendSrv) {
|
||||
$scope.formModel = {
|
||||
user: '',
|
||||
email: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
$scope.grafana.sidemenu = false;
|
||||
|
||||
$scope.googleAuthEnabled = config.googleAuthEnabled;
|
||||
$scope.githubAuthEnabled = config.githubAuthEnabled;
|
||||
$scope.disableUserSignUp = config.disableUserSignUp;
|
||||
|
||||
$scope.loginMode = true;
|
||||
$scope.submitBtnClass = 'btn-inverse';
|
||||
$scope.submitBtnText = 'Log in';
|
||||
$scope.strengthClass = '';
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.$watch("loginMode", $scope.loginModeChanged);
|
||||
$scope.passwordChanged();
|
||||
};
|
||||
|
||||
// build info view model
|
||||
$scope.buildInfo = {
|
||||
version: config.buildInfo.version,
|
||||
commit: config.buildInfo.commit,
|
||||
buildstamp: new Date(config.buildInfo.buildstamp * 1000)
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
if ($scope.loginMode) {
|
||||
$scope.login();
|
||||
} else {
|
||||
$scope.signUp();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.loginModeChanged = function(newValue) {
|
||||
$scope.submitBtnText = newValue ? 'Log in' : 'Sign up';
|
||||
};
|
||||
|
||||
$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.post('/api/user/signup', $scope.formModel).then(function() {
|
||||
window.location.href = config.appSubUrl + '/';
|
||||
});
|
||||
};
|
||||
|
||||
$scope.login = function() {
|
||||
delete $scope.loginError;
|
||||
|
||||
if (!$scope.loginForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.post('/login', $scope.formModel).then(function(result) {
|
||||
if (result.redirectUrl) {
|
||||
window.location.href = result.redirectUrl;
|
||||
} else {
|
||||
window.location.href = config.appSubUrl + '/';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -64,33 +64,18 @@ function (angular, _, config, $) {
|
||||
$scope.selectedIndex = Math.max(Math.min($scope.selectedIndex + direction, $scope.resultCount - 1), 0);
|
||||
};
|
||||
|
||||
$scope.goToDashboard = function(id) {
|
||||
$scope.goToDashboard = function(slug) {
|
||||
$location.search({});
|
||||
$location.path("/dashboard/db/" + id);
|
||||
};
|
||||
|
||||
$scope.shareDashboard = function(title, id, $event) {
|
||||
$event.stopPropagation();
|
||||
var baseUrl = window.location.href.replace(window.location.hash,'');
|
||||
|
||||
$scope.share = {
|
||||
title: title,
|
||||
url: baseUrl + '#dashboard/db/' + encodeURIComponent(id)
|
||||
};
|
||||
$location.path("/dashboard/db/" + slug);
|
||||
};
|
||||
|
||||
$scope.searchDashboards = function(queryString) {
|
||||
// bookeeping for determining stale search requests
|
||||
var searchId = $scope.currentSearchId + 1;
|
||||
$scope.currentSearchId = searchId > $scope.currentSearchId ? searchId : $scope.currentSearchId;
|
||||
$scope.currentSearchId = $scope.currentSearchId + 1;
|
||||
var localSearchId = $scope.currentSearchId;
|
||||
|
||||
return $scope.db.searchDashboards(queryString)
|
||||
.then(function(results) {
|
||||
// since searches are async, it's possible that these results are not for the latest search. throw
|
||||
// them away if so
|
||||
if (searchId < $scope.currentSearchId) {
|
||||
return;
|
||||
}
|
||||
if (localSearchId < $scope.currentSearchId) { return; }
|
||||
|
||||
$scope.tagsOnly = results.tagsOnly;
|
||||
$scope.results.dashboards = results.dashboards;
|
||||
@ -125,11 +110,11 @@ function (angular, _, config, $) {
|
||||
|
||||
$scope.deleteDashboard = function(dash, evt) {
|
||||
evt.stopPropagation();
|
||||
$scope.appEvent('delete-dashboard', { id: dash.id, title: dash.title });
|
||||
$scope.appEvent('delete-dashboard', { slug: dash.slug, title: dash.title });
|
||||
};
|
||||
|
||||
$scope.dashboardDeleted = function(evt, id) {
|
||||
var dash = _.findWhere($scope.results.dashboards, {id: id});
|
||||
$scope.dashboardDeleted = function(evt, payload) {
|
||||
var dash = _.findWhere($scope.results.dashboards, { slug: payload.slug });
|
||||
$scope.results.dashboards = _.without($scope.results.dashboards, dash);
|
||||
};
|
||||
|
||||
@ -154,7 +139,7 @@ function (angular, _, config, $) {
|
||||
};
|
||||
|
||||
$scope.newDashboard = function() {
|
||||
$location.url('/dashboard/file/empty.json');
|
||||
$location.url('dashboard/new');
|
||||
};
|
||||
|
||||
});
|
||||
|
107
src/app/controllers/sidemenuCtrl.js
Normal file
107
src/app/controllers/sidemenuCtrl.js
Normal file
@ -0,0 +1,107 @@
|
||||
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 = [];
|
||||
$scope.menu.push({
|
||||
text: "Dashbords",
|
||||
icon: "fa fa-th-large",
|
||||
href: $scope.getUrl("/"),
|
||||
//startsWith: config.appSubUrl + '/dashboard/',
|
||||
});
|
||||
|
||||
if ($scope.grafana.user.accountRole === 'Admin') {
|
||||
$scope.menu.push({
|
||||
text: "Data Sources",
|
||||
icon: "fa fa-database",
|
||||
href: $scope.getUrl("/account/datasources"),
|
||||
});
|
||||
$scope.menu.push({
|
||||
text: "Account", href: $scope.getUrl("/account"),
|
||||
requireRole: "Admin",
|
||||
icon: "fa fa-shield",
|
||||
});
|
||||
$scope.menu.push({
|
||||
text: "Users", href: $scope.getUrl("/account/users"),
|
||||
requireRole: "Admin",
|
||||
icon: "fa fa-users",
|
||||
});
|
||||
$scope.menu.push({
|
||||
text: "API Keys", href: $scope.getUrl("/account/apikeys"),
|
||||
requireRole: "Admin",
|
||||
icon: "fa fa-key",
|
||||
});
|
||||
}
|
||||
|
||||
if ($scope.grafana.user.isGrafanaAdmin) {
|
||||
$scope.menu.push({
|
||||
text: "Admin", href: $scope.getUrl("/admin/users"),
|
||||
icon: "fa fa-cube",
|
||||
requireSignedIn: true,
|
||||
links: [
|
||||
{ text: 'Settings', href: $scope.getUrl("/admin/settings")},
|
||||
{ text: 'Users', href: $scope.getUrl("/admin/users"), icon: "fa fa-lock" },
|
||||
{ text: 'Log', href: "", icon: "fa fa-lock" },
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
$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();
|
||||
};
|
||||
});
|
||||
|
||||
});
|
@ -1,7 +1,6 @@
|
||||
define([
|
||||
'./arrayJoin',
|
||||
'./dashUpload',
|
||||
'./grafanaPanel',
|
||||
'./grafanaSimplePanel',
|
||||
'./ngBlur',
|
||||
'./dashEditLink',
|
||||
@ -16,4 +15,5 @@ define([
|
||||
'./graphiteSegment',
|
||||
'./grafanaVersionCheck',
|
||||
'./dropdown.typeahead',
|
||||
'./topnav',
|
||||
], function () {});
|
||||
|
@ -5,6 +5,12 @@ define([
|
||||
function (angular, $) {
|
||||
'use strict';
|
||||
|
||||
var editViewMap = {
|
||||
'settings': { src: 'app/partials/dasheditor.html', title: "Settings" },
|
||||
'annotations': { src: 'app/features/annotations/partials/editor.html', title: "Annotations" },
|
||||
'templating': { src: 'app/partials/templating_editor.html', title: "Templating" }
|
||||
};
|
||||
|
||||
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,12 @@ function (angular, $) {
|
||||
if (editorScope) { editorScope.dismiss(); }
|
||||
}
|
||||
|
||||
scope.$on("$destroy", hideEditorPane);
|
||||
scope.onAppEvent('hide-dash-editor', hideEditorPane);
|
||||
function showEditorPane(evt, payload, editview) {
|
||||
if (editview) {
|
||||
scope.grafana.editview = editViewMap[editview];
|
||||
payload.src = scope.grafana.editview.src;
|
||||
}
|
||||
|
||||
scope.onAppEvent('show-dash-editor', function(evt, payload) {
|
||||
if (lastEditor === payload.src) {
|
||||
hideEditorPane();
|
||||
return;
|
||||
@ -65,23 +73,43 @@ function (angular, $) {
|
||||
editorScope = payload.scope ? payload.scope.$new() : scope.$new();
|
||||
|
||||
editorScope.dismiss = function() {
|
||||
console.log('dismiss: ');
|
||||
editorScope.$destroy();
|
||||
elem.empty();
|
||||
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
|
||||
hideScrollbars(true);
|
||||
|
||||
var src = "'" + payload.src + "'";
|
||||
var view = $('<div class="dashboard-edit-view" ng-include="' + src + '"></div>');
|
||||
var view = $('<div class="gf-box" ng-include="' + src + '"></div>');
|
||||
elem.append(view);
|
||||
$compile(elem.contents())(editorScope);
|
||||
}
|
||||
|
||||
scope.$watch("dashboardViewState.state.editview", function(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
showEditorPane(null, {}, newValue);
|
||||
} else if (oldValue) {
|
||||
scope.grafana.editview = null;
|
||||
hideEditorPane();
|
||||
}
|
||||
});
|
||||
|
||||
scope.grafana.editview = null;
|
||||
scope.$on("$destroy", hideEditorPane);
|
||||
scope.onAppEvent('hide-dash-editor', hideEditorPane);
|
||||
scope.onAppEvent('show-dash-editor', showEditorPane);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,89 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config',
|
||||
'./panelMenu',
|
||||
],
|
||||
function (angular, $, config) {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('grafana.directives')
|
||||
.directive('grafanaPanel', function($compile, $parse) {
|
||||
|
||||
var container = '<div class="panel-container"></div>';
|
||||
var content = '<div class="panel-content"></div>';
|
||||
|
||||
var panelHeader =
|
||||
'<div class="panel-header">'+
|
||||
'<span class="alert-error panel-error small pointer"' +
|
||||
'config-modal="app/partials/inspector.html" ng-if="panelMeta.error">' +
|
||||
'<span data-placement="top" bs-tooltip="panelMeta.error">' +
|
||||
'<i class="fa fa-exclamation"></i><span class="panel-error-arrow"></span>' +
|
||||
'</span>' +
|
||||
'</span>' +
|
||||
|
||||
'<span class="panel-loading" ng-show="panelMeta.loading">' +
|
||||
'<i class="fa fa-spinner fa-spin"></i>' +
|
||||
'</span>' +
|
||||
|
||||
'<div class="panel-title-container drag-handle" panel-menu></div>' +
|
||||
'</div>'+
|
||||
'</div>';
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function($scope, elem, attr) {
|
||||
var getter = $parse(attr.type), panelType = getter($scope);
|
||||
var newScope = $scope.$new();
|
||||
|
||||
$scope.kbnJqUiDraggableOptions = {
|
||||
revert: 'invalid',
|
||||
helper: function() {
|
||||
return $('<div style="width:200px;height:100px;background: rgba(100,100,100,0.50);"/>');
|
||||
},
|
||||
placeholder: 'keep'
|
||||
};
|
||||
|
||||
// compile the module and uncloack. We're done
|
||||
function loadModule($module) {
|
||||
$module.appendTo(elem);
|
||||
elem.wrap(container);
|
||||
/* jshint indent:false */
|
||||
$compile(elem.contents())(newScope);
|
||||
elem.removeClass("ng-cloak");
|
||||
|
||||
var panelCtrlElem = $(elem.children()[0]);
|
||||
var panelCtrlScope = panelCtrlElem.data().$scope;
|
||||
|
||||
panelCtrlScope.$watchGroup(['fullscreen', 'panel.height', 'row.height'], function() {
|
||||
panelCtrlElem.css({ minHeight: panelCtrlScope.panel.height || panelCtrlScope.row.height });
|
||||
panelCtrlElem.toggleClass('panel-fullscreen', panelCtrlScope.fullscreen ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
newScope.$on('$destroy',function() {
|
||||
elem.unbind();
|
||||
elem.remove();
|
||||
});
|
||||
|
||||
elem.addClass('ng-cloak');
|
||||
|
||||
var panelPath = config.panels[panelType].path;
|
||||
|
||||
$scope.require([
|
||||
'jquery',
|
||||
'text!'+panelPath+'/module.html',
|
||||
panelPath + "/module",
|
||||
], function ($, moduleTemplate) {
|
||||
var $module = $(moduleTemplate);
|
||||
$module.prepend(panelHeader);
|
||||
$module.first().find('.panel-header').nextAll().wrapAll(content);
|
||||
loadModule($module);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
@ -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) {
|
||||
|
48
src/app/directives/topnav.js
Normal file
48
src/app/directives/topnav.js
Normal file
@ -0,0 +1,48 @@
|
||||
define([
|
||||
'angular',
|
||||
'kbn'
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('grafana.directives')
|
||||
.directive('topnav', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: "@",
|
||||
section: "@",
|
||||
titleAction: "&",
|
||||
toggle: "&",
|
||||
showMenuBtn: "=",
|
||||
},
|
||||
template:
|
||||
'<div class="navbar navbar-static-top"><div class="navbar-inner"><div class="container-fluid">' +
|
||||
'<div class="top-nav">' +
|
||||
'<a class="top-nav-menu-btn pointer" ng-if="showMenuBtn" ng-click="toggle()">' +
|
||||
'<img class="logo-icon" src="img/fav32.png"></img> ' +
|
||||
'<i class="fa fa-angle-right"></i>' +
|
||||
'</a>' +
|
||||
|
||||
'<span class="top-nav-breadcrumb">' +
|
||||
'<i class="top-nav-icon" ng-class="icon"></i>' +
|
||||
'</span>' +
|
||||
|
||||
'<span class="top-nav-section" ng-show="section">' +
|
||||
'{{section}}' +
|
||||
'<i class="fa fa-angle-right"></i>' +
|
||||
'</span>' +
|
||||
|
||||
'<a ng-click="titleAction()" class="top-nav-title">' +
|
||||
'{{title}}' +
|
||||
'</a>' +
|
||||
'</div><div ng-transclude></div></div></div></div>',
|
||||
link: function(scope, elem, attrs) {
|
||||
scope.icon = attrs.icon;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
29
src/app/features/account/accountCtrl.js
Normal file
29
src/app/features/account/accountCtrl.js
Normal file
@ -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();
|
||||
|
||||
});
|
||||
});
|
38
src/app/features/account/accountUsersCtrl.js
Normal file
38
src/app/features/account/accountUsersCtrl.js
Normal file
@ -0,0 +1,38 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AccountUsersCtrl', function($scope, $http, backendSrv) {
|
||||
|
||||
$scope.user = {
|
||||
loginOrEmail: '',
|
||||
role: 'Viewer',
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.get();
|
||||
};
|
||||
|
||||
$scope.get = function() {
|
||||
backendSrv.get('/api/account/users').then(function(users) {
|
||||
$scope.users = users;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeUser = function(user) {
|
||||
backendSrv.delete('/api/account/users/' + user.userId).then($scope.get);
|
||||
};
|
||||
|
||||
$scope.addUser = function() {
|
||||
if (!$scope.form.$valid) { return; }
|
||||
backendSrv.post('/api/account/users', $scope.user).then($scope.get);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
35
src/app/features/account/apiKeysCtrl.js
Normal file
35
src/app/features/account/apiKeysCtrl.js
Normal file
@ -0,0 +1,35 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ApiKeysCtrl', function($scope, $http, backendSrv) {
|
||||
|
||||
$scope.roleTypes = ['Viewer', 'Editor', 'Admin'];
|
||||
$scope.token = { role: 'Viewer' };
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.getTokens();
|
||||
};
|
||||
|
||||
$scope.getTokens = function() {
|
||||
backendSrv.get('/api/auth/keys').then(function(tokens) {
|
||||
$scope.tokens = tokens;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeToken = function(id) {
|
||||
backendSrv.delete('/api/auth/keys/'+id).then($scope.getTokens);
|
||||
};
|
||||
|
||||
$scope.addToken = function() {
|
||||
backendSrv.post('/api/auth/keys', $scope.token).then($scope.getTokens);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
88
src/app/features/account/datasourcesCtrl.js
Normal file
88
src/app/features/account/datasourcesCtrl.js
Normal file
@ -0,0 +1,88 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('DataSourcesCtrl', function($scope, $http, backendSrv) {
|
||||
|
||||
var defaults = {
|
||||
name: '',
|
||||
type: 'graphite',
|
||||
url: '',
|
||||
access: 'proxy'
|
||||
};
|
||||
|
||||
$scope.types = [
|
||||
{ name: 'Graphite', type: 'graphite' },
|
||||
{ name: 'InfluxDB', type: 'influxdb' },
|
||||
{ name: 'Elasticsearch', type: 'elasticsearch' },
|
||||
{ name: 'OpenTSDB', type: 'opentsdb' },
|
||||
];
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.reset();
|
||||
$scope.editor = {index: 0};
|
||||
$scope.datasources = [];
|
||||
$scope.getDatasources();
|
||||
|
||||
$scope.$watch('editor.index', function(newVal) {
|
||||
if (newVal !== 2) {
|
||||
$scope.reset();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.current = angular.copy(defaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.edit = function(ds) {
|
||||
$scope.current = ds;
|
||||
$scope.currentIsNew = false;
|
||||
$scope.editor.index = 2;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.reset();
|
||||
$scope.editor.index = 0;
|
||||
};
|
||||
|
||||
$scope.getDatasources = function() {
|
||||
backendSrv.get('/api/datasources').then(function(results) {
|
||||
$scope.datasources = results;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.remove = function(ds) {
|
||||
backendSrv.delete('/api/datasources/' + ds.id).then(function() {
|
||||
$scope.getDatasources();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
backendSrv.post('/api/datasources', $scope.current).then(function() {
|
||||
$scope.editor.index = 0;
|
||||
$scope.getDatasources();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.add = function() {
|
||||
if (!$scope.editForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
backendSrv.put('/api/datasources', $scope.current)
|
||||
.then(function() {
|
||||
$scope.editor.index = 0;
|
||||
$scope.getDatasources();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
78
src/app/features/account/importCtrl.js
Normal file
78
src/app/features/account/importCtrl.js
Normal file
@ -0,0 +1,78 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
],
|
||||
function (angular, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ImportCtrl', function($scope, $http, backendSrv, datasourceSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.datasources = [];
|
||||
$scope.sourceName = 'grafana';
|
||||
$scope.destName = 'grafana';
|
||||
$scope.imported = [];
|
||||
$scope.dashboards = [];
|
||||
$scope.infoText = '';
|
||||
$scope.importing = false;
|
||||
|
||||
_.each(datasourceSrv.getAll(), function(ds) {
|
||||
if (ds.type === 'influxdb' || ds.type === 'elasticsearch') {
|
||||
$scope.sourceName = ds.name;
|
||||
$scope.datasources.push(ds.name);
|
||||
} else if (ds.type === 'grafana') {
|
||||
$scope.datasources.push(ds.name);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.startImport = function() {
|
||||
$scope.sourceDs = datasourceSrv.get($scope.sourceName);
|
||||
$scope.destDs = datasourceSrv.get($scope.destName);
|
||||
|
||||
$scope.sourceDs.searchDashboards('title:').then(function(results) {
|
||||
$scope.dashboards = results.dashboards;
|
||||
|
||||
if ($scope.dashboards.length === 0) {
|
||||
$scope.infoText = 'No dashboards found';
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.importing = true;
|
||||
$scope.imported = [];
|
||||
$scope.next();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.next = function() {
|
||||
if ($scope.dashboards.length === 0) {
|
||||
$scope.infoText = "Done! Imported " + $scope.imported.length + " dashboards";
|
||||
}
|
||||
|
||||
var dash = $scope.dashboards.shift();
|
||||
if (!dash.title) {
|
||||
console.log(dash);
|
||||
return;
|
||||
}
|
||||
|
||||
var infoObj = {name: dash.title, info: 'Importing...'};
|
||||
$scope.imported.push(infoObj);
|
||||
$scope.infoText = "Importing " + $scope.imported.length + '/' + ($scope.imported.length + $scope.dashboards.length);
|
||||
|
||||
$scope.sourceDs.getDashboard(dash.id).then(function(loadedDash) {
|
||||
$scope.destDs.saveDashboard(loadedDash).then(function() {
|
||||
infoObj.info = "Done!";
|
||||
$scope.next();
|
||||
}, function(err) {
|
||||
infoObj.info = "Error: " + err;
|
||||
$scope.next();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
35
src/app/features/account/partials/account.html
Normal file
35
src/app/features/account/partials/account.html
Normal file
@ -0,0 +1,35 @@
|
||||
<topnav toggle="toggleSideMenu()" icon="fa fa-shield" section="Account" show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="row editor-row">
|
||||
<div class="section">
|
||||
<form name="accountForm">
|
||||
<div>
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 120px">
|
||||
<strong>Account name</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" required ng-model="account.name" class="input-xlarge tight-form-input last" >
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<panel-loader type="'test'"></panel-loader>
|
||||
|
||||
<br>
|
||||
<button type="submit" class="pull-right btn btn-success" ng-click="update()">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
55
src/app/features/account/partials/apikeys.html
Normal file
55
src/app/features/account/partials/apikeys.html
Normal file
@ -0,0 +1,55 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="API Keys"
|
||||
icon="fa fa-shield"
|
||||
section="Account"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<form name="addTokenrForm" class="form-inline tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
Add a Token
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-xlarge tight-form-input" ng-model='token.name' placeholder="Name"></input>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Role
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="token.role" ng-options="r for r in roleTypes"></select>
|
||||
</li>
|
||||
<button class="btn btn-success tight-form-btn" ng-click="addToken()">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="editor-row row">
|
||||
<div class="section span6">
|
||||
<table class="grafana-options-table">
|
||||
<tr ng-repeat="t in tokens">
|
||||
<td>{{t.name}}</td>
|
||||
<td>{{t.role}}</td>
|
||||
<td>{{t.key}}</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="removeToken(t.id)" class="btn btn-danger btn-mini">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
119
src/app/features/account/partials/datasources.html
Normal file
119
src/app/features/account/partials/datasources.html
Normal file
@ -0,0 +1,119 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="Data sources"
|
||||
icon="fa fa-shield"
|
||||
section="Account"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
<div class="gf-box-header">
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['Overview', 'Add', 'Edit']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form name="editForm">
|
||||
<div class="gf-box-body">
|
||||
<div class="editor-row row" ng-if="editor.index == 0">
|
||||
<div class="span8">
|
||||
<div ng-if="datasources.length === 0">
|
||||
<em>No datasources defined</em>
|
||||
</div>
|
||||
<table class="grafana-options-table" ng-if="datasources.length > 0">
|
||||
<tr>
|
||||
<td><strong>Name</strong></td>
|
||||
<td><strong>Url</strong></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ng-repeat="ds in datasources">
|
||||
<td style="width:1%">
|
||||
<i class="fa fa-database"></i>
|
||||
{{ds.name}}
|
||||
</td>
|
||||
<td style="width:90%">
|
||||
{{ds.url}}
|
||||
</td>
|
||||
<td style="width:2%" class="text-center">
|
||||
<span ng-if="ds.isDefault">
|
||||
<span class="label label-info">default</span>
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="edit(ds)" class="btn btn-success btn-mini">
|
||||
<i class="fa fa-edit"></i>
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="remove(ds)" class="btn btn-danger btn-mini">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 1 || (editor.index == 2 && !currentIsNew)">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Data source name</label>
|
||||
<input type="text" class="input-large" ng-model='current.name' placeholder="production" required></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Type</label>
|
||||
<select class="input-medium" ng-model="current.type" ng-options="f.type as f.name for f in types" ng-change="typeChanged()"></select>
|
||||
</div>
|
||||
<editor-opt-bool text="Mark as default" model="current.isDefault" change="render()"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Url</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.url' placeholder="http://my.graphite.com:8080" required></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Access method <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</label>
|
||||
<select class="input-medium" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="current.type === 'influxdb'">
|
||||
<div class="section">
|
||||
<h5>InfluxDB Details</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Database name</label>
|
||||
<input type="text" class="input-large" required ng-model='current.database' placeholder=""></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">User</label>
|
||||
<input type="text" class="input-large" ng-model='current.user' placeholder=""></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Password</label>
|
||||
<input type="password" class="input-large" ng-model='current.password' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-row" ng-if="current.type === 'elasticsearch'">
|
||||
<div class="section">
|
||||
<h5>Elastic search details</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Index name</label>
|
||||
<input type="text" class="input-large" required ng-model='current.database' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button type="submit" class="btn btn-success" ng-show="editor.index === 1" ng-click="add()">Add</button>
|
||||
<button type="submit" class="btn btn-success" ng-show="editor.index === 2 && !currentIsNew" ng-click="update()">Update</button>
|
||||
<button type="submit" class="btn btn-inverse" ng-show="editor.index === 2 && !currentIsNew" ng-click="cancel()">Cancel</button>
|
||||
<br>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
63
src/app/features/account/partials/import.html
Normal file
63
src/app/features/account/partials/import.html
Normal file
@ -0,0 +1,63 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="Import"
|
||||
icon="fa fa-download"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-th-large"></i>
|
||||
Import Dashboards
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 160px">
|
||||
<strong>Dashboard source</strong>
|
||||
</li>
|
||||
<li>
|
||||
<select type="text" ng-model="sourceName" class="input-small tight-form-input" ng-options="f for f in datasources">
|
||||
</select>
|
||||
</li>
|
||||
<li class="tight-form-item" style="width: 160px">
|
||||
<strong>Destination</strong>
|
||||
</li>
|
||||
<li>
|
||||
<select type="text" ng-model="destName" class="input-small tight-form-input" ng-options="f for f in datasources">
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-success tight-form-btn" ng-click="startImport()">Import</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="importing">
|
||||
<section class="section">
|
||||
<h5>{{infoText}}</h5>
|
||||
|
||||
<div class="editor-row row">
|
||||
<table class="grafana-options-table span5">
|
||||
<tr ng-repeat="dash in imported">
|
||||
<td>{{dash.name}}</td>
|
||||
<td>
|
||||
{{dash.info}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
54
src/app/features/account/partials/users.html
Normal file
54
src/app/features/account/partials/users.html
Normal file
@ -0,0 +1,54 @@
|
||||
<topnav toggle="toggleSideMenu()" title="Users" icon="fa fa-shield" section="Account" show-menu-btn="!grafana.sidemenu"></topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<form name="form">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 160px">
|
||||
<strong>Username or Email</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" ng-model="user.loginOrEmail" required class="input-xlarge tight-form-input" placeholder="user@email.com or username">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
role
|
||||
</li>
|
||||
<li>
|
||||
<select type="text" ng-model="user.role" class="input-small tight-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Admin']">
|
||||
</select>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-success tight-form-btn" ng-click="addUser()">Add</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="editor-row row">
|
||||
<table class="grafana-options-table span5">
|
||||
<tr ng-repeat="user in users">
|
||||
<td>{{user.email}}</td>
|
||||
<td>
|
||||
{{user.role}}
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="removeUser(user)" class="btn btn-danger btn-mini">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
18
src/app/features/admin/adminCtrl.js
Normal file
18
src/app/features/admin/adminCtrl.js
Normal file
@ -0,0 +1,18 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminCtrl', function($scope) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.editor = {index: 0};
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
18
src/app/features/admin/adminSettingsCtrl.js
Normal file
18
src/app/features/admin/adminSettingsCtrl.js
Normal file
@ -0,0 +1,18 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminSettingsCtrl', function($scope) {
|
||||
|
||||
$scope.init = function() {
|
||||
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
25
src/app/features/admin/adminUsersCtrl.js
Normal file
25
src/app/features/admin/adminUsersCtrl.js
Normal file
@ -0,0 +1,25 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminUsersCtrl', function($scope, backendSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.accounts = [];
|
||||
$scope.getUsers();
|
||||
};
|
||||
|
||||
$scope.getUsers = function() {
|
||||
backendSrv.get('/api/admin/users').then(function(users) {
|
||||
$scope.users = users;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
9
src/app/features/admin/partials/admin.html
Normal file
9
src/app/features/admin/partials/admin.html
Normal file
@ -0,0 +1,9 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="Users"
|
||||
icon="fa fa-cube"
|
||||
section="Admin"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
</div>
|
11
src/app/features/admin/partials/settings.html
Normal file
11
src/app/features/admin/partials/settings.html
Normal file
@ -0,0 +1,11 @@
|
||||
<div ng-include="'app/partials/navbar.html'" ng-init="pageTitle='Admin > Settings'"></div>
|
||||
|
||||
<div class="dashboard-edit-view" style="min-height: 500px">
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="editor-row row">
|
||||
<div class="section span6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
43
src/app/features/admin/partials/users.html
Normal file
43
src/app/features/admin/partials/users.html
Normal file
@ -0,0 +1,43 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="Users"
|
||||
icon="fa fa-cube"
|
||||
section="Admin"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
<div class="gf-box" style="min-height: 500px">
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="editor-row row">
|
||||
<div class="section span6">
|
||||
<table class="grafana-options-table">
|
||||
<tr>
|
||||
<th style="text-align:left">Id</th>
|
||||
<th>Login</th>
|
||||
<th>Email</th>
|
||||
<th>Name</th>
|
||||
<th>Admin</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr ng-repeat="user in users">
|
||||
<td>{{user.id}}</td>
|
||||
<td>{{user.login}}</td>
|
||||
<td>{{user.email}}</td>
|
||||
<td>{{user.name}}</td>
|
||||
<td>{{user.isAdmin}}</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="edit(variable)" class="btn btn-success btn-small">
|
||||
<i class="fa fa-edit"></i>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<a ng-click="edit(variable)" class="btn btn-danger btn-small">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -7,4 +7,14 @@ define([
|
||||
'./opentsdb/datasource',
|
||||
'./elasticsearch/datasource',
|
||||
'./dashboard/all',
|
||||
'./panel/all',
|
||||
'./profile/profileCtrl',
|
||||
'./account/accountUsersCtrl',
|
||||
'./account/datasourcesCtrl',
|
||||
'./account/apiKeysCtrl',
|
||||
'./account/importCtrl',
|
||||
'./account/accountCtrl',
|
||||
'./admin/adminUsersCtrl',
|
||||
'./admin/adminSettingsCtrl',
|
||||
'./grafanaDatasource/datasource',
|
||||
], function () {});
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
|
||||
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-bolt"></i>
|
||||
Annotations
|
||||
</div>
|
||||
@ -10,10 +10,12 @@
|
||||
<div ng-repeat="tab in ['Overview', 'Add', 'Edit']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();dashboard.refresh();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
<div class="editor-row row" ng-if="editor.index == 0">
|
||||
<div class="span6">
|
||||
<div ng-if="annotations.length === 0">
|
||||
@ -71,12 +73,12 @@
|
||||
<div ng-include src="currentDatasource.editorSrc">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button ng-show="editor.index === 1" type="button" class="btn btn-success" ng-click="add()">Add</button>
|
||||
<button ng-show="editor.index === 2" type="button" class="btn btn-success pull-left" ng-click="update();">Update</button>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button ng-show="editor.index === 1" type="button" class="btn btn-success" ng-click="add()">Add</button>
|
||||
<button ng-show="editor.index === 2" type="button" class="btn btn-success pull-left" ng-click="update();">Update</button>
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="close_edit();dismiss();dashboard.refresh();">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,7 +9,6 @@ define([
|
||||
'./keybindings',
|
||||
'./viewStateSrv',
|
||||
'./playlistSrv',
|
||||
'./panelSrv',
|
||||
'./timeSrv',
|
||||
'./unsavedChangesSrv',
|
||||
], function () {});
|
||||
|
@ -4,7 +4,7 @@ define([
|
||||
'config',
|
||||
'lodash',
|
||||
],
|
||||
function (angular, $, config, _) {
|
||||
function (angular, $, config) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
@ -20,15 +20,15 @@ function (angular, $, config, _) {
|
||||
$timeout) {
|
||||
|
||||
$scope.editor = { index: 0 };
|
||||
$scope.panelNames = _.map(config.panels, function(value, key) { return key; });
|
||||
$scope.panels = config.panels;
|
||||
|
||||
var resizeEventTimeout;
|
||||
|
||||
this.init = function(dashboardData) {
|
||||
$scope.availablePanels = config.panels;
|
||||
this.init = function(dashboard) {
|
||||
$scope.reset_row();
|
||||
$scope.registerWindowResizeEvent();
|
||||
$scope.onAppEvent('show-json-editor', $scope.showJsonEditor);
|
||||
$scope.setupDashboard(dashboardData);
|
||||
$scope.setupDashboard(dashboard);
|
||||
};
|
||||
|
||||
$scope.registerWindowResizeEvent = function() {
|
||||
@ -38,13 +38,14 @@ function (angular, $, config, _) {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setupDashboard = function(dashboardData) {
|
||||
$scope.setupDashboard = function(dashboard) {
|
||||
$rootScope.performance.dashboardLoadStart = new Date().getTime();
|
||||
$rootScope.performance.panelsInitialized = 0;
|
||||
$rootScope.performance.panelsRendered = 0;
|
||||
|
||||
$scope.dashboard = dashboardSrv.create(dashboardData);
|
||||
$scope.dashboard = dashboardSrv.create(dashboard.model);
|
||||
$scope.dashboardViewState = dashboardViewStateSrv.create($scope);
|
||||
$scope.dashboardMeta = dashboard.meta;
|
||||
|
||||
// init services
|
||||
timeSrv.init($scope.dashboard);
|
||||
@ -60,15 +61,11 @@ function (angular, $, config, _) {
|
||||
|
||||
$scope.setWindowTitleAndTheme = function() {
|
||||
window.document.title = config.window_title_prefix + $scope.dashboard.title;
|
||||
$scope.grafana.style = $scope.dashboard.style;
|
||||
$scope.grafana.lightTheme = $scope.dashboard.style === 'light';
|
||||
};
|
||||
|
||||
$scope.isPanel = function(obj) {
|
||||
if(!_.isNull(obj) && !_.isUndefined(obj) && !_.isUndefined(obj.type)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$scope.styleUpdated = function() {
|
||||
$scope.grafana.lightTheme = $scope.dashboard.style === 'light';
|
||||
};
|
||||
|
||||
$scope.add_row = function(dash, row) {
|
||||
|
@ -22,7 +22,6 @@ function (angular, _, moment, config, store) {
|
||||
$scope.onAppEvent('zoom-out', function() {
|
||||
$scope.zoom(2);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.set_default = function() {
|
||||
@ -35,17 +34,29 @@ function (angular, _, moment, config, store) {
|
||||
alertSrv.set('Local Default Clear','Your default dashboard has been reset to the default','success', 5000);
|
||||
};
|
||||
|
||||
$scope.saveForSharing = function() {
|
||||
var clone = angular.copy($scope.dashboard);
|
||||
clone.temp = true;
|
||||
$scope.db.saveDashboard(clone)
|
||||
.then(function(result) {
|
||||
$scope.openEditView = function(editview) {
|
||||
var search = _.extend($location.search(), {editview: editview});
|
||||
$location.search(search);
|
||||
};
|
||||
|
||||
$scope.share = { url: result.url, title: result.title };
|
||||
|
||||
}, function(err) {
|
||||
alertSrv.set('Save for sharing failed', err, 'error',5000);
|
||||
$scope.starDashboard = function() {
|
||||
if ($scope.dashboardMeta.isStarred) {
|
||||
$scope.db.unstarDashboard($scope.dashboard.id).then(function() {
|
||||
$scope.dashboardMeta.isStarred = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.db.starDashboard($scope.dashboard.id).then(function() {
|
||||
$scope.dashboardMeta.isStarred = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.shareDashboard = function() {
|
||||
$scope.appEvent('show-modal', {
|
||||
src: './app/features/dashboard/partials/shareModal.html',
|
||||
scope: $scope.$new(),
|
||||
});
|
||||
};
|
||||
|
||||
$scope.passwordCache = function(pwd) {
|
||||
@ -105,10 +116,10 @@ function (angular, _, moment, config, store) {
|
||||
};
|
||||
|
||||
$scope.deleteDashboardConfirmed = function(options) {
|
||||
var id = options.id;
|
||||
$scope.db.deleteDashboard(id).then(function(id) {
|
||||
$scope.appEvent('dashboard-deleted', id);
|
||||
$scope.appEvent('alert-success', ['Dashboard Deleted', id + ' has been deleted']);
|
||||
var slug = options.slug;
|
||||
$scope.db.deleteDashboard(slug).then(function() {
|
||||
$scope.appEvent('dashboard-deleted', options);
|
||||
$scope.appEvent('alert-success', ['Dashboard Deleted', options.title + ' has been deleted']);
|
||||
}, function(err) {
|
||||
$scope.appEvent('alert-error', ['Deleted failed', err]);
|
||||
});
|
||||
@ -140,10 +151,6 @@ function (angular, _, moment, config, store) {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.styleUpdated = function() {
|
||||
$scope.grafana.style = $scope.dashboard.style;
|
||||
};
|
||||
|
||||
$scope.editJson = function() {
|
||||
$scope.appEvent('show-json-editor', { object: $scope.dashboard });
|
||||
};
|
||||
|
35
src/app/features/dashboard/partials/shareModal.html
Normal file
35
src/app/features/dashboard/partials/shareModal.html
Normal file
@ -0,0 +1,35 @@
|
||||
<div class="modal-body gf-box gf-box-no-margin" ng-controller="SharePanelCtrl">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-share"></i>
|
||||
Share
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['Link']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="editor-row">
|
||||
<editor-opt-bool text="Current time range" model="forCurrent" change="buildUrl()"></editor-opt-bool>
|
||||
<editor-opt-bool text="To this panel only" model="toPanel" change="buildUrl()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Include template variables" model="includeTemplateVars" change="buildUrl()"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin-top: 20px;">
|
||||
<input type="text" data-share-panel-url class="input input-fluid" ng-model='shareUrl'></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin-top: 20px;">
|
||||
<a href="{{imageUrl}}" target="_blank">Link to rendered image</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1,9 +1,10 @@
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'lodash'
|
||||
'lodash',
|
||||
'config'
|
||||
],
|
||||
function (angular, app, _) {
|
||||
function (angular, app, _, config) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
@ -107,11 +108,11 @@ function (angular, app, _) {
|
||||
var _as = 12 - $scope.dashboard.rowSpan($scope.row);
|
||||
|
||||
$scope.panel = {
|
||||
title: 'no title (click here)',
|
||||
error : false,
|
||||
span : _as < defaultSpan && _as > 0 ? _as : defaultSpan,
|
||||
title: config.new_panel_title,
|
||||
error: false,
|
||||
span: _as < defaultSpan && _as > 0 ? _as : defaultSpan,
|
||||
editable: true,
|
||||
type : type
|
||||
type: type
|
||||
};
|
||||
|
||||
function fixRowHeight(height) {
|
||||
|
@ -12,9 +12,12 @@ function (angular, _) {
|
||||
$scope.init = function() {
|
||||
$scope.editor = { index: 0 };
|
||||
$scope.forCurrent = true;
|
||||
$scope.toPanel = true;
|
||||
$scope.includeTemplateVars = true;
|
||||
|
||||
if ($scope.panel) {
|
||||
$scope.toPanel = true;
|
||||
}
|
||||
|
||||
$scope.includeTemplateVars = true;
|
||||
$scope.buildUrl();
|
||||
};
|
||||
|
||||
@ -26,7 +29,6 @@ function (angular, _) {
|
||||
baseUrl = baseUrl.substring(0, queryStart);
|
||||
}
|
||||
|
||||
var panelId = $scope.panel.id;
|
||||
var params = angular.copy($location.search());
|
||||
|
||||
var range = timeSrv.timeRangeForUrl();
|
||||
@ -50,7 +52,7 @@ function (angular, _) {
|
||||
}
|
||||
|
||||
if ($scope.toPanel) {
|
||||
params.panelId = panelId;
|
||||
params.panelId = $scope.panel.id;
|
||||
params.fullscreen = true;
|
||||
} else {
|
||||
delete params.panelId;
|
||||
@ -68,7 +70,10 @@ function (angular, _) {
|
||||
}
|
||||
});
|
||||
|
||||
$scope.shareUrl = baseUrl + "?" + paramsArray.join('&') ;
|
||||
$scope.shareUrl = baseUrl + "?" + paramsArray.join('&');
|
||||
$scope.imageUrl = $scope.shareUrl.replace('/dashboard/db/', '/render/dashboard/solo/');
|
||||
$scope.imageUrl += '&width=1000';
|
||||
$scope.imageUrl += '&height=500';
|
||||
|
||||
$timeout(function() {
|
||||
var input = $element.find('[data-share-panel-url]');
|
||||
|
@ -60,6 +60,7 @@ function(angular, _, config) {
|
||||
this.open_modal = function() {
|
||||
var confirmModal = $modal({
|
||||
template: './app/partials/unsaved-changes.html',
|
||||
modalClass: 'confirm-modal',
|
||||
persist: true,
|
||||
show: false,
|
||||
scope: modalScope,
|
||||
|
@ -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;
|
||||
};
|
||||
@ -117,7 +118,7 @@ function (angular, _, $) {
|
||||
self.$scope.dashboard.emit_refresh();
|
||||
}
|
||||
else {
|
||||
self.fullscreenPanel.$emit('render');
|
||||
self.fullscreenPanel.$broadcast('render');
|
||||
}
|
||||
delete self.fullscreenPanel;
|
||||
});
|
||||
@ -138,7 +139,7 @@ function (angular, _, $) {
|
||||
panelScope.fullscreen = true;
|
||||
|
||||
$timeout(function() {
|
||||
panelScope.$emit('render');
|
||||
panelScope.$broadcast('render');
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@ function (angular, _, config, kbn, moment) {
|
||||
module.factory('ElasticDatasource', function($q, $http, templateSrv) {
|
||||
|
||||
function ElasticDatasource(datasource) {
|
||||
this.type = 'elastic';
|
||||
this.type = 'elasticsearch';
|
||||
this.basicAuth = datasource.basicAuth;
|
||||
this.url = datasource.url;
|
||||
this.name = datasource.name;
|
||||
|
78
src/app/features/grafanaDatasource/datasource.js
Normal file
78
src/app/features/grafanaDatasource/datasource.js
Normal file
@ -0,0 +1,78 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'kbn',
|
||||
],
|
||||
function (angular, _, kbn) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.factory('GrafanaDatasource', function($q, backendSrv) {
|
||||
|
||||
function GrafanaDatasource() {
|
||||
this.type = 'grafana';
|
||||
this.grafanaDB = true;
|
||||
this.name = "grafana";
|
||||
this.supportMetrics = true;
|
||||
this.editorSrc = 'app/features/grafanaDatasource/partials/query.editor.html';
|
||||
}
|
||||
|
||||
GrafanaDatasource.prototype.getDashboard = function(slug, isTemp) {
|
||||
var url = '/dashboards/' + slug;
|
||||
|
||||
if (isTemp) {
|
||||
url = '/temp/' + slug;
|
||||
}
|
||||
|
||||
return backendSrv.get('/api/dashboards/db/' + slug);
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.query = function(options) {
|
||||
// get from & to in seconds
|
||||
var from = kbn.parseDate(options.range.from).getTime();
|
||||
var to = kbn.parseDate(options.range.to).getTime();
|
||||
|
||||
return backendSrv.get('/api/metrics/test', { from: from, to: to, maxDataPoints: options.maxDataPoints });
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.starDashboard = function(dashId) {
|
||||
return backendSrv.post('/api/user/stars/dashboard/' + dashId);
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.unstarDashboard = function(dashId) {
|
||||
return backendSrv.delete('/api/user/stars/dashboard/' + dashId);
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.saveDashboard = function(dashboard) {
|
||||
// remove id if title has changed
|
||||
if (dashboard.title !== dashboard.originalTitle) {
|
||||
dashboard.id = null;
|
||||
}
|
||||
|
||||
return backendSrv.post('/api/dashboards/db/', { dashboard: dashboard })
|
||||
.then(function(data) {
|
||||
return { title: dashboard.title, url: '/dashboard/db/' + data.slug };
|
||||
}, function(err) {
|
||||
err.isHandled = true;
|
||||
err.data = err.data || {};
|
||||
throw err.data.message || "Unknown error";
|
||||
});
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.deleteDashboard = function(id) {
|
||||
return backendSrv.delete('/api/dashboards/db/' + id);
|
||||
};
|
||||
|
||||
GrafanaDatasource.prototype.searchDashboards = function(query) {
|
||||
return backendSrv.get('/api/search/', {q: query})
|
||||
.then(function(data) {
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
return GrafanaDatasource;
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
|
||||
<div class="fluid-row" style="margin-top: 20px">
|
||||
<div class="span2"></div>
|
||||
<div class="grafana-info-box span8">
|
||||
<h5>Test graph</h5>
|
||||
|
||||
<p>
|
||||
This is just a test data source that generates random walk series. If this is your only data source
|
||||
open the left side menu and navigate to the data sources admin screen and add your data sources. You can change
|
||||
data source using the button to the left of the <strong>Add query</strong> button.
|
||||
</p>
|
||||
</div>
|
||||
<div class="span2"></div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
|
||||
module.factory('InfluxDatasource', function($q, $http, templateSrv) {
|
||||
|
||||
function InfluxDatasource(datasource) {
|
||||
this.type = 'influxDB';
|
||||
this.type = 'influxdb';
|
||||
this.urls = datasource.urls;
|
||||
this.username = datasource.username;
|
||||
this.password = datasource.password;
|
||||
|
6
src/app/features/panel/all.js
Normal file
6
src/app/features/panel/all.js
Normal file
@ -0,0 +1,6 @@
|
||||
define([
|
||||
'./panelMenu',
|
||||
'./panelDirective',
|
||||
'./panelSrv',
|
||||
'./soloPanelCtrl',
|
||||
], function () {});
|
40
src/app/features/panel/panelDirective.js
Normal file
40
src/app/features/panel/panelDirective.js
Normal file
@ -0,0 +1,40 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config',
|
||||
],
|
||||
function (angular, $, config) {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('grafana.directives')
|
||||
.directive('panelLoader', function($compile, $parse) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attr) {
|
||||
var getter = $parse(attr.type), panelType = getter(scope);
|
||||
var panelPath = config.panels[panelType].path;
|
||||
|
||||
scope.require([panelPath + "/module"], function () {
|
||||
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
|
||||
elem.append(panelEl);
|
||||
$compile(panelEl)(scope);
|
||||
});
|
||||
}
|
||||
};
|
||||
}).directive('grafanaPanel', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'app/features/panel/partials/panel.html',
|
||||
transclude: true,
|
||||
link: function(scope, elem) {
|
||||
var panelContainer = elem.find('.panel-container');
|
||||
|
||||
scope.$watchGroup(['fullscreen', 'panel.height', 'row.height'], function() {
|
||||
panelContainer.css({ minHeight: scope.panel.height || scope.row.height, display: 'block' });
|
||||
elem.toggleClass('panel-fullscreen', scope.fullscreen ? true : false);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
@ -1,8 +1,9 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'config',
|
||||
],
|
||||
function (angular, _) {
|
||||
function (angular, _, config) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
@ -24,7 +25,7 @@ function (angular, _) {
|
||||
|
||||
$scope.sharePanel = function() {
|
||||
$scope.appEvent('show-modal', {
|
||||
src: './app/partials/share-panel.html',
|
||||
src: './app/features/dashboard/partials/shareModal.html',
|
||||
scope: $scope.$new()
|
||||
});
|
||||
};
|
||||
@ -77,6 +78,10 @@ function (angular, _) {
|
||||
$scope.editorHelpIndex = index;
|
||||
};
|
||||
|
||||
$scope.isNewPanel = function() {
|
||||
return $scope.panel.title === config.new_panel_title;
|
||||
};
|
||||
|
||||
$scope.toggleFullscreen = function(edit) {
|
||||
$scope.dashboardViewState.update({ fullscreen: true, edit: edit, panelId: $scope.panel.id });
|
||||
};
|
42
src/app/features/panel/partials/panel.html
Normal file
42
src/app/features/panel/partials/panel.html
Normal file
@ -0,0 +1,42 @@
|
||||
<div class="panel-container">
|
||||
<div class="panel-header">
|
||||
<span class="alert-error panel-error small pointer" config-modal="app/partials/inspector.html" ng-if="panelMeta.error">
|
||||
<span data-placement="top" bs-tooltip="panelMeta.error">
|
||||
<i class="fa fa-exclamation"></i><span class="panel-error-arrow"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="panel-loading" ng-show="panelMeta.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</span>
|
||||
|
||||
<div class="panel-title-container drag-handle" panel-menu></div>
|
||||
</div>
|
||||
|
||||
<div class="panel-content">
|
||||
<ng-transclude></ng-transclude>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-full-edit" ng-if="editMode">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i ng-class="panelMeta.editIcon"></i>
|
||||
{{panelMeta.panelName}}
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs>
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
|
||||
<div ng-include src="tab.src"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
8
src/app/features/panel/partials/soloPanel.html
Normal file
8
src/app/features/panel/partials/soloPanel.html
Normal file
@ -0,0 +1,8 @@
|
||||
<div class="container-fluid main">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="panel nospace" ng-if="panel" style="width: 100%">
|
||||
<panel-loader type="panel.type" ng-cloak></panel-loader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
70
src/app/features/panel/soloPanelCtrl.js
Normal file
70
src/app/features/panel/soloPanelCtrl.js
Normal file
@ -0,0 +1,70 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
],
|
||||
function (angular, $) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
|
||||
module.controller('SoloPanelCtrl', function($scope, $rootScope, datasourceSrv, $routeParams, dashboardSrv, timeSrv, $location) {
|
||||
var panelId;
|
||||
|
||||
$scope.init = function() {
|
||||
var db = datasourceSrv.getGrafanaDB();
|
||||
var params = $location.search();
|
||||
panelId = parseInt(params.panelId);
|
||||
|
||||
db.getDashboard($routeParams.id, false)
|
||||
.then(function(dashboard) {
|
||||
$scope.initPanelScope(dashboard);
|
||||
}).then(null, function(error) {
|
||||
$scope.appEvent('alert-error', ['Load panel error', error]);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.initPanelScope = function(dashboard) {
|
||||
$scope.dashboard = dashboardSrv.create(dashboard.model);
|
||||
$scope.grafana.style = $scope.dashboard.style;
|
||||
$scope.row = {
|
||||
height: $(window).height() + 'px',
|
||||
};
|
||||
$scope.test = "Hej";
|
||||
$scope.$index = 0;
|
||||
$scope.panel = $scope.getPanelById(panelId);
|
||||
|
||||
if (!$scope.panel) {
|
||||
$scope.appEvent('alert-error', ['Panel not found', '']);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.panel.span = 12;
|
||||
$scope.dashboardViewState = {
|
||||
registerPanel: function() {
|
||||
}
|
||||
};
|
||||
|
||||
timeSrv.init($scope.dashboard);
|
||||
};
|
||||
|
||||
$scope.getPanelById = function(id) {
|
||||
var rows = $scope.dashboard.rows;
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
for (var j = 0; j < row.panels.length; j++) {
|
||||
var panel = row.panels[j];
|
||||
if (panel.id === id) {
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!$scope.skipAutoInit) {
|
||||
$scope.init();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
127
src/app/features/profile/partials/profile.html
Normal file
127
src/app/features/profile/partials/profile.html
Normal file
@ -0,0 +1,127 @@
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="Details"
|
||||
icon="fa fa-user"
|
||||
section="Profile"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-user"></i>
|
||||
Personal information
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div class="row">
|
||||
<form name="userForm">
|
||||
<div>
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Name</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form" style="margin-top: 10px">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Email</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" required ng-model="user.email" class="input-xxlarge tight-form-input last" >
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form" style="margin-top: 10px">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Username</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" required ng-model="user.login" class="input-xxlarge tight-form-input last" >
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button type="submit" class="pull-right btn btn-success" ng-click="update()">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-shield"></i>
|
||||
Your accounts
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-box-body">
|
||||
<table class="grafana-options-table">
|
||||
<tr ng-repeat="ac in accounts">
|
||||
<td>Name: {{ac.name}}</td>
|
||||
<td>Role: {{ac.role}}</td>
|
||||
<td ng-show="ac.isUsing">
|
||||
<span class="label label-info">
|
||||
active now
|
||||
</span>
|
||||
</td>
|
||||
<td ng-show="!ac.isUsing">
|
||||
<a ng-click="setUsingAccount(ac)" class="btn btn-success btn-mini">
|
||||
Select
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-plus-square"></i>
|
||||
Add account
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-box-body">
|
||||
<form name="form">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
<strong>Account name</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" ng-model="newAccount.name" required class="input-xlarge tight-form-input" placeholder="account name">
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-success tight-form-btn" ng-click="createAccount()">Create</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
46
src/app/features/profile/profileCtrl.js
Normal file
46
src/app/features/profile/profileCtrl.js
Normal file
@ -0,0 +1,46 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ProfileCtrl', function($scope, $http, backendSrv) {
|
||||
|
||||
$scope.newAccount = {name: ''};
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.getUser();
|
||||
$scope.getUserAccounts();
|
||||
};
|
||||
|
||||
$scope.getUser = function() {
|
||||
backendSrv.get('/api/user').then(function(user) {
|
||||
$scope.user = user;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getUserAccounts = function() {
|
||||
backendSrv.get('/api/user/accounts').then(function(accounts) {
|
||||
$scope.accounts = accounts;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setUsingAccount = function(account) {
|
||||
backendSrv.post('/api/user/using/' + account.accountId).then($scope.getUserAccounts);
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
if (!$scope.userForm.$valid) { return; }
|
||||
backendSrv.put('/api/user/', $scope.user);
|
||||
};
|
||||
|
||||
$scope.createAccount = function() {
|
||||
backendSrv.post('/api/account/', $scope.newAccount).then($scope.getUserAccounts);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
41
src/app/panels/dashlist/editor.html
Normal file
41
src/app/panels/dashlist/editor.html
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section" style="margin-bottom: 20px">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 110px">
|
||||
<strong>Mode</strong>
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" ng-model="panel.mode" ng-options="f for f in modes" ng-change="get_data()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" style="margin-bottom: 20px" ng-if="panel.mode === 'search'">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 110px">
|
||||
<strong>Search options</strong>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Query
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input" placeholder="title query"
|
||||
ng-model="panel.query" ng-change="get_data()" ng-model-onblur>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Tag
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-small tight-form-input" placeholder="full tag name"
|
||||
ng-model="panel.tag" ng-change="get_data()" ng-model-onblur>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
13
src/app/panels/dashlist/module.html
Normal file
13
src/app/panels/dashlist/module.html
Normal file
@ -0,0 +1,13 @@
|
||||
<grafana-panel>
|
||||
<div class="dashlist">
|
||||
<div class="dashlist-item" ng-repeat="dash in dashList">
|
||||
<a class="dashlist-link" href="{{dash.url}}">
|
||||
<span class="dashlist-title">
|
||||
{{dash.title}}
|
||||
</span>
|
||||
<span class="dashlist-star">
|
||||
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': !dash.isStarred}"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</grafana-panel>
|
69
src/app/panels/dashlist/module.js
Normal file
69
src/app/panels/dashlist/module.js
Normal file
@ -0,0 +1,69 @@
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'lodash',
|
||||
'config',
|
||||
'components/panelmeta',
|
||||
],
|
||||
function (angular, app, _, config, PanelMeta) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.panels.dashlist', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.directive('grafanaPanelDashlist', function() {
|
||||
return {
|
||||
controller: 'DashListPanelCtrl',
|
||||
templateUrl: 'app/panels/dashlist/module.html',
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('DashListPanelCtrl', function($scope, panelSrv, backendSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Dash list',
|
||||
editIcon: "fa fa-star",
|
||||
fullscreen: true,
|
||||
});
|
||||
|
||||
$scope.panelMeta.addEditorTab('Options', 'app/panels/dashlist/editor.html');
|
||||
|
||||
var defaults = {
|
||||
mode: 'starred',
|
||||
query: '',
|
||||
tag: '',
|
||||
};
|
||||
|
||||
$scope.modes = ['starred', 'search'];
|
||||
|
||||
_.defaults($scope.panel, defaults);
|
||||
|
||||
$scope.dashList = [];
|
||||
|
||||
$scope.init = function() {
|
||||
panelSrv.init($scope);
|
||||
|
||||
if ($scope.isNewPanel()) {
|
||||
$scope.panel.title = "Starred Dashboards";
|
||||
}
|
||||
|
||||
$scope.$on('refresh', $scope.get_data);
|
||||
};
|
||||
|
||||
$scope.get_data = function() {
|
||||
var params = {};
|
||||
if ($scope.panel.mode === 'starred') {
|
||||
params.starred = 1;
|
||||
} else {
|
||||
params.q = "tags:" + $scope.panel.tag + " AND title:" + $scope.panel.query;
|
||||
}
|
||||
|
||||
backendSrv.get('/api/search', params).then(function(result) {
|
||||
$scope.dashList = result.dashboards;
|
||||
$scope.panelMeta.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
<div ng-controller='GraphCtrl'>
|
||||
<grafana-panel>
|
||||
|
||||
<div class="graph-wrapper" ng-class="{'graph-legend-rightside': panel.legend.rightSide}">
|
||||
<div class="graph-canvas-wrapper">
|
||||
@ -24,23 +24,6 @@
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div style="margin-top: 30px" ng-if="editMode">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<i class="fa fa-bar-chart"></i>
|
||||
Graph
|
||||
</div>
|
||||
</grafana-panel>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs>
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
|
||||
<div ng-include src="tab.src"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,17 +16,25 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
||||
|
||||
var module = angular.module('grafana.panels.graph');
|
||||
|
||||
module.directive('grafanaPanelGraph', function() {
|
||||
return {
|
||||
controller: 'GraphCtrl',
|
||||
templateUrl: 'app/panels/graph/module.html',
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
description: 'Graph panel',
|
||||
panelName: 'Graph',
|
||||
editIcon: "fa fa-bar-chart",
|
||||
fullscreen: true,
|
||||
metricsEditor: true
|
||||
});
|
||||
|
||||
$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.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html');
|
||||
|
||||
$scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()');
|
||||
$scope.panelMeta.addExtendedMenuItem('Toggle legend', '', 'toggleLegend()');
|
||||
|
@ -1,26 +1,4 @@
|
||||
<div ng-controller='SingleStatCtrl'>
|
||||
|
||||
<grafana-panel>
|
||||
<div class="singlestat-panel" singlestat-panel></div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div style="margin-top: 30px" ng-if="editMode">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<i class="fa fa-dashboard"></i>
|
||||
Singlestat
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs>
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" ng-if="editor.index === $index">
|
||||
<div ng-include src="tab.src"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</grafana-panel>
|
||||
|
@ -13,10 +13,18 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
|
||||
var module = angular.module('grafana.panels.singlestat');
|
||||
app.useModule(module);
|
||||
|
||||
module.directive('grafanaPanelSinglestat', function() {
|
||||
return {
|
||||
controller: 'SingleStatCtrl',
|
||||
templateUrl: 'app/panels/singlestat/module.html',
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('SingleStatCtrl', function($scope, panelSrv, timeSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
description: 'Singlestat panel',
|
||||
panelName: 'Singlestat',
|
||||
editIcon: "fa fa-dashboard",
|
||||
fullscreen: true,
|
||||
metricsEditor: true
|
||||
});
|
||||
@ -192,7 +200,7 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
|
||||
data.colorMap = $scope.panel.colors;
|
||||
|
||||
$scope.data = data;
|
||||
$scope.$emit('render');
|
||||
$scope.$broadcast('render');
|
||||
};
|
||||
|
||||
$scope.getFormatedValue = function(mainValue) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
<div ng-controller='text'>
|
||||
<p ng-bind-html="content" ng-show="content">
|
||||
</p>
|
||||
</div>
|
||||
<grafana-panel>
|
||||
<p ng-bind-html="content" ng-show="content"></p>
|
||||
</grafana-panel>
|
||||
|
@ -8,15 +8,24 @@ define([
|
||||
function (angular, app, _, require, PanelMeta) {
|
||||
'use strict';
|
||||
|
||||
var converter;
|
||||
|
||||
var module = angular.module('grafana.panels.text', []);
|
||||
app.useModule(module);
|
||||
|
||||
var converter;
|
||||
module.directive('grafanaPanelText', function() {
|
||||
return {
|
||||
controller: 'TextPanelCtrl',
|
||||
templateUrl: 'app/panels/text/module.html',
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('text', function($scope, templateSrv, $sce, panelSrv) {
|
||||
module.controller('TextPanelCtrl', function($scope, templateSrv, $sce, panelSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
description : "A static text panel that can use plain text, markdown, or (sanitized) HTML"
|
||||
panelName: 'Text',
|
||||
editIcon: "fa fa-text-width",
|
||||
fullscreen: true,
|
||||
});
|
||||
|
||||
$scope.panelMeta.addEditorTab('Edit text', 'app/panels/text/editor.html');
|
||||
|
@ -1,11 +1,14 @@
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
Custom time range
|
||||
</div>
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
<style>
|
||||
.timepicker-to-column {
|
||||
margin-top: 10px;
|
||||
@ -73,12 +76,9 @@
|
||||
</form>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<form name="input" style="margin-bottom:0">
|
||||
<span class="" ng-hide="input.$valid">Invalid date or range</span>
|
||||
<button ng-click="setAbsoluteTimeFilter(validate(temptime));dismiss();" ng-disabled="!input.$valid" class="btn btn-success">Apply</button>
|
||||
<button ng-click="dismiss();" class="btn btn-success pull-right">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<div>
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 148px">
|
||||
@ -34,6 +35,7 @@
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<br>
|
||||
|
@ -21,6 +21,7 @@
|
||||
<li class="dropdown">
|
||||
|
||||
<a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" href="" bs-tooltip="time.tooltip" data-placement="bottom" ng-click="dismiss();">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
<span ng-bind="time.rangeString"></span>
|
||||
<span ng-show="dashboard.refresh" class="text-warning">refreshed every {{dashboard.refresh}} </span>
|
||||
<i class="fa fa-caret-down"></i>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<div class="modal-body">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="modal-body gf-box gf-box-no-margin">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-check"></i>
|
||||
{{title}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body" style="min-height: 0px;">
|
||||
<p class="row-fluid text-center large">
|
||||
{{text}}
|
||||
<br>
|
||||
|
@ -1,11 +0,0 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>{{share.title}} <small>shareable link</small></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label>Share this dashboard with this URL</label>
|
||||
<input ng-model='share.url' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" ng-click="dismiss();$broadcast('render')">Close</button>
|
||||
</div>
|
@ -35,8 +35,8 @@
|
||||
<li class="dropdown-submenu">
|
||||
<a href="javascript:void(0);">Add Panel</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li bindonce ng-repeat="name in panelNames">
|
||||
<a ng-click="add_panel_default(name)" bo-text="name"></a>
|
||||
<li bindonce ng-repeat="(key, value) in panels">
|
||||
<a ng-click="add_panel_default(key)" bo-text="value.name"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -79,12 +79,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Panels, draggable needs to be disabled in fullscreen because Firefox bug -->
|
||||
<div ng-repeat="(name, panel) in row.panels"
|
||||
class="panel"
|
||||
<div ng-repeat="(name, panel) in row.panels" class="panel"
|
||||
ui-draggable="{{!dashboardViewState.fullscreen}}" drag="panel.id"
|
||||
ui-on-Drop="onDrop($data, row, panel)"
|
||||
drag-handle-class="drag-handle" panel-width ng-model="panel">
|
||||
<grafana-panel type="panel.type" ng-cloak></grafana-panel>
|
||||
drag-handle-class="drag-handle" panel-width>
|
||||
<panel-loader type="panel.type" class="panel-margin"></panel-loader>
|
||||
</div>
|
||||
|
||||
<div panel-drop-zone class="panel panel-drop-zone"
|
||||
|
@ -1,12 +1,53 @@
|
||||
<div class="navbar navbar-static-top">
|
||||
<div class="navbar navbar-static-top" ng-controller='DashboardNavCtrl' ng-init="init()">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<span class="brand">
|
||||
<img class="logo-icon" src="img/fav32.png" bs-tooltip="'Grafana'" data-placement="bottom"></img>
|
||||
<span class="page-title">{{dashboard.title}}</span>
|
||||
</span>
|
||||
<ul class="nav pull-right" ng-controller='DashboardNavCtrl' ng-init="init()">
|
||||
|
||||
<div class="top-nav">
|
||||
<a class="pointer top-nav-menu-btn" ng-if="!grafana.sidemenu" ng-click="toggleSideMenu()">
|
||||
<img class="logo-icon" src="img/fav32.png"></img>
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
|
||||
<div class="top-nav-dashboards-btn">
|
||||
<a class="pointer" ng-click="openSearch()">
|
||||
<i class="fa fa-th-large"></i>
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<span class="fa fa-angle-right top-nav-breadcrumb-icon">
|
||||
</span>
|
||||
|
||||
<a ng-click="asd()" class="top-nav-title pointer">
|
||||
{{dashboard.title}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav pull-left" ng-if="!dashboardMeta.isHome">
|
||||
<li>
|
||||
<a class="pointer" ng-click="starDashboard()">
|
||||
<i class="fa" ng-class="{'fa-star-o': !dashboardMeta.isStarred, 'fa-star': dashboardMeta.isStarred,}" style="color: orange;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="pointer" ng-click="shareDashboard()"><i class="fa fa-share-square-o"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="saveDashboard()"><i class="fa fa-save"></i></a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a class="pointer" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="pointer" ng-click="openEditView('settings');">Settings</a></li>
|
||||
<li><a class="pointer" ng-click="openEditView('annotations');">Annotations</a></li>
|
||||
<li><a class="pointer" ng-click="openEditView('templating');">Templating</a></li>
|
||||
<li><a class="pointer" ng-click="exportDashboard();">Export</a></li>
|
||||
<li><a class="pointer" ng-click="editJson();">View JSON</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav pull-right">
|
||||
<li ng-show="dashboardViewState.fullscreen">
|
||||
<a ng-click="exitFullscreen()">
|
||||
Back to dashboard
|
||||
@ -18,55 +59,6 @@
|
||||
</grafana-simple-panel>
|
||||
</li>
|
||||
|
||||
<li class="dropdown grafana-menu-save">
|
||||
<a bs-tooltip="'Save'" data-placement="bottom" class="dropdown-toggle" data-toggle="dropdown" ng-click="openSaveDropdown()">
|
||||
<i class='fa fa-save'></i>
|
||||
</a>
|
||||
|
||||
<ul class="save-dashboard-dropdown dropdown-menu" ng-if="saveDropdownOpened">
|
||||
<li>
|
||||
<form class="input-prepend nomargin save-dashboard-dropdown-save-form">
|
||||
<input class='input-medium' ng-model="dashboard.title" type="text" />
|
||||
<button class="btn" ng-click="saveDashboard()"><i class="fa fa-save"></i></button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="link" ng-click="set_default()">Save as Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="link" ng-click="purge_default()">Reset Home</a>
|
||||
</li>
|
||||
<li ng-show="!isFavorite">
|
||||
<a class="link" ng-click="markAsFavorite()">Mark as favorite</a>
|
||||
</li>
|
||||
<li ng-show="isFavorite">
|
||||
<a class="link" ng-click="removeAsFavorite()">Remove as favorite</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="link" ng-click="editJson()">Dashboard JSON</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="link" ng-click="exportDashboard()">Export dashboard</a>
|
||||
</li>
|
||||
<li ng-show="db.saveTemp">
|
||||
<a bs-tooltip="'Share'" data-placement="bottom" ng-click="saveForSharing()" config-modal="app/partials/dashLoaderShare.html">
|
||||
Share temp copy
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="dropdown grafana-menu-load">
|
||||
<a ng-click="openSearch()" bs-tooltip="'Search'" data-placement="bottom">
|
||||
<i class='fa fa-folder-open'></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="grafana-menu-home"><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/'><i class='fa fa-home'></i></a></li>
|
||||
|
||||
<li class="grafana-menu-edit" ng-show="dashboard.editable" bs-tooltip="'Configure dashboard'" data-placement="bottom"><a class="link" dash-editor-link="app/partials/dasheditor.html"><i class='fa fa-cog pointer'></i></a></li>
|
||||
|
||||
<li class="grafana-menu-stop-playlist hide">
|
||||
<a class='small' ng-click='stopPlaylist(2)'>
|
||||
Stop playlist
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-cogs"></i>
|
||||
Dashboard settings
|
||||
Settings
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
@ -11,9 +11,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div ng-if="editor.index == 0">
|
||||
<div class="editor-row">
|
||||
@ -93,14 +97,12 @@
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<div class="gf-box-footer">
|
||||
<div class="grafana-version-info" ng-show="editor.index === 0">
|
||||
<span class="editor-option small">
|
||||
Grafana version: {{grafanaVersion}}
|
||||
Grafana version: {{grafana.version}}
|
||||
</span>
|
||||
<span grafana-version-check>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="editor.index=0;dismiss();reset_panel();dashboard.emit_refresh()">Close</button>
|
||||
</div>
|
||||
|
@ -1,19 +1,22 @@
|
||||
<div ng-controller="JsonEditorCtrl">
|
||||
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-edit"></i>
|
||||
JSON
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body" style="height: 500px">
|
||||
<textarea ng-model="json" rows="20" spellcheck="false" style="width: 90%;"></textarea>
|
||||
</div>
|
||||
<div class="gf-box-body" style="height: 500px">
|
||||
<textarea ng-model="json" rows="20" spellcheck="false" style="width: 100%;"></textarea>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button type="button" class="btn btn-success pull-left" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="dismiss();">Close</button>
|
||||
<button type="button" class="btn btn-success" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
11
src/app/partials/error.html
Normal file
11
src/app/partials/error.html
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
<div class="row-fluid" style="margin-top: 100px;">
|
||||
<div class="span2"></div>
|
||||
|
||||
<div class="grafana-info-box span8 text-center">
|
||||
<h3>Page not found (404)</h3>
|
||||
</div>
|
||||
|
||||
<div class="span2"></div>
|
||||
|
||||
</div>
|
@ -1,12 +1,16 @@
|
||||
<div class="modal-body">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="modal-body gf-box-no-margin">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
Keyboard shutcuts
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
<table class="shortcut-table">
|
||||
<tr>
|
||||
<th></th>
|
||||
@ -45,6 +49,3 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-info" ng-click="dismiss()">Close</button>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="modal-body" ng-controller="InspectCtrl" ng-init="init()">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="modal-body gf-box gf-box-no-margin" ng-controller="InspectCtrl" ng-init="init()">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-eye"></i>
|
||||
Inspector
|
||||
</div>
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div ng-if="editor.index == 0">
|
||||
<h5>Request details</h5>
|
||||
@ -72,9 +72,5 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-info" ng-click="dismiss()">Close</button>
|
||||
</div>
|
||||
|
@ -1,36 +0,0 @@
|
||||
<div ng-controller="MetricKeysCtrl" ng-init="init()">
|
||||
<h5>Load metrics keys into elastic search</h5>
|
||||
|
||||
<p>
|
||||
Work in progress...
|
||||
</p>
|
||||
<!-- <div class="row-fluid">
|
||||
<div class="span12">
|
||||
<label class="small">Load metrics recursive starting from this metric path</label>
|
||||
<input type="text" class="input-xlarge" ng-model="metricPath"> </input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid" style="margin-top: 15px;">
|
||||
<div class="span12">
|
||||
<button class="btn btn-success" ng-click="createIndex()">Clear/Create index</button>
|
||||
<button class="btn btn-success" ng-click="loadMetricsFromPath()">Load from metric path</button>
|
||||
<button class="btn btn-danger" ng-click="loadAll()">Load all</button>
|
||||
<tip>Load all will fetch all metrics in one call, can be intensive for graphite and for the browser if you have a lot of metrics</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid" style="margin-top: 15px;">
|
||||
<div class="span12" ng-show="infoText" style="padding-top: 10px;">
|
||||
{{infoText}}
|
||||
</div>
|
||||
<div class="span12 alert alert-error" ng-show="errorText">
|
||||
{{errorText}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid" ng-show="metricCounter">
|
||||
<div class="span12" style="padding-top: 10px;">
|
||||
Metrics indexed: {{metricCounter}}
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
103
src/app/partials/login.html
Normal file
103
src/app/partials/login.html
Normal file
@ -0,0 +1,103 @@
|
||||
<div class="container">
|
||||
|
||||
<div class="login-box">
|
||||
|
||||
<div class="login-box-logo">
|
||||
<img src="img/logo_transparent_200x75.png">
|
||||
</div>
|
||||
|
||||
<div class="login-inner-box">
|
||||
<div class="login-tab-header">
|
||||
<button class="btn-login-tab" ng-click="loginMode = true;" ng-class="{active: loginMode}">
|
||||
Log in
|
||||
</button>
|
||||
<button class="btn-login-tab" ng-click="loginMode = false;" ng-class="{active: !loginMode}" ng-show="!disableUserSignUp">
|
||||
Sign up
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form name="loginForm" class="login-form">
|
||||
<div class="tight-form" ng-if="loginMode">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>User</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" name="username" class="tight-form-input last" required ng-model='formModel.user' placeholder="email or username" style="width: 246px">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form" ng-if="loginMode">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Password</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="password" name="password" class="tight-form-input last" required ng-model="formModel.password" id="inputPassword" style="width: 246px" placeholder="password">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="!loginMode">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Email</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="email" class="tight-form-input last" required ng-model='formModel.email' placeholder="email" style="width: 246px">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="!loginMode">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Password</strong>
|
||||
</li>
|
||||
<li>
|
||||
<input type="password" class="tight-form-input last" watch-change="passwordChanged(inputValue)" ng-minlength="4" required ng-model='formModel.password' placeholder="password" style="width: 246px">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="password-strength small" ng-if="!loginMode" ng-class="strengthClass">
|
||||
<em>{{strengthText}}</em>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="login-submit-button-row">
|
||||
<button type="submit" class="btn" ng-click="submit();"
|
||||
ng-class="{'btn-inverse': !loginForm.$valid, 'btn-primary': loginForm.$valid}">
|
||||
{{submitBtnText}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="login-oauth text-center">
|
||||
<a class="btn btn-google" href="login/google" target="_self" ng-if="googleAuthEnabled">
|
||||
<i class="fa fa-google"></i>
|
||||
with Google
|
||||
</a>
|
||||
<a class="btn btn-github" href="login/github" target="_self" ng-if="githubAuthEnabled">
|
||||
<i class="fa fa-github"></i>
|
||||
with Github
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 100px">
|
||||
<div class="version-footer text-center small">
|
||||
Grafana version: {{buildInfo.version}}, commit: {{buildInfo.commit}},
|
||||
build date: {{buildInfo.buildstamp | date: 'yyyy-MM-dd HH:mm:ss' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
14
src/app/partials/navbar.html
Normal file
14
src/app/partials/navbar.html
Normal file
@ -0,0 +1,14 @@
|
||||
<div class="navbar navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<topnav toggle="toggleSideMenu()"
|
||||
title="{{pageTitle}}"
|
||||
icon="{{pageIcon}}"
|
||||
section="{{pageSection}}"
|
||||
show-menu-btn="!grafana.sidemenu">
|
||||
</topnav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
<div bindonce class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<i class="fa fa-text-width"></i>
|
||||
<span bo-text="panel.type+' settings'"></span>
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" data-title="{{tab.title}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div ng-repeat="tab in panelMeta.editorTabs" ng-show="editor.index == $index">
|
||||
<div ng-include src="tab.src"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="editor.index=0;dismiss()">Close</button>
|
||||
</div>
|
@ -1,12 +1,17 @@
|
||||
<div ng-controller="PlaylistCtrl" ng-init="init()">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-play"></i>
|
||||
Start dashboard playlist
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
@ -52,11 +57,11 @@
|
||||
<input type="text" class="input-small" ng-model="timespan" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button class="btn btn-success" ng-click="start();dismiss();"><i class="fa fa-play"></i> Start</button>
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="dismiss();">Close</button>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<button class="btn btn-success pull-right" ng-click="start();dismiss();"><i class="fa fa-play"></i> Start</button>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,56 +1,54 @@
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-th-list"></i>
|
||||
Row settings
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['General','Panels']" data-title="{{tab}}">
|
||||
<div ng-repeat="tab in ['General','Panels']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div class="editor-row" ng-if="editor.index == 0">
|
||||
<div class="editor-option">
|
||||
<label class="small">Title</label><input type="text" class="input-medium" ng-model='row.title'></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Height</label><input type="text" class="input-mini" ng-model='row.height'></input>
|
||||
</div>
|
||||
<div class="editor-row" ng-if="editor.index == 0">
|
||||
<div class="editor-option">
|
||||
<label class="small">Title</label><input type="text" class="input-medium" ng-model='row.title'></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Height</label><input type="text" class="input-mini" ng-model='row.height'></input>
|
||||
</div>
|
||||
<editor-opt-bool text="Editable" model="row.editable"></editor-opt-bool>
|
||||
<editor-opt-bool text="Show title" model="row.showTitle"></editor-opt-bool>
|
||||
</div>
|
||||
<div class="row-fluid" ng-if="editor.index == 1">
|
||||
<div class="span12">
|
||||
<div class="row-fluid" ng-if="editor.index == 1">
|
||||
<div class="span12">
|
||||
<table class="grafana-options-table" style="max-width: 400px; width: auto">
|
||||
<thead>
|
||||
<th>Title</th>
|
||||
<th>Type</th>
|
||||
<th>Span</span></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr ng-repeat="panel in row.panels">
|
||||
<td style="width: 95%">{{panel.title}}</td>
|
||||
<td>{{panel.type}}</td>
|
||||
<td><select ng-hide="panel.sizeable == false" class="input-mini" style="margin-bottom: 0;" ng-model="panel.span" ng-options="size for size in [1,2,3,4,5,6,7,8,9,10,11,12]"></select></td>
|
||||
<td><i ng-click="_.move(row.panels,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
||||
<td><i ng-click="_.move(row.panels,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
||||
<td>
|
||||
<a ng-click="row.panels = _.without(row.panels,panel)" class="btn btn-danger btn-small">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<thead>
|
||||
<th>Title</th>
|
||||
<th>Type</th>
|
||||
<th>Span</span></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr ng-repeat="panel in row.panels">
|
||||
<td style="width: 95%">{{panel.title}}</td>
|
||||
<td>{{panel.type}}</td>
|
||||
<td><select ng-hide="panel.sizeable == false" class="input-mini" style="margin-bottom: 0;" ng-model="panel.span" ng-options="size for size in [1,2,3,4,5,6,7,8,9,10,11,12]"></select></td>
|
||||
<td><i ng-click="_.move(row.panels,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
||||
<td><i ng-click="_.move(row.panels,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
||||
<td>
|
||||
<a ng-click="row.panels = _.without(row.panels,panel)" class="btn btn-danger btn-small">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="editor.index=0;dismiss();reset_panel();close_edit()">Close</button>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div ng-controller="SearchCtrl" ng-init="init()">
|
||||
<div ng-controller="SearchCtrl" ng-init="init()" class="search-box">
|
||||
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title" style="border: 0; line-height: 41px;">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title" style="border: 0; line-height: 41px;">
|
||||
<i class="fa fa-search"></i>
|
||||
Search
|
||||
</div>
|
||||
@ -30,7 +30,6 @@
|
||||
</div>
|
||||
|
||||
<div ng-if="!showImport">
|
||||
<h6 ng-hide="results.dashboards.length">No dashboards matching your query were found.</h6>
|
||||
<div class="search-results-container" ng-if="tagsOnly">
|
||||
<div class="row">
|
||||
<div class="span6 offset1">
|
||||
@ -47,13 +46,12 @@
|
||||
</div>
|
||||
|
||||
<div class="search-results-container" ng-if="!tagsOnly">
|
||||
<h6 ng-hide="results.dashboards.length">No dashboards matching your query were found.</h6>
|
||||
|
||||
<div class="search-result-item pointer" bindonce ng-repeat="row in results.dashboards"
|
||||
ng-class="{'selected': $index === selectedIndex }" ng-click="goToDashboard(row.id)">
|
||||
ng-class="{'selected': $index === selectedIndex }" ng-click="goToDashboard(row.slug)">
|
||||
|
||||
<div class="search-result-actions small">
|
||||
<a ng-click="shareDashboard(row.id, row.id, $event)" config-modal="app/partials/dashLoaderShare.html">
|
||||
<i class="fa fa-share"></i> share
|
||||
</a>
|
||||
<a ng-click="deleteDashboard(row, $event)">
|
||||
<i class="fa fa-remove"></i> delete
|
||||
</a>
|
||||
@ -63,6 +61,7 @@
|
||||
<a ng-click="filterByTag(tag, $event)" ng-repeat="tag in row.tags" tag-color-from-name class="label label-tag">
|
||||
{{tag}}
|
||||
</a>
|
||||
<i class="fa" ng-class="{'fa-star': row.isStarred, 'fa-star-o': !row.isStarred}"></i>
|
||||
</div>
|
||||
|
||||
<a class="search-result-link">
|
||||
|
@ -1,33 +0,0 @@
|
||||
<div ng-controller="SharePanelCtrl">
|
||||
<div class="modal-header">
|
||||
<div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<i class="fa fa-share"></i>
|
||||
Share
|
||||
</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['Link']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="editor-row">
|
||||
<editor-opt-bool text="Current time range" model="forCurrent" change="buildUrl()"></editor-opt-bool>
|
||||
<editor-opt-bool text="To this panel only" model="toPanel" change="buildUrl()"></editor-opt-bool>
|
||||
<editor-opt-bool text="Include template variables" model="includeTemplateVars" change="buildUrl()"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin-top: 20px;">
|
||||
<input type="text" data-share-panel-url class="input input-fluid" ng-model='shareUrl'></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-success pull-right" ng-click="dismiss();">close</button>
|
||||
</div>
|
||||
</div>
|
18
src/app/partials/shareDashboard.html
Normal file
18
src/app/partials/shareDashboard.html
Normal file
@ -0,0 +1,18 @@
|
||||
<div class="modal-body gf-box gf-box-no-margin">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-share-alt"></i>
|
||||
Share dashboard
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<label>Share this dashboard with this URL</label>
|
||||
<input ng-model='share.url' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()">
|
||||
</div>
|
||||
|
||||
</div>
|
39
src/app/partials/sidemenu.html
Normal file
39
src/app/partials/sidemenu.html
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
<div ng-controller="SideMenuCtrl" ng-init="init()">
|
||||
<ul class="sidemenu">
|
||||
<li style="margin-bottom: 2px;">
|
||||
<a class="pointer sidemenu-top-btn" ng-click="toggleSideMenu()">
|
||||
<img class="logo-icon" src="img/fav32.png"></img>
|
||||
<i class="pull-right fa fa-angle-left"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-repeat="item in menu">
|
||||
<a href="{{item.href}}" class="sidemenu-item" target="{{item.target}}">
|
||||
<span class="sidemenu-icon"><i class="{{item.icon}}"></i></span>
|
||||
<span class="sidemenu-item-text">{{item.text}}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-if="grafana.user.isSignedIn" style="margin-top:50px">
|
||||
<a href="profile" class="sidemenu-item sidemenu-user">
|
||||
<img ng-src="{{grafana.user.gravatarUrl}}">
|
||||
<span class="sidemenu-item-text">{{grafana.user.name}}</a>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-if="grafana.user.isSignedIn">
|
||||
<a href="logout" class="sidemenu-item" target="_self">
|
||||
<span class="sidemenu-item-text no-icon">Sign out</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-if="!grafana.user.isSignedIn" style="margin-top:50px">
|
||||
<a href="/login" class="sidemenu-item" target="_self">
|
||||
<span class="sidemenu-icon"><i class="fa fa-sign-in"></i></span>
|
||||
<span class="sidemenu-item-text">Sign in</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
@ -1,20 +1,6 @@
|
||||
<div class="submenu-controls" ng-controller="SubmenuCtrl">
|
||||
<div class="tight-form" style="border-top: none">
|
||||
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
<div class="dropdown">
|
||||
<a class="pointer" data-toggle="dropdown">
|
||||
<i class="fa fa-cog"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="pointer" dash-editor-link="app/partials/templating_editor.html">Templating</a></li>
|
||||
<li><a class="pointer" dash-editor-link="app/features/annotations/partials/editor.html">Annotations</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="tight-form-list" ng-if="dashboard.templating.enable">
|
||||
<li ng-repeat-start="variable in variables" class="tight-form-item template-param-name">
|
||||
<span class="template-variable ">
|
||||
|
@ -1,17 +1,23 @@
|
||||
<div ng-controller="TemplateEditorCtrl" ng-init="init()"> <div class="dashboard-editor-header">
|
||||
<div class="dashboard-editor-title">
|
||||
<div ng-controller="TemplateEditorCtrl" ng-init="init()">
|
||||
<div class="gf-box-header">
|
||||
<div class="gf-box-title">
|
||||
<i class="fa fa-code"></i>
|
||||
Templating
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['Variables', 'Add', 'Edit']" data-title="{{tab}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();dashboard.refresh();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-body">
|
||||
<div class="gf-box-body">
|
||||
|
||||
<div ng-if="editor.index == 0">
|
||||
|
||||
@ -50,104 +56,97 @@
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 1 || (editor.index == 2 && !currentIsNew)">
|
||||
<div class="row">
|
||||
<div class="editor-option">
|
||||
<div class="editor-option">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Variable name</label>
|
||||
<input type="text" class="input-medium" ng-model='current.name' placeholder="name" required></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Type</label>
|
||||
<select class="input-medium" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.type === 'query'">
|
||||
<label class="small">Datasource</label>
|
||||
<select class="input input-medium" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
|
||||
</div>
|
||||
|
||||
<editor-opt-bool text="Refresh on load" show-if="current.type === 'query'"
|
||||
tip="Check if you want values to be updated on dashboard load, will slow down dashboard load time"
|
||||
model="current.refresh"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div ng-show="current.type === 'interval'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Variable name</label>
|
||||
<input type="text" class="input-medium" ng-model='current.name' placeholder="name" required></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Type</label>
|
||||
<select class="input-medium" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.type === 'query'">
|
||||
<label class="small">Datasource</label>
|
||||
<select class="input input-medium" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
|
||||
</div>
|
||||
|
||||
<editor-opt-bool text="Refresh on load" show-if="current.type === 'query'"
|
||||
tip="Check if you want values to be updated on dashboard load, will slow down dashboard load time"
|
||||
model="current.refresh"></editor-opt-bool>
|
||||
</div>
|
||||
|
||||
<div ng-show="current.type === 'interval'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Values</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' ng-blur="runQuery()" placeholder="name"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-row">
|
||||
<editor-opt-bool text="Include auto interval" model="current.auto" change="runQuery()"></editor-opt-bool>
|
||||
<div class="editor-option" ng-show="current.auto">
|
||||
<label class="small">Auto interval steps <tip>How many steps, roughly, the interval is rounded and will not always match this count<tip></label>
|
||||
<select class="input-mini" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
|
||||
</div>
|
||||
<label class="small">Values</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' ng-blur="runQuery()" placeholder="name"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="current.type === 'custom'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Values seperated by comma</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="current.type === 'query'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option form-inline">
|
||||
<label class="small">Variable values query</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' placeholder="apps.servers.*"></input>
|
||||
<button class="btn btn-small btn-success" ng-click="runQuery()" bs-tooltip="'Execute query'" data-placement="right"><i class="fa fa-play"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin: 15px 0">
|
||||
<div class="editor-option form-inline">
|
||||
<label class="small">regex (optional, if you want to extract part of a series name or metric node segment)</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.regex' placeholder="/.*-(.*)-.*/"></input>
|
||||
<button class="btn btn-small btn-success" ng-click="runQuery()" bs-tooltip="'execute query'" data-placement="right"><i class="fa fa-play"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin: 15px 0">
|
||||
<editor-opt-bool text="All option" model="current.includeAll" change="runQuery()"></editor-opt-bool>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All format</label>
|
||||
<select class="input-medium" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values']"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All value</label>
|
||||
<input type="text" class="input-xlarge" ng-model='current.options[0].value'></input>
|
||||
</div>
|
||||
<div class="editor-row">
|
||||
<editor-opt-bool text="Include auto interval" model="current.auto" change="runQuery()"></editor-opt-bool>
|
||||
<div class="editor-option" ng-show="current.auto">
|
||||
<label class="small">Auto interval steps <tip>How many steps, roughly, the interval is rounded and will not always match this count<tip></label>
|
||||
<select class="input-mini" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
|
||||
<div ng-show="current.type === 'custom'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option" >
|
||||
<label class="small">Variable values (showing 20/{{current.options.length}})</label>
|
||||
<ul class="grafana-options-list">
|
||||
<li ng-repeat="option in current.options | limitTo: 20">
|
||||
{{option.text}}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="editor-option">
|
||||
<label class="small">Values seperated by comma</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="current.type === 'query'">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option form-inline">
|
||||
<label class="small">Variable values query</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.query' placeholder="apps.servers.*"></input>
|
||||
<button class="btn btn-small btn-success" ng-click="runQuery()" bs-tooltip="'Execute query'" data-placement="right"><i class="fa fa-play"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin: 15px 0">
|
||||
<div class="editor-option form-inline">
|
||||
<label class="small">regex (optional, if you want to extract part of a series name or metric node segment)</label>
|
||||
<input type="text" class="input-xxlarge" ng-model='current.regex' placeholder="/.*-(.*)-.*/"></input>
|
||||
<button class="btn btn-small btn-success" ng-click="runQuery()" bs-tooltip="'execute query'" data-placement="right"><i class="fa fa-play"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" style="margin: 15px 0">
|
||||
<editor-opt-bool text="All option" model="current.includeAll" change="runQuery()"></editor-opt-bool>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All format</label>
|
||||
<select class="input-medium" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values']"></select>
|
||||
</div>
|
||||
<div class="editor-option" ng-show="current.includeAll">
|
||||
<label class="small">All value</label>
|
||||
<input type="text" class="input-xlarge" ng-model='current.options[0].value'></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<div class="editor-row">
|
||||
<div class="editor-option" >
|
||||
<label class="small">Variable values (showing 20/{{current.options.length}})</label>
|
||||
<ul class="grafana-options-list">
|
||||
<li ng-repeat="option in current.options | limitTo: 20">
|
||||
{{option.text}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dashboard-editor-footer">
|
||||
<button type="button" class="btn btn-success pull-left" ng-show="editor.index === 2" ng-click="update();">Update</button>
|
||||
<button type="button" class="btn btn-success pull-left" ng-show="editor.index === 1" ng-click="add();">Add</button>
|
||||
<button type="button" class="btn btn-success pull-right" ng-click="dismiss();">Close</button>
|
||||
<button type="button" class="btn btn-success" ng-show="editor.index === 2" ng-click="update();">Update</button>
|
||||
<button type="button" class="btn btn-success" ng-show="editor.index === 1" ng-click="add();">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,18 +1,20 @@
|
||||
<div class="modal-header">
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<h4 class="text-center"><i class="fa fa-warning"></i> Unsaved changes</h4>
|
||||
<div class="row-fluid">
|
||||
<span class="span3">
|
||||
{{changes}}
|
||||
<div class="modal-body gf-box gf-box-no-margin">
|
||||
<div class="gf-box-header text-center">
|
||||
<span class="gf-box-title">
|
||||
<i class="fa fa-exclamation"></i>
|
||||
Unsaved changes
|
||||
</span>
|
||||
<button type="button" class="btn btn-success span2" ng-click="dismiss()">Cancel</button>
|
||||
<button type="button" class="btn btn-success span2" ng-click="save();dismiss();">Save</button>
|
||||
<button type="button" class="btn btn-warning span2" ng-click="ignore();dismiss();">Ignore</button>
|
||||
<span class="span3"></span>
|
||||
</div>
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body" style="min-height: 0;">
|
||||
<div class="row-fluid text-center">
|
||||
<button type="button" class="btn btn-success span4" ng-click="save();dismiss();">Save</button>
|
||||
<button type="button" class="btn btn-danger span4" ng-click="ignore();dismiss();">Ignore</button>
|
||||
<button type="button" class="btn btn-inverse span4" ng-click="dismiss()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
</div>
|
||||
|
79
src/app/routes/backend/all.js
Normal file
79
src/app/routes/backend/all.js
Normal file
@ -0,0 +1,79 @@
|
||||
define([
|
||||
'angular',
|
||||
'./dashboard',
|
||||
], function(angular) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
|
||||
module.config(function($routeProvider, $locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromDBProvider',
|
||||
reloadOnSearch: false,
|
||||
})
|
||||
.when('/dashboard/db/:id', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromDBProvider',
|
||||
reloadOnSearch: false,
|
||||
})
|
||||
.when('/dashboard/import/:id', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromImportCtrl',
|
||||
reloadOnSearch: false,
|
||||
})
|
||||
.when('/dashboard/new', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'NewDashboardCtrl',
|
||||
reloadOnSearch: false,
|
||||
})
|
||||
.when('/account', {
|
||||
templateUrl: 'app/features/account/partials/account.html',
|
||||
controller : 'AccountCtrl',
|
||||
})
|
||||
.when('/account/datasources', {
|
||||
templateUrl: 'app/features/account/partials/datasources.html',
|
||||
controller : 'DataSourcesCtrl',
|
||||
})
|
||||
.when('/account/users', {
|
||||
templateUrl: 'app/features/account/partials/users.html',
|
||||
controller : 'AccountUsersCtrl',
|
||||
})
|
||||
.when('/account/apikeys', {
|
||||
templateUrl: 'app/features/account/partials/apikeys.html',
|
||||
controller : 'ApiKeysCtrl',
|
||||
})
|
||||
.when('/account/import', {
|
||||
templateUrl: 'app/features/account/partials/import.html',
|
||||
controller : 'ImportCtrl',
|
||||
})
|
||||
.when('/profile', {
|
||||
templateUrl: 'app/features/profile/partials/profile.html',
|
||||
controller : 'ProfileCtrl',
|
||||
})
|
||||
.when('/admin/settings', {
|
||||
templateUrl: 'app/features/admin/partials/settings.html',
|
||||
controller : 'AdminSettingsCtrl',
|
||||
})
|
||||
.when('/admin/users', {
|
||||
templateUrl: 'app/features/admin/partials/users.html',
|
||||
controller : 'AdminUsersCtrl',
|
||||
})
|
||||
.when('/login', {
|
||||
templateUrl: 'app/partials/login.html',
|
||||
controller : 'LoginCtrl',
|
||||
})
|
||||
.when('/dashboard/solo/:id/', {
|
||||
templateUrl: 'app/features/panel/partials/soloPanel.html',
|
||||
controller : 'SoloPanelCtrl',
|
||||
})
|
||||
.otherwise({
|
||||
templateUrl: 'app/partials/error.html',
|
||||
controller: 'ErrorCtrl'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
54
src/app/routes/backend/dashboard.js
Normal file
54
src/app/routes/backend/dashboard.js
Normal file
@ -0,0 +1,54 @@
|
||||
define([
|
||||
'angular',
|
||||
'store',
|
||||
],
|
||||
function (angular) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
|
||||
module.controller('DashFromDBProvider', function($scope, datasourceSrv, $routeParams, backendSrv) {
|
||||
|
||||
var db = datasourceSrv.getGrafanaDB();
|
||||
|
||||
if (!$routeParams.id) {
|
||||
backendSrv.get('/api/dashboards/home').then(function(result) {
|
||||
$scope.initDashboard(result, $scope);
|
||||
},function() {
|
||||
$scope.initDashboard({}, $scope);
|
||||
$scope.appEvent('alert-error', ['Load dashboard failed', '']);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
db.getDashboard($routeParams.id, false).then(function(result) {
|
||||
$scope.initDashboard(result, $scope);
|
||||
}).then(null, function() {
|
||||
$scope.initDashboard({
|
||||
meta: {},
|
||||
model: { title: 'Not found' }
|
||||
}, $scope);
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('DashFromImportCtrl', function($scope, $location, alertSrv) {
|
||||
if (!window.grafanaImportDashboard) {
|
||||
alertSrv.set('Not found', 'Cannot reload page with unsaved imported dashboard', 'warning', 7000);
|
||||
$location.path('');
|
||||
return;
|
||||
}
|
||||
$scope.initDashboard({ meta: {}, model: window.grafanaImportDashboard }, $scope);
|
||||
});
|
||||
|
||||
module.controller('NewDashboardCtrl', function($scope) {
|
||||
$scope.initDashboard({
|
||||
meta: {},
|
||||
model: {
|
||||
title: "New dashboard",
|
||||
rows: [{ height: '250px', panels:[] }]
|
||||
},
|
||||
}, $scope);
|
||||
});
|
||||
|
||||
});
|
24
src/app/routes/standalone/all.js
Normal file
24
src/app/routes/standalone/all.js
Normal file
@ -0,0 +1,24 @@
|
||||
define([
|
||||
'angular',
|
||||
'config',
|
||||
'store',
|
||||
'./fromDB',
|
||||
'./fromFile',
|
||||
'./fromScript',
|
||||
],
|
||||
function (angular, config, store) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.routes.standalone');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.otherwise({ redirectTo: config.default_route })
|
||||
.when('/', {
|
||||
redirectTo: function() {
|
||||
return store.get('grafanaDashboardDefault') || config.default_route;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -1,23 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'config',
|
||||
'store',
|
||||
'./fromDB',
|
||||
'./fromFile',
|
||||
'./fromScript',
|
||||
],
|
||||
function (angular, config, store) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
redirectTo: function() {
|
||||
return store.get('grafanaDashboardDefault') || config.default_route;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -4,7 +4,7 @@ define([
|
||||
function (angular) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
var module = angular.module('grafana.routes.standalone');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user