aboutsummaryrefslogtreecommitdiff
path: root/src/main/resources/webapp/apps
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/resources/webapp/apps')
-rwxr-xr-xsrc/main/resources/webapp/apps/app.css58
-rwxr-xr-xsrc/main/resources/webapp/apps/app.js97
-rwxr-xr-xsrc/main/resources/webapp/apps/buildApp/build.html30
-rwxr-xr-xsrc/main/resources/webapp/apps/buildApp/buildApp.js12
-rw-r--r--src/main/resources/webapp/apps/core/CoreResources.js25
-rwxr-xr-xsrc/main/resources/webapp/apps/core/PagingTableService.js72
-rwxr-xr-xsrc/main/resources/webapp/apps/core/navbar.html17
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/badge.html30
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/badgeList.html46
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/buildList.html50
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/frontPage.html31
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/frontPageApp.js110
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/person.html100
-rwxr-xr-xsrc/main/resources/webapp/apps/frontPageApp/personList.html47
-rw-r--r--src/main/resources/webapp/apps/jenkinsApp/JenkinsResources.js25
-rw-r--r--src/main/resources/webapp/apps/jenkinsApp/build.html43
-rw-r--r--src/main/resources/webapp/apps/jenkinsApp/jenkinsApp.js57
-rwxr-xr-xsrc/main/resources/webapp/apps/jenkinsApp/job.html61
-rw-r--r--src/main/resources/webapp/apps/jenkinsApp/server-list.html32
-rw-r--r--src/main/resources/webapp/apps/jenkinsApp/server.html64
20 files changed, 1007 insertions, 0 deletions
diff --git a/src/main/resources/webapp/apps/app.css b/src/main/resources/webapp/apps/app.css
new file mode 100755
index 0000000..3088282
--- /dev/null
+++ b/src/main/resources/webapp/apps/app.css
@@ -0,0 +1,58 @@
+/*
+ * Badges
+ */
+/*
+ bronze = #8c7853
+ bronze ii = #a67d3d
+ */
+
+.badge-level-1 { background-color: #a67d3d; color: #000000 }
+.badge-level-2 { background-color: silver; color: #000000 }
+.badge-level-3 { background-color: #ffd700; color: #000000 }
+
+/*
+ * Avatar
+ */
+
+/* This has to match the Gravatar image */
+.avatar80 {
+ width: 80px;
+ height: 80px;
+}
+
+/*
+ *
+ */
+
+#content {
+ background-color: #ffffff;
+ padding-bottom: 60px;
+}
+
+#footer {
+ background-color: #f5f5f5;
+ border-top: 1px solid #ccc;
+ color: #000000;
+}
+
+#footer .container {
+ padding: 60px 0;
+}
+
+#footer abbr[title] {
+ border-bottom: 1px dotted #000;
+}
+
+#footer p {
+ margin-bottom: 0;
+ color: #777;
+}
+
+#footer-links {
+ margin: 10px 0;
+}
+
+#footer-links li {
+ display: inline;
+ margin-right: 10px;
+}
diff --git a/src/main/resources/webapp/apps/app.js b/src/main/resources/webapp/apps/app.js
new file mode 100755
index 0000000..068f454
--- /dev/null
+++ b/src/main/resources/webapp/apps/app.js
@@ -0,0 +1,97 @@
+var directives = angular.module('core.directives', []);
+
+directives.filter('countBadgeByLevel', function () {
+ return function (badges) {
+ // 5 levels
+ var levels = [0, 0, 0, 0, 0];
+ angular.forEach(badges, function (value, key) {
+ levels[value.level - 1]++;
+ });
+ return levels;
+ }
+});
+
+directives.filter('isodate', function () {
+ return function (date) {
+ return date.toISOString();
+ }
+});
+
+directives.filter('gz', function () {
+ return function (num) {
+ if (angular.isArray(num)) {
+ var out = [];
+ angular.forEach(num, function (x) {
+ if (x > 0) {
+ out.push(x);
+ }
+ });
+
+ return out;
+ }
+ else if (angular.isNumber(num)) {
+ return num > 0;
+ }
+ console.log("fail");
+ return undefined;
+ }
+});
+
+directives.directive('navbar', function () {
+ return {
+ restrict: 'E',
+ templateUrl: '/apps/core/navbar.html?noCache=' + noCache
+ };
+});
+
+directives.directive('badge', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ badgeDetail: '=badgeDetail'
+ },
+ template: '<span class="badge-inverse badge-level-{{badgeDetail.badge.level}} badge">' +
+ ' <strong style="padding-right: 0.3em">{{badgeDetail.badge.name}}</strong>' +
+ ' <i class="icon-user"></i>' +
+ '</span>' +
+ ' awarded to ' +
+ '<a href="/#/person/{{badgeDetail.person.uuid}}">{{badgeDetail.person.name}}</a>. ' +
+ '<a href="/#/badge/{{badgeDetail.badge.uuid}}">More</a>'
+ }
+});
+
+directives.directive('badgeSpan', function () {
+ var template =
+ '<span class="badge-inverse badge-level-{{badge.level}} badge">' +
+ ' <strong style="padding-right: 0.3em">{{badge.name}}</strong>' +
+ ' <i class="icon-user"></i>' +
+ '</span>';
+
+ return {
+ restrict: 'E',
+ scope: {
+ badge: '=badge'
+ },
+ template: template
+ }
+});
+
+directives.directive('personLink', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ person: '=person'
+ },
+ template: '<a href="/#/person/{{person.uuid}}">{{person.name}}</a>'
+ }
+});
+
+directives.directive('personAvatar', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ person: '=person'
+ },
+ template: '<img ng-src="{{person.gravatar}}?default=identicon" class="avatar-image avatar80" title="{{person.name}}"/>'
+ }
+});
diff --git a/src/main/resources/webapp/apps/buildApp/build.html b/src/main/resources/webapp/apps/buildApp/build.html
new file mode 100755
index 0000000..fec38d2
--- /dev/null
+++ b/src/main/resources/webapp/apps/buildApp/build.html
@@ -0,0 +1,30 @@
+<div class="container">
+
+ <div class="page-header">
+ <h1>Build</h1>
+ </div>
+
+ <div>
+ <h3>Participants</h3>
+ <table>
+ <tr>
+ <th>Date</th>
+ <td>{{build.build.timestamp | date:'medium'}}</td>
+ </tr>
+ <tr>
+ <th>Status</th>
+ <td>
+ <span ng-show="build.build.success">SUCCESS</span>
+ <span ng-hide="build.build.success">FAILURE</span>
+ </td>
+ </tr>
+ </table>
+ <h3>Participants</h3>
+
+ <p ng-repeat="participant in build.participants">
+ <span>{{participant.name}}</span>
+ </p>
+
+ </div>
+
+</div>
diff --git a/src/main/resources/webapp/apps/buildApp/buildApp.js b/src/main/resources/webapp/apps/buildApp/buildApp.js
new file mode 100755
index 0000000..170af16
--- /dev/null
+++ b/src/main/resources/webapp/apps/buildApp/buildApp.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var buildApp = angular.module('buildApp', ['build', 'buildParticipant']).config(function ($routeProvider) {
+ $routeProvider.
+ when('/', {controller: BuildCtrl, templateUrl: '/apps/buildApp/build.html?noCache=' + noCache});
+});
+
+function BuildCtrl($scope, Build, BuildParticipant) {
+ Build.get({uuid: uuid}, function(build) {
+ $scope.build = build;
+ });
+}
diff --git a/src/main/resources/webapp/apps/core/CoreResources.js b/src/main/resources/webapp/apps/core/CoreResources.js
new file mode 100644
index 0000000..b258c85
--- /dev/null
+++ b/src/main/resources/webapp/apps/core/CoreResources.js
@@ -0,0 +1,25 @@
+'use strict';
+
+function Person($resource) {
+ return $resource('/resource/core/person/:uuid', {uuid: '@uuid'});
+}
+
+angular.module('person', ['ngResource']).factory('Person', Person);
+
+function Build($resource) {
+ return $resource('/resource/core/build/:uuid', {uuid: '@uuid'});
+}
+
+angular.module('build', ['ngResource']).factory('Build', Build);
+
+function BuildParticipant($resource) {
+ return $resource('/resource/core/build-participant/:uuid', {uuid: '@uuid'});
+}
+
+angular.module('buildParticipant', ['ngResource']).factory('BuildParticipant', BuildParticipant);
+
+function Badge($resource) {
+ return $resource('/resource/core/badge/:uuid', {uuid: '@uuid'});
+}
+
+angular.module('badge', ['ngResource']).factory('Badge', Badge);
diff --git a/src/main/resources/webapp/apps/core/PagingTableService.js b/src/main/resources/webapp/apps/core/PagingTableService.js
new file mode 100755
index 0000000..af593df
--- /dev/null
+++ b/src/main/resources/webapp/apps/core/PagingTableService.js
@@ -0,0 +1,72 @@
+function PagingTableService() {
+ var create = function ($scope, fetchCallback, options) {
+ options = options || {};
+ var watcher = options.watcher || function(){};
+ var self = {
+ rows: [],
+ startIndex: options.startIndex || 0,
+ count: options.count || 10
+ };
+
+ var update = function(){
+ fetchCallback(self.startIndex, self.count, function(data) {
+ self.rows = data.rows;
+ watcher();
+ });
+ };
+
+ self.first = function () {
+ self.startIndex = 0;
+ update();
+ };
+
+ self.next = function () {
+ self.startIndex += self.count;
+ update();
+ };
+
+ self.prev = function () {
+ if (self.startIndex == 0) {
+ return;
+ }
+ self.startIndex -= self.count;
+ update();
+ };
+
+ // Do an initial fetch
+ update();
+
+ return self;
+ };
+
+ var defaultCallback = function(Resource, args) {
+ args = args || {};
+ return function(startIndex, count, cb) {
+ if(startIndex) {
+ args.startIndex = startIndex;
+ }
+ if(count) {
+ args.count = count;
+ }
+ console.log("fetching", args);
+ Resource.query(args, function(data, headers) {
+ var totalResults = headers("total-results");
+ console.log("totalResults", totalResults);
+ console.log("got data", data);
+ cb({
+ totalResults: totalResults,
+ rows: data
+ });
+ });
+ };
+ };
+
+ return {
+ create: create,
+ defaultCallback: defaultCallback
+ }
+}
+
+angular.
+ module('pagingTableService', ['ngResource']).
+ factory('PagingTableService', PagingTableService);
diff --git a/src/main/resources/webapp/apps/core/navbar.html b/src/main/resources/webapp/apps/core/navbar.html
new file mode 100755
index 0000000..914258e
--- /dev/null
+++ b/src/main/resources/webapp/apps/core/navbar.html
@@ -0,0 +1,17 @@
+<div class="navbar">
+ <div class="navbar-inner">
+ <div class="container">
+ <span class="brand"><a href="/">Wat</a></span>
+ <div class="nav-collapse collapse">
+ <ul class="nav">
+ <li class=""><a href="#/">Home</a></li>
+ <li class=""><a href="#/badge/">Badges</a></li>
+ <li class=""><a href="#/person/">People</a></li>
+ <li class=""><a href="#/build/">Builds</a></li>
+ <li class="divider-vertical"></li>
+ <li class=""><a href="/jenkins">Jenkins</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/badge.html b/src/main/resources/webapp/apps/frontPageApp/badge.html
new file mode 100755
index 0000000..92fc7ae
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/badge.html
@@ -0,0 +1,30 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Badge</h1>
+ </div>
+
+ <div class="row">
+ <div class="span12">
+ The badge <badge-span badge="badge.badge"></badge-span> was awarded to <person-link person="badge.person"></person-link> at
+ {{badge.badge.createdDate | date:'medium'}}.
+ </div>
+ </div>
+
+ <div class="row" ng-switch="badge.badge.name">
+ <div class="span12" ng-switch-when="UNBREAKABLE">
+ <h2>Details</h2>
+ <p>
+ The badge was awarded for having {{badge.personalBadge.builds.length}} successful builds in a row:
+ </p>
+ <ul class="unstyled">
+ <li ng-repeat="b in badge.personalBadge.builds">
+ <a class="btn" href="/build/{{b}}">{{$index + 1}} <i class="icon-chevron-right"></i></a>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/badgeList.html b/src/main/resources/webapp/apps/frontPageApp/badgeList.html
new file mode 100755
index 0000000..7671a55
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/badgeList.html
@@ -0,0 +1,46 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Badges Awarded</h1>
+ </div>
+
+ <style>
+ .awarded-badge {
+ text-align: center;
+ margin-bottom: 1em;
+ margin-right: 1em;
+ }
+
+ .avatar-image {
+ margin-bottom: 1em;
+ }
+ </style>
+
+ <div class="row">
+ <div class="span12">
+ <div class="row" ng-repeat="(date, group) in badgeGroups">
+ <div class="span12">
+ <h2>{{date | date:'mediumDate'}}</h2>
+ </div>
+ <div class="span12">
+ <div class="awarded-badge pull-left" ng-repeat="badge in group">
+ <div><person-avatar person="badge.person"></person-avatar></div>
+
+ <badge-span badge="badge.badge" ></badge-span> <br/>
+ <!--was awarded to<br/>-->
+ </div>
+ </div>
+ </div>
+ <ul class="pager">
+ <li class="previous" ng-show="persons.startIndex > 0">
+ <a ng-click="persons.prev()">&larr; Prev</a>
+ </li>
+ <li class="next">
+ <a ng-click="persons.next()">Next &rarr;</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/buildList.html b/src/main/resources/webapp/apps/frontPageApp/buildList.html
new file mode 100755
index 0000000..13a1dc3
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/buildList.html
@@ -0,0 +1,50 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Builds</h1>
+ </div>
+
+ <style>
+ .avatar-image {
+ /* This has to match bootstrap's row margin. */
+ margin-left: 30px;
+ padding-right: 1em;
+ float: left;
+ margin-top: 0;
+ }
+ </style>
+
+ <div class="row">
+ <div class="span12">
+ <table class="table">
+ <tbody ng-repeat="build in builds.rows">
+ <tr class="{{{true: 'success', false: 'error'}[build.build.success]}}">
+ <td>{{build.build.timestamp | date:'medium'}}</td>
+ <td>
+ <span ng-show="build.build.success">SUCCESS</span>
+ <span ng-hide="build.build.success">FAILURE</span>
+ </td>
+ <td><a class="btn" href="/build/{{build.build.uuid}}"><i class="icon-chevron-right"></i></a></td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <span ng-repeat="p in build.participants">
+ <person-avatar person="p"/>
+ </span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <ul class="pager">
+ <li class="previous" ng-show="builds.startIndex > 0">
+ <a ng-click="builds.prev()">&larr; Prev</a>
+ </li>
+ <li class="next">
+ <a ng-click="builds.next()">Next &rarr;</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/frontPage.html b/src/main/resources/webapp/apps/frontPageApp/frontPage.html
new file mode 100755
index 0000000..c3db55c
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/frontPage.html
@@ -0,0 +1,31 @@
+<div class="container">
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Recent</h1>
+ </div>
+
+ <div class="row">
+ <div class="span6">
+ <h3>Badges</h3>
+
+ <div>
+ <ul class="unstyled">
+ <li ng-repeat="b in recentBadges">
+ <badge badge-detail="b"/>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <div class="span6">
+ <h3>Newcomers</h3>
+
+ <ul class="unstyled">
+ <li ng-repeat="person in persons">
+ <a href="#/person/{{person.person.uuid}}">{{person.person.name}}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/frontPageApp.js b/src/main/resources/webapp/apps/frontPageApp/frontPageApp.js
new file mode 100755
index 0000000..6f80d0f
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/frontPageApp.js
@@ -0,0 +1,110 @@
+'use strict';
+
+var frontPageApp = angular.module('frontPageApp', ['ngGrid', 'person', 'badge', 'build', 'pagingTableService', 'core.directives']).config(function ($routeProvider) {
+ $routeProvider.
+ when('/', {controller: FrontPageCtrl, templateUrl: '/apps/frontPageApp/frontPage.html?noCache=' + noCache}).
+ when('/badge/', {controller: BadgeListCtrl, templateUrl: '/apps/frontPageApp/badgeList.html?noCache=' + noCache}).
+ when('/badge/:badgeUuid', {controller: BadgeCtrl, templateUrl: '/apps/frontPageApp/badge.html?noCache=' + noCache}).
+ when('/person/', {controller: PersonListCtrl, templateUrl: '/apps/frontPageApp/personList.html?noCache=' + noCache}).
+ when('/person/:personUuid', {controller: PersonCtrl, templateUrl: '/apps/frontPageApp/person.html?noCache=' + noCache}).
+ when('/build/', {controller: BuildListCtrl, templateUrl: '/apps/frontPageApp/buildList.html?noCache=' + noCache});
+});
+
+function FrontPageCtrl($scope, Person, Badge) {
+ $scope.persons = Person.query();
+ $scope.recentBadges = Badge.query();
+}
+
+function groupBy(array, size) {
+ var group = [];
+ var groups = [];
+ angular.forEach(array, function (element) {
+ group.push(element);
+ if (group.length == size) {
+ groups.push(group);
+ group = [];
+ }
+ });
+
+ if (group.length != 0) {
+ groups.push(group);
+ }
+ return groups;
+}
+
+function BadgeListCtrl($scope, Badge, PagingTableService) {
+ var groupSize = 6;
+
+ var personsWatcher = function () {
+ var withDay = _.map($scope.badges.rows, function(badge) {
+ badge.day = new Date(badge.badge.createdDate).clearTime().getTime();
+// badge.day.clearTime();
+ return badge;
+ });
+
+ var byDay = _.groupBy(withDay, 'day');
+ console.log("byDay", byDay);
+// var dateGroups = _.map(byDay, function(group, date) {
+// return {date: groupBy(group, groupSize)}
+// });
+
+ $scope.badgeGroups = byDay;
+ };
+
+ $scope.badges = PagingTableService.create($scope, PagingTableService.defaultCallback(Badge),
+ {count: groupSize * 6, watcher: personsWatcher});
+
+ $scope.badgeGroups = [];
+}
+
+function BadgeCtrl($scope, $routeParams, Badge) {
+ var badgeUuid = $routeParams.badgeUuid;
+ Badge.get({uuid: badgeUuid}, function (badge) {
+ $scope.badge = badge;
+ });
+}
+
+function PersonListCtrl($scope, Person, PagingTableService) {
+ var groupSize = 4;
+ var personsWatcher = function () {
+ $scope.personGroups = groupBy($scope.persons.rows, groupSize);
+ };
+
+ $scope.persons = PagingTableService.create($scope, PagingTableService.defaultCallback(Person, {orderBy: "name"}),
+ {count: groupSize * 6, watcher: personsWatcher});
+
+ $scope.personGroups = [];
+}
+
+function PersonCtrl($scope, $routeParams, Person, Build, PagingTableService) {
+ var personUuid = $routeParams.personUuid;
+
+ $scope.mode = 'overview';
+ $scope.builds = PagingTableService.create($scope, PagingTableService.defaultCallback(Build, {person: personUuid}));
+
+ $scope.setMode = function(mode) {
+ $scope.mode = mode;
+ switch(mode) {
+ case 'builds':
+ var builds = $scope.builds;
+
+ console.log("$scope.builds.length=" + builds.rows.length);
+ if (builds.rows.length == 0) {
+ $scope.builds.first();
+ }
+ break;
+ }
+ };
+
+ Person.get({uuid: personUuid}, function (person) {
+ $scope.person = person;
+ });
+
+ Build.query({person: personUuid}, function (builds) {
+ $scope.recentBuilds = builds;
+ });
+}
+
+function BuildListCtrl($scope, Build, PagingTableService) {
+ $scope.builds = PagingTableService.create($scope, PagingTableService.defaultCallback(Build, {fields: "detailed"}));
+}
diff --git a/src/main/resources/webapp/apps/frontPageApp/person.html b/src/main/resources/webapp/apps/frontPageApp/person.html
new file mode 100755
index 0000000..ba5c18d
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/person.html
@@ -0,0 +1,100 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>{{person.person.name}}</h1>
+ </div>
+
+ <ul class="nav nav-tabs">
+ <li ng-class="{active: mode == 'overview'}"><a ng-click="setMode('overview')">Overview</a></li>
+ <li ng-class="{active: mode == 'builds'}"><a ng-click="setMode('builds')">Builds</a></li>
+ </ul>
+
+ <div id="overview" ng-show="mode == 'overview'" class="row">
+ <div class="span6">
+ <h3>Upcoming</h3>
+ <table>
+ <tr ng-repeat="badge in person.badgesInProgress">
+ <td style="padding-right: 1em">{{badge.name}}</td>
+ <td style="width: 100%">
+ <div class="progress" style="margin-bottom: 0;" title="Progress: {{badge.progress}} of {{badge.goal}}">
+ <div class="bar" style="width: {{badge.progress / badge.goal * 100}}%;"></div>
+ </div>
+ </td>
+ </tr>
+ </table>
+
+ <h3>Badges</h3>
+ <ul class="unstyled">
+ <li ng-repeat="badge in person.badges">
+ <!--
+ <span class="badge-level-{{badge.level}} badge">{{badge.name}}</span>
+ -->
+ <strong>{{badge.name}}</strong>
+ <!--
+ <i class="icon-user ng-class: {{{1: 'badge-level-1', 2: 'badge-level-2', 3: 'badge-level-3'}[badge.level]}}"></i>
+ -->
+ <span class="badge-level-{{badge.level}} badge">
+ <i class="icon-user"></i>
+ </span>
+
+ {{badge.createdDate | date:'medium'}}
+ </li>
+ </ul>
+ </div>
+ <div class="span6">
+ <h3>Recent builds</h3>
+ <table class="table">
+ <thead>
+ <!--
+ <tr>
+ <th>Date</th>
+ <th>Success</th>
+ <th></th>
+ </tr>
+ -->
+ </thead>
+ <tbody>
+ <tr ng-repeat="build in recentBuilds" class="{{{true: 'success', false: 'error'}[build.success]}}">
+ <td>{{build.timestamp | date:'medium'}}</td>
+ <td>{{{true: 'Success', false: 'Failure'}[build.success]}}</td>
+ <td><a href="/build/{{build.uuid}}">Details</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div id="builds" ng-show="mode == 'builds'">
+ <h3>Builds</h3>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Success</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="build in builds.rows" class="{{{true: 'build-success', false: 'build-error'}[build.success]}}">
+ <td>{{build.timestamp | date:'medium'}}</td>
+ <td>{{build.success}}</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan="2">
+ <ul class="pager">
+ <li class="previous" ng-class="{disabled: builds.startIndex == 0}">
+ <a ng-click="builds.prev()">&larr; Older</a>
+ </li>
+ <li class="next">
+ <a ng-click="builds.next()">Newer &rarr;</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/frontPageApp/personList.html b/src/main/resources/webapp/apps/frontPageApp/personList.html
new file mode 100755
index 0000000..5d7d8c6
--- /dev/null
+++ b/src/main/resources/webapp/apps/frontPageApp/personList.html
@@ -0,0 +1,47 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>People</h1>
+ </div>
+
+ <style>
+ .avatar-image {
+ /* This has to match bootstrap's row margin. */
+ margin-left: 30px;
+ padding-right: 1em;
+ float: left;
+ margin-top: 0;
+
+ /* This has to match the Gravatar image */
+ width: 80px;
+ height: 80px;
+ }
+ </style>
+
+ <div class="row">
+ <div class="span12">
+ <div class="row" ng-repeat="group in personGroups">
+ <div class="span3" ng-repeat="person in group" style="padding-bottom: 1em">
+ <div class="row">
+ <person-avatar person="person.person"></person-avatar>
+ <a href="/#/person/{{person.person.uuid}}">{{person.person.name}}</a>
+ <br/>
+ <span ng-repeat="level in person.badges | countBadgeByLevel | gz">
+ <span class="badge-inverse badge-level-{{$index + 1}} badge"><i class="icon-user"></i> x {{level}}</span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <ul class="pager">
+ <li class="previous" ng-show="persons.startIndex > 0">
+ <a ng-click="persons.prev()">&larr; Prev</a>
+ </li>
+ <li class="next">
+ <a ng-click="persons.next()">Next &rarr;</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
diff --git a/src/main/resources/webapp/apps/jenkinsApp/JenkinsResources.js b/src/main/resources/webapp/apps/jenkinsApp/JenkinsResources.js
new file mode 100644
index 0000000..89f3139
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/JenkinsResources.js
@@ -0,0 +1,25 @@
+'use strict';
+
+function JenkinsServer($resource) {
+ return $resource('/resource/jenkins/server/:uuid', {uuid: '@uuid'});
+}
+
+angular.
+ module('jenkinsServer', ['ngResource']).
+ factory('JenkinsServer', JenkinsServer);
+
+function JenkinsJob($resource) {
+ return $resource('/resource/jenkins/job/:uuid', {uuid: '@uuid'});
+}
+
+angular.
+ module('jenkinsJob', ['ngResource']).
+ factory('JenkinsJob', JenkinsJob);
+
+function JenkinsBuild($resource) {
+ return $resource('/resource/jenkins/build/:uuid', {uuid: '@uuid'});
+}
+
+angular.
+ module('jenkinsBuild', ['ngResource']).
+ factory('JenkinsBuild', JenkinsBuild);
diff --git a/src/main/resources/webapp/apps/jenkinsApp/build.html b/src/main/resources/webapp/apps/jenkinsApp/build.html
new file mode 100644
index 0000000..7239c90
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/build.html
@@ -0,0 +1,43 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Jenkins Build</h1>
+ </div>
+
+ <ul class="breadcrumb">
+ <li><a ng-click="showServers()">All Servers</a> <span class="divider">/</span></li>
+ <li><a ng-click="showServer()">Servers</a> <span class="divider">/</span></li>
+ <li><a ng-click="showJob()">Job</a> <span class="divider">/</span></li>
+ <li class="active">Build</li>
+ </ul>
+
+ <h3>Overview</h3>
+ <table class="table">
+ <tbody>
+ <tr>
+ <th>Timestamp</th>
+ <td>{{details.build.timestamp | date:'medium'}}</td>
+ </tr>
+ <tr>
+ <th>Number</th>
+ <td>{{details.build.number}}</td>
+ </tr>
+ <tr>
+ <th>Duration</th>
+ <td>{{details.build.duration / 1000 | number:0}}s</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Users</h3>
+ <table>
+ <tbody>
+ <tr ng-repeat="user in details.participants">
+ <td>{{user.uuid}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
diff --git a/src/main/resources/webapp/apps/jenkinsApp/jenkinsApp.js b/src/main/resources/webapp/apps/jenkinsApp/jenkinsApp.js
new file mode 100644
index 0000000..e42c67b
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/jenkinsApp.js
@@ -0,0 +1,57 @@
+'use strict';
+
+var jenkinsApp = angular.module('jenkinsApp', ['jenkinsServer', 'jenkinsJob', 'jenkinsBuild', 'core.directives', 'pagingTableService']).config(function ($routeProvider) {
+ $routeProvider.
+ when('/', {controller: ServerListCtrl, templateUrl: '/apps/jenkinsApp/server-list.html?noCache=' + noCache}).
+ when('/server/:uuid', {controller: ServerCtrl, templateUrl: '/apps/jenkinsApp/server.html?noCache=' + noCache}).
+ when('/job/:uuid', {controller: JobCtrl, templateUrl: '/apps/jenkinsApp/job.html?noCache=' + noCache}).
+ when('/build/:uuid', {controller: BuildCtrl, templateUrl: '/apps/jenkinsApp/build.html?noCache=' + noCache});
+});
+
+function ServerListCtrl($scope, $location, JenkinsServer) {
+ JenkinsServer.query(function (servers) {
+ $scope.servers = servers;
+ });
+
+ $scope.showServers = function () { $location.path('/'); };
+ $scope.showServer = function (uuid) { $location.path('/server/' + uuid); };
+}
+
+function ServerCtrl($scope, $location, $routeParams, JenkinsServer, JenkinsJob, PagingTableService) {
+ var serverUuid = $routeParams.uuid;
+
+ JenkinsServer.get({uuid: serverUuid}, function (server) {
+ $scope.server = server;
+ });
+
+ $scope.jobs = PagingTableService.create($scope, PagingTableService.defaultCallback(JenkinsJob, {server: serverUuid}));
+
+ $scope.showServers = function () { $location.path('/'); };
+ $scope.showJob = function (uuid) { $location.path('/job/' + uuid); };
+}
+
+function JobCtrl($scope, $location, $routeParams, JenkinsJob, JenkinsBuild, PagingTableService) {
+ var jobUuid = $routeParams.uuid;
+
+ JenkinsJob.get({uuid: jobUuid}, function (details) {
+ $scope.details = details;
+ });
+
+ $scope.builds = PagingTableService.create($scope, PagingTableService.defaultCallback(JenkinsBuild, {job: jobUuid}));
+
+ $scope.showServers = function () { $location.path('/'); };
+ $scope.showServer = function () { $location.path('/server/' + $scope.job.server); };
+ $scope.showBuild = function (uuid) { $location.path('/build/' + uuid); };
+}
+
+function BuildCtrl($scope, $location, $routeParams, JenkinsBuild) {
+ var buildUuid = $routeParams.uuid;
+
+ JenkinsBuild.get({uuid: buildUuid}, function (details) {
+ $scope.details = details;
+ });
+
+ $scope.showServers = function () { $location.path('/'); };
+ $scope.showServer = function (uuid) { $location.path('/server/' + $scope.server.uuid); };
+ $scope.showJob = function (uuid) { $location.path('/job/' + $scope.build.job); };
+}
diff --git a/src/main/resources/webapp/apps/jenkinsApp/job.html b/src/main/resources/webapp/apps/jenkinsApp/job.html
new file mode 100755
index 0000000..8942ab7
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/job.html
@@ -0,0 +1,61 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Jenkins Job</h1>
+ </div>
+
+ <ul class="breadcrumb">
+ <li><a ng-click="showServers()">All Servers</a> <span class="divider">/</span></li>
+ <li><a ng-click="showServer()">Servers</a> <span class="divider">/</span></li>
+ <li class="active">Job</li>
+ </ul>
+
+ <h3>Overview</h3>
+
+ <table class="table">
+ <tbody>
+ <tr>
+ <th>URL</th>
+ <td><a href="{{details.job.displayName}}">{{details.job.displayName}}</a></td>
+ </tr>
+ <tr>
+ <th>Build count</th>
+ <td>{{details.buildCount}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Recent Builds</h3>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Build</th>
+ <th>Result</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="build in builds.rows" class="{{{true: 'success', false: 'error'}[build.success]}}">
+ <td>{{build.timestamp | date:'medium'}}</td>
+ <td>{{build.result}}</td>
+ <td><a class="btn" ng-click="showBuild(build.uuid)"><i class="icon-chevron-right"></i></a></td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan="3">
+ <ul class="pager">
+ <li class="previous" ng-class="{disabled: builds.startIndex == 0}">
+ <a ng-click="builds.prev()">&larr; Older</a>
+ </li>
+ <li class="next">
+ <a ng-click="builds.next()">Newer &rarr;</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+</div>
diff --git a/src/main/resources/webapp/apps/jenkinsApp/server-list.html b/src/main/resources/webapp/apps/jenkinsApp/server-list.html
new file mode 100644
index 0000000..9e297e6
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/server-list.html
@@ -0,0 +1,32 @@
+<div class="container">
+
+ <navbar/>
+
+ <div class="page-header">
+ <h1>Jenkins Servers</h1>
+ </div>
+
+ <ul class="breadcrumb">
+ <li class="active">All Servers</li>
+ </ul>
+
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>URL</th>
+ <th>Enabled</th>
+ <th></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="server in servers">
+ <td>{{server.url}}</td>
+ <td>{{server.enabled}}</td>
+ <td><a href="{{server.url}}">Visit</a></td>
+ <td><a class="btn" ng-click="showServer(server.uuid)"><i class="icon-chevron-right"></i></a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
diff --git a/src/main/resources/webapp/apps/jenkinsApp/server.html b/src/main/resources/webapp/apps/jenkinsApp/server.html
new file mode 100644
index 0000000..7fbd9f5
--- /dev/null
+++ b/src/main/resources/webapp/apps/jenkinsApp/server.html
@@ -0,0 +1,64 @@
+<div class="container">
+
+ <navbar/>
+
+
+ <div class="page-header">
+ <h1>Jenkins Server</h1>
+ </div>
+
+ <ul class="breadcrumb">
+ <li><a ng-click="showServers()">All Servers</a> <span class="divider">/</span></li>
+ <li class="active">Server</li>
+ </ul>
+
+ <h3>Overview</h3>
+ <table class="table">
+ <tbody>
+ <tr>
+ <th>URL</th>
+ <td><a href="{{server.url}}">{{server.url}}</a></td>
+ </tr>
+ <tr>
+ <th>Enabled</th>
+ <td>{{server.enabled}}</td>
+ </tr>
+ <tr>
+ <th>Stats</th>
+ <td>{{server.jobCount}} jobs, {{server.buildCount}} builds</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Recent Jobs</h3>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Job</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="job in jobs.rows">
+ <td>{{job.createdDate | date:'medium'}}</td>
+ <td>{{job.displayName}}</td>
+ <td>{{job.uuid}}</td>
+ <td><a class="btn" ng-click="showJob(job.uuid)"><i class="icon-chevron-right"></i></a></td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td colspan="3">
+ <ul class="pager">
+ <li class="previous" ng-class="{disabled: jobs.startIndex == 0}">
+ <a ng-click="jobs.prev()">&larr; Older</a>
+ </li>
+ <li class="next">
+ <a ng-click="jobs.next()">Newer &rarr;</a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+
+</div>