Edit project approvers
This commit is contained in:
parent
103c23a194
commit
534c5ea3dc
@ -3,11 +3,11 @@
|
|||||||
*ngFor="let userId of displayedMembers"
|
*ngFor="let userId of displayedMembers"
|
||||||
class="member"
|
class="member"
|
||||||
[class.large-spacing]="largeSpacing"
|
[class.large-spacing]="largeSpacing"
|
||||||
[class.can-remove]="canRemove"
|
[class.can-remove]="canRemoveMember(userId)"
|
||||||
(click)="canRemove && remove.emit(userId)"
|
(click)="canRemoveMember(userId) && remove.emit(userId)"
|
||||||
>
|
>
|
||||||
<redaction-initials-avatar [userId]="userId" size="large" color="gray"></redaction-initials-avatar>
|
<redaction-initials-avatar [userId]="userId" size="large" color="gray"></redaction-initials-avatar>
|
||||||
<div class="remove">
|
<div class="remove" *ngIf="canRemoveMember(userId)">
|
||||||
<mat-icon svgIcon="red:close"></mat-icon>
|
<mat-icon svgIcon="red:close"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export class TeamMembersComponent implements OnInit {
|
|||||||
@Input() public canAdd = true;
|
@Input() public canAdd = true;
|
||||||
@Input() public largeSpacing = false;
|
@Input() public largeSpacing = false;
|
||||||
@Input() public canRemove = false;
|
@Input() public canRemove = false;
|
||||||
|
@Input() public unremovableMembers: string[] = [];
|
||||||
@Output() public openAssignProjectMembersDialog = new EventEmitter();
|
@Output() public openAssignProjectMembersDialog = new EventEmitter();
|
||||||
@Output() public remove = new EventEmitter<string>();
|
@Output() public remove = new EventEmitter<string>();
|
||||||
|
|
||||||
@ -38,4 +39,8 @@ export class TeamMembersComponent implements OnInit {
|
|||||||
public get overflowCount() {
|
public get overflowCount() {
|
||||||
return this.memberIds.length > this.maxTeamMembersBeforeExpand ? this.memberIds.length - (this.maxTeamMembersBeforeExpand - 1) : 0;
|
return this.memberIds.length > this.maxTeamMembersBeforeExpand ? this.memberIds.length - (this.maxTeamMembersBeforeExpand - 1) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public canRemoveMember(userId: string) {
|
||||||
|
return this.canRemove && this.unremovableMembers.indexOf(userId) === -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,17 +14,39 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="data.type === 'project'">
|
<ng-container *ngIf="data.type === 'project'">
|
||||||
<div class="all-caps-label mt-10" [translate]="'assign-' + data.type + '-owner.dialog.members'"></div>
|
<div class="all-caps-label mt-16" [translate]="'assign-' + data.type + '-owner.dialog.approvers'"></div>
|
||||||
<redaction-team-members
|
<redaction-team-members
|
||||||
[memberIds]="selectedUserList"
|
[memberIds]="selectedApproversList"
|
||||||
[canAdd]="false"
|
[canAdd]="false"
|
||||||
[largeSpacing]="true"
|
[largeSpacing]="true"
|
||||||
[canRemove]="true"
|
[canRemove]="true"
|
||||||
|
[unremovableMembers]="[selectedSingleUser]"
|
||||||
(remove)="toggleSelected($event)"
|
(remove)="toggleSelected($event)"
|
||||||
[perLine]="13"
|
[perLine]="13"
|
||||||
></redaction-team-members>
|
></redaction-team-members>
|
||||||
|
|
||||||
<pre *ngIf="selectedUserList.length === 0" class="info" [innerHTML]="'assign-' + data.type + '-owner.dialog.no-members' | translate"></pre>
|
<pre
|
||||||
|
*ngIf="selectedApproversList.length === 0"
|
||||||
|
class="info"
|
||||||
|
[innerHTML]="'assign-' + data.type + '-owner.dialog.no-approvers' | translate"
|
||||||
|
></pre>
|
||||||
|
|
||||||
|
<div class="all-caps-label mt-16" [translate]="'assign-' + data.type + '-owner.dialog.reviewers'"></div>
|
||||||
|
<redaction-team-members
|
||||||
|
[memberIds]="selectedReviewersList"
|
||||||
|
[canAdd]="false"
|
||||||
|
[largeSpacing]="true"
|
||||||
|
[canRemove]="true"
|
||||||
|
[unremovableMembers]="[selectedSingleUser]"
|
||||||
|
(remove)="toggleSelected($event)"
|
||||||
|
[perLine]="13"
|
||||||
|
></redaction-team-members>
|
||||||
|
|
||||||
|
<pre
|
||||||
|
*ngIf="selectedReviewersList.length === 0"
|
||||||
|
class="info"
|
||||||
|
[innerHTML]="'assign-' + data.type + '-owner.dialog.no-reviewers' | translate"
|
||||||
|
></pre>
|
||||||
|
|
||||||
<redaction-search-input
|
<redaction-search-input
|
||||||
[width]="560"
|
[width]="560"
|
||||||
@ -36,8 +58,13 @@
|
|||||||
<div class="members-list">
|
<div class="members-list">
|
||||||
<div *ngFor="let userId of multiUsersSelectOptions" [class.selected]="isMemberSelected(userId)" (click)="toggleSelected(userId)">
|
<div *ngFor="let userId of multiUsersSelectOptions" [class.selected]="isMemberSelected(userId)" (click)="toggleSelected(userId)">
|
||||||
<redaction-initials-avatar [userId]="userId" [withName]="true" size="large"></redaction-initials-avatar>
|
<redaction-initials-avatar [userId]="userId" [withName]="true" size="large"></redaction-initials-avatar>
|
||||||
<mat-icon svgIcon="red:check" class="add"></mat-icon>
|
<div class="actions">
|
||||||
<mat-icon svgIcon="red:close" class="remove"></mat-icon>
|
<div class="make-approver" (click)="toggleApprover(userId, $event)">
|
||||||
|
<redaction-round-checkbox class="mr-8" [active]="isApprover(userId)"></redaction-round-checkbox>
|
||||||
|
<span [translate]="'assign-' + data.type + '-owner.dialog.make-approver'"></span>
|
||||||
|
</div>
|
||||||
|
<mat-icon *ngIf="!isOwner(userId)" [svgIcon]="'red:' + (isMemberSelected(userId) ? 'close' : 'check')"></mat-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -8,6 +8,11 @@
|
|||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redaction-team-members {
|
||||||
|
margin-top: -4px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.members-list {
|
.members-list {
|
||||||
max-height: 220px;
|
max-height: 220px;
|
||||||
height: 220px;
|
height: 220px;
|
||||||
@ -30,40 +35,35 @@
|
|||||||
width: 560px;
|
width: 560px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
mat-icon {
|
.make-approver {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 13px;
|
right: 13px;
|
||||||
top: 12px;
|
top: 0;
|
||||||
width: 14px;
|
height: 38px;
|
||||||
height: 14px;
|
align-items: center;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background-color: $green-2;
|
background-color: $green-2;
|
||||||
|
|
||||||
mat-icon.add {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $grey-2;
|
background-color: $grey-2;
|
||||||
|
|
||||||
&.selected {
|
.actions {
|
||||||
mat-icon.remove {
|
display: flex;
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
mat-icon.add {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.selected) {
|
|
||||||
mat-icon.add {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +72,3 @@
|
|||||||
.info {
|
.info {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-10 {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -42,11 +42,18 @@ export class AssignOwnerDialogComponent {
|
|||||||
const project = this.data.project;
|
const project = this.data.project;
|
||||||
this.usersForm = this._formBuilder.group({
|
this.usersForm = this._formBuilder.group({
|
||||||
singleUser: [project?.ownerId, Validators.required],
|
singleUser: [project?.ownerId, Validators.required],
|
||||||
userList: [[...project?.memberIds]]
|
approvers: [[...project?.approverIds]],
|
||||||
|
members: [[...project?.memberIds]]
|
||||||
});
|
});
|
||||||
this.searchForm = this._formBuilder.group({
|
this.searchForm = this._formBuilder.group({
|
||||||
query: ['']
|
query: ['']
|
||||||
});
|
});
|
||||||
|
this.usersForm.get('singleUser').valueChanges.subscribe((singleUser) => {
|
||||||
|
if (!this.isApprover(singleUser)) {
|
||||||
|
this.toggleApprover(singleUser);
|
||||||
|
}
|
||||||
|
// If it is an approver, it is already a member, no need to check
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.type === 'file') {
|
if (this.data.type === 'file') {
|
||||||
@ -67,8 +74,20 @@ export class AssignOwnerDialogComponent {
|
|||||||
return this.usersForm.get('singleUser').value;
|
return this.usersForm.get('singleUser').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get selectedUserList(): string[] {
|
public get selectedApproversList(): string[] {
|
||||||
return this.usersForm.get('userList').value;
|
return this.usersForm.get('approvers').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get selectedReviewersList(): string[] {
|
||||||
|
return this.selectedUsersList.filter((m) => this.selectedApproversList.indexOf(m) === -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get selectedUsersList(): string[] {
|
||||||
|
return this.usersForm.get('members').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isOwner(userId: string): boolean {
|
||||||
|
return userId === this.selectedSingleUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveUsers() {
|
async saveUsers() {
|
||||||
@ -76,9 +95,11 @@ export class AssignOwnerDialogComponent {
|
|||||||
try {
|
try {
|
||||||
if (this.data.type === 'project') {
|
if (this.data.type === 'project') {
|
||||||
const ownerId = this.selectedSingleUser;
|
const ownerId = this.selectedSingleUser;
|
||||||
const memberIds = this.selectedUserList;
|
const memberIds = this.selectedUsersList;
|
||||||
|
const approverIds = this.selectedApproversList;
|
||||||
const pw = Object.assign({}, this.data.project);
|
const pw = Object.assign({}, this.data.project);
|
||||||
pw.project.memberIds = memberIds;
|
pw.project.memberIds = memberIds;
|
||||||
|
pw.project.approverIds = approverIds;
|
||||||
pw.project.ownerId = ownerId;
|
pw.project.ownerId = ownerId;
|
||||||
result = await this._appStateService.addOrUpdateProject(pw.project);
|
result = await this._appStateService.addOrUpdateProject(pw.project);
|
||||||
}
|
}
|
||||||
@ -118,18 +139,56 @@ export class AssignOwnerDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isMemberSelected(userId: string): boolean {
|
public isMemberSelected(userId: string): boolean {
|
||||||
return this.selectedUserList.indexOf(userId) !== -1;
|
return this.selectedUsersList.indexOf(userId) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isApprover(userId: string): boolean {
|
||||||
|
return this.selectedApproversList.indexOf(userId) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleApprover(userId: string, $event?: MouseEvent) {
|
||||||
|
$event?.stopPropagation();
|
||||||
|
|
||||||
|
if (this.isOwner(userId) && this.isApprover(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isApprover(userId)) {
|
||||||
|
this.selectedApproversList.splice(this.selectedApproversList.indexOf(userId), 1);
|
||||||
|
} else {
|
||||||
|
this.selectedApproversList.push(userId);
|
||||||
|
if (!this.isMemberSelected(userId)) {
|
||||||
|
this.toggleSelected(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleSelected(userId: string) {
|
public toggleSelected(userId: string) {
|
||||||
if (this.isMemberSelected(userId)) {
|
if (this.isMemberSelected(userId)) {
|
||||||
const idx = this.selectedUserList.indexOf(userId);
|
this.selectedUsersList.splice(this.selectedUsersList.indexOf(userId), 1);
|
||||||
this.selectedUserList.splice(idx, 1);
|
|
||||||
|
if (this.isApprover(userId)) {
|
||||||
|
this.selectedApproversList.splice(this.selectedApproversList.indexOf(userId), 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.selectedUserList.push(userId);
|
this.selectedUsersList.push(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _compareLists(l1: string[], l2: string[]) {
|
||||||
|
if (l1.length !== l2.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let idx = 0; idx < l1.length; ++idx) {
|
||||||
|
if (l1[idx] !== l2[idx]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public get changed(): boolean {
|
public get changed(): boolean {
|
||||||
if (this.data.ignoreChanged) {
|
if (this.data.ignoreChanged) {
|
||||||
return true;
|
return true;
|
||||||
@ -140,18 +199,15 @@ export class AssignOwnerDialogComponent {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initial = this.data.project.memberIds.sort();
|
const initialMembers = this.data.project.memberIds.sort();
|
||||||
const current = this.selectedUserList.sort();
|
const currentMembers = this.selectedUsersList.sort();
|
||||||
|
|
||||||
if (initial.length !== current.length) {
|
const initialApprovers = this.data.project.approverIds.sort();
|
||||||
|
const currentApprovers = this.selectedApproversList.sort();
|
||||||
|
|
||||||
|
if (this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let idx = 0; idx < initial.length; ++idx) {
|
|
||||||
if (initial[idx] !== current[idx]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this.data.type === 'file') {
|
} else if (this.data.type === 'file') {
|
||||||
const reviewerId = this.selectedSingleUser;
|
const reviewerId = this.selectedSingleUser;
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,10 @@ export class ProjectWrapper {
|
|||||||
return this.project.memberIds;
|
return this.project.memberIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get approverIds() {
|
||||||
|
return this.project.approverIds;
|
||||||
|
}
|
||||||
|
|
||||||
get files() {
|
get files() {
|
||||||
return this._files;
|
return this._files;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -455,11 +455,14 @@
|
|||||||
"single-user": "Owner",
|
"single-user": "Owner",
|
||||||
"multi-user": "Review Team",
|
"multi-user": "Review Team",
|
||||||
"title": "Manage Dossier Team",
|
"title": "Manage Dossier Team",
|
||||||
"members": "Members",
|
"approvers": "Approvers",
|
||||||
|
"reviewers": "Reviewers",
|
||||||
"save": "Save Changes",
|
"save": "Save Changes",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"search": "Search...",
|
"search": "Search...",
|
||||||
"no-members": "No members yet.\nSelect from the list below."
|
"no-approvers": "No approvers yet.\nSelect from the list below.",
|
||||||
|
"no-reviewers": "No reviewers yet.\nSelect from the list below.",
|
||||||
|
"make-approver": "Make Approver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"project-member-guard": {
|
"project-member-guard": {
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
|
approverIds?: Array<string>;
|
||||||
date?: string;
|
date?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
downloadFileTypes?: Array<Project.DownloadFileTypesEnum>;
|
downloadFileTypes?: Array<Project.DownloadFileTypesEnum>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user