Modify filters' position and modify member module switch members' apperence

This commit is contained in:
pfh 2018-01-05 10:25:01 +08:00
parent f20103dc0c
commit 9371bd41e8
23 changed files with 229 additions and 98 deletions

View File

@ -22,8 +22,9 @@ export const CONFIRMATION_DIALOG_STYLE: string = `
padding: 20px; list-style-type: none;
}
.batchInfoUl li {line-height: 24px;border-bottom: 1px solid #e8e8e8;}
.batchInfoUl li span:first-child {padding-right: 20px; width: 210px; display: inline-block; color:#666;}
.batchInfoUl li span:last-child {width: 260px; display: inline-block; color:#666;}
.batchInfoUl li span:first-child {padding-right: 20px; width: 240px; display: inline-block; color:#666;
text-overflow: ellipsis; overflow: hidden; vertical-align: middle;}
.batchInfoUl li span:last-child {width: 230px; display: inline-block; color:#666;}
.batchInfoUl li span i {display: inline-block; line-height: 1.2em; font-size: 0.8em; color: #999;}
.batchInfoUl li span a{cursor: pointer; text-decoration: underline;}
`;

View File

@ -12,4 +12,10 @@ export const ENDPOINT_STYLE: string = `
.refresh-btn:hover {
color: #007CBB;
}
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
height: 24px;}
`;

View File

@ -1,8 +1,8 @@
export const ENDPOINT_TEMPLATE: string = `
<div style="margin-top: -24px;">
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-between" style="height: 24px; float:right;">
<div>
<div class="row" style="position:relative;">
<div>
<div class="row flex-items-xs-between rightPos">
<div class="flex-items-xs-middle option-right">
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_TARGETS_PLACEHOLDER" | translate}}' (filter)="doSearchTargets($event)" [currentValue]="targetName"></hbr-filter>
<span class="refresh-btn" (click)="refreshTargets()">

View File

@ -1,5 +1,5 @@
export const LIST_REPLICATION_RULE_TEMPLATE: string = `
<div style="margin-top: -24px;">
<div>
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectedChange()">
<clr-dg-action-bar>
<div class="btn-group">

View File

@ -21,4 +21,11 @@ export const REPLICATION_STYLE: string = `
.option-right-down {
padding-right: 16px;
margin-top: 24px;
}`;
}
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
}
`;

View File

@ -1,7 +1,7 @@
export const REPLICATION_TEMPLATE: string = `
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-between" style="height:32px; float:right">
<div class="row" style="position:relative;">
<div>
<div class="row flex-items-xs-between rightPos">
<div class="flex-xs-middle option-right">
<div class="select" style="float: left; top: 8px;">
<select (change)="doFilterRuleStatus($event)">

View File

@ -1,2 +1,8 @@
export const REPOSITORY_LISTVIEW_STYLE = `
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
}
`;

View File

@ -1,8 +1,8 @@
export const REPOSITORY_LISTVIEW_TEMPLATE = `
<div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-right option-right">
<div class="row" style="position:relative;">
<div>
<div class="row flex-items-xs-right option-right rightPos">
<div class="flex-xs-middle">
<hbr-push-image-button style="display: inline-block;" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>

View File

@ -31,10 +31,17 @@ export const REPOSITORY_STACKVIEW_STYLES: string = `
:host >>> .datagrid .datagrid-placeholder-container {
display: none;
}
:host >>> .datagrid-overlay-wrapper{margin-top:24px;}
.db-status-warning {
position: absolute;
left: 24px;
display: inline-block;
}
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
}
`;

View File

@ -1,8 +1,8 @@
export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
<div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="height: 32px;">
<div class="row flex-items-xs-right option-right">
<div class="row" style="margin-top: 20px; position: relative;">
<div>
<div class="row flex-items-xs-right rightPos">
<div class="flex-xs-middle">
<hbr-push-image-button style="display: inline-block;" [registryUrl]="registryUrl" [projectName]="projectName"></hbr-push-image-button>
<hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)" [currentValue]="lastFilteredRepoName"></hbr-filter>

View File

@ -48,5 +48,10 @@ export const TAG_STYLE = `
:host >>> .datagrid clr-dg-column {
min-width: 80px;
}
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
}
`;

View File

@ -12,9 +12,9 @@ export const TAG_TEMPLATE = `
<button type="button" class="btn btn-primary" [ngxClipboard]="digestTarget" (cbOnSuccess)="onSuccess($event)" (cbOnError)="onError($event)">{{'BUTTON.COPY' | translate}}</button>
</div>
</clr-modal>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="row flex-items-xs-right option-right">
<div class="row" style="position:relative;">
<div>
<div class="row flex-items-xs-right rightPos">
<div class="flex-xs-middle">
<hbr-filter [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filter)="doSearchTagNames($event)" [currentValue]="lastFilteredTagName"></hbr-filter>
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.10.17",
"clarity-ui": "^0.10.17",
"core-js": "^2.4.1",
"harbor-ui": "0.6.6",
"harbor-ui": "0.6.8",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",

View File

@ -38,6 +38,8 @@ import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {User} from "../../../user/user";
import {ActivatedRoute, Router} from "@angular/router";
import {Project} from "../../project";
@Component({
selector: 'add-member',
@ -79,54 +81,63 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
private userService: UserService,
private messageHandlerService: MessageHandlerService,
private translateService: TranslateService,
private route: ActivatedRoute,
private ref: ChangeDetectorRef) { }
ngOnInit(): void {
let resolverData = this.route.snapshot.parent.data;
let hasProjectAdminRole: boolean;
if (resolverData) {
hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
}
if (hasProjectAdminRole) {
this.userService.getUsers()
.then(users => {
this.userLists = users;
});
this.nameChecker
.debounceTime(500)
.distinctUntilChanged()
.subscribe((name: string) => {
let cont = this.currentForm.controls['member_name'];
if (cont) {
this.isMemberNameValid = cont.valid;
if (cont.valid) {
this.checkOnGoing = true;
this.memberService
.listMembers(this.projectId, cont.value).toPromise()
.then((members: Member[]) => {
if (members.filter(m => { return m.username === cont.value }).length > 0) {
this.isMemberNameValid = false;
this.memberTooltip = 'MEMBER.USERNAME_ALREADY_EXISTS';
}
this.checkOnGoing = false;
})
.catch(error => {
this.checkOnGoing = false;
});
//username autocomplete
if (this.userLists.length) {
this.selectUserName = [];
this.userLists.filter(data => {
if (data.username.startsWith(cont.value)) {
if (this.selectUserName.length < 10) {
this.selectUserName.push(data.username);
.debounceTime(500)
.distinctUntilChanged()
.subscribe((name: string) => {
let cont = this.currentForm.controls['member_name'];
if (cont) {
this.isMemberNameValid = cont.valid;
if (cont.valid) {
this.checkOnGoing = true;
this.memberService
.listMembers(this.projectId, cont.value).toPromise()
.then((members: Member[]) => {
if (members.filter(m => { return m.username === cont.value }).length > 0) {
this.isMemberNameValid = false;
this.memberTooltip = 'MEMBER.USERNAME_ALREADY_EXISTS';
}
this.checkOnGoing = false;
})
.catch(error => {
this.checkOnGoing = false;
});
//username autocomplete
if (this.userLists && this.userLists.length) {
this.selectUserName = [];
this.userLists.filter(data => {
if (data.username.startsWith(cont.value)) {
if (this.selectUserName.length < 10) {
this.selectUserName.push(data.username);
}
}
}
});
setTimeout(() => {
setInterval(() => this.ref.markForCheck(), 100);
}, 1000);
});
setTimeout(() => {
setInterval(() => this.ref.markForCheck(), 100);
}, 1000);
}
} else {
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
}
} else {
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
}
}
});
});
}
}
ngOnDestroy(): void {

View File

@ -16,4 +16,10 @@
:host >>> .btn-group-overflow .dropdown-toggle {
line-height: 24px;
height: 24px;
}
.rightPos{
position: absolute;
z-index: 100;
right: 35px;
margin-top: 4px;
}

View File

@ -1,6 +1,6 @@
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="top: 8px;">
<div class="row flex-items-xs-between">
<div class="row" style="margin-top: 24px;position: relative;">
<div>
<div class="row flex-items-xs-between rightPos">
<div class="flex-xs-middle option-left" style="position: relative; top: 10px;">
</div>
<div class="flex-xs-middle option-right">
@ -15,7 +15,7 @@
<clr-datagrid [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="SelectedChange()">
<clr-dg-action-bar>
<div class="btn-group">
<clr-button-group [clrMenuPosition]="'bottom-right'">
<clr-button-group [clrMenuPosition]="'bottom-right'" >
<clr-button class="btn btn-sm btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasProjectAdminRole">{{'MEMBER.NEW_MEMBER' | translate }}</clr-button>
<clr-button class="btn btn-sm btn-secondary" (click)="deleteMembers(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole)">{{'MEMBER.DELETE' | translate}}</clr-button>
<clr-button class="btn btn-sm btn-secondary" [clrInMenu]="true" (click)="changeRole(selectedRow, 1)" [disabled]="!(selectedRow.length && hasProjectAdminRole)">{{'MEMBER.PROJECT_ADMIN' | translate}}</clr-button>

View File

@ -60,7 +60,10 @@ export class MemberComponent implements OnInit, OnDestroy {
hasProjectAdminRole: boolean;
searchMember: string;
selectedRow: Member[] = [];
selectedRow: Member[] = []
roleNum: number;
isDelete: boolean =false;
isChangeRole: boolean =false;
batchDelectionInfos: BatchInfo[] = [];
constructor(
@ -69,15 +72,20 @@ export class MemberComponent implements OnInit, OnDestroy {
private memberService: MemberService,
private translate: TranslateService,
private messageHandlerService: MessageHandlerService,
private deletionDialogService: ConfirmationDialogService,
private OperateDialogService: ConfirmationDialogService,
private session: SessionService,
private ref: ChangeDetectorRef) {
this.delSub = deletionDialogService.confirmationConfirm$.subscribe(message => {
this.delSub = OperateDialogService.confirmationConfirm$.subscribe(message => {
if (message &&
message.state === ConfirmationState.CONFIRMED &&
message.source === ConfirmationTargets.PROJECT_MEMBER) {
this.deleteMem(message.data);
if (this.isDelete) {
this.deleteMem(message.data);
}
if (this.isChangeRole) {
this.changeOpe(message.data);
}
}
});
let hnd = setInterval(()=>ref.markForCheck(), 100);
@ -127,27 +135,75 @@ export class MemberComponent implements OnInit, OnDestroy {
}
changeRole(m: Member[], roleId: number) {
if (m) {
let promiseList: any[] = [];
if (m && m.length) {
this.isDelete = false;
this.isChangeRole = true;
this.roleNum = roleId;
let nameArr: string[] = [];
this.batchDelectionInfos = [];
m.forEach(data => {
if (!(data.user_id === this.currentUser.user_id || !this.hasProjectAdminRole)) {
promiseList.push(this.memberService.changeMemberRole(this.projectId, data.user_id, roleId));
}
})
Promise.all(promiseList).then(num => {
if (num.length === promiseList.length) {
this.messageHandlerService.showSuccess('MEMBER.SWITCHED_SUCCESS');
this.retrieve(this.projectId, '');
}
},
error => {
this.messageHandlerService.handleError(error);
}
nameArr.push(data.username);
let initBatchMessage = new BatchInfo();
initBatchMessage.name = data.username;
this.batchDelectionInfos.push(initBatchMessage);
});
this.OperateDialogService.addBatchInfoList(this.batchDelectionInfos);
let switchMessage = new ConfirmationMessage(
'MEMBER.SWITCH_TITLE',
'MEMBER.SWITCH_SUMMARY',
nameArr.join(','),
m,
ConfirmationTargets.PROJECT_MEMBER,
ConfirmationButtons.DELETE_CANCEL
);
}
this.OperateDialogService.openComfirmDialog(switchMessage);
}
}
changeOpe(members: Member[]) {
if (members && members.length) {
let promiseList: any[] = [];
members.forEach(member => {
if (member.user_id === this.currentUser.user_id || member.role_id >= this.roleNum) {
let findedList = this.batchDelectionInfos.find(data => data.name === member.username);
this.translate.get('BATCH.SWITCH_FAILURE').subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});
}else {
promiseList.push(this.changeOperate(this.projectId, member.user_id, this.roleNum, member.username));
}
});
Promise.all(promiseList).then(num => {
this.retrieve(this.projectId, '');
},
);
}
}
changeOperate(projectId: number, memberId: number, roleId: number, username: string) {
let findedList = this.batchDelectionInfos.find(data => data.name === username);
return this.memberService
.changeMemberRole(projectId, memberId, roleId)
.then(
response => {
this.translate.get('BATCH.SWITCH_SUCCESS').subscribe(res => {
findedList = BathInfoChanges(findedList, res);
});
},
error => {
this.translate.get('BATCH.SWITCH_FAILURE').subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});
}
);
}
deleteMembers(m: Member[]) {
this.isDelete = true;
this.isChangeRole = false;
let nameArr: string[] = [];
this.batchDelectionInfos = [];
if (m && m.length) {
@ -157,17 +213,17 @@ export class MemberComponent implements OnInit, OnDestroy {
initBatchMessage.name = data.username;
this.batchDelectionInfos.push(initBatchMessage);
});
this.deletionDialogService.addBatchInfoList(this.batchDelectionInfos);
this.OperateDialogService.addBatchInfoList(this.batchDelectionInfos);
let deletionMessage = new ConfirmationMessage(
'PROJECT.DELETION_TITLE',
'PROJECT.DELETION_SUMMARY',
'MEMBER.DELETION_TITLE',
'MEMBER.DELETION_SUMMARY',
nameArr.join(','),
m,
ConfirmationTargets.PROJECT_MEMBER,
ConfirmationButtons.DELETE_CANCEL
);
this.deletionDialogService.openComfirmDialog(deletionMessage);
this.OperateDialogService.openComfirmDialog(deletionMessage);
}
}

View File

@ -18,5 +18,5 @@
color: #007CBB;
}
.rightPos {
position: absolute; right: 20px; margin-top: 16px; height:32px;
position: absolute; right: 20px; margin-top: 5px; height:32px; z-index: 100;
}

View File

@ -21,7 +21,8 @@
padding: 20px; list-style-type: none;
}
.batchInfoUl li {line-height: 24px;border-bottom: 1px solid #e8e8e8;}
.batchInfoUl li span:first-child {padding-right: 20px; width: 210px; display: inline-block; color:#666;}
.batchInfoUl li span:last-child {width: 260px; display: inline-block; color:#666;}
.batchInfoUl li span:first-child {padding-right: 20px; width: 240px; display: inline-block; color:#666;
text-overflow: ellipsis; overflow: hidden; vertical-align: middle;}
.batchInfoUl li span:last-child {width: 230px; display: inline-block; color:#666;}
.batchInfoUl li span i {display: inline-block; line-height: 1.2em; font-size: 0.8em; color: #999;}
.batchInfoUl li span a{cursor: pointer; text-decoration: underline;}

View File

@ -37,7 +37,9 @@
},
"BATCH": {
"DELETED_SUCCESS": "Deleted successfully",
"DELETED_FAILURE": "Deleted failed"
"DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed"
},
"TOOLTIP": {
"EMAIL": "Email should be a valid email address like name@example.com.",
@ -201,12 +203,15 @@
"USERNAME_ALREADY_EXISTS": "Username already exists.",
"UNKNOWN_ERROR": "Unknown error occurred while adding member.",
"FILTER_PLACEHOLDER": "Filter Members",
"DELETION_TITLE": "Confirm project member deletion",
"DELETION_SUMMARY": "Do you want to delete project member {{param}}?",
"DELETION_TITLE": "Confirm project members deletion",
"DELETION_SUMMARY": "Do you want to delete project members {{param}}?",
"ADDED_SUCCESS": "Added member successfully.",
"DELETED_SUCCESS": "Deleted members successfully.",
"SWITCHED_SUCCESS": "Switched members role successfully.",
"OF": "of"
"DELETED_SUCCESS": "Deleted member successfully.",
"SWITCHED_SUCCESS": "Switched member role successfully.",
"OF": "of",
"SWITCH_TITLE": "Confirm project members switch",
"SWITCH_SUMMARY": "Do you want to switch project members {{param}}?"
},
"AUDIT_LOG": {
"USERNAME": "Username",

View File

@ -35,6 +35,12 @@
"COPY": "COPY",
"EDIT": "EDITAR"
},
"BATCH": {
"DELETED_SUCCESS": "Deleted successfully",
"DELETED_FAILURE": "Deleted failed",
"SWITCH_SUCCESS": "Switch successfully",
"SWITCH_FAILURE": "Switch failed"
},
"TOOLTIP": {
"EMAIL": "El email debe ser una dirección válida como nombre@ejemplo.com.",
"USER_NAME": "Debe tener una longitud máxima de 20 caracteres y no puede contener caracteres especiales.",
@ -202,7 +208,9 @@
"ADDED_SUCCESS": "Miembro añadido satisfactoriamente.",
"DELETED_SUCCESS": "Miembro eliminado satisfactoriamente",
"SWITCHED_SUCCESS": "Rol del miembro cambiado satisfactoriamente.",
"OF": "of"
"OF": "of",
"SWITCH_TITLE": "Confirm project members switch",
"SWITCH_SUMMARY": "Do you want to switch project members {{param}}?"
},
"AUDIT_LOG": {
"USERNAME": "Nombre de usuario",

View File

@ -35,6 +35,12 @@
"COPY": "拷贝",
"EDIT": "编辑"
},
"BATCH": {
"DELETED_SUCCESS": "删除成功",
"DELETED_FAILURE": "删除失败",
"SWITCH_SUCCESS": "切换成功",
"SWITCH_FAILURE": "切换失败"
},
"TOOLTIP": {
"EMAIL": "请使用正确的邮箱地址比如name@example.com。",
"USER_NAME": "不能包含特殊字符且长度不能超过20。",
@ -202,7 +208,9 @@
"DELETION_SUMMARY": "你确认删除项目成员 {{param}}?",
"ADDED_SUCCESS": "成功新增成员。",
"DELETED_SUCCESS": "成功删除成员。",
"SWITCHED_SUCCESS": "切换角色成功。"
"SWITCHED_SUCCESS": "切换角色成功。",
"SWITCH_TITLE": "切换项目成员确认",
"SWITCH_SUMMARY": "你确认切换项目成员 {{param}}??"
},
"AUDIT_LOG": {
"USERNAME": "用户名",

View File

@ -69,7 +69,11 @@ Change Project Member Role
#change role
Click Element //button[@class='btn dropdown-toggle']
Click Element //button[contains(.,'${role}')]
#Click Element xpath=//project-detail//clr-dg-action-overflow//button[contains(.,"${role}")]
sleep 1
Click Element xpath=//clr-modal//button[contains(.,'SWITCH')];
sleep 1
Click Element xpath=//clr-modal//button[contains(.,'CLOSE')];
Sleep 2
Wait Until Page Contains ${role}