Skip to content

Commit c57198c

Browse files
authored
Merge pull request #6 from maxceem/dev
challenge #30057919 - 48H ADMIN APP - GROUP MEMBERSHIP MANAGEMENT
2 parents a57c7c0 + 6e97dd7 commit c57198c

15 files changed

+959
-0
lines changed

src/app/app.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,39 @@ angular.module('supportAdminApp', [
270270
data: { pageTitle: 'Edit Client' },
271271
resolve: { auth: authenticate }
272272
})
273+
.state('index.groups', {
274+
abstract: true,
275+
url: '/groups',
276+
templateUrl: 'app/groups/groups.html',
277+
data: { pageTitle: 'Groups' },
278+
controller: 'permissionmanagement.GroupsController'
279+
})
280+
.state('index.groups.list', {
281+
url: '/list',
282+
templateUrl: 'app/groups/groups.list.html',
283+
controller: 'permissionmanagement.GroupsListController',
284+
resolve: { auth: authenticate }
285+
})
286+
.state('index.groupmembers', {
287+
abstract: true,
288+
url: '/groupmembers/:groupId',
289+
templateUrl: 'app/groupmembers/groupmembers.html',
290+
data: { pageTitle: 'Group Members' },
291+
controller: 'permissionmanagement.GroupMembersController'
292+
})
293+
.state('index.groupmembers.list', {
294+
url: '/list',
295+
templateUrl: 'app/groupmembers/groupmembers.list.html',
296+
controller: 'permissionmanagement.GroupMembersListController',
297+
resolve: { auth: authenticate }
298+
})
299+
.state('index.groupmembers.new', {
300+
url: '/new',
301+
templateUrl: 'app/groupmembers/groupmembers.new.html',
302+
controller: 'permissionmanagement.GroupMembersNewController',
303+
data: { pageTitle: 'Add Group Members' },
304+
resolve: { auth: authenticate }
305+
})
273306
.state('index.billingaccounts', {
274307
abstract: true,
275308
url: '/billingaccounts',
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
3+
var module = angular.module('supportAdminApp');
4+
5+
/**
6+
* The parent controller for the groupmembers states
7+
*/
8+
module.controller('permissionmanagement.GroupMembersController', ['$scope', 'AuthService', '$state',
9+
function ($scope, $authService, $state) {
10+
$scope.$state = $state;
11+
12+
/**
13+
* Validate the user authentication
14+
*/
15+
$scope.authorized = function() {
16+
return $authService.isLoggedIn();
17+
};
18+
}
19+
]);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="row wrapper border-bottom white-bg page-heading">
2+
<div class="col-lg-10">
3+
<h2>{{$state.current.data.pageTitle}}</h2>
4+
</div>
5+
<div class="col-md-10 col-lg-12" ng-include src="'components/alert/alert.html'"></div>
6+
</div>
7+
<div ui-view=""></div>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
'use strict';
2+
3+
var module = angular.module('supportAdminApp');
4+
5+
module.controller('permissionmanagement.GroupMembersListController', [
6+
'$scope', '$rootScope', 'GroupMemberService', 'IdResolverService', '$stateParams', '$state', '$q', 'Alert',
7+
function ($scope, $rootScope, GroupMemberService, IdResolverService, $stateParams, $state, $q, $alert) {
8+
9+
// keep member types for easy iterating
10+
$scope.memberTypes = ['group', 'user'];
11+
12+
// true if list is loading
13+
$scope.isLoading = false;
14+
15+
// true if something is we are removing a bulk of entries
16+
$scope.processing = false;
17+
18+
// list data
19+
$scope.memberships = {};
20+
$scope.memberTypes.forEach(function(memberType) {
21+
$scope.memberships[memberType] = [];
22+
});
23+
24+
// current group id
25+
$scope.groupId = $stateParams.groupId;
26+
27+
// true if any members were selected in the list
28+
$scope.hasSelected = false;
29+
30+
// keeps
31+
$scope.isAllSelected = {};
32+
$scope.memberTypes.forEach(function(memberType) {
33+
$scope.isAllSelected[memberType] = false;
34+
});
35+
36+
/* Maps user ids, present in the page, into user handles. */
37+
$scope.users = {};
38+
var loadUser = IdResolverService.getUserResolverFunction($scope.users);
39+
40+
/* Maps groups ids, present in the page, into group names. */
41+
$scope.groups = {};
42+
var loadGroup = IdResolverService.getGroupResolverFunction($scope.groups);
43+
44+
/**
45+
* Return membership records which are selected in the table by checkboxes
46+
*
47+
* @return {Array} membership records
48+
*/
49+
function getSelectedMemberships() {
50+
return $scope.memberTypes.reduce(function(selectedMemberships, memberType) {
51+
return selectedMemberships.concat($scope.memberships[memberType].filter(function(membership) {
52+
return membership.isSelected;
53+
}));
54+
}, []);
55+
}
56+
57+
/**
58+
* Get list of group members for specified group
59+
*
60+
* @param {String} groupId group id for getting members
61+
*/
62+
$scope.fetch = function(groupId) {
63+
$alert.clear();
64+
$scope.isLoading = true;
65+
66+
GroupMemberService.fetch(groupId).then(function(data) {
67+
$scope.memberTypes.forEach(function(memberType) {
68+
$scope.memberships[memberType] = data.content.filter(function(membership) { return membership.membershipType === memberType });
69+
$scope.memberships[memberType].forEach(function(membership) {
70+
loadUser(membership.createdBy);
71+
loadUser(membership.modifiedBy);
72+
73+
if (memberType === 'user') {
74+
// for user members load handles
75+
loadUser(membership.memberId);
76+
} else {
77+
// for group members load names
78+
loadGroup(membership.memberId);
79+
}
80+
});
81+
});
82+
}).catch(function (error) {
83+
$alert.error(error.error, $rootScope);
84+
}).finally(function() {
85+
$scope.isLoading = false;
86+
});
87+
};
88+
89+
/**
90+
* Checks if any membership records are selected in the table
91+
* and updates $scope.hasSelected value
92+
*/
93+
$scope.checkSelected = function() {
94+
$scope.hasSelected = !!getSelectedMemberships().length;
95+
}
96+
97+
/**
98+
* Toggle all selected membership records of specified type
99+
*
100+
* @param {String} memberType type of membership
101+
*/
102+
$scope.toggleAll = function(memberType) {
103+
$scope.memberships[memberType].forEach(function(membership) { membership.isSelected = $scope.isAllSelected[memberType] });
104+
}
105+
106+
/**
107+
* Removes member from the current group
108+
* After removing record from the server, it removes the record from the table
109+
*
110+
* @param {Object} membership membership record
111+
* @return {Promise} promise to remove member
112+
*/
113+
$scope.removeMember = function(membership) {
114+
membership.isRemoving = true;
115+
return GroupMemberService.removeMember($stateParams.groupId, membership.id).then(function() {
116+
$scope.memberships[membership.membershipType] = $scope.memberships[membership.membershipType].filter(function(record) {
117+
return record.id !== membership.id;
118+
});
119+
}).catch(function(error) {
120+
membership.isRemoving = false;
121+
$alert.error('Cannot remove member with id `' + membership.memberId + '`. ' + error.error, $rootScope);
122+
});
123+
}
124+
125+
/**
126+
* Remove all selected membership records
127+
*/
128+
$scope.removeSelected = function() {
129+
$alert.clear();
130+
$scope.processing = true;
131+
132+
var selectedMemberships = getSelectedMemberships();
133+
134+
// for now we remove all members in parallel
135+
// it's preferable, because it's faster
136+
// though if there will be any issues with server overload
137+
// it can be rewritten so requests go one by one
138+
$q.all(selectedMemberships.map(function(membership) {
139+
return $scope.removeMember(membership);
140+
})).catch(function(error) {
141+
$alert.error(error.error, $rootScope);
142+
}).finally(function() {
143+
$scope.processing = false;
144+
});
145+
}
146+
147+
// load the clients on controller init
148+
$scope.fetch($stateParams.groupId);
149+
150+
// load name of current group
151+
loadGroup($scope.groupId)
152+
}
153+
]);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<div class="wrapper wrapper-content animated fadeInRight" ng-show="authorized()">
2+
<!-- add membership button row -->
3+
<div class="row">
4+
<div class="col col-md-12 col-lg-12">
5+
<a class="btn btn-primary pull-right m-b" ui-sref="index.groups.list" style="margin-left: 20px">
6+
<strong>Back</strong>
7+
</a>
8+
<a class="btn btn-info pull-right m-b" ui-sref="index.groupmembers.new({groupId: groupId})">
9+
<strong><i class="fa fa-plus"></i> Add Members</strong>
10+
</a>
11+
</div>
12+
</div>
13+
<!-- list members row -->
14+
<div class="row">
15+
<div class="col-lg-12">
16+
<div class="ibox float-e-margins">
17+
<div class="ibox-title">
18+
<h2>
19+
<span ng-if="groups[groupId]">{{groups[groupId]}}</span>
20+
<span class="text-info" ng-if="groupId && !groups[groupId]">loading...</span>
21+
</h2>
22+
</div>
23+
<div class="ibox-content">
24+
<div class="text-center" ng-show="isLoading">
25+
<img src="assets/images/loading.gif" />
26+
</div>
27+
<div ng-show="!isLoading">
28+
<div ng-repeat="memberType in memberTypes">
29+
<h2>{{memberType === 'user' ? 'Users' : 'Groups'}}</h2>
30+
<table class="table table-stripped" ng-show="memberships[memberType].length">
31+
<thead>
32+
<tr>
33+
<th><input type="checkbox" ng-model="isAllSelected[memberType]" ng-change="toggleAll(memberType); checkSelected();" /></th>
34+
<th>{{memberType === 'user' ? 'User Id' : 'Group Id'}}</th>
35+
<th>{{memberType === 'user' ? 'Handle' : 'Name'}}</th>
36+
<th>Created by</th>
37+
<th>Created at</th>
38+
<th>Modified by</th>
39+
<th>Modified at</th>
40+
<th>&nbsp;</th>
41+
</tr>
42+
</thead>
43+
44+
<tbody>
45+
<tr class="animate-repeat" ng-repeat="membership in memberships[memberType]">
46+
<td><input type="checkbox" ng-model="membership.isSelected" ng-change="checkSelected();" /></td>
47+
<td>{{membership.memberId}}</td>
48+
<td>
49+
<span ng-show="memberType === 'user'">
50+
<span ng-if="users[membership.memberId]">{{users[membership.memberId]}}</span>
51+
<span class="text-info" ng-if="membership.memberId && !users[membership.memberId]">loading...</span>
52+
</span>
53+
<span ng-show="memberType === 'group'">
54+
<span ng-if="groups[membership.memberId]">{{groups[membership.memberId]}}</span>
55+
<span class="text-info" ng-if="membership.memberId && !groups[membership.memberId]">loading...</span>
56+
</span>
57+
</td>
58+
<td>
59+
<span ng-if="users[membership.createdBy]">{{users[membership.createdBy]}}</span>
60+
<span class="text-info" ng-if="membership.createdBy && !users[membership.createdBy]">loading...</span>
61+
</td>
62+
<td>{{membership.createdAt | date:'MMM dd, yyyy'}}</td>
63+
<td>
64+
<span ng-if="users[membership.modifiedBy]">{{users[membership.modifiedBy]}}</span>
65+
<span class="text-info" ng-if="membership.modifiedBy && !users[membership.modifiedBy]">loading...</span>
66+
</td>
67+
<td>{{membership.modifiedAt | date:'MMM dd, yyyy'}}</td>
68+
<td>
69+
<button data-ng-click='removeMember(membership)' class="btn btn-sm btn-danger" ng-disabled="membership.isRemoving">
70+
<strong>Remove</strong>
71+
</button>
72+
</td>
73+
</tr>
74+
</tbody>
75+
</table>
76+
<div ng-show="!memberships[memberType].length">No members</div><br/>
77+
</div>
78+
79+
<button data-ng-click='removeSelected()' class="btn btn-sm btn-danger" ng-disabled="!hasSelected || processing" ng-show="memberships['group'].length || memberships['user'].length"><strong>Remove Selected</strong></button>
80+
</div>
81+
</div>
82+
</div>
83+
</div>
84+
</div>
85+
</div>

0 commit comments

Comments
 (0)