From 69fe712b8d1b0c3d11e37eb0a4b959cf52e6bdbc Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Mon, 17 Jul 2017 10:34:48 +0800 Subject: [PATCH] challenge #30058339 - 48H ADMIN APP - SEARCH AND SORTING ON ROLES AND GROUPS PAGES --- .../groupmembers.list.controller.js | 370 +++++++++++++++++- src/app/groupmembers/groupmembers.list.html | 190 ++++++--- src/app/groups/groups.list.controller.js | 22 +- src/app/groups/groups.list.html | 16 +- .../rolemembers.list.controller.js | 104 ++++- src/app/rolemembers/rolemembers.list.html | 33 +- src/app/roles/roles.list.html | 4 +- src/app/users/users.service.js | 8 +- 8 files changed, 663 insertions(+), 84 deletions(-) diff --git a/src/app/groupmembers/groupmembers.list.controller.js b/src/app/groupmembers/groupmembers.list.controller.js index a67379a..e916d33 100644 --- a/src/app/groupmembers/groupmembers.list.controller.js +++ b/src/app/groupmembers/groupmembers.list.controller.js @@ -3,14 +3,17 @@ var module = angular.module('supportAdminApp'); module.controller('permissionmanagement.GroupMembersListController', [ - '$scope', '$rootScope', 'GroupMemberService', 'IdResolverService', '$stateParams', '$state', '$q', 'Alert', - function ($scope, $rootScope, GroupMemberService, IdResolverService, $stateParams, $state, $q, $alert) { + '$scope', '$rootScope', 'GroupMemberService', 'IdResolverService', 'UserService', 'GroupService', '$stateParams', '$state', '$q', 'Alert', '$timeout', 'moment', '$animate', + function ($scope, $rootScope, GroupMemberService, IdResolverService, UserService, GroupService, $stateParams, $state, $q, $alert, $timeout, moment, $animate) { // keep member types for easy iterating $scope.memberTypes = ['group', 'user']; // true if list is loading - $scope.isLoading = false; + $scope.isLoading = {}; + $scope.memberTypes.forEach(function(memberType) { + $scope.isLoading[memberType] = false; + }); // true if something is we are removing a bulk of entries $scope.processing = false; @@ -41,6 +44,27 @@ module.controller('permissionmanagement.GroupMembersListController', [ $scope.groups = {}; var loadGroup = IdResolverService.getGroupResolverFunction($scope.groups); + // all memberships to implement client side filters + var allMemberships = {}; + $scope.memberTypes.forEach(function(memberType) { + allMemberships[memberType] = []; + }); + + // filter criteria + $scope.filterCriteria = {}; + $scope.memberTypes.forEach(function(memberType) { + $scope.filterCriteria[memberType] = { + memberId: '', + memberName: '', + createdBy: '', + modifiedBy: '', + createdAtFrom: '', + createdAtTo: '', + modifiedAtFrom: '', + modifiedAtTo: '' + }; + }); + /** * Return membership records which are selected in the table by checkboxes * @@ -61,11 +85,14 @@ module.controller('permissionmanagement.GroupMembersListController', [ */ $scope.fetch = function(groupId) { $alert.clear(); - $scope.isLoading = true; + $scope.memberTypes.forEach(function(memberType) { + $scope.isLoading[memberType] = true; + }); GroupMemberService.fetch(groupId).then(function(data) { $scope.memberTypes.forEach(function(memberType) { $scope.memberships[memberType] = data.content.filter(function(membership) { return membership.membershipType === memberType }); + allMemberships[memberType] = _.clone($scope.memberships[memberType]); $scope.memberships[memberType].forEach(function(membership) { loadUser(membership.createdBy); loadUser(membership.modifiedBy); @@ -78,11 +105,24 @@ module.controller('permissionmanagement.GroupMembersListController', [ loadGroup(membership.memberId); } }); + + // if have some members we will init footable plugin + if ($scope.memberships[memberType].length) { + // make sure changes to scope are applied + // and redraw footable table with current member list + $timeout(function() { + $('.footable.table-type-' + memberType).footable(); + $scope.isLoading[memberType] = false; + }); + } else { + $scope.isLoading[memberType] = false; + } }); }).catch(function (error) { $alert.error(error.error, $rootScope); - }).finally(function() { - $scope.isLoading = false; + $scope.memberTypes.forEach(function(memberType) { + $scope.isLoading[memberType] = false; + }); }); }; @@ -116,6 +156,9 @@ module.controller('permissionmanagement.GroupMembersListController', [ $scope.memberships[membership.membershipType] = $scope.memberships[membership.membershipType].filter(function(record) { return record.id !== membership.id; }); + allMemberships[membership.membershipType] = allMemberships[membership.membershipType].filter(function(record) { + return record.id !== membership.id; + }); }).catch(function(error) { membership.isRemoving = false; $alert.error('Cannot remove member with id `' + membership.memberId + '`. ' + error.error, $rootScope); @@ -144,10 +187,323 @@ module.controller('permissionmanagement.GroupMembersListController', [ }); } + /** + * Open datetimepicker + * + * @param {Object} e event object + * @param {String} inputName name of the input + * @param {String} memberType member type + */ + $scope.openCalendar = function (e, inputName, memberType) { + if (!$scope[inputName + 'Open']) { + $scope[inputName + 'Open'] = {}; + } + + if ($scope[inputName + 'Open'][memberType]) { + return; + } + + $scope[inputName + 'Open'][memberType] = true; + + if (e && e.preventDefault) { + e.preventDefault(); + } + + if (e && e.stopPropagation) { + e.stopPropagation(); + } + }; + + /** + * Helper function which redraws table with new member list + * and properly takes care about updating footable plugin + * + * The essential problem this function is solving is that after we + * update $scope.members, rows from the table are not being deleted immediately + * because we are using animations for rows. + * That's why if we try to force update footable plugin it still find old rows which are in + * process of animation and still in DOM tree. + * As a workaround in this function we disable animations for rows before updating them, + * thus footable plugin can see changes immediately after scope is updated. + * + * @param {String} memberType type of membership we are redrawing + * @param {Array} memberships new array of memberships which has to displayed + * + * @return {Promise} resolved after table was completely updated + */ + function redrawTableWithMemberships(memberType, memberships) { + var $footable = $('.footable.table-type-' + memberType); + + // disable animations for rows, so old rows will be removed immediately + // from the DOM tree and footable can see changes + $footable.find('tr').each(function(index, el) { + $animate.enabled(el, false); + }); + + // update memberships list in scope + $scope.memberships[memberType] = memberships; + + // make sure that changes are applied to the scope + return $timeout(function() { + // force footable plugin to redraw + $footable.trigger('footable_redraw'); + // enable animation for table rows again + $footable.find('tr').each(function(index, el) { + $animate.enabled(el, true); + }); + }); + } + + // promise to get list of groups + // this list is used to resolve groups ids by its group names + var groupIdsByNamePromise = null; + + /** + * Find group id by its name + * + * This is helper function which return value in special shape + * along with group ids array it return label defined by 'valueType' + * + * This function just retrieves all the groups and after resolves names by + * searching in group list client side. + * + * @param {String} groupName group name + * @param {String} valueType label of the returned value + * @return {Promise} resolved to object {value: , type: valueType} + */ + function getGroupIdsFilteredByName(groupName, valueType) { + if (!groupIdsByNamePromise) { + groupIdsByNamePromise = GroupService.fetch().then(function(data) { + return data.content; + }); + } + + groupName = groupName.toLowerCase(); + + return groupIdsByNamePromise.then(function(groups) { + var filteredGroups = _.filter(groups, function(group) { + return _.includes(group.name.toLowerCase(), groupName); + }); + + return { + type: valueType, + value: _.map(filteredGroups, 'id') + } + }); + } + + // object keys of which are user handles + // and values are promises to resolve according user handles by ids + var userIdsByHandlePromises = {}; + + /** + * Find user id by its handle + * + * This is helper function which return value in special shape + * along with user ids array it return label defined by 'valueType' + * + * This function makes request for each user handle and saves resolved promise, + * so second time server request is not being sent to the server for the same user handle. + * + * @param {String} userHandle user handle + * @param {String} valueType label of the returned value + * @return {Promise} resolved to object {value: , type: valueType} + */ + function getUserIdsFilteredByHandle(userHandle, valueType) { + if (!userIdsByHandlePromises[userHandle]) { + userIdsByHandlePromises[userHandle] = UserService.find({ + fields: 'id', + filter: 'handle=*' + userHandle + '*&like=true', + limit: 1000000 // set big limit to make sure server returns all records + }).then(function(users) { + return _.map(users, 'id'); + }); + } + + return userIdsByHandlePromises[userHandle].then(function(userIds) { + return { + type: valueType, + value: userIds + } + }); + } + + /** + * Helper function which performs all the requests to the server which are required to filter membership tables + * + * @param {String} memberType member type + * @param {Array} filteredMembersips list of membership to filter + * @return {Promise} resolves to filtered membership list + */ + function filterWithRequests(memberType, filteredMembersips) { + // list of all the server requests which we have to make to filter members + var requests = []; + + // as on client side we don't know user handles for member, createdBy, modifiedBy users + // and we don't group names + // to filter by them we have to get their according user ids and group ids + // so we create requests to the server + + if (memberType === 'group' && $scope.filterCriteria[memberType].memberName) { + requests.push(getGroupIdsFilteredByName($scope.filterCriteria[memberType].memberName, 'memberName')); + } + + if (memberType === 'user' && $scope.filterCriteria[memberType].memberName) { + requests.push(getUserIdsFilteredByHandle($scope.filterCriteria[memberType].memberName, 'memberName')); + } + + if ($scope.filterCriteria[memberType].createdBy) { + requests.push(getUserIdsFilteredByHandle($scope.filterCriteria[memberType].createdBy, 'createdBy')); + } + + if ($scope.filterCriteria[memberType].modifiedBy) { + requests.push(getUserIdsFilteredByHandle($scope.filterCriteria[memberType].modifiedBy, 'modifiedBy')); + } + + // after we get all ids from the server we can filter data client side + return $q.all(requests).then(function(ids) { + var idsObj = {}; + + ids.forEach(function(result) { + idsObj[result.type] = result.value; + }); + + // memberName + if ($scope.filterCriteria[memberType].memberName) { + if (!idsObj['memberName'].length) { + filteredMembersips = []; + } else { + filteredMembersips = filteredMembersips = _.filter(filteredMembersips, function(membership) { + return _.includes(idsObj['memberName'], membership.memberId.toString()); + }); + } + } + + // createdBy + if ($scope.filterCriteria[memberType].createdBy) { + if (!idsObj['createdBy']) { + filteredMembersips = []; + } else { + filteredMembersips = filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.createdBy && _.includes(idsObj['createdBy'], membership.createdBy.toString()); + }); + } + } + + // modifiedBy + if ($scope.filterCriteria[memberType].modifiedBy) { + if (!idsObj['modifiedBy']) { + filteredMembersips = []; + } else { + filteredMembersips = filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.modifiedBy && _.includes(idsObj['modifiedBy'], membership.modifiedBy.toString()); + }); + } + } + + return filteredMembersips; + }); + } + + /** + * Returns date in EDT timezone + * Ignores time and set it to 00:00:00.0000 + * + * As we display dates in EDT timezone, but datepicker selects dates in a local user timezone + * we have to treat returned date by datepicker as date in EDT + * + * @param {Mixed} date date from datepicker + * @return {Moment} date in EDT timezone + */ + function getDateInEDTTimezone(date) { + var momentDate = moment(date); + var momentDateEDT = moment.tz([momentDate.year(), momentDate.month(), momentDate.date()], 'America/New_York'); + + return momentDateEDT; + } + + /** + * Applies filter to the member list + */ + $scope.applyFilter = function(memberType) { + // wait until we filter everything not matter synchronously or making requests to server + var filteredMembersips = _.clone(allMemberships[memberType]); + + // memberId + if ($scope.filterCriteria[memberType].memberId) { + filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.memberId.toString() === $scope.filterCriteria[memberType].memberId; + }); + } + + // createdAtFrom + if ($scope.filterCriteria[memberType].createdAtFrom) { + var createdAtFrom = getDateInEDTTimezone($scope.filterCriteria[memberType].createdAtFrom); + + filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.createdAt && createdAtFrom.isSameOrBefore(membership.createdAt, 'day'); + }); + } + + // createdAtTo + if ($scope.filterCriteria[memberType].createdAtTo) { + var createdAtTo = getDateInEDTTimezone($scope.filterCriteria[memberType].createdAtTo); + + filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.createdAt && createdAtTo.isSameOrAfter(membership.createdAt, 'day'); + }); + } + + // modifiedAtFrom + if ($scope.filterCriteria[memberType].modifiedAtFrom) { + var modifiedAtFrom = getDateInEDTTimezone($scope.filterCriteria[memberType].modifiedAtFrom); + + filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.modifiedAt && modifiedAtFrom.isSameOrBefore(membership.modifiedAt, 'day'); + }); + } + + // modifiedAtTo + if ($scope.filterCriteria[memberType].modifiedAtTo) { + var modifiedAtTo = getDateInEDTTimezone($scope.filterCriteria[memberType].modifiedAtTo).add(1, 'days'); + + filteredMembersips = _.filter(filteredMembersips, function(membership) { + return membership.modifiedAt && modifiedAtTo.isSameOrAfter(membership.modifiedAt, 'day'); + }); + } + + // if we still have some membership to filter + // and have any filter that require making requests to server + if (filteredMembersips.length > 0 && ( + $scope.filterCriteria[memberType].memberName || + $scope.filterCriteria[memberType].createdBy || + $scope.filterCriteria[memberType].modifiedBy + )) { + + $scope.isLoading[memberType] = true; + + return filterWithRequests(memberType, filteredMembersips).then(function(filteredMembersips) { + // after we filtered data we redraw table + redrawTableWithMemberships(memberType, filteredMembersips).then(function() { + $scope.isLoading[memberType] = false; + }); + }).catch(function(error) { + // is any error occurs show it and leave table untouched + $scope.isLoading[memberType] = false; + $alert.error(error.error, $rootScope); + }); + + // if we don't filter by handle which makes server request + // redraw table immediately + } else { + redrawTableWithMemberships(memberType, filteredMembersips); + } + } + // load the clients on controller init $scope.fetch($stateParams.groupId); // load name of current group - loadGroup($scope.groupId) + loadGroup($scope.groupId); } ]); diff --git a/src/app/groupmembers/groupmembers.list.html b/src/app/groupmembers/groupmembers.list.html index 33ae5bd..1faa98a 100644 --- a/src/app/groupmembers/groupmembers.list.html +++ b/src/app/groupmembers/groupmembers.list.html @@ -15,69 +15,143 @@
-

- {{groups[groupId]}} - loading... -

-
-
-
- +
+
+

+ {{groups[groupId]}} + loading... +

+
-
-
-

{{memberType === 'user' ? 'Users' : 'Groups'}}

- - - - - - - - - - - - - - - - - - - - - - - - - -
{{memberType === 'user' ? 'User Id' : 'Group Id'}}{{memberType === 'user' ? 'Handle' : 'Name'}}Created byCreated atModified byModified at 
{{membership.memberId}} - - {{users[membership.memberId]}} - loading... - - - {{groups[membership.memberId]}} - loading... - - - {{users[membership.createdBy]}} - loading... - {{membership.createdAt | date:'MMM dd, yyyy'}} - {{users[membership.modifiedBy]}} - loading... - {{membership.modifiedAt | date:'MMM dd, yyyy'}} - -
-
No members

+
+
+
+

{{memberType === 'user' ? 'Users' : 'Groups'}}

+
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + - + + + + + + + + + + + + +
{{memberType === 'user' ? 'User Id' : 'Group Id'}}{{memberType === 'user' ? 'Handle' : 'Name'}}Created byCreated atModified byModified at 
{{membership.memberId}} + + {{users[membership.memberId]}} + loading... + + + {{groups[membership.memberId]}} + loading... + + + {{users[membership.createdBy]}} + loading... + {{membership.createdAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{membership.createdAt ? 'EDT' : ''}} + {{users[membership.modifiedBy]}} + loading... + {{membership.modifiedAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{membership.modifiedAt ? 'EDT' : ''}} + +
+
No members

+ +
diff --git a/src/app/groups/groups.list.controller.js b/src/app/groups/groups.list.controller.js index c196a95..2501efa 100644 --- a/src/app/groups/groups.list.controller.js +++ b/src/app/groups/groups.list.controller.js @@ -3,8 +3,8 @@ var module = angular.module('supportAdminApp'); module.controller('permissionmanagement.GroupsListController', [ - '$scope', '$rootScope', 'GroupService', 'UserService', 'IdResolverService', 'Alert', - function ($scope, $rootScope, GroupService, UserService, IdResolverService, $alert) { + '$scope', '$rootScope', 'GroupService', 'UserService', 'IdResolverService', 'Alert', '$timeout', + function ($scope, $rootScope, GroupService, UserService, IdResolverService, $alert, $timeout) { // true data is loading $scope.isLoading = false; @@ -27,14 +27,28 @@ module.controller('permissionmanagement.GroupsListController', [ $scope.groups.forEach(function(group) { loadUser(group.createdBy); loadUser(group.modifiedBy); - }) + }); + if ($scope.groups.length) { + // make sure changes to scope are applied + // and redraw footable table with current group list + $timeout(function() { + $('.footable').trigger('footable_redraw'); + $scope.isLoading = false; + }); + } else { + $scope.isLoading = false; + } }).catch(function (error) { $alert.error(error.error, $rootScope); - }).finally(function() { $scope.isLoading = false; }); }; + // init footable plugin + angular.element(document).ready(function() { + $('.footable').footable(); + }); + // load the groups on controller init $scope.fetch(); } diff --git a/src/app/groups/groups.list.html b/src/app/groups/groups.list.html index 77837c1..544320e 100644 --- a/src/app/groups/groups.list.html +++ b/src/app/groups/groups.list.html @@ -7,10 +7,10 @@
- +
- + @@ -33,15 +33,23 @@ {{users[group.createdBy]}}loading... - + - + + + + + + +
Group IDGroup ID Name Description Created by {{group.createdAt | date:'MMM dd, yyyy'}}{{group.createdAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{group.createdAt ? 'EDT' : ''}} {{users[group.modifiedBy]}} loading... {{group.modifiedAt | date:'MMM dd, yyyy'}}{{group.modifiedAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{group.modifiedAt ? 'EDT' : ''}}
+
    +
    No records found
    diff --git a/src/app/rolemembers/rolemembers.list.controller.js b/src/app/rolemembers/rolemembers.list.controller.js index 4732260..517d649 100644 --- a/src/app/rolemembers/rolemembers.list.controller.js +++ b/src/app/rolemembers/rolemembers.list.controller.js @@ -3,8 +3,8 @@ var module = angular.module('supportAdminApp'); module.controller('permissionmanagement.RoleMembersListController', [ - '$scope', '$rootScope', 'RoleService', 'IdResolverService', '$stateParams', '$state', '$q', 'Alert', '$timeout', - function ($scope, $rootScope, RoleService, IdResolverService, $stateParams, $state, $q, $alert, $timeout) { + '$scope', '$rootScope', 'RoleService', 'IdResolverService', 'UserService', '$animate', '$stateParams', '$state', '$q', 'Alert', '$timeout', + function ($scope, $rootScope, RoleService, IdResolverService, UserService, $animate, $stateParams, $state, $q, $alert, $timeout) { // true if role is loading $scope.isLoading = false; @@ -31,6 +31,15 @@ module.controller('permissionmanagement.RoleMembersListController', [ $scope.users = {}; var loadUser = IdResolverService.getUserResolverFunction($scope.users); + // keep list of all members for client side filtering + var allMembers = []; + + // criteria to filter members + $scope.filterCriteria = { + userId: '', + userHandle: '' + }; + /** * Return members which are selected in the table by checkboxes * @@ -58,6 +67,8 @@ module.controller('permissionmanagement.RoleMembersListController', [ id: memberId } }); + // save all members list for filtering + allMembers = _.clone($scope.members); // if have some members we will redraw table using footable plugin if ($scope.members.length) { // make sure changes to scope are applied @@ -102,6 +113,7 @@ module.controller('permissionmanagement.RoleMembersListController', [ member.isRemoving = true; return RoleService.unassignRole($stateParams.roleId, member.id).then(function() { _.remove($scope.members, { id: member.id }); + _.remove(allMembers, { id: member.id }); // we remove row of deleted member from footable table // which will also triggers footable table redraw // we don't worry to call it after $scope.members is updated so we don't use $timeout here @@ -140,6 +152,93 @@ module.controller('permissionmanagement.RoleMembersListController', [ }); } + /** + * Helper function which redraws table with new member list + * and properly takes care about updating footable plugin + * + * The essential problem this function is solving is that after we + * update $scope.members, rows from the table are not being deleted immediately + * because we are using animations for rows. + * That's why if we try to force update footable plugin it still find old rows which are in + * process of animation and still in DOM tree. + * As a workaround in this function we disable animations for rows before updating them, + * thus footable plugin can see changes immediately after scope is updated. + * + * @param {Array} members new array of members which has to displayed + * @param {Function} callback optional callback after table is fully redrawn + * + * @return {Promise} resolved after table was completely updated + */ + function redrawTableWithMembers(members) { + var $footable = $('.footable'); + + // disable animations for rows, so old rows will be removed immediately + // from the DOM tree and footable can see changes + $footable.find('tr').each(function(index, el) { + $animate.enabled(el, false); + }); + + // update members list in scope + $scope.members = members; + + // make sure that changes are applied to the scope + return $timeout(function() { + // force footable plugin to redraw + $footable.trigger('footable_redraw'); + // enable animation for table rows again + $footable.find('tr').each(function(index, el) { + $animate.enabled(el, true); + }); + }); + } + + /** + * Applies filter to the member list + */ + $scope.applyFilter = function() { + var filteredMembers = _.clone(allMembers); + + // filter by ids first, it works immediately as we know all the data + // so we don't need to show loader for this + if ($scope.filterCriteria.userId) { + filteredMembers = _.filter(filteredMembers, { id: $scope.filterCriteria.userId }); + } + + // if handle filter is defined and we still have some rows to filter + if ($scope.filterCriteria.userHandle && filteredMembers.length > 0) { + // we show loader as we need to make request to the server + $scope.isLoading = true; + + // As there is no server API to filter role members and we don't have + // user handles to filter, we first have to find user ids by it's handle + // and after we can filter users by id + UserService.find({ + fields: 'id', + filter: 'handle=*' + $scope.filterCriteria.userHandle + '*&like=true', + limit: 1000000 // set big limit to make sure server returns all records + }).then(function(users) { + var foundIds = _.map(users, 'id'); + + filteredMembers = _.filter(filteredMembers, function(member) { + return _.includes(foundIds, member.id); + }); + + redrawTableWithMembers(filteredMembers).then(function() { + $scope.isLoading = false; + }); + }).catch(function(error) { + // is any error occurs show it and leave table untouched + $scope.isLoading = false; + $alert.error(error.error, $rootScope); + }); + + // if we don't filter by handle which makes server request + // redraw table immediately + } else { + redrawTableWithMembers(filteredMembers); + } + } + /** * Uncheck all checkboxes */ @@ -174,6 +273,7 @@ module.controller('permissionmanagement.RoleMembersListController', [ } } + // init footable plugin angular.element(document).ready(function() { $('.footable').on({ // we watch footable jquery plugin footable_page_filled event diff --git a/src/app/rolemembers/rolemembers.list.html b/src/app/rolemembers/rolemembers.list.html index 5b61970..0a67a13 100644 --- a/src/app/rolemembers/rolemembers.list.html +++ b/src/app/rolemembers/rolemembers.list.html @@ -15,10 +15,35 @@
    -

    - {{role.roleName}} - loading... -

    +
    +
    +

    + {{role.roleName}} + loading... +

    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    diff --git a/src/app/roles/roles.list.html b/src/app/roles/roles.list.html index bbfb5bf..85f6c5c 100644 --- a/src/app/roles/roles.list.html +++ b/src/app/roles/roles.list.html @@ -94,7 +94,7 @@ ng-if="role.createdBy && !ctrl.users[role.createdBy]" >loading... - {{role.createdAt | date : 'MM dd yyyy HH:mm' : 'EDT'}} {{role.createdAt ? 'EDT' : ''}} + {{role.createdAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{role.createdAt ? 'EDT' : ''}} {{ctrl.users[role.modifiedBy]}} @@ -104,7 +104,7 @@ ng-if="role.modifiedBy && !ctrl.users[role.modifiedBy]" >loading... - {{role.modifiedAt | date : 'MM dd yyyy HH:mm' : 'EDT'}} {{role.modifiedAt ? 'EDT' : ''}} + {{role.modifiedAt | date : 'yyyy-MM-dd HH:mm' : 'EDT'}} {{role.modifiedAt ? 'EDT' : ''}} diff --git a/src/app/users/users.service.js b/src/app/users/users.service.js index 9c4499a..50cc8f1 100644 --- a/src/app/users/users.service.js +++ b/src/app/users/users.service.js @@ -54,12 +54,14 @@ angular.module('supportAdminApp') var query = ""; angular.forEach({ "fields": opts.fields || "id,handle,email,active,emailActive,status,credential,firstName,lastName,createdAt,modifiedAt", - "filter": opts.filter - //"limit" : null, + "filter": opts.filter, + "limit" : opts.limit, //"offset": null, //"orderBy": null, }, function(value, key) { - query += ('&' + key + '=' + encodeURIComponent(value)); + if (value) { + query += ('&' + key + '=' + encodeURIComponent(value)); + } }); var request = $http({