ui: more acl integration, condensing top bar

This commit is contained in:
Jack Pearkes 2014-08-22 12:25:10 -07:00
parent a133d9ecba
commit e970ea8ddf
7 changed files with 219 additions and 161 deletions

View File

@ -77,21 +77,25 @@
<script type="text/x-handlebars" id="actionbar">
<div class="row">
<div class="action-bar">
<div class="col-md-5">
<div {{ bind-attr class="searchBar:col-md-12:col-md-5" }} >
<div class="form-group">
{{ input type="text" value=filter class="form-control form-control-mini" placeholder="Filter by name"}}
{{ input type="text" value=filter class="form-control form-control-mini" placeholder=filterText}}
</div>
</div>
{{#if statuses}}
<div class="col-md-5">
<div class="form-group">
{{view Ember.Select content=statuses value=status class="form-control form-control-mini"}}
</div>
</div>
{{/if}}
{{#if hasExpanded }}
<div class="col-md-2 hidden-xs hidden-sm">
<div class="form-group">
<button {{ bind-attr class=":btn :btn-mini :pull-right condensed:btn-default:btn-primary" }} {{action toggleCondensed }}>Expand</button>
</div>
</div>
{{/if}}
</div>
</div>
</script>
@ -105,11 +109,11 @@
</div>
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
{{#link-to 'services' class='btn btn-default col-xs-12'}}Services{{/link-to}}
{{#link-to 'services' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>Services</a>{{/link-to}}
</div>
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
{{#link-to 'nodes' class='btn btn-default col-xs-12'}}Nodes{{/link-to}}
{{#link-to 'nodes' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>Nodes</a>{{/link-to}}
</div>
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
@ -120,12 +124,8 @@
{{#link-to 'acls' class='btn btn-default col-xs-12'}}ACL{{/link-to}}
</div>
<div class="col-md-1 col-md-offset-1 col-sm-3 col-sm-offset-1 col-xs-10 col-xs-offset-1">
{{#link-to 'services' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>{{ checkMessage }}</a>{{/link-to}}
</div>
<div class="col-md-1 col-sm-2 col-xs-6 col-sm-offset-0 col-xs-offset-1">
<a {{bind-attr class=":col-xs-6 :btn isDropDownVisible:btn-primary:btn-default"}} {{action "toggle"}}> <span class="elip-overflow">{{model}} <span class="caret"></span></span> </a>
<div class="col-md-2 col-md-offset-1 col-sm-2 col-xs-6 col-sm-offset-0 col-xs-offset-1">
<a {{bind-attr class=":col-xs-12 :btn isDropDownVisible:btn-primary:btn-default"}} {{action "toggle"}}> <span class="elip-overflow">{{model}} <span class="caret"></span></span> </a>
{{#if isDropdownVisible}}
<ul class="dropdown-menu col-xs-8" style="display:block;">
@ -559,44 +559,16 @@
{{view App.ActionBarView }}
{{#if filteredContent}}
{{#if condensed }}
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=44 }}
{{#link-to 'acl.show' Node tagName="div" href=false class="list-group-item list-condensed-link" }}
<div {{bind-attr class=":list-bar-horizontal"}}></div>
<div class="name">
foo
<small class="pull-right">
bar
</small>
</div>
{{/link-to}}
{{/collection}}
{{else}}
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=120 }}
{{#link-to 'nodes.show' Node tagName="div" href=false class="list-group-item list-link" }}
<div {{bind-attr class="hasFailingChecks:bg-orange:bg-green :list-bar"}}></div>
<h4 class="list-group-item-heading">
{{Node}}
<small>{{Address}}</small>
<div class="heading-helper">
<a class="subtle" href="#">{{checkMessage}}</a>
</div>
</h4>
<ul class="list-inline">
{{#each service in services}}
<li class="bold">{{service.Service}}</li>
{{/each}}
</ul>
{{/link-to}}
{{/collection}}
{{/if}}
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=44 }}
{{#link-to 'acls.show' ID tagName="div" href=false class="list-group-item list-condensed-link" }}
<div class="bg-light-gray list-bar-horizontal"></div>
<div class="name">
{{ aclName Name ID }}
</div>
{{/link-to}}
{{/collection}}
{{else}}
<p class="light">There are no nodes to show.</p>
<p class="light">There are no ACLs to show.</p>
{{/if}}
</div>
@ -613,81 +585,25 @@
<script type="text/x-handlebars" id="acl">
<div class="col-xs-12 col-sm-12 visible-xs visible-sm">
{{#link-to "nodes" class="btn btn-default btn-block" }}Back to all nodes{{/link-to}}
{{#link-to "acls" class="btn btn-default btn-block" }}Back to all ACLs{{/link-to}}
<hr>
</div>
<h3 class="no-margin">{{ model.Node }} <small> {{ model.Address }}</small></h3>
{{errorMessage}}
<h3 class="no-margin">{{aclName model.Name model.ID }}</h3>
<hr>
<h5>Services</h5>
{{#each service in model.Services }}
{{#link-to 'services.show' service.Service tagName="div" href=false class="list-group-item list-condensed-link double-line" }}
<div class="list-bar-horizontal bg-light-gray"></div>
<div class="name">
{{service.Service}}
<small class="pull-right">
:{{service.Port}}
</small>
</div>
<ul class="list-inline sub">
{{#each tag in service.Tags}}
<li>{{tag}}</li>
{{/each}}
{{serviceTagMessage service.Tags}}
</ul>
{{/link-to}}
{{/each}}
<h5>Checks</h5>
{{#each check in model.Checks }}
<div class="panel">
{{ panelBar check.Status }}
<div class="panel-heading">
<h3 class="panel-title">
{{check.Name}}
<small>{{check.CheckID}}</small>
<span class="panel-note">{{check.Status}}</span>
</h3>
</div>
<div class="panel-body">
<h5>Notes</h5>
<p>{{ check.Notes }}</p>
<h5>Output</h5>
<pre>{{check.Output}}</pre>
</div>
</div>
{{/each}}
<h5>Lock Sessions</h5>
{{#if sessions }}
{{errorMessage}}
{{#each session in sessions }}
<div class="list-group-item list-condensed double-line">
<div class="bg-light-gray list-bar-horizontal"></div>
<div class="name">
{{ sessionName session }}
<button {{ action "invalidateSession" session.ID }} {{ bind-attr class=":btn :btn-danger :pull-right :btn-list isLoading:btn-warning" }}>Invalidate</button>
</div>
<ul class="list-inline sub">
{{#each check in session.Checks}}
<li class="bold">{{check}}</li>
{{/each}}
</ul>
</div>
{{/each}}
{{else}}
<p class="light small">No sessions</p>
<button {{ action "clone" }} {{ bind-attr class=":btn :btn-default" }}>Clone</button>
<button {{ action "set" }} {{ bind-attr class=":btn :btn-default" }}>Use Token</button>
{{# if model.isNotAnon }}
<button {{ action "delete"}} {{ bind-attr class=":btn isLoading:btn-warning:btn-danger" }}>Delete</button>
{{/if}}
<h5>Type</h5>
<p>{{Type}}</p>
<h5>Rules</h5>
<pre>{{formatRules model.Rules}}</pre>
</script>
<script type="text/x-handlebars" id="index">

View File

@ -241,6 +241,8 @@ ItemBaseController = Ember.ArrayController.extend({
queryParams: ["filter", "status", "condensed"],
dc: Ember.computed.alias("controllers.dc"),
condensed: true,
hasExpanded: true,
filterText: "Filter by name",
filter: "", // default
status: "any status", // default
statuses: ["any status", "passing", "failing"],
@ -313,10 +315,16 @@ App.ServicesController = ItemBaseController.extend({
items: Ember.computed.alias("services"),
});
App.AclsIndexController = Ember.ArrayController.extend({
App.AclsController = Ember.ArrayController.extend({
needs: ["dc", "application"],
queryParams: ["filter"],
filterText: "Filter by name or ID",
searchBar: true,
dc: Ember.computed.alias("controllers.dc"),
items: Ember.computed.alias("acls"),
filter: "",
isShowingItem: function() {
var currentPath = this.get('controllers.application.currentPath');
@ -329,20 +337,95 @@ App.AclsIndexController = Ember.ArrayController.extend({
var items = this.get('items').filter(function(item, index, enumerable){
// First try to match on the name
var nameMatch = item.get('Name').toLowerCase().match(filter.toLowerCase());
if (nameMatch.length > 0) {
if (nameMatch !== null) {
return nameMatch;
// Otherwise match on the ID
} else {
return item.get('ID').toLowerCase().match(filter.toLowerCase());
}
});
return items;
}.property('filter', 'items.@each'),
});
App.AclsShowController = Ember.ArrayController.extend({
needs: ["dc"],
App.AclsShowController = Ember.ObjectController.extend({
needs: ["dc", "acls"],
dc: Ember.computed.alias("controllers.dc"),
isLoading: false,
actions: {
set: function() {
this.set('isLoading', true);
var controller = this;
var acl = controller.get('model');
var dc = controller.get('dc').get('datacenter');
if (window.confirm("Are you sure you want to use this token for your session?")) {
// Set
var token = App.set('settings.token', acl.ID);
controller.transitionToRoute('services');
this.set('isLoading', false);
}
},
clone: function() {
this.set('isLoading', true);
var controller = this;
var acl = controller.get('model');
var dc = controller.get('dc').get('datacenter');
var token = App.get('settings.token');
// Set
controller.transitionToRoute('services');
Ember.$.ajax({
url: formatUrl('/v1/acl/clone/'+ acl.ID, dc, token),
type: 'PUT'
}).then(function(response) {
controller.transitionToRoute('acls.show', response.ID);
controller.set('isLoading', false);
}).fail(function(response) {
// Render the error message on the form if the request failed
controller.set('errorMessage', 'Received error while processing: ' + response.statusText);
controller.set('isLoading', false);
});
},
delete: function() {
this.set('isLoading', true);
var controller = this;
var acl = controller.get('model');
var dc = controller.get('dc').get('datacenter');
var token = App.get('settings.token');
if (window.confirm("Are you sure you want to delete this token?")) {
Ember.$.ajax({
url: formatUrl('/v1/acl/destroy/'+ acl.ID, dc, token),
type: 'PUT'
}).then(function(response) {
Ember.$.getJSON(formatUrl('/v1/acl/list', dc, token)).then(function(data) {
objs = [];
data.map(function(obj){
if (obj.ID === "anonymous") {
objs.unshift(App.Acl.create(obj));
} else {
objs.push(App.Acl.create(obj));
}
});
controller.get('controllers.acls').set('acls', objs);
}).then(function() {
controller.transitionToRoute('acls');
controller.set('isLoading', false);
});
}).fail(function(response) {
// Render the error message on the form if the request failed
controller.set('errorMessage', 'Received error while processing: ' + response.statusText);
controller.set('isLoading', false);
});
}
}
}
});

View File

@ -28,6 +28,24 @@ Ember.Handlebars.helper('sessionName', function(session) {
}
});
Ember.Handlebars.helper('aclName', function(name, id) {
if (name === "") {
return id;
} else {
return new Handlebars.SafeString(name + ' <small class="pull-right">' + id + '</small>');
}
});
Ember.Handlebars.helper('formatRules', function(rules) {
if (rules === "") {
return "No rules defined";
} else {
return rules;
}
});
// We need to do this because of our global namespace properties. The
// service.Tags
Ember.Handlebars.helper('serviceTagMessage', function(tags) {

View File

@ -244,4 +244,37 @@ App.Key = Ember.Object.extend(Ember.Validations.Mixin, {
// An ACL
//
App.Acl = Ember.Object.extend({
isNotAnon: function() {
if (this.get('ID') === "anonymous"){
return false;
} else {
return true;
}
}.property('ID')
});
// Wrap localstorage with an ember object
App.Settings = Ember.Object.extend({
unknownProperty: function(key) {
return localStorage[key];
},
setUnknownProperty: function(key, value) {
if(Ember.isNone(value)) {
delete localStorage[key];
} else {
localStorage[key] = value;
}
this.notifyPropertyChange(key);
return value;
},
clear: function() {
this.beginPropertyChanges();
for (var i=0, l=localStorage.length; i<l; i++){
this.set(localStorage.key(i));
}
localStorage.clear();
this.endPropertyChanges();
}
});

View File

@ -11,32 +11,6 @@ Ember.Application.initializer({
}
});
// Wrap localstorage with an ember object
App.Settings = Ember.Object.extend({
unknownProperty: function(key) {
return localStorage[key];
},
setUnknownProperty: function(key, value) {
if(Ember.isNone(value)) {
delete localStorage[key];
} else {
localStorage[key] = value;
}
this.notifyPropertyChange(key);
return value;
},
clear: function() {
this.beginPropertyChanges();
for (var i=0, l=localStorage.length; i<l; i++){
this.set(localStorage.key(i));
}
localStorage.clear();
this.endPropertyChanges();
}
});
App.Router.map(function() {
// Our parent datacenter resource sets the namespace
// for the entire application
@ -61,7 +35,7 @@ App.Router.map(function() {
});
// ACLs
this.resource("acls", { path: "/acls" }, function(){
this.route("show", { path: "/:name" });
this.route("show", { path: "/:id" });
});
// Shows a page explaining that ACLs haven't been set-up

View File

@ -309,7 +309,11 @@ App.AclsRoute = App.BaseRoute.extend({
return Ember.$.getJSON(formatUrl('/v1/acl/list', dc, token)).then(function(data) {
objs = [];
data.map(function(obj){
objs.push(App.Acl.create(obj));
if (obj.ID === "anonymous") {
objs.unshift(App.Acl.create(obj))
} else {
objs.push(App.Acl.create(obj));
}
});
return objs;
});
@ -334,6 +338,24 @@ App.AclsRoute = App.BaseRoute.extend({
}
});
App.AclsShowRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise hash of the node and nodes
return Ember.RSVP.hash({
dc: dc,
acl: Ember.$.getJSON(formatUrl('/v1/acl/info/'+ params.id, dc, token)).then(function(data) {
return App.Acl.create(data[0]);
})
});
},
setupController: function(controller, models) {
controller.set('content', models.acl);
}
});
// Adds any global parameters we need to set to a url/path
function formatUrl(url, dc, token) {
if (url.indexOf("?") > 0) {

View File

@ -29,17 +29,6 @@
box-shadow: none;
}
&.btn-primary, &.active {
color: $purple-dark;
background-color: transparent;
border: 2px solid $purple;
&:hover {
background-color: $light-purple;
color: darken($purple, 10%);
}
}
&.btn-warning {
color: $orange-faded;
background-color: transparent;
@ -61,7 +50,6 @@
background-color: lighten($green-faded, 24%);
color: darken($green-dark, 10%);
}
}
&.btn-danger {
@ -73,7 +61,18 @@
background-color: lighten($red, 38%);
color: darken($red, 10%);
}
}
&.active {
color: $purple-dark;
background-color: transparent;
border: 2px solid $purple;
&:hover {
background-color: $light-purple;
color: darken($purple, 10%);
}
}
&.btn-mini {
@ -89,3 +88,16 @@
}
}
.active {
.btn {
color: $purple-dark;
background-color: transparent;
border: 2px solid $purple;
&:hover {
background-color: $light-purple;
color: darken($purple, 10%);
}
}
}