feat(ui): new containers view (#25)

feat(ui): new containers view
pull/26/head
Anthony Lapenna 2016-06-29 21:04:29 +12:00 committed by GitHub
parent 813c14d93c
commit 66ae15b4fb
6 changed files with 182 additions and 179 deletions

View File

@ -33,10 +33,17 @@
</rd-widget-taskbar>
<rd-widget-body classes="no-padding">
<div class="table-responsive">
<table class="table">
<table class="table table-hover">
<thead>
<tr>
<th><label><input type="checkbox" ng-model="state.toggle" ng-change="toggleSelectAll()" /> Select</label></th>
<th></th>
<th>
<a ui-sref="containers" ng-click="order('State')">
State
<span ng-show="sortType == 'State' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'State' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ui-sref="containers" ng-click="order('Names')">
Name
@ -44,6 +51,20 @@
<span ng-show="sortType == 'Names' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ui-sref="containers" ng-click="order('IP')">
IP Address
<span ng-show="sortType == 'IP' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'IP' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th ng-if="swarm">
<a ui-sref="containers" ng-click="order('Host')">
Host
<span ng-show="sortType == 'Host' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Host' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ui-sref="containers" ng-click="order('Image')">
Image
@ -58,30 +79,18 @@
<span ng-show="sortType == 'Command' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ui-sref="containers" ng-click="order('Created')">
Created
<span ng-show="sortType == 'Created' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Created' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
<th>
<a ui-sref="containers" ng-click="order('Status')">
Status
<span ng-show="sortType == 'Status' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
<span ng-show="sortType == 'Status' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
</a>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse))">
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
<td><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
<td><span class="label label-{{ container.State|containerstatusbadge }}">{{ container.State }}</span></td>
<td ng-if="swarm"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
<td ng-if="!swarm"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
<td>{{ container.IP ? container.IP : '-' }}</td>
<td ng-if="swarm">{{ container|swarmhostname}}</td>
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
<td>{{ container.Command|truncate:40 }}</td>
<td>{{ container.Created|getdate }}</td>
<td><span class="label label-{{ container.Status|statusbadge }}">{{ container.Status }}</span></td>
<td>{{ container.Command|truncate:60 }}</td>
</tr>
</tbody>
</table>

View File

@ -4,9 +4,8 @@ function ($scope, Container, Settings, Messages, ViewSpinner, Config) {
$scope.state = {};
$scope.state.displayAll = Settings.displayAll;
$scope.sortType = 'Created';
$scope.sortType = 'State';
$scope.sortReverse = true;
$scope.state.toggle = false;
$scope.state.selectedItemCount = 0;
$scope.order = function (sortType) {
@ -91,18 +90,6 @@ function ($scope, Container, Settings, Messages, ViewSpinner, Config) {
}
};
$scope.toggleSelectAll = function () {
$scope.state.selectedItem = $scope.state.toggle;
angular.forEach($scope.state.filteredContainers, function (i) {
i.Checked = $scope.state.toggle;
});
if ($scope.state.toggle) {
$scope.state.selectedItemCount = $scope.state.filteredContainers.length;
} else {
$scope.state.selectedItemCount = 0;
}
};
$scope.toggleGetAll = function () {
Settings.displayAll = $scope.state.displayAll;
update({all: Settings.displayAll ? 1 : 0});
@ -151,9 +138,10 @@ function ($scope, Container, Settings, Messages, ViewSpinner, Config) {
});
};
var hiddenLabels;
$scope.swarm = false;
Config.$promise.then(function (c) {
hiddenLabels = c.hiddenLabels;
$scope.swarm = c.swarm;
update({all: Settings.displayAll ? 1 : 0});
});
}]);

View File

@ -137,7 +137,7 @@
<td>{{ node.ip }}</td>
<td>{{ node.containers }}</td>
<td>{{ node.version }}</td>
<td><span class="label label-{{ node.status|statusbadge }}">{{ node.status }}</span></td>
<td><span class="label label-{{ node.status|nodestatusbadge }}">{{ node.status }}</span></td>
</tr>
</tbody>
</table>

View File

@ -1,128 +1,149 @@
angular.module('dockerui.filters', [])
.filter('truncate', function () {
'use strict';
return function (text, length, end) {
if (isNaN(length)) {
length = 10;
}
.filter('truncate', function () {
'use strict';
return function (text, length, end) {
if (isNaN(length)) {
length = 10;
}
if (end === undefined) {
end = '...';
}
if (end === undefined) {
end = '...';
}
if (text.length <= length || text.length - end.length <= length) {
return text;
}
else {
return String(text).substring(0, length - end.length) + end;
}
};
})
.filter('statusbadge', function () {
'use strict';
return function (text) {
if (text === 'Ghost') {
return 'important';
} else if (text === 'Unhealthy') {
return 'danger';
} else if (text.indexOf('Exit') !== -1 && text !== 'Exit 0') {
return 'warning';
}
return 'success';
};
})
.filter('trimcontainername', function () {
'use strict';
return function (name) {
if (name) {
return (name.indexOf('/') === 0 ? name.replace('/','') : name);
}
return '';
};
})
.filter('getstatetext', function () {
'use strict';
return function (state) {
if (state === undefined) {
return '';
}
if (state.Ghost && state.Running) {
return 'Ghost';
}
if (state.Running && state.Paused) {
return 'Running (Paused)';
}
if (state.Running) {
return 'Running';
}
return 'Stopped';
};
})
.filter('getstatelabel', function () {
'use strict';
return function (state) {
if (state === undefined) {
return 'label-default';
}
if (text.length <= length || text.length - end.length <= length) {
return text;
}
else {
return String(text).substring(0, length - end.length) + end;
}
};
})
.filter('containerstatusbadge', function () {
'use strict';
return function (text) {
if (text === 'paused') {
return 'warning';
} else if (text === 'created') {
return 'info';
} else if (text === 'exited') {
return 'danger';
}
return 'success';
};
})
.filter('nodestatusbadge', function () {
'use strict';
return function (text) {
if (text === 'Unhealthy') {
return 'danger';
}
return 'success';
};
})
.filter('trimcontainername', function () {
'use strict';
return function (name) {
if (name) {
return (name.indexOf('/') === 0 ? name.replace('/','') : name);
}
return '';
};
})
.filter('getstatetext', function () {
'use strict';
return function (state) {
if (state === undefined) {
return '';
}
if (state.Ghost && state.Running) {
return 'Ghost';
}
if (state.Running && state.Paused) {
return 'Running (Paused)';
}
if (state.Running) {
return 'Running';
}
return 'Stopped';
};
})
.filter('getstatelabel', function () {
'use strict';
return function (state) {
if (state === undefined) {
return 'label-default';
}
if (state.Ghost && state.Running) {
return 'label-important';
}
if (state.Running) {
return 'label-success';
}
return 'label-default';
};
})
.filter('humansize', function () {
'use strict';
return function (bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return 'n/a';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
var value = bytes / Math.pow(1024, i);
var decimalPlaces = (i < 1) ? 0 : (i - 1);
return value.toFixed(decimalPlaces) + ' ' + sizes[[i]];
};
})
.filter('containername', function () {
'use strict';
return function (container) {
var name = container.Names[0];
return name.substring(1, name.length);
};
})
.filter('repotag', function () {
'use strict';
return function (image) {
if (image.RepoTags && image.RepoTags.length > 0) {
var tag = image.RepoTags[0];
if (tag === '<none>:<none>') {
tag = '';
}
return tag;
}
return '';
};
})
.filter('getdate', function () {
'use strict';
return function (data) {
//Multiply by 1000 for the unix format
var date = new Date(data * 1000);
return date.toDateString();
};
})
.filter('errorMsg', function () {
return function (object) {
var idx = 0;
var msg = '';
while (object[idx] && typeof(object[idx]) === 'string') {
msg += object[idx];
idx++;
}
return msg;
};
});
if (state.Ghost && state.Running) {
return 'label-important';
}
if (state.Running) {
return 'label-success';
}
return 'label-default';
};
})
.filter('humansize', function () {
'use strict';
return function (bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return 'n/a';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
var value = bytes / Math.pow(1024, i);
var decimalPlaces = (i < 1) ? 0 : (i - 1);
return value.toFixed(decimalPlaces) + ' ' + sizes[[i]];
};
})
.filter('containername', function () {
'use strict';
return function (container) {
var name = container.Names[0];
return name.substring(1, name.length);
};
})
.filter('swarmcontainername', function () {
'use strict';
return function (container) {
return _.split(container.Names[0], '/')[2];
};
})
.filter('swarmhostname', function () {
'use strict';
return function (container) {
return _.split(container.Names[0], '/')[1];
};
})
.filter('repotag', function () {
'use strict';
return function (image) {
if (image.RepoTags && image.RepoTags.length > 0) {
var tag = image.RepoTags[0];
if (tag === '<none>:<none>') {
tag = '';
}
return tag;
}
return '';
};
})
.filter('getdate', function () {
'use strict';
return function (data) {
//Multiply by 1000 for the unix format
var date = new Date(data * 1000);
return date.toDateString();
};
})
.filter('errorMsg', function () {
return function (object) {
var idx = 0;
var msg = '';
while (object[idx] && typeof(object[idx]) === 'string') {
msg += object[idx];
idx++;
}
return msg;
};
});

View File

@ -10,11 +10,10 @@ function ImageViewModel(data) {
function ContainerViewModel(data) {
this.Id = data.Id;
this.State = data.State;
this.Names = data.Names;
this.IP = data.NetworkSettings.Networks[Object.keys(data.NetworkSettings.Networks)[0]].IPAddress;
this.Image = data.Image;
this.Command = data.Command;
this.Created = data.Created;
this.SizeRw = data.SizeRw;
this.Status = data.Status;
this.Checked = false;
this.Names = data.Names;
}

View File

@ -15,20 +15,6 @@ describe('filters', function () {
}));
});
describe('statusbadge', function () {
it('should be "important" when input is "Ghost"', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Ghost')).toBe('important');
}));
it('should be "success" when input is "Exit 0"', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Exit 0')).toBe('success');
}));
it('should be "warning" when exit code is non-zero', inject(function (statusbadgeFilter) {
expect(statusbadgeFilter('Exit 1')).toBe('warning');
}));
});
describe('getstatetext', function () {
it('should return an empty string when state is undefined', inject(function (getstatetextFilter) {
@ -352,4 +338,4 @@ describe('filters', function () {
expect(errorMsgFilter(response)).toBe(message);
}));
});
});
});