Redesign the teams page to use a table
Allows for faster loading and easier viewing of important information about teams
This commit is contained in:
parent
98206310bd
commit
6ebb417923
11 changed files with 194 additions and 168 deletions
|
@ -1,7 +1,9 @@
|
||||||
from data.database import Team, TeamMember, TeamRole, User, TeamMemberInvite
|
from data.database import Team, TeamMember, TeamRole, User, TeamMemberInvite, RepositoryPermission
|
||||||
from data.model import (DataModelException, InvalidTeamException, UserAlreadyInTeam,
|
from data.model import (DataModelException, InvalidTeamException, UserAlreadyInTeam,
|
||||||
InvalidTeamMemberException, user, _basequery)
|
InvalidTeamMemberException, user, _basequery)
|
||||||
from util.validation import validate_username
|
from util.validation import validate_username
|
||||||
|
from peewee import fn, JOIN_LEFT_OUTER
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
|
||||||
def create_team(name, org_obj, team_role_name, description=''):
|
def create_team(name, org_obj, team_role_name, description=''):
|
||||||
|
@ -186,7 +188,32 @@ def get_matching_teams(team_prefix, organization):
|
||||||
|
|
||||||
|
|
||||||
def get_teams_within_org(organization):
|
def get_teams_within_org(organization):
|
||||||
return Team.select().where(Team.organization == organization)
|
""" Returns a AttrDict of team info (id, name, description), its role under the org,
|
||||||
|
the number of repositories on which it has permission, and the number of members.
|
||||||
|
"""
|
||||||
|
query = (Team.select(Team.id, Team.name, Team.description, TeamRole.name,
|
||||||
|
fn.Count(RepositoryPermission.id), fn.Count(TeamMember.id))
|
||||||
|
.where(Team.organization == organization)
|
||||||
|
.join(TeamRole)
|
||||||
|
.switch(Team)
|
||||||
|
.join(RepositoryPermission, JOIN_LEFT_OUTER)
|
||||||
|
.switch(Team)
|
||||||
|
.join(TeamMember, JOIN_LEFT_OUTER)
|
||||||
|
.group_by(Team.id)
|
||||||
|
.tuples())
|
||||||
|
|
||||||
|
def _team_view(team_tuple):
|
||||||
|
return AttrDict({
|
||||||
|
'id': team_tuple[0],
|
||||||
|
'name': team_tuple[1],
|
||||||
|
'description': team_tuple[2],
|
||||||
|
'role_name': team_tuple[3],
|
||||||
|
|
||||||
|
'repo_count': team_tuple[4],
|
||||||
|
'member_count': team_tuple[5],
|
||||||
|
})
|
||||||
|
|
||||||
|
return [_team_view(team_tuple) for team_tuple in query]
|
||||||
|
|
||||||
|
|
||||||
def get_user_teams_within_org(username, organization):
|
def get_user_teams_within_org(username, organization):
|
||||||
|
|
|
@ -8,13 +8,12 @@ import features
|
||||||
|
|
||||||
from app import billing as stripe, avatar
|
from app import billing as stripe, avatar
|
||||||
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
||||||
related_user_resource, internal_only, require_user_admin, log_action,
|
related_user_resource, internal_only, require_user_admin, log_action,
|
||||||
show_if, path_param, require_scope)
|
show_if, path_param, require_scope)
|
||||||
from endpoints.exception import Unauthorized, NotFound
|
from endpoints.exception import Unauthorized, NotFound
|
||||||
from endpoints.api.team import team_view
|
|
||||||
from endpoints.api.user import User, PrivateRepositories
|
from endpoints.api.user import User, PrivateRepositories
|
||||||
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
|
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
|
||||||
CreateRepositoryPermission)
|
CreateRepositoryPermission, ViewTeamPermission)
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from auth import scopes
|
from auth import scopes
|
||||||
from data import model
|
from data import model
|
||||||
|
@ -24,6 +23,18 @@ from data.billing import get_plan
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def team_view(orgname, team):
|
||||||
|
return {
|
||||||
|
'name': team.name,
|
||||||
|
'description': team.description,
|
||||||
|
'role': team.role_name,
|
||||||
|
'avatar': avatar.get_data_for_team(team),
|
||||||
|
'can_view': ViewTeamPermission(orgname, team.name).can(),
|
||||||
|
|
||||||
|
'repo_count': team.repo_count,
|
||||||
|
'member_count': team.member_count,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def org_view(o, teams):
|
def org_view(o, teams):
|
||||||
is_admin = AdministerOrganizationPermission(o.username).can()
|
is_admin = AdministerOrganizationPermission(o.username).can()
|
||||||
|
|
|
@ -15,39 +15,3 @@
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-entity-dialog-element label {
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element .co-table {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element .fa-hdd-o {
|
|
||||||
margin-right: 4px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element .co-filter-box {
|
|
||||||
display: block;
|
|
||||||
float: right;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element .co-filter-box .filter-message {
|
|
||||||
left: -180px;
|
|
||||||
top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element .co-filter-box input {
|
|
||||||
width: 100%;
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-entity-dialog-element label .avatar {
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
|
@ -1,5 +1,19 @@
|
||||||
.teams-manager .popup-input-button {
|
.teams-manager .co-filter-box {
|
||||||
|
display: block;
|
||||||
float: right;
|
float: right;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teams-manager.co-filter-box .filter-message {
|
||||||
|
left: -180px;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teams-manager .co-filter-box input {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.teams-manager .manager-header {
|
.teams-manager .manager-header {
|
||||||
|
@ -20,6 +34,10 @@
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.teams-manager .co-table .avatar {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.teams-manager .cor-confirm-dialog .entity-reference .avatar {
|
.teams-manager .cor-confirm-dialog .entity-reference .avatar {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
|
@ -36,14 +54,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.teams-manager .control-col {
|
.teams-manager .co-filter-box {
|
||||||
padding-left: 55px;
|
display: block;
|
||||||
padding-bottom: 10px;
|
float: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.teams-manager .header-col .info-icon {
|
.teams-manager .info-icon {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teams-manager .popover-content {
|
||||||
|
color: black;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.teams-manager .header-col .header-text {
|
.teams-manager .header-col .header-text {
|
||||||
|
|
|
@ -634,13 +634,6 @@ i.toggle-icon:hover {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-icon {
|
|
||||||
display: inline-block;
|
|
||||||
float: right;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accordion-toggle {
|
.accordion-toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="add-repo-permissions-entity">
|
<div class="add-repo-permissions-element">
|
||||||
<span class="co-filter-box">
|
<span class="co-filter-box">
|
||||||
<span class="filter-message" ng-if="options.filter">
|
<span class="filter-message" ng-if="options.filter">
|
||||||
Showing {{ orderedRepositories.entries.length }} of {{ repositories.length }} repositories
|
Showing {{ orderedRepositories.entries.length }} of {{ repositories.length }} repositories
|
||||||
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
Select repositories in
|
Select repositories in
|
||||||
<span class="avatar" size="16" data="namespace.avatar"></span>
|
<span class="avatar" size="16" data="namespaceInfo.avatar"></span>
|
||||||
{{ namespace }}:
|
{{ namespace }}:
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<table class="co-table" style="margin-bottom: 210px;">
|
<table class="co-table">
|
||||||
<thead>
|
<thead>
|
||||||
<td class="checkbox-col checkbox-menu-col">
|
<td class="checkbox-col checkbox-menu-col">
|
||||||
<span class="cor-checkable-menu" controller="checkedRepos">
|
<span class="cor-checkable-menu" controller="checkedRepos">
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
has-checked-repositories="context.hasCheckedRepositories"
|
has-checked-repositories="context.hasCheckedRepositories"
|
||||||
repositories-loaded="repositoriesLoaded(repositories)"
|
repositories-loaded="repositoriesLoaded(repositories)"
|
||||||
adding-permissions="addingPermissions()"
|
adding-permissions="addingPermissions()"
|
||||||
permissions-added="permissionsAdded()"
|
permissions-added="permissionsAdded(repositories)"
|
||||||
add-permissions="context.addPermissionsCounter"
|
add-permissions="context.addPermissionsCounter"
|
||||||
ng-if="entity"></div>
|
ng-if="entity"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
<div class="teams-manager-element">
|
<div class="teams-manager-element">
|
||||||
<div class="feedback-bar" feedback="feedback"></div>
|
<div class="feedback-bar" feedback="feedback"></div>
|
||||||
<div class="manager-header" header-title="Teams and Membership">
|
<div class="manager-header" header-title="Teams and Membership">
|
||||||
|
<div class="tab-header-controls visible-xs">
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
ng-show="organization.is_admin"
|
||||||
|
ng-click="askCreateTeam()">
|
||||||
|
<i class="fa fa-plus" style="margin-right: 4px;"></i> Create New Team
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-header-controls hidden-xs">
|
<div class="tab-header-controls hidden-xs">
|
||||||
<div class="btn-group btn-group-sm" ng-show="organization.is_admin">
|
<div class="btn-group btn-group-sm" ng-show="organization.is_admin">
|
||||||
<button class="btn"
|
<button class="btn"
|
||||||
|
@ -17,66 +25,69 @@
|
||||||
|
|
||||||
<!-- Teams List -->
|
<!-- Teams List -->
|
||||||
<div ng-show="!showingMembers">
|
<div ng-show="!showingMembers">
|
||||||
<div class="row" style="margin-left: 0px; margin-right: 0px;">
|
<button class="btn btn-primary hidden-xs"
|
||||||
<button class="btn btn-primary hidden-xs"
|
ng-show="organization.is_admin"
|
||||||
ng-show="organization.is_admin"
|
style="margin-bottom: 10px; "
|
||||||
style="margin-bottom: 10px; float: right;"
|
ng-click="askCreateTeam()">
|
||||||
ng-click="askCreateTeam()">
|
<i class="fa fa-plus" style="margin-right: 4px;"></i> Create New Team
|
||||||
<i class="fa fa-plus" style="margin-right: 4px;"></i> Create New Team
|
</button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row hidden-xs">
|
<span class="co-filter-box">
|
||||||
<div class="col-sm-7 col-md-8 header-col">
|
<span class="filter-message" ng-if="options.filter">
|
||||||
<span class="header-text">Team Summary</span>
|
Showing {{ orderedTeams.entries.length }} of {{ teams.length }} teams
|
||||||
</div>
|
</span>
|
||||||
<div class="col-md-4 col-sm-5 header-col" ng-show="organization.is_admin">
|
<input class="form-control" type="text" ng-model="options.filter" placeholder="Filter Teams...">
|
||||||
<span class="header-text">Team Permissions</span>
|
</span>
|
||||||
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
|
|
||||||
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"
|
|
||||||
data-html="true"
|
|
||||||
data-trigger="hover"
|
|
||||||
bs-popover></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="team-listing" ng-repeat="team in orderedTeams">
|
<table class="co-table" style="margin-top: 10px;">
|
||||||
<div id="team-{{team.name}}" class="row">
|
<thead>
|
||||||
<div class="col-sm-7 col-md-8">
|
<td ng-class="TableService.tablePredicateClass('name', options.predicate, options.reverse)">
|
||||||
<div class="team-title">
|
<a ng-click="TableService.orderBy('name', options)">Team Name</a>
|
||||||
<span class="avatar" data="team.avatar" size="30"></span>
|
</td>
|
||||||
<span ng-show="team.can_view">
|
<td ng-class="TableService.tablePredicateClass('member_count', options.predicate, options.reverse)">
|
||||||
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
|
<a ng-click="TableService.orderBy('member_count', options)">Members</a>
|
||||||
</span>
|
</td>
|
||||||
<span ng-show="!team.can_view">
|
<td class="hidden-xs" ng-class="TableService.tablePredicateClass('repo_count', options.predicate, options.reverse)">
|
||||||
{{ team.name }}
|
<a ng-click="TableService.orderBy('repo_count', options)">Repositories</a>
|
||||||
</span>
|
</td>
|
||||||
</div>
|
<td ng-class="TableService.tablePredicateClass('role_index', options.predicate, options.reverse)">
|
||||||
|
<a ng-click="TableService.orderBy('role_index', options)">Team Role</a>
|
||||||
|
|
||||||
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
|
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
|
||||||
|
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"
|
||||||
|
data-html="true"
|
||||||
|
data-trigger="hover"
|
||||||
|
bs-popover></i>
|
||||||
|
</td>
|
||||||
|
<td class="options-col"></td>
|
||||||
|
</thead>
|
||||||
|
|
||||||
<div class="team-member-list hidden-xs" ng-if="members[team.name]">
|
<tr class="co-checkable-row"
|
||||||
<div class="cor-loader" ng-if="!members[team.name].members"></div>
|
ng-repeat="team in orderedTeams.visibleEntries"
|
||||||
<span class="team-member"
|
bindonce>
|
||||||
ng-repeat="member in members[team.name].members | orderBy:'is_robot' | limitTo: 20">
|
<td style="white-space: nowrap;">
|
||||||
<span data-title="{{ member.name }}" bs-tooltip>
|
<span class="avatar" data="team.avatar" size="24"></span>
|
||||||
<a href="/user/{{ member.name }}" ng-if="!member.is_robot">
|
<span bo-show="team.can_view">
|
||||||
<span class="avatar" data="member.avatar" size="26"></span>
|
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}"><span bo-text="team.name"></span></a>
|
||||||
</a>
|
</span>
|
||||||
<i class="fa ci-robot fa-lg" ng-if="member.is_robot"></i>
|
<span bo-show="!team.can_view" bo-text="team.name"></span>
|
||||||
</span>
|
</td>
|
||||||
</span>
|
<td>
|
||||||
<span class="team-member-more"
|
<span bo-show="team.can_view">
|
||||||
ng-if="members[team.name].members.length > 20">+ {{ members[team.name].members.length - 20 }} more team members.</span>
|
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}"><span bo-text="team.member_count"></span> <span class="hidden-xs">member<span bo-if="team.member_count != 1">s</span></span></a>
|
||||||
<span class="team-member-more"
|
</span>
|
||||||
ng-if="members[team.name].members && !members[team.name].members.length">(Empty Team)</span>
|
<span bo-show="!team.can_view">
|
||||||
</div>
|
<span bo-text="team.member_count"></span> <span class="hidden-xs">member<span bo-if="team.member_count != 1">s</span></span>
|
||||||
</div>
|
</span>
|
||||||
|
</td>
|
||||||
<div class="col-sm-5 col-md-4 control-col" ng-show="organization.is_admin">
|
<td class="hidden-xs">
|
||||||
|
<span bo-text="team.repo_count"></span> repositories
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
<span class="role-group" current-role="team.role" pull-left="true"
|
<span class="role-group" current-role="team.role" pull-left="true"
|
||||||
role-changed="setRole(role, team.name)" roles="teamRoles"></span>
|
role-changed="setRole(role, team.name)" roles="teamRoles"></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
<span class="cor-options-menu">
|
<span class="cor-options-menu">
|
||||||
<span class="cor-option" option-click="viewTeam(team.name)">
|
<span class="cor-option" option-click="viewTeam(team.name)">
|
||||||
<i class="fa fa-user"></i> Manage Team Members
|
<i class="fa fa-user"></i> Manage Team Members
|
||||||
|
@ -85,8 +96,14 @@
|
||||||
<i class="fa fa-times"></i> Delete Team {{ team.name }}
|
<i class="fa fa-times"></i> Delete Team {{ team.name }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="empty" ng-if="!orderedTeams.entries.length"
|
||||||
|
style="margin-top: 20px;">
|
||||||
|
<div class="empty-primary-msg">No matching teams found.</div>
|
||||||
|
<div class="empty-secondary-msg">Try expanding your filtering terms.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ angular.module('quay').directive('addRepoPermissions', function () {
|
||||||
'permissionsAdded': '&permissionsAdded',
|
'permissionsAdded': '&permissionsAdded',
|
||||||
},
|
},
|
||||||
|
|
||||||
controller: function($scope, $element, ApiService, UIService, TableService, RolesService) {
|
controller: function($scope, $element, ApiService, UIService, TableService, RolesService, UserService) {
|
||||||
$scope.TableService = TableService;
|
$scope.TableService = TableService;
|
||||||
|
|
||||||
$scope.options = {
|
$scope.options = {
|
||||||
|
@ -71,6 +71,8 @@ angular.module('quay').directive('addRepoPermissions', function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.namespaceInfo = UserService.getNamespace($scope.namespace);
|
||||||
|
|
||||||
// Load the repositories under the entity's namespace.
|
// Load the repositories under the entity's namespace.
|
||||||
var params = {
|
var params = {
|
||||||
'namespace': $scope.namespace,
|
'namespace': $scope.namespace,
|
||||||
|
@ -127,7 +129,7 @@ angular.module('quay').directive('addRepoPermissions', function () {
|
||||||
|
|
||||||
var addPerm = function() {
|
var addPerm = function() {
|
||||||
if (counter >= repos.length) {
|
if (counter >= repos.length) {
|
||||||
$scope.permissionsAdded();
|
$scope.permissionsAdded({'repositories': repos});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,8 @@ angular.module('quay').directive('createEntityDialog', function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.permissionsAdded = function() {
|
$scope.permissionsAdded = function(repositories) {
|
||||||
|
$scope.entity['repo_count'] = repositories.length;
|
||||||
$scope.hide();
|
$scope.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,15 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
'organization': '=organization',
|
'organization': '=organization',
|
||||||
'isEnabled': '=isEnabled'
|
'isEnabled': '=isEnabled'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, ApiService, $timeout, UserService) {
|
controller: function($scope, $element, ApiService, $timeout, UserService, TableService, UIService) {
|
||||||
|
$scope.TableService = TableService;
|
||||||
|
|
||||||
|
$scope.options = {
|
||||||
|
'predicate': 'ordered_team_index',
|
||||||
|
'reverse': false,
|
||||||
|
'filter': ''
|
||||||
|
};
|
||||||
|
|
||||||
$scope.teamRoles = [
|
$scope.teamRoles = [
|
||||||
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
||||||
{ 'id': 'creator', 'title': 'Creator', 'kind': 'success' },
|
{ 'id': 'creator', 'title': 'Creator', 'kind': 'success' },
|
||||||
|
@ -21,65 +29,42 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
|
|
||||||
UserService.updateUserIn($scope);
|
UserService.updateUserIn($scope);
|
||||||
|
|
||||||
$scope.members = {};
|
$scope.teams = null;
|
||||||
$scope.orderedTeams = [];
|
$scope.orderedTeams = null;
|
||||||
$scope.showingMembers = false;
|
$scope.showingMembers = false;
|
||||||
$scope.fullMemberList = null;
|
$scope.fullMemberList = null;
|
||||||
$scope.feedback = null;
|
$scope.feedback = null;
|
||||||
$scope.createTeamInfo = null;
|
$scope.createTeamInfo = null;
|
||||||
|
|
||||||
var loadTeamMembers = function() {
|
var getRoleIndex = function(name) {
|
||||||
if (!$scope.organization || !$scope.isEnabled) { return; }
|
for (var i = 0; i < $scope.teamRoles.length; ++i) {
|
||||||
|
if ($scope.teamRoles[i]['id'] == name) {
|
||||||
// Skip loading team members on mobile.
|
return i;
|
||||||
if (!window.matchMedia('(min-width: 768px)').matches) {
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var name in $scope.organization.teams) {
|
return -1;
|
||||||
if (!$scope.organization.teams.hasOwnProperty(name) || $scope.members[name]) { continue; }
|
|
||||||
|
|
||||||
// Load fully async to prevent it from blocking the UI.
|
|
||||||
(function(teamname) {
|
|
||||||
$timeout(function() {
|
|
||||||
loadMembersOfTeam(teamname);
|
|
||||||
}, 1);
|
|
||||||
})(name);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadMembersOfTeam = function(name) {
|
var setTeamsState = function() {
|
||||||
var params = {
|
|
||||||
'orgname': $scope.organization.name,
|
|
||||||
'teamname': name
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.members[name] = {};
|
|
||||||
|
|
||||||
ApiService.getOrganizationTeamMembers(null, params).then(function(resp) {
|
|
||||||
$scope.members[name].members = resp.members;
|
|
||||||
}, function() {
|
|
||||||
delete $scope.members[name];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadOrderedTeams = function() {
|
|
||||||
if (!$scope.organization || !$scope.organization.ordered_teams || !$scope.isEnabled) {
|
if (!$scope.organization || !$scope.organization.ordered_teams || !$scope.isEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.orderedTeams = [];
|
$scope.teams = [];
|
||||||
$scope.organization.ordered_teams.map(function(name) {
|
$scope.organization.ordered_teams.map(function(name, index) {
|
||||||
$scope.orderedTeams.push($scope.organization.teams[name]);
|
var team = $scope.organization.teams[name];
|
||||||
|
team['ordered_team_index'] = $scope.organization.ordered_teams.length - index;
|
||||||
|
team['role_index'] = getRoleIndex(team['role']);
|
||||||
|
$scope.teams.push(team);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.orderedTeams = TableService.buildOrderedItems(
|
||||||
|
$scope.teams, $scope.options,
|
||||||
|
['name'],
|
||||||
|
['ordered_team_index', 'member_count', 'repo_count', 'role_index']);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.$watch('organization', loadOrderedTeams);
|
|
||||||
$scope.$watch('organization', loadTeamMembers);
|
|
||||||
|
|
||||||
$scope.$watch('isEnabled', loadOrderedTeams);
|
|
||||||
$scope.$watch('isEnabled', loadTeamMembers);
|
|
||||||
|
|
||||||
$scope.setRole = function(role, teamname) {
|
$scope.setRole = function(role, teamname) {
|
||||||
var previousRole = $scope.organization.teams[teamname].role;
|
var previousRole = $scope.organization.teams[teamname].role;
|
||||||
$scope.organization.teams[teamname].role = role;
|
$scope.organization.teams[teamname].role = role;
|
||||||
|
@ -115,9 +100,9 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
|
|
||||||
$scope.handleTeamCreated = function(created) {
|
$scope.handleTeamCreated = function(created) {
|
||||||
var teamname = created.name;
|
var teamname = created.name;
|
||||||
|
created['member_count'] = 0;
|
||||||
|
|
||||||
$scope.organization.teams[teamname] = created;
|
$scope.organization.teams[teamname] = created;
|
||||||
$scope.members[teamname] = {};
|
|
||||||
$scope.members[teamname].members = [];
|
|
||||||
$scope.organization.ordered_teams.push(teamname);
|
$scope.organization.ordered_teams.push(teamname);
|
||||||
$scope.orderedTeams.push(created);
|
$scope.orderedTeams.push(created);
|
||||||
|
|
||||||
|
@ -150,8 +135,8 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
$scope.organization.ordered_teams.splice(index, 1);
|
$scope.organization.ordered_teams.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOrderedTeams();
|
|
||||||
delete $scope.organization.teams[teamname];
|
delete $scope.organization.teams[teamname];
|
||||||
|
setTeamsState();
|
||||||
|
|
||||||
$scope.feedback = {
|
$scope.feedback = {
|
||||||
'kind': 'success',
|
'kind': 'success',
|
||||||
|
@ -192,12 +177,7 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
|
|
||||||
ApiService.removeOrganizationMember(null, params).then(function(resp) {
|
ApiService.removeOrganizationMember(null, params).then(function(resp) {
|
||||||
// Reset the state of the directive.
|
// Reset the state of the directive.
|
||||||
$scope.members = {};
|
|
||||||
$scope.orderedTeams = [];
|
|
||||||
$scope.fullMemberList = null;
|
$scope.fullMemberList = null;
|
||||||
|
|
||||||
loadOrderedTeams();
|
|
||||||
loadTeamMembers();
|
|
||||||
$scope.showMembers(true);
|
$scope.showMembers(true);
|
||||||
|
|
||||||
callback(true);
|
callback(true);
|
||||||
|
@ -215,6 +195,13 @@ angular.module('quay').directive('teamsManager', function () {
|
||||||
$scope.askRemoveMember = function(memberInfo) {
|
$scope.askRemoveMember = function(memberInfo) {
|
||||||
$scope.removeMemberInfo = $.extend({}, memberInfo);
|
$scope.removeMemberInfo = $.extend({}, memberInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.$watch('organization', setTeamsState);
|
||||||
|
$scope.$watch('isEnabled', setTeamsState);
|
||||||
|
|
||||||
|
$scope.$watch('options.predicate', setTeamsState);
|
||||||
|
$scope.$watch('options.reverse', setTeamsState);
|
||||||
|
$scope.$watch('options.filter', setTeamsState);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Reference in a new issue