Rewrote and redesign how the data source edit views look and work so they conform better to how account views look, removed tabs and put top nav items to add data source etc, made list, edit and new seperate url routes, #1483

This commit is contained in:
Torkel Ödegaard 2015-02-14 10:04:27 +01:00
parent ed0fabd9de
commit f6c07fdabd
9 changed files with 229 additions and 170 deletions

View File

@ -75,6 +75,7 @@ func Register(r *macaron.Macaron) {
r.Group("/datasources", func() {
r.Combo("/").Get(GetDataSources).Put(AddDataSource).Post(UpdateDataSource)
r.Delete("/:id", DeleteDataSource)
r.Get("/:id", GetDataSourceById)
}, reqAccountAdmin)
r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)

View File

@ -9,9 +9,8 @@ import (
func GetDataSources(c *middleware.Context) {
query := m.GetDataSourcesQuery{AccountId: c.AccountId}
err := bus.Dispatch(&query)
if err != nil {
if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(500, "Failed to query datasources", err)
return
}
@ -36,6 +35,34 @@ func GetDataSources(c *middleware.Context) {
c.JSON(200, result)
}
func GetDataSourceById(c *middleware.Context) {
query := m.GetDataSourceByIdQuery{
Id: c.ParamsInt64(":id"),
AccountId: c.AccountId,
}
if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(500, "Failed to query datasources", err)
return
}
ds := query.Result
c.JSON(200, &dtos.DataSource{
Id: ds.Id,
AccountId: ds.AccountId,
Name: ds.Name,
Url: ds.Url,
Type: ds.Type,
Access: ds.Access,
Password: ds.Password,
Database: ds.Database,
User: ds.User,
BasicAuth: ds.BasicAuth,
IsDefault: ds.IsDefault,
})
}
func DeleteDataSource(c *middleware.Context) {
id := c.ParamsInt64(":id")

View File

@ -37,7 +37,7 @@ function (angular, _, $, config) {
if (contextSrv.user.isGrafanaAdmin) {
$scope.menu.push({
text: "Admin", href: $scope.getUrl("/admin/users"),
text: "Admin", href: $scope.getUrl("/admin/settings"),
icon: "fa fa-cube",
requireSignedIn: true,
});

View File

@ -1,6 +1,7 @@
define([
'./accountUsersCtrl',
'./datasourcesCtrl',
'./datasourceEditCtrl',
'./apiKeysCtrl',
'./importCtrl',
'./accountCtrl',

View File

@ -0,0 +1,69 @@
define([
'angular',
'lodash',
],
function (angular) {
'use strict';
var module = angular.module('grafana.controllers');
module.controller('DataSourceEditCtrl', function($scope, $http, backendSrv, $routeParams, $location) {
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.isNew = true;
$scope.datasources = [];
if ($routeParams.id) {
$scope.isNew = false;
$scope.getDatasourceById($routeParams.id);
} else {
$scope.current = angular.copy(defaults);
}
};
$scope.getDatasourceById = function(id) {
backendSrv.get('/api/datasources/' + id).then(function(ds) {
$scope.current = ds;
});
};
$scope.update = function() {
if (!$scope.editForm.$valid) {
return;
}
backendSrv.post('/api/datasources', $scope.current).then(function() {
$location.path("account/datasources");
});
};
$scope.add = function() {
if (!$scope.editForm.$valid) {
return;
}
backendSrv.put('/api/datasources', $scope.current)
.then(function() {
$scope.editor.index = 0;
$scope.getDatasources();
});
};
$scope.init();
});
});

View File

@ -1,5 +1,6 @@
define([
'angular',
'lodash',
],
function (angular) {
'use strict';
@ -8,47 +9,9 @@ function (angular) {
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() {
@ -63,25 +26,6 @@ function (angular) {
});
};
$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();
});

View File

@ -0,0 +1,74 @@
<topnav title="Data sources" icon="fa fa-database" subnav="true">
<ul class="nav">
<li><a href="account/datasources">Overview</a></li>
<li ng-class="{active: isNew}"><a href="account/datasources/new">Add new</a></li>
<li class="active" ng-show="!isNew"><a href="account/datasources/edit/{{current.name}}">Edit</a></li>
</ul>
</topnav>
<div class="page-container">
<div class="page">
<h2 ng-show="isNew">Add data source</h2>
<h2 ng-show="!isNew">Edit {{current.name}}</h2>
<form name="editForm">
<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>
<br>
<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="add()">Add</button>
<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="update()">Update</button>
<a class="btn btn-inverse" ng-show="!isNew" href="account/datasources">Cancel</a>
<br>
</form>
</div>
</div>

View File

@ -1,117 +1,52 @@
<topnav title="Data sources"
icon="fa fa-database">
<topnav title="Data sources" icon="fa fa-database" subnav="true">
<ul class="nav">
<li class="active" ><a href="account/datasources">Overview</a></li>
<li><a href="account/datasources/new">Add new</a></li>
</ul>
</topnav>
<div class="gf-box" style="min-height: 500px; max-width: 900px">
<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 class="page-container">
<div class="page">
<h2>Data sources</h2>
<div ng-if="datasources.length === 0">
<em>No datasources defined</em>
</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> &nbsp;
{{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>
<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> &nbsp;
{{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 href="account/datasources/edit/{{ds.id}}" class="btn btn-inverse 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 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>

View File

@ -38,6 +38,14 @@ define([
templateUrl: 'app/features/account/partials/datasources.html',
controller : 'DataSourcesCtrl',
})
.when('/account/datasources/edit/:id', {
templateUrl: 'app/features/account/partials/datasourceEdit.html',
controller : 'DataSourceEditCtrl',
})
.when('/account/datasources/new', {
templateUrl: 'app/features/account/partials/datasourceEdit.html',
controller : 'DataSourceEditCtrl',
})
.when('/account/users', {
templateUrl: 'app/features/account/partials/users.html',
controller : 'AccountUsersCtrl',