mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-31 21:18:21 +01:00
Add async task progress and delete dialog task progress #4371
norm code
This commit is contained in:
parent
c2c667c6d6
commit
1121c8a76b
17
src/ui_ng/lib/src/_animations/fade-in.animation.ts
Normal file
17
src/ui_ng/lib/src/_animations/fade-in.animation.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// import the required animation functions from the angular animations module
|
||||
import {AnimationTriggerMetadata, trigger, animate, transition, style } from '@angular/animations';
|
||||
|
||||
export const FadeInAnimation: AnimationTriggerMetadata =
|
||||
// trigger name for attaching this animation to an element using the [@triggerName] syntax
|
||||
trigger('FadeInAnimation', [
|
||||
|
||||
// route 'enter' transition
|
||||
transition(':enter', [
|
||||
|
||||
// css styles at start of transition
|
||||
style({ opacity: 0 }),
|
||||
|
||||
// animation and styles at end of transition
|
||||
animate('.3s', style({ opacity: 1 }))
|
||||
]),
|
||||
]);
|
2
src/ui_ng/lib/src/_animations/index.ts
Normal file
2
src/ui_ng/lib/src/_animations/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './fade-in.animation';
|
||||
export * from './slide-in-out.animation';
|
47
src/ui_ng/lib/src/_animations/slide-in-out.animation.ts
Normal file
47
src/ui_ng/lib/src/_animations/slide-in-out.animation.ts
Normal file
@ -0,0 +1,47 @@
|
||||
// import the required animation functions from the angular animations module
|
||||
import {AnimationTriggerMetadata, trigger, state, animate, transition, style } from '@angular/animations';
|
||||
|
||||
export const SlideInOutAnimation: AnimationTriggerMetadata =
|
||||
// trigger name for attaching this animation to an element using the [@triggerName] syntax
|
||||
trigger('SlideInOutAnimation', [
|
||||
|
||||
// end state styles for route container (host)
|
||||
state('in', style({
|
||||
// the view covers the whole screen with a semi tranparent background
|
||||
position: 'fix',
|
||||
right: 0,
|
||||
width: '350px',
|
||||
bottom: 0
|
||||
// backgroundColor: 'rgba(0, 0, 0, 0.8)'
|
||||
})),
|
||||
state('out', style({
|
||||
// the view covers the whole screen with a semi tranparent background
|
||||
position: 'fix',
|
||||
width: '30px',
|
||||
bottom: 0
|
||||
// backgroundColor: 'rgba(0, 0, 0, 0.8)'
|
||||
})),
|
||||
// route 'enter' transition
|
||||
transition('out => in', [
|
||||
// animation and styles at end of transition
|
||||
animate('.5s ease-in-out', style({
|
||||
// transition the right position to 0 which slides the content into view
|
||||
width: '350px',
|
||||
|
||||
// transition the background opacity to 0.8 to fade it in
|
||||
// backgroundColor: 'rgba(0, 0, 0, 0.8)'
|
||||
}))
|
||||
]),
|
||||
|
||||
// route 'leave' transition
|
||||
transition('in => out', [
|
||||
// animation and styles at end of transition
|
||||
animate('.5s ease-in-out', style({
|
||||
// transition the right position to -400% which slides the content out of view
|
||||
width: '30px',
|
||||
|
||||
// transition the background opacity to 0 to fade it out
|
||||
// backgroundColor: 'rgba(0, 0, 0, 0)'
|
||||
}))
|
||||
])
|
||||
]);
|
@ -5,19 +5,6 @@
|
||||
<clr-icon shape="warning" class="is-warning" size="64"></clr-icon>
|
||||
</div>
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
<div>
|
||||
<ul class="batchInfoUl">
|
||||
<li *ngFor="let info of batchInfors">
|
||||
<span><i class="spinner spinner-inline spinner-pos" [hidden]='!info.loading'></i> {{info.name}}</span>
|
||||
<span *ngIf="!info.errorInfo.length" [style.color]="colorChange(info)">{{info.status}}</span>
|
||||
<span *ngIf="info.errorInfo.length" [style.color]="colorChange(info)">
|
||||
<a (click)="toggleErrorTitle(errorInfo)">{{info.status}}</a>
|
||||
<br>
|
||||
<i #errorInfo style="display: none;">{{info.errorInfo}}</i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" [ngSwitch]="buttons">
|
||||
<ng-template [ngSwitchCase]="0">
|
||||
@ -30,16 +17,14 @@
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="operate()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {Component, EventEmitter, Output} from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { ConfirmationMessage } from './confirmation-message';
|
||||
@ -35,7 +35,6 @@ export class ConfirmationDialogComponent {
|
||||
|
||||
@Output() confirmAction = new EventEmitter<ConfirmationAcknowledgement>();
|
||||
@Output() cancelAction = new EventEmitter<ConfirmationAcknowledgement>();
|
||||
@Input() batchInfors: BatchInfo[] = [];
|
||||
isDelete = false;
|
||||
|
||||
constructor(
|
||||
@ -52,12 +51,6 @@ export class ConfirmationDialogComponent {
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
get batchOverStatus(): boolean {
|
||||
if (this.batchInfors.length) {
|
||||
return this.batchInfors.every(item => item.loading === false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
colorChange(list: BatchInfo) {
|
||||
if (!list.loading && !list.errorState) {
|
||||
@ -74,7 +67,6 @@ export class ConfirmationDialogComponent {
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.batchInfors = [];
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
@ -96,27 +88,6 @@ export class ConfirmationDialogComponent {
|
||||
this.close();
|
||||
}
|
||||
|
||||
operate(): void {
|
||||
if (!this.message) {// Inproper condition
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.batchInfors.length) {
|
||||
this.batchInfors.every(item => item.loading = true);
|
||||
this.isDelete = true;
|
||||
}
|
||||
|
||||
let data: any = this.message.data ? this.message.data : {};
|
||||
let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY;
|
||||
let message = new ConfirmationAcknowledgement(
|
||||
ConfirmationState.CONFIRMED,
|
||||
data,
|
||||
target
|
||||
);
|
||||
this.confirmAction.emit(message);
|
||||
}
|
||||
|
||||
confirm(): void {
|
||||
if (!this.message) {// Inproper condition
|
||||
this.close();
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
ProjectService
|
||||
} from "../service/project.service";
|
||||
import { JobLogViewerComponent } from "../job-log-viewer/job-log-viewer.component";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
describe("CreateEditRuleComponent (inline template)", () => {
|
||||
let mockRules: ReplicationRule[] = [
|
||||
@ -246,7 +247,8 @@ describe("CreateEditRuleComponent (inline template)", () => {
|
||||
{ provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||
{ provide: EndpointService, useClass: EndpointDefaultService },
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: JobLogService, useClass: JobLogDefaultService }
|
||||
{ provide: JobLogService, useClass: JobLogDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -38,6 +38,6 @@
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<hbr-create-edit-endpoint (reload)="reload($event)"></hbr-create-edit-endpoint>
|
||||
</div>
|
@ -16,6 +16,7 @@ import {
|
||||
EndpointDefaultService
|
||||
} from "../service/endpoint.service";
|
||||
import { IServiceConfig, SERVICE_CONFIG } from "../service.config";
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
import { click } from "../utils";
|
||||
|
||||
@ -94,7 +95,8 @@ describe("EndpointComponent (inline template)", () => {
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: EndpointService, useClass: EndpointDefaultService }
|
||||
{ provide: EndpointService, useClass: EndpointDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -12,12 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
} from "@angular/core";
|
||||
import { Subscription } from "rxjs/Subscription";
|
||||
import { Observable } from "rxjs/Observable";
|
||||
@ -30,220 +30,211 @@ import { EndpointService } from "../service/endpoint.service";
|
||||
|
||||
import { ErrorHandler } from "../error-handler/index";
|
||||
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
|
||||
import {
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
ConfirmationButtons
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
ConfirmationButtons
|
||||
} from "../shared/shared.const";
|
||||
|
||||
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
|
||||
import { toPromise, CustomComparator } from "../utils";
|
||||
|
||||
import {
|
||||
BatchInfo,
|
||||
BathInfoChanges
|
||||
} from "../confirmation-dialog/confirmation-batch-message";
|
||||
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
@Component({
|
||||
selector: "hbr-endpoint",
|
||||
templateUrl: "./endpoint.component.html",
|
||||
styleUrls: ["./endpoint.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
selector: "hbr-endpoint",
|
||||
templateUrl: "./endpoint.component.html",
|
||||
styleUrls: ["./endpoint.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EndpointComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(CreateEditEndpointComponent)
|
||||
createEditEndpointComponent: CreateEditEndpointComponent;
|
||||
@ViewChild(CreateEditEndpointComponent)
|
||||
createEditEndpointComponent: CreateEditEndpointComponent;
|
||||
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
|
||||
targets: Endpoint[];
|
||||
target: Endpoint;
|
||||
targets: Endpoint[];
|
||||
target: Endpoint;
|
||||
|
||||
targetName: string;
|
||||
subscription: Subscription;
|
||||
targetName: string;
|
||||
subscription: Subscription;
|
||||
|
||||
loading: boolean = false;
|
||||
loading: boolean = false;
|
||||
|
||||
creationTimeComparator: Comparator<Endpoint> = new CustomComparator<Endpoint>(
|
||||
"creation_time",
|
||||
"date"
|
||||
);
|
||||
creationTimeComparator: Comparator<Endpoint> = new CustomComparator<Endpoint>(
|
||||
"creation_time",
|
||||
"date"
|
||||
);
|
||||
|
||||
timerHandler: any;
|
||||
selectedRow: Endpoint[] = [];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
timerHandler: any;
|
||||
selectedRow: Endpoint[] = [];
|
||||
|
||||
get initEndpoint(): Endpoint {
|
||||
return {
|
||||
endpoint: "",
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
insecure: false,
|
||||
type: 0
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
private endpointService: EndpointService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private ref: ChangeDetectorRef
|
||||
) {
|
||||
this.forceRefreshView(1000);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.targetName = "";
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
get initEndpoint(): Endpoint {
|
||||
return {
|
||||
endpoint: "",
|
||||
name: "",
|
||||
username: "",
|
||||
password: "",
|
||||
insecure: false,
|
||||
type: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
selectedChange(): void {
|
||||
this.forceRefreshView(5000);
|
||||
}
|
||||
|
||||
retrieve(): void {
|
||||
this.loading = true;
|
||||
this.selectedRow = [];
|
||||
toPromise<Endpoint[]>(this.endpointService.getEndpoints(this.targetName))
|
||||
.then(targets => {
|
||||
this.targets = targets || [];
|
||||
constructor(private endpointService: EndpointService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
this.forceRefreshView(1000);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
doSearchTargets(targetName: string) {
|
||||
this.targetName = targetName;
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
refreshTargets() {
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
reload($event: any) {
|
||||
this.targetName = "";
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
openModal() {
|
||||
this.createEditEndpointComponent.openCreateEditTarget(true);
|
||||
this.target = this.initEndpoint;
|
||||
}
|
||||
|
||||
editTargets(targets: Endpoint[]) {
|
||||
if (targets && targets.length === 1) {
|
||||
let target = targets[0];
|
||||
let editable = true;
|
||||
if (!target.id) {
|
||||
return;
|
||||
}
|
||||
let id: number | string = target.id;
|
||||
this.createEditEndpointComponent.openCreateEditTarget(editable, id);
|
||||
}
|
||||
}
|
||||
|
||||
deleteTargets(targets: Endpoint[]) {
|
||||
if (targets && targets.length) {
|
||||
let targetNames: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
targets.forEach(target => {
|
||||
targetNames.push(target.name);
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = target.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
});
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"REPLICATION.DELETION_TITLE_TARGET",
|
||||
"REPLICATION.DELETION_SUMMARY_TARGET",
|
||||
targetNames.join(", ") || "",
|
||||
targets,
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.confirmationDialogComponent.open(deletionMessage);
|
||||
ngOnInit(): void {
|
||||
this.targetName = "";
|
||||
this.retrieve();
|
||||
}
|
||||
}
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (
|
||||
message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
let targetLists: Endpoint[] = message.data;
|
||||
if (targetLists && targetLists.length) {
|
||||
let promiseLists: any[] = [];
|
||||
targetLists.forEach(target => {
|
||||
promiseLists.push(this.delOperate(target.id, target.name));
|
||||
});
|
||||
Promise.all(promiseLists).then(item => {
|
||||
this.selectedRow = [];
|
||||
this.reload(true);
|
||||
this.forceRefreshView(2000);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(id: number | string, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return toPromise<number>(this.endpointService.deleteEndpoint(id))
|
||||
.then(response => {
|
||||
this.translateService.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(
|
||||
this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||
this.translateService.get(
|
||||
"DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED"
|
||||
)
|
||||
).subscribe(res => {
|
||||
findedList = BathInfoChanges(
|
||||
findedList,
|
||||
res[0],
|
||||
false,
|
||||
true,
|
||||
res[1]
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Forcely refresh the view
|
||||
forceRefreshView(duration: number): void {
|
||||
// Reset timer
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
}
|
||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => {
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
this.forceRefreshView(5000);
|
||||
}
|
||||
|
||||
retrieve(): void {
|
||||
this.loading = true;
|
||||
this.selectedRow = [];
|
||||
toPromise<Endpoint[]>(this.endpointService.getEndpoints(this.targetName))
|
||||
.then(targets => {
|
||||
this.targets = targets || [];
|
||||
this.forceRefreshView(1000);
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
doSearchTargets(targetName: string) {
|
||||
this.targetName = targetName;
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
refreshTargets() {
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
reload($event: any) {
|
||||
this.targetName = "";
|
||||
this.retrieve();
|
||||
}
|
||||
|
||||
openModal() {
|
||||
this.createEditEndpointComponent.openCreateEditTarget(true);
|
||||
this.target = this.initEndpoint;
|
||||
}
|
||||
|
||||
editTargets(targets: Endpoint[]) {
|
||||
if (targets && targets.length === 1) {
|
||||
let target = targets[0];
|
||||
let editable = true;
|
||||
if (!target.id) {
|
||||
return;
|
||||
}
|
||||
let id: number | string = target.id;
|
||||
this.createEditEndpointComponent.openCreateEditTarget(editable, id);
|
||||
}
|
||||
}
|
||||
|
||||
deleteTargets(targets: Endpoint[]) {
|
||||
if (targets && targets.length) {
|
||||
let targetNames: string[] = [];
|
||||
targets.forEach(target => {
|
||||
targetNames.push(target.name);
|
||||
});
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
'REPLICATION.DELETION_TITLE_TARGET',
|
||||
'REPLICATION.DELETION_SUMMARY_TARGET',
|
||||
targetNames.join(', ') || '',
|
||||
targets,
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.confirmationDialogComponent.open(deletionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let targetLists: Endpoint[] = message.data;
|
||||
if (targetLists && targetLists.length) {
|
||||
let promiseLists: any[] = [];
|
||||
targetLists.forEach(target => {
|
||||
promiseLists.push(this.delOperate(target));
|
||||
});
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
this.selectedRow = [];
|
||||
this.reload(true);
|
||||
this.forceRefreshView(2000);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(target: Endpoint) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_REGISTRY';
|
||||
operMessage.data.id = target.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = target.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return toPromise<number>(this.endpointService
|
||||
.deleteEndpoint(target.id))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(
|
||||
error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Forcely refresh the view
|
||||
forceRefreshView(duration: number): void {
|
||||
// Reset timer
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
}
|
||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => {
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ import { JOB_LOG_VIEWER_DIRECTIVES } from './job-log-viewer/index';
|
||||
import { PROJECT_POLICY_CONFIG_DIRECTIVES } from './project-policy-config/index';
|
||||
import { HBR_GRIDVIEW_DIRECTIVES } from './gridview/index';
|
||||
import { REPOSITORY_GRIDVIEW_DIRECTIVES } from './repository-gridview/index';
|
||||
import { OPERATION_DIRECTIVES } from './operation/index';
|
||||
import {LABEL_DIRECTIVES} from "./label/index";
|
||||
import {CREATE_EDIT_LABEL_DIRECTIVES} from "./create-edit-label/index";
|
||||
import {LABEL_PIECE_DIRECTIVES} from "./label-piece/index";
|
||||
|
||||
import {
|
||||
SystemInfoService,
|
||||
@ -47,7 +51,7 @@ import {
|
||||
ProjectService,
|
||||
ProjectDefaultService,
|
||||
LabelService,
|
||||
LabelDefaultService
|
||||
LabelDefaultService,
|
||||
} from './service/index';
|
||||
import {
|
||||
ErrorHandler,
|
||||
@ -59,9 +63,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateServiceInitializer } from './i18n/index';
|
||||
import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from './utils';
|
||||
import { ChannelService } from './channel/index';
|
||||
import {LABEL_DIRECTIVES} from "./label/index";
|
||||
import {CREATE_EDIT_LABEL_DIRECTIVES} from "./create-edit-label/index";
|
||||
import {LABEL_PIECE_DIRECTIVES} from "./label-piece/index";
|
||||
import { OperationService } from './operation/operation.service';
|
||||
|
||||
/**
|
||||
* Declare default service configuration; all the endpoints will be defined in
|
||||
@ -182,6 +184,7 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
LABEL_PIECE_DIRECTIVES,
|
||||
HBR_GRIDVIEW_DIRECTIVES,
|
||||
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
||||
OPERATION_DIRECTIVES
|
||||
],
|
||||
exports: [
|
||||
LOG_DIRECTIVES,
|
||||
@ -207,6 +210,7 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
LABEL_PIECE_DIRECTIVES,
|
||||
HBR_GRIDVIEW_DIRECTIVES,
|
||||
REPOSITORY_GRIDVIEW_DIRECTIVES,
|
||||
OPERATION_DIRECTIVES
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
@ -237,7 +241,8 @@ export class HarborLibraryModule {
|
||||
deps: [TranslateServiceInitializer, SERVICE_CONFIG],
|
||||
multi: true
|
||||
},
|
||||
ChannelService
|
||||
ChannelService,
|
||||
OperationService
|
||||
]
|
||||
};
|
||||
}
|
||||
@ -259,7 +264,8 @@ export class HarborLibraryModule {
|
||||
config.jobLogService || { provide: JobLogService, useClass: JobLogDefaultService },
|
||||
config.projectPolicyService || { provide: ProjectService, useClass: ProjectDefaultService },
|
||||
config.labelService || {provide: LabelService, useClass: LabelDefaultService},
|
||||
ChannelService
|
||||
ChannelService,
|
||||
OperationService
|
||||
]
|
||||
};
|
||||
}
|
||||
|
@ -24,3 +24,5 @@ export * from './label/index';
|
||||
export * from './create-edit-label/index';
|
||||
export * from './gridview/index';
|
||||
export * from './repository-gridview/index';
|
||||
export * from './operation/index';
|
||||
export * from './_animations/index';
|
||||
|
@ -37,5 +37,6 @@
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {InlineAlertComponent} from "../inline-alert/inline-alert.component";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
|
||||
import {IServiceConfig, SERVICE_CONFIG} from "../service.config";
|
||||
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
describe('LabelComponent (inline template)', () => {
|
||||
|
||||
@ -79,7 +79,8 @@ describe('LabelComponent (inline template)', () => {
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{provide: LabelService, useClass: LabelDefaultService}
|
||||
{provide: LabelService, useClass: LabelDefaultService},
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -12,174 +12,175 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input
|
||||
Component,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input
|
||||
} from "@angular/core";
|
||||
import { Label } from "../service/interface";
|
||||
import { LabelService } from "../service/label.service";
|
||||
import { toPromise } from "../utils";
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { CreateEditLabelComponent } from "../create-edit-label/create-edit-label.component";
|
||||
import {Label} from "../service/interface";
|
||||
import {LabelService} from "../service/label.service";
|
||||
import {toPromise} from "../utils";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {CreateEditLabelComponent} from "../create-edit-label/create-edit-label.component";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {
|
||||
BatchInfo,
|
||||
BathInfoChanges
|
||||
} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets
|
||||
} from "../shared/shared.const";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
@Component({
|
||||
selector: "hbr-label",
|
||||
templateUrl: "./label.component.html",
|
||||
styleUrls: ["./label.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
selector: "hbr-label",
|
||||
templateUrl: "./label.component.html",
|
||||
styleUrls: ["./label.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class LabelComponent implements OnInit {
|
||||
timerHandler: any;
|
||||
loading: boolean;
|
||||
targets: Label[];
|
||||
targetName: string;
|
||||
timerHandler: any;
|
||||
loading: boolean;
|
||||
targets: Label[];
|
||||
targetName: string;
|
||||
selectedRow: Label[] = [];
|
||||
|
||||
selectedRow: Label[] = [];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
@Input() scope: string;
|
||||
@Input() projectId = 0;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
|
||||
@Input() scope: string;
|
||||
@Input() projectId = 0;
|
||||
@Input() hasProjectAdminRole: boolean;
|
||||
@ViewChild(CreateEditLabelComponent)
|
||||
createEditLabel: CreateEditLabelComponent;
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
|
||||
@ViewChild(CreateEditLabelComponent)
|
||||
createEditLabel: CreateEditLabelComponent;
|
||||
@ViewChild("confirmationDialog")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
constructor(
|
||||
private labelService: LabelService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private ref: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
|
||||
retrieve(scope: string, name = "") {
|
||||
this.loading = true;
|
||||
this.selectedRow = [];
|
||||
this.targetName = "";
|
||||
toPromise<Label[]>(this.labelService.getLabels(scope, this.projectId, name))
|
||||
.then(targets => {
|
||||
this.targets = targets || [];
|
||||
this.loading = false;
|
||||
this.forceRefreshView(2000);
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
openModal(): void {
|
||||
this.createEditLabel.openModal();
|
||||
}
|
||||
|
||||
reload(): void {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
|
||||
doSearchTargets(targetName: string) {
|
||||
this.retrieve(this.scope, targetName);
|
||||
}
|
||||
|
||||
refreshTargets() {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
// this.forceRefreshView(5000);
|
||||
}
|
||||
|
||||
editLabel(label: Label[]): void {
|
||||
this.createEditLabel.editModel(label[0].id, label);
|
||||
}
|
||||
|
||||
deleteLabels(targets: Label[]): void {
|
||||
if (targets && targets.length) {
|
||||
let targetNames: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
targets.forEach(target => {
|
||||
targetNames.push(target.name);
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = target.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
});
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"LABEL.DELETION_TITLE_TARGET",
|
||||
"LABEL.DELETION_SUMMARY_TARGET",
|
||||
targetNames.join(", ") || "",
|
||||
targets,
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.confirmationDialogComponent.open(deletionMessage);
|
||||
constructor(private labelService: LabelService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private translateService: TranslateService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
}
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (
|
||||
message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
let targetLists: Label[] = message.data;
|
||||
if (targetLists && targetLists.length) {
|
||||
let promiseLists: any[] = [];
|
||||
targetLists.forEach(target => {
|
||||
promiseLists.push(this.delOperate(target.id, target.name));
|
||||
});
|
||||
Promise.all(promiseLists).then(item => {
|
||||
this.selectedRow = [];
|
||||
this.retrieve(this.scope);
|
||||
});
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(id: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return toPromise<number>(this.labelService.deleteLabel(id))
|
||||
.then(response => {
|
||||
this.translateService.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Forcely refresh the view
|
||||
forceRefreshView(duration: number): void {
|
||||
// Reset timer
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
retrieve(scope: string, name = "") {
|
||||
this.loading = true;
|
||||
this.selectedRow = [];
|
||||
this.targetName = "";
|
||||
toPromise<Label[]>(this.labelService.getLabels(scope, this.projectId, name))
|
||||
.then(targets => {
|
||||
this.targets = targets || [];
|
||||
this.loading = false;
|
||||
this.forceRefreshView(2000);
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
openModal(): void {
|
||||
this.createEditLabel.openModal();
|
||||
}
|
||||
|
||||
reload(): void {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
|
||||
doSearchTargets(targetName: string) {
|
||||
this.retrieve(this.scope, targetName);
|
||||
}
|
||||
|
||||
refreshTargets() {
|
||||
this.retrieve(this.scope);
|
||||
}
|
||||
|
||||
selectedChange(): void {
|
||||
// this.forceRefreshView(5000);
|
||||
}
|
||||
|
||||
editLabel(label: Label[]): void {
|
||||
this.createEditLabel.editModel(label[0].id, label);
|
||||
}
|
||||
|
||||
deleteLabels(targets: Label[]): void {
|
||||
if (targets && targets.length) {
|
||||
let targetNames: string[] = [];
|
||||
targets.forEach(target => {
|
||||
targetNames.push(target.name);
|
||||
});
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
'LABEL.DELETION_TITLE_TARGET',
|
||||
'LABEL.DELETION_SUMMARY_TARGET',
|
||||
targetNames.join(', ') || '',
|
||||
targets,
|
||||
ConfirmationTargets.TARGET,
|
||||
ConfirmationButtons.DELETE_CANCEL);
|
||||
this.confirmationDialogComponent.open(deletionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.TARGET &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
let targetLists: Label[] = message.data;
|
||||
if (targetLists && targetLists.length) {
|
||||
let promiseLists: any[] = [];
|
||||
targetLists.forEach(target => {
|
||||
promiseLists.push(this.delOperate(target));
|
||||
});
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
this.selectedRow = [];
|
||||
this.retrieve(this.scope);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(target: Label) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_LABEL';
|
||||
operMessage.data.id = target.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = target.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return toPromise<number>(this.labelService
|
||||
.deleteLabel(target.id))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(
|
||||
error => {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Forcely refresh the view
|
||||
forceRefreshView(duration: number): void {
|
||||
// Reset timer
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
}
|
||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => {
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => {
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
<confirmation-dialog #deletionConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||
</div>
|
||||
<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||
</div>
|
||||
|
@ -13,6 +13,7 @@ import { ReplicationRule } from '../service/interface';
|
||||
import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||
import { OperationService } from "../operation/operation.service";
|
||||
|
||||
describe('ListReplicationRuleComponent (inline template)', () => {
|
||||
|
||||
@ -124,7 +125,8 @@ describe('ListReplicationRuleComponent (inline template)', () => {
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: ReplicationService, useClass: ReplicationDefaultService }
|
||||
{ provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -12,283 +12,260 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
OnInit,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
SimpleChanges
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
OnInit,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
SimpleChanges
|
||||
} from "@angular/core";
|
||||
import { Observable } from "rxjs/Observable";
|
||||
import "rxjs/add/observable/forkJoin";
|
||||
import { Comparator } from "clarity-angular";
|
||||
import { TranslateService } from "@ngx-translate/core";
|
||||
|
||||
import { ReplicationService } from "../service/replication.service";
|
||||
import {ReplicationService} from "../service/replication.service";
|
||||
import {
|
||||
ReplicationJob,
|
||||
ReplicationJobItem,
|
||||
ReplicationRule
|
||||
ReplicationJob,
|
||||
ReplicationJobItem,
|
||||
ReplicationRule
|
||||
} from "../service/interface";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import {ConfirmationMessage} from "../confirmation-dialog/confirmation-message";
|
||||
import {ConfirmationAcknowledgement} from "../confirmation-dialog/confirmation-state-message";
|
||||
import {
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
ConfirmationButtons
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
ConfirmationButtons
|
||||
} from "../shared/shared.const";
|
||||
import { ErrorHandler } from "../error-handler/error-handler";
|
||||
import { toPromise, CustomComparator } from "../utils";
|
||||
import {
|
||||
BatchInfo,
|
||||
BathInfoChanges
|
||||
} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import {ErrorHandler} from "../error-handler/error-handler";
|
||||
import {toPromise, CustomComparator} from "../utils";
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: "hbr-list-replication-rule",
|
||||
templateUrl: "./list-replication-rule.component.html",
|
||||
styleUrls: ["./list-replication-rule.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
selector: "hbr-list-replication-rule",
|
||||
templateUrl: "./list-replication-rule.component.html",
|
||||
styleUrls: ["./list-replication-rule.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
||||
nullTime = "0001-01-01T00:00:00Z";
|
||||
nullTime = "0001-01-01T00:00:00Z";
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() selectedId: number | string;
|
||||
@Input() withReplicationJob: boolean;
|
||||
@Input() projectId: number;
|
||||
@Input() isSystemAdmin: boolean;
|
||||
@Input() selectedId: number | string;
|
||||
@Input() withReplicationJob: boolean;
|
||||
|
||||
@Input() loading = false;
|
||||
@Input() loading = false;
|
||||
|
||||
@Output() reload = new EventEmitter<boolean>();
|
||||
@Output() selectOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() editOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() toggleOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() hideJobs = new EventEmitter<any>();
|
||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||
@Output() openNewRule = new EventEmitter<any>();
|
||||
@Output() replicateManual = new EventEmitter<ReplicationRule[]>();
|
||||
@Output() reload = new EventEmitter<boolean>();
|
||||
@Output() selectOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() editOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() toggleOne = new EventEmitter<ReplicationRule>();
|
||||
@Output() hideJobs = new EventEmitter<any>();
|
||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||
@Output() openNewRule = new EventEmitter<any>();
|
||||
@Output() replicateManual = new EventEmitter<ReplicationRule[]>();
|
||||
|
||||
projectScope = false;
|
||||
projectScope = false;
|
||||
|
||||
rules: ReplicationRule[];
|
||||
changedRules: ReplicationRule[];
|
||||
ruleName: string;
|
||||
canDeleteRule: boolean;
|
||||
rules: ReplicationRule[];
|
||||
changedRules: ReplicationRule[];
|
||||
ruleName: string;
|
||||
canDeleteRule: boolean;
|
||||
|
||||
selectedRow: ReplicationRule;
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
selectedRow: ReplicationRule;
|
||||
|
||||
@ViewChild("toggleConfirmDialog")
|
||||
toggleConfirmDialog: ConfirmationDialogComponent;
|
||||
@ViewChild("toggleConfirmDialog")
|
||||
toggleConfirmDialog: ConfirmationDialogComponent;
|
||||
|
||||
@ViewChild("deletionConfirmDialog")
|
||||
deletionConfirmDialog: ConfirmationDialogComponent;
|
||||
@ViewChild("deletionConfirmDialog")
|
||||
deletionConfirmDialog: ConfirmationDialogComponent;
|
||||
|
||||
startTimeComparator: Comparator<ReplicationRule> = new CustomComparator<
|
||||
ReplicationRule
|
||||
>("start_time", "date");
|
||||
enabledComparator: Comparator<ReplicationRule> = new CustomComparator<
|
||||
ReplicationRule
|
||||
>("enabled", "number");
|
||||
startTimeComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("start_time", "date");
|
||||
enabledComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("enabled", "number");
|
||||
|
||||
constructor(
|
||||
private replicationService: ReplicationService,
|
||||
private translateService: TranslateService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private ref: ChangeDetectorRef
|
||||
) {
|
||||
setInterval(() => ref.markForCheck(), 500);
|
||||
}
|
||||
|
||||
trancatedDescription(desc: string): string {
|
||||
if (desc.length > 35) {
|
||||
return desc.substr(0, 35);
|
||||
} else {
|
||||
return desc;
|
||||
constructor(private replicationService: ReplicationService,
|
||||
private translateService: TranslateService,
|
||||
private errorHandler: ErrorHandler,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
setInterval(() => ref.markForCheck(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// Global scope
|
||||
if (!this.projectScope) {
|
||||
this.retrieveRules();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
let proIdChange: SimpleChange = changes["projectId"];
|
||||
if (proIdChange) {
|
||||
if (proIdChange.currentValue !== proIdChange.previousValue) {
|
||||
if (proIdChange.currentValue) {
|
||||
this.projectId = proIdChange.currentValue;
|
||||
this.projectScope = true; // Scope is project, not global list
|
||||
// Initially load the replication rule data
|
||||
this.retrieveRules();
|
||||
trancatedDescription(desc: string): string {
|
||||
if (desc.length > 35) {
|
||||
return desc.substr(0, 35);
|
||||
} else {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
retrieveRules(ruleName = ""): void {
|
||||
this.loading = true;
|
||||
/*this.selectedRow = null;*/
|
||||
toPromise<ReplicationRule[]>(
|
||||
this.replicationService.getReplicationRules(this.projectId, ruleName)
|
||||
)
|
||||
.then(rules => {
|
||||
this.rules = rules || [];
|
||||
// job list hidden
|
||||
this.hideJobs.emit();
|
||||
this.changedRules = this.rules;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
replicateRule(rules: ReplicationRule[]): void {
|
||||
this.replicateManual.emit(rules);
|
||||
}
|
||||
|
||||
deletionConfirm(message: ConfirmationAcknowledgement) {
|
||||
if (
|
||||
message &&
|
||||
message.source === ConfirmationTargets.POLICY &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
this.deleteOpe(message.data);
|
||||
ngOnInit(): void {
|
||||
// Global scope
|
||||
if (!this.projectScope) {
|
||||
this.retrieveRules();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectRule(rule: ReplicationRule): void {
|
||||
this.selectedId = rule.id || "";
|
||||
this.selectOne.emit(rule);
|
||||
}
|
||||
|
||||
redirectTo(rule: ReplicationRule): void {
|
||||
this.redirect.emit(rule);
|
||||
}
|
||||
|
||||
openModal(): void {
|
||||
this.openNewRule.emit();
|
||||
}
|
||||
|
||||
editRule(rule: ReplicationRule) {
|
||||
this.editOne.emit(rule);
|
||||
}
|
||||
|
||||
jobList(id: string | number): Promise<void> {
|
||||
let ruleData: ReplicationJobItem[];
|
||||
this.canDeleteRule = true;
|
||||
let count = 0;
|
||||
return toPromise<ReplicationJob>(this.replicationService.getJobs(id))
|
||||
.then(response => {
|
||||
ruleData = response.data;
|
||||
if (ruleData.length) {
|
||||
ruleData.forEach(job => {
|
||||
if (
|
||||
job.status === "pending" ||
|
||||
job.status === "running" ||
|
||||
job.status === "retrying"
|
||||
) {
|
||||
count++;
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
let proIdChange: SimpleChange = changes["projectId"];
|
||||
if (proIdChange) {
|
||||
if (proIdChange.currentValue !== proIdChange.previousValue) {
|
||||
if (proIdChange.currentValue) {
|
||||
this.projectId = proIdChange.currentValue;
|
||||
this.projectScope = true; // Scope is project, not global list
|
||||
// Initially load the replication rule data
|
||||
this.retrieveRules();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.canDeleteRule = count > 0 ? false : true;
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
deleteRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
this.batchDelectionInfos = [];
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = rule.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"REPLICATION.DELETION_TITLE",
|
||||
"REPLICATION.DELETION_SUMMARY",
|
||||
rule.name,
|
||||
rule,
|
||||
ConfirmationTargets.POLICY,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.deletionConfirmDialog.open(deletionMessage);
|
||||
}
|
||||
}
|
||||
deleteOpe(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
let promiseLists: any[] = [];
|
||||
Promise.all([this.jobList(rule.id)]).then(items => {
|
||||
|
||||
retrieveRules(ruleName = ""): void {
|
||||
this.loading = true;
|
||||
/*this.selectedRow = null;*/
|
||||
toPromise<ReplicationRule[]>(
|
||||
this.replicationService.getReplicationRules(this.projectId, ruleName)
|
||||
)
|
||||
.then(rules => {
|
||||
this.rules = rules || [];
|
||||
// job list hidden
|
||||
this.hideJobs.emit();
|
||||
this.changedRules = this.rules;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
replicateRule(rules: ReplicationRule[]): void {
|
||||
this.replicateManual.emit(rules);
|
||||
}
|
||||
|
||||
deletionConfirm(message: ConfirmationAcknowledgement) {
|
||||
if (
|
||||
message &&
|
||||
message.source === ConfirmationTargets.POLICY &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
this.deleteOpe(message.data);
|
||||
}
|
||||
}
|
||||
|
||||
selectRule(rule: ReplicationRule): void {
|
||||
this.selectedId = rule.id || "";
|
||||
this.selectOne.emit(rule);
|
||||
}
|
||||
|
||||
redirectTo(rule: ReplicationRule): void {
|
||||
this.redirect.emit(rule);
|
||||
}
|
||||
|
||||
openModal(): void {
|
||||
this.openNewRule.emit();
|
||||
}
|
||||
|
||||
editRule(rule: ReplicationRule) {
|
||||
this.editOne.emit(rule);
|
||||
}
|
||||
|
||||
jobList(id: string | number): Promise<void> {
|
||||
let ruleData: ReplicationJobItem[];
|
||||
this.canDeleteRule = true;
|
||||
let count = 0;
|
||||
return toPromise<ReplicationJob>(this.replicationService.getJobs(id))
|
||||
.then(response => {
|
||||
ruleData = response.data;
|
||||
if (ruleData.length) {
|
||||
ruleData.forEach(job => {
|
||||
if (
|
||||
job.status === "pending" ||
|
||||
job.status === "running" ||
|
||||
job.status === "retrying"
|
||||
) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.canDeleteRule = count > 0 ? false : true;
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
deleteRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"REPLICATION.DELETION_TITLE",
|
||||
"REPLICATION.DELETION_SUMMARY",
|
||||
rule.name,
|
||||
rule,
|
||||
ConfirmationTargets.POLICY,
|
||||
ConfirmationButtons.DELETE_CANCEL
|
||||
);
|
||||
this.deletionConfirmDialog.open(deletionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
deleteOpe(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
let promiseLists: any[] = [];
|
||||
Promise.all([this.jobList(rule.id)]).then(items => {
|
||||
promiseLists.push(this.delOperate(rule));
|
||||
|
||||
Promise.all(promiseLists).then(item => {
|
||||
this.selectedRow = null;
|
||||
this.reload.emit(true);
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 200);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(rule: ReplicationRule) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_REPLICATION';
|
||||
operMessage.data.id = +rule.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = rule.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
if (!this.canDeleteRule) {
|
||||
let findedList = this.batchDelectionInfos.find(
|
||||
data => data.name === rule.name
|
||||
);
|
||||
Observable.forkJoin(
|
||||
this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||
this.translateService.get("REPLICATION.DELETION_SUMMARY_FAILURE")
|
||||
).subscribe(res => {
|
||||
findedList = BathInfoChanges(
|
||||
findedList,
|
||||
res[0],
|
||||
false,
|
||||
true,
|
||||
res[1]
|
||||
);
|
||||
});
|
||||
} else {
|
||||
promiseLists.push(this.delOperate(+rule.id, rule.name));
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.DELETION_SUMMARY_FAILURE')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
Promise.all(promiseLists).then(item => {
|
||||
this.selectedRow = null;
|
||||
this.reload.emit(true);
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 200);
|
||||
setTimeout(() => clearInterval(hnd), 2000);
|
||||
});
|
||||
});
|
||||
return toPromise<any>(this.replicationService
|
||||
.deleteReplicationRule(+rule.id))
|
||||
.then(() => {
|
||||
this.translateService.get('BATCH.DELETED_SUCCESS')
|
||||
.subscribe(res => operateChanges(operMessage, OperationState.success));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')).subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(ruleId: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return toPromise<any>(this.replicationService.deleteReplicationRule(ruleId))
|
||||
.then(() => {
|
||||
this.translateService
|
||||
.get("BATCH.DELETED_SUCCESS")
|
||||
.subscribe(res => (findedList = BathInfoChanges(findedList, res)));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(
|
||||
this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||
this.translateService.get(
|
||||
"REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED"
|
||||
)
|
||||
).subscribe(res => {
|
||||
findedList = BathInfoChanges(
|
||||
findedList,
|
||||
res[0],
|
||||
false,
|
||||
true,
|
||||
res[1]
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
13
src/ui_ng/lib/src/operation/index.ts
Normal file
13
src/ui_ng/lib/src/operation/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Created by pengf on 5/11/2018.
|
||||
*/
|
||||
|
||||
import {Type} from "@angular/core";
|
||||
import {OperationComponent} from "./operation.component";
|
||||
|
||||
export * from "./operation.component";
|
||||
export * from './operate';
|
||||
export * from './operation.service';
|
||||
export const OPERATION_DIRECTIVES: Type<any>[] = [
|
||||
OperationComponent
|
||||
];
|
30
src/ui_ng/lib/src/operation/operate.ts
Normal file
30
src/ui_ng/lib/src/operation/operate.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export class OperateInfo {
|
||||
name: string;
|
||||
state: string;
|
||||
data: {[key: string]: string| number};
|
||||
timeStamp: number;
|
||||
timeDiff: string;
|
||||
constructor() {
|
||||
this.name = '';
|
||||
this.state = '';
|
||||
this.data = {id: -1, name: '', errorInf: ''};
|
||||
this.timeStamp = 0;
|
||||
this.timeDiff = 'less 1 minute';
|
||||
}
|
||||
}
|
||||
|
||||
export function operateChanges(list: OperateInfo, state?: string, errorInfo?: string, timeStamp?: 0) {
|
||||
list.state = state;
|
||||
list.data.errorInf = errorInfo;
|
||||
list.timeStamp = new Date().getTime();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
export const OperationState = {
|
||||
progressing: 'progressing',
|
||||
success : 'success',
|
||||
failure : 'failure',
|
||||
interrupt: 'interrupt'
|
||||
};
|
||||
|
43
src/ui_ng/lib/src/operation/operation.component.css
Normal file
43
src/ui_ng/lib/src/operation/operation.component.css
Normal file
@ -0,0 +1,43 @@
|
||||
/* side form */
|
||||
.side-form {
|
||||
position: absolute;
|
||||
width: 325px;
|
||||
height: 100%;
|
||||
left:28px;
|
||||
padding-top: 20px;
|
||||
background: #fff;
|
||||
border-left: 1px solid #e0e0e0;
|
||||
}
|
||||
.eventInfo {display: flex; justify-content: flex-start; align-content: flex-start;
|
||||
padding: 8px 5px 8px 10px; border-bottom: 1px solid #ccc;}
|
||||
.iconsArea{ flex-shrink: 1;}
|
||||
.infoArea{ margin-left: 10px; width: 270px;}
|
||||
.eventName{display: block; margin-bottom: -5px;font-size: 16px; color: rgb(11, 127, 189); }
|
||||
.eventErrorInf {display:block; font-size: 12px;color:red;line-height: .6rem;}
|
||||
.eventTarget{display: inline-flex; width: 172px; font-size: 12px; flex-shrink:1; overflow: hidden; text-overflow: ellipsis;white-space: nowrap;}
|
||||
.eventTime{ float: right; font-size: 12px;}
|
||||
:host >>> .nav{padding-left: 38px;}
|
||||
.operDiv{position: fixed; top: 60px; right: 0; z-index:100}
|
||||
.toolBar{
|
||||
float: left;border-top: 1px solid #ccc;
|
||||
transform: rotate(-90deg);
|
||||
margin-top: 10px;
|
||||
padding: 2px 4px;
|
||||
width: 86px;
|
||||
height: 84px;
|
||||
background-color: white;
|
||||
letter-spacing: 1.2px;
|
||||
font-weight: 500;
|
||||
box-shadow: -2px -1px 3px #bebbbb;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
.freshIcon{float: right; margin-right: 20px; margin-top: -10px;cursor: pointer;}
|
||||
#contentFailed, #contentAll, #contentRun{
|
||||
position: absolute;
|
||||
top: 95px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
64
src/ui_ng/lib/src/operation/operation.component.html
Normal file
64
src/ui_ng/lib/src/operation/operation.component.html
Normal file
@ -0,0 +1,64 @@
|
||||
<div class="operDiv" [@SlideInOutAnimation]="animationState">
|
||||
<a class="toolBar" (click)="slideOut()">{{'OPERATION.EVENT_LOG' | translate}}<!--<clr-icon shape="angle-double" style="transform: rotate(90deg)"></clr-icon>--></a>
|
||||
<div class="side-form">
|
||||
<clr-icon shape="refresh" class="freshIcon" (click)="TabEvent()"></clr-icon>
|
||||
<h3 class="custom-h2" style="margin-left: 34px;">{{'OPERATION.LOCAL_EVENT' | translate}}</h3>
|
||||
<div style="margin-top: 10px;">
|
||||
<clr-tabs>
|
||||
<clr-tab>
|
||||
<button clrTabLink id="link1" (click)="TabEvent()">{{'OPERATION.ALL' | translate}}</button>
|
||||
<clr-tab-content id="contentAll" *clrIfActive="true">
|
||||
<div class="eventInfo" *ngFor="let list of resultLists">
|
||||
<div class="iconsArea">
|
||||
<i class="spinner spinner-inline spinner-pos" [hidden]="list.state != 'progressing'"></i>
|
||||
<clr-icon [hidden]="list.state != 'success'" size="18" shape="success-standard" style="color: green"></clr-icon>
|
||||
<clr-icon [hidden]="list.state != 'failure'" size="18" shape="error-standard" style="color: red"></clr-icon>
|
||||
<clr-icon [hidden]="list.state != 'interrupt'" size="18" shape="unlink" style="color: orange"></clr-icon>
|
||||
</div>
|
||||
<div class="infoArea">
|
||||
<label class="eventName" (click)="toggleTitle(spanErrorInfo)">{{list.name | translate}}</label>
|
||||
<span class="eventTarget">{{list.data.name}}</span><span class="eventTime">{{list.timeDiff | translate}}</span>
|
||||
<span #spanErrorInfo class="eventErrorInf" style="display: none;">{{list.data.errorInf}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
<clr-tab>
|
||||
<button clrTabLink (click)="TabEvent()">{{'OPERATION.RUNNING' | translate}}</button>
|
||||
<clr-tab-content id="contentRun" *clrIfActive>
|
||||
<div class="eventInfo" *ngFor="let list of runningLists">
|
||||
<div class="iconsArea">
|
||||
<i class="spinner spinner-inline spinner-pos" [hidden]="list.state != 'progressing'"></i>
|
||||
<clr-icon [hidden]="list.state != 'success'" size="18" shape="success-standard" style="color: green"></clr-icon>
|
||||
<clr-icon [hidden]="list.state != 'failure'" size="18" shape="error-standard" style="color: red"></clr-icon>
|
||||
</div>
|
||||
<div class="infoArea">
|
||||
<label class="eventName" (click)="toggleTitle(spanErrorInfo)">{{list.name | translate}}</label>
|
||||
<span class="eventTarget">{{list.data.name}}</span><span class="eventTime">{{list.timeDiff | translate}}</span>
|
||||
<span #spanErrorInfo class="eventErrorInf" style="display: none;">{{list.data.errorInf}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
<clr-tab>
|
||||
<button clrTabLink (click)="TabEvent()">{{'OPERATION.FAILED' | translate}}</button>
|
||||
<clr-tab-content id="contentFailed" *clrIfActive>
|
||||
<div class="eventInfo" *ngFor="let list of failLists">
|
||||
<div class="iconsArea">
|
||||
<i class="spinner spinner-inline spinner-pos" [hidden]="list.state != 'progressing'"></i>
|
||||
<clr-icon [hidden]="list.state != 'success'" size="18" shape="success-standard" style="color: green"></clr-icon>
|
||||
<clr-icon [hidden]="list.state != 'failure'" size="18" shape="error-standard" style="color: red"></clr-icon>
|
||||
</div>
|
||||
<div class="infoArea">
|
||||
<label class="eventName" (click)="toggleTitle(spanErrorInfo)">{{list.name | translate}}</label>
|
||||
<span class="eventTarget">{{list.data.name}}</span><span class="eventTime">{{list.timeDiff | translate}}</span>
|
||||
<span #spanErrorInfo class="eventErrorInf" style="display: none;">{{list.data.errorInf}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</clr-tab-content>
|
||||
</clr-tab>
|
||||
</clr-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
144
src/ui_ng/lib/src/operation/operation.component.ts
Normal file
144
src/ui_ng/lib/src/operation/operation.component.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import {Component, OnInit, OnDestroy, HostListener} from '@angular/core';
|
||||
import {OperationService} from "./operation.service";
|
||||
import {Subscription} from "rxjs/Subscription";
|
||||
import {OperateInfo, OperationState} from "./operate";
|
||||
import {SlideInOutAnimation} from "../_animations/slide-in-out.animation";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-operation-model',
|
||||
templateUrl: './operation.component.html',
|
||||
styleUrls: ['./operation.component.css'],
|
||||
animations: [SlideInOutAnimation],
|
||||
})
|
||||
export class OperationComponent implements OnInit, OnDestroy {
|
||||
batchInfoSubscription: Subscription;
|
||||
resultLists: OperateInfo[] = [];
|
||||
animationState = "out";
|
||||
|
||||
@HostListener('window:beforeunload', ['$event'])
|
||||
beforeUnloadHander(event) {
|
||||
// storage to localStorage
|
||||
let timp = new Date().getTime();
|
||||
localStorage.setItem('operaion', JSON.stringify({timp: timp, data: this.resultLists}));
|
||||
}
|
||||
|
||||
constructor(
|
||||
private operationService: OperationService,
|
||||
private translate: TranslateService) {
|
||||
|
||||
this.batchInfoSubscription = operationService.operationInfo$.subscribe(data => {
|
||||
// this.resultLists = data;
|
||||
this.openSlide();
|
||||
if (data) {
|
||||
if (this.resultLists.length >= 50) {
|
||||
this.resultLists.splice(49, this.resultLists.length - 49);
|
||||
}
|
||||
this.resultLists.unshift(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get runningLists(): OperateInfo[] {
|
||||
let runningList: OperateInfo[] = [];
|
||||
this.resultLists.forEach(data => {
|
||||
if (data.state === 'progressing') {
|
||||
runningList.push(data);
|
||||
}
|
||||
});
|
||||
return runningList;
|
||||
}
|
||||
|
||||
public get failLists(): OperateInfo[] {
|
||||
let failedList: OperateInfo[] = [];
|
||||
this.resultLists.forEach(data => {
|
||||
if (data.state === 'failure') {
|
||||
failedList.push(data);
|
||||
}
|
||||
});
|
||||
return failedList;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
let requestCookie = localStorage.getItem('operaion');
|
||||
if (requestCookie) {
|
||||
let operInfors: any = JSON.parse(requestCookie);
|
||||
if (operInfors) {
|
||||
if ((new Date().getTime() - operInfors.timp) > 1000 * 60 * 60 * 24) {
|
||||
localStorage.removeItem('operaion');
|
||||
}else {
|
||||
if (operInfors.data) {
|
||||
operInfors.data.forEach(operInfo => {
|
||||
if (operInfo.state === OperationState.progressing) {
|
||||
operInfo.state = OperationState.interrupt;
|
||||
operInfo.data.errorInf = 'operation been interrupted';
|
||||
}
|
||||
});
|
||||
this.resultLists = operInfors.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
if (this.batchInfoSubscription) {
|
||||
this.batchInfoSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
toggleTitle(errorSpan: any) {
|
||||
errorSpan.style.display = (errorSpan.style.display === 'none') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
slideOut(): void {
|
||||
this.animationState = this.animationState === 'out' ? 'in' : 'out';
|
||||
}
|
||||
|
||||
openSlide(): void {
|
||||
this.animationState = 'in';
|
||||
}
|
||||
|
||||
|
||||
TabEvent(): void {
|
||||
let timp: any;
|
||||
this.resultLists.forEach(data => {
|
||||
timp = new Date().getTime() - +data.timeStamp;
|
||||
data.timeDiff = this.calculateTime(timp);
|
||||
});
|
||||
}
|
||||
|
||||
calculateTime(timp: number) {
|
||||
let dist = Math.floor(timp / 1000 / 60); // change to minute;
|
||||
if (dist > 0 && dist < 60) {
|
||||
return Math.floor(dist) + ' minute(s) ago';
|
||||
}else if (dist >= 60 && Math.floor(dist / 60) < 24) {
|
||||
return Math.floor(dist / 60) + ' hour(s) ago';
|
||||
} else if (Math.floor(dist / 60) >= 24) {
|
||||
return Math.floor(dist / 60 / 24) + ' day ago';
|
||||
} else {
|
||||
return 'less 1 minute';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*calculateTime(timp: number) {
|
||||
let dist = Math.floor(timp / 1000 / 60); // change to minute;
|
||||
if (dist > 0 && dist < 60) {
|
||||
return this.translateTime('OPERATION.MINUTE_AGO', Math.floor(dist));
|
||||
}else if (dist > 60 && Math.floor(dist / 60) < 24) {
|
||||
return this.translateTime('OPERATION.HOUR_AGO', Math.floor(dist / 60));
|
||||
} else if (Math.floor(dist / 60) >= 24 && Math.floor(dist / 60) <= 48) {
|
||||
return this.translateTime('OPERATION.DAY_AGO', Math.floor(dist / 60 / 24));
|
||||
} else {
|
||||
return this.translateTime('OPERATION.SECOND_AGO');
|
||||
}
|
||||
}*/
|
||||
|
||||
translateTime(tim: string, param?: number) {
|
||||
this.translate.get(tim, { 'param': param }).subscribe((res: string) => {
|
||||
return res;
|
||||
});
|
||||
}
|
||||
}
|
15
src/ui_ng/lib/src/operation/operation.service.ts
Normal file
15
src/ui_ng/lib/src/operation/operation.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import {OperateInfo} from "./operate";
|
||||
|
||||
@Injectable()
|
||||
export class OperationService {
|
||||
subjects: Subject<any> = null;
|
||||
|
||||
operationInfoSource = new Subject<OperateInfo>();
|
||||
operationInfo$ = this.operationInfoSource.asObservable();
|
||||
|
||||
publishInfo(data: OperateInfo): void {
|
||||
this.operationInfoSource.next(data);
|
||||
}
|
||||
}
|
@ -73,5 +73,6 @@
|
||||
</div>
|
||||
<job-log-viewer #replicationLogViewer></job-log-viewer>
|
||||
<hbr-create-edit-rule *ngIf="isSystemAdmin" [projectId]="projectId" [projectName]="projectName" (goToRegistry)="goRegistry()" (reload)="reloadRules($event)"></hbr-create-edit-rule>
|
||||
<confirmation-dialog #replicationConfirmDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmReplication($event)"></confirmation-dialog>
|
||||
</div>
|
||||
<confirmation-dialog #replicationConfirmDialog (confirmAction)="confirmReplication($event)"></confirmation-dialog>
|
||||
</div>
|
||||
|
||||
|
@ -20,6 +20,7 @@ import { EndpointService, EndpointDefaultService } from '../service/endpoint.ser
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||
import { JobLogService, JobLogDefaultService, ReplicationJobItem } from '../service/index';
|
||||
import {ProjectDefaultService, ProjectService} from "../service/project.service";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
describe('Replication Component (inline template)', () => {
|
||||
|
||||
@ -231,7 +232,8 @@ describe('Replication Component (inline template)', () => {
|
||||
{ provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||
{ provide: EndpointService, useClass: EndpointDefaultService },
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: JobLogService, useClass: JobLogDefaultService }
|
||||
{ provide: JobLogService, useClass: JobLogDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -56,12 +56,10 @@ import {
|
||||
ConfirmationState
|
||||
} from "../shared/shared.const";
|
||||
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message";
|
||||
import {
|
||||
BatchInfo,
|
||||
BathInfoChanges
|
||||
} from "../confirmation-dialog/confirmation-batch-message";
|
||||
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
|
||||
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message";
|
||||
import {operateChanges, OperationState, OperateInfo} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
const ruleStatus: { [key: string]: any } = [
|
||||
{ key: "all", description: "REPLICATION.ALL_STATUS" },
|
||||
@ -130,7 +128,6 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
hiddenJobList = true;
|
||||
|
||||
jobs: ReplicationJobItem[];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
|
||||
toggleJobSearchOption = optionalSearch;
|
||||
currentJobSearchOption: number;
|
||||
@ -165,8 +162,8 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private replicationService: ReplicationService,
|
||||
private translateService: TranslateService
|
||||
) {}
|
||||
private operationService: OperationService,
|
||||
private translateService: TranslateService) {}
|
||||
|
||||
public get showPaginationIndex(): boolean {
|
||||
return this.totalCount > 0;
|
||||
@ -307,10 +304,6 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
replicateManualRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
this.batchDelectionInfos = [];
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = rule.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
let replicationMessage = new ConfirmationMessage(
|
||||
"REPLICATION.REPLICATION_TITLE",
|
||||
"REPLICATION.REPLICATION_SUMMARY",
|
||||
@ -332,43 +325,37 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
let rule: ReplicationRule = message.data;
|
||||
|
||||
if (rule) {
|
||||
Promise.all([this.replicationOperate(+rule.id, rule.name)]).then(
|
||||
item => {
|
||||
this.selectOneRule(rule);
|
||||
}
|
||||
);
|
||||
Promise.all([this.replicationOperate(rule)]).then((item) => {
|
||||
this.selectOneRule(rule);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replicationOperate(ruleId: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
replicationOperate(rule: ReplicationRule) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.REPLICATION';
|
||||
operMessage.data.id = rule.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = rule.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return toPromise<any>(this.replicationService.replicateRule(ruleId))
|
||||
.then(response => {
|
||||
this.translateService
|
||||
.get("BATCH.REPLICATE_SUCCESS")
|
||||
.subscribe(res => (findedList = BathInfoChanges(findedList, res)));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(
|
||||
this.translateService.get("BATCH.REPLICATE_FAILURE"),
|
||||
this.translateService.get("REPLICATION.REPLICATE_SUMMARY_FAILURE")
|
||||
).subscribe(function(res) {
|
||||
findedList = BathInfoChanges(
|
||||
findedList,
|
||||
res[0],
|
||||
false,
|
||||
true,
|
||||
res[1]
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.translateService
|
||||
.get("BATCH.REPLICATE_FAILURE")
|
||||
.subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
return toPromise<any>(this.replicationService.replicateRule(+rule.id))
|
||||
.then(response => {
|
||||
this.translateService.get('BATCH.REPLICATE_SUCCESS')
|
||||
.subscribe(res => operateChanges(operMessage, OperationState.success));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.REPLICATE_FAILURE'),
|
||||
this.translateService.get('REPLICATION.REPLICATE_SUMMARY_FAILURE'))
|
||||
.subscribe(function (res) {
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translateService.get('BATCH.REPLICATE_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -17,33 +17,36 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||
<clr-dg-action-bar>
|
||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="provisionItemEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DEPLOY' | translate}}</button>
|
||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="itemAddInfoEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.ADDITIONAL_INFO' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteRepos(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="watchRepoClickEvt(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<div class="row">
|
||||
<div *ngIf="!isCardView" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid (clrDgRefresh)="clrLoad($event)" [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()">
|
||||
<clr-dg-action-bar>
|
||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="provisionItemEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DEPLOY' | translate}}</button>
|
||||
<button *ngIf="withAdmiral" type="button" class="btn btn-sm btn-secondary" (click)="itemAddInfoEvent($event, selectedRow[0])" [disabled]="!(selectedRow.length===1 && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.ADDITIONAL_INFO' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" (click)="deleteRepos(selectedRow)" [disabled]="!(selectedRow.length && hasProjectAdminRole)"><clr-icon shape="times" size="16"></clr-icon> {{'REPOSITORY.DELETE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
|
||||
<clr-dg-cell><a href="javascript:void(0)" (click)="watchRepoClickEvt(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="showDBStatusWarning" class="db-status-warning">
|
||||
<clr-icon shape="warning" class="is-warning" size="24"></clr-icon>
|
||||
{{'CONFIG.SCANNING.DB_NOT_READY' | translate }}
|
||||
</span>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
|
||||
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
|
||||
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hbr-gridview *ngIf="isCardView" #gridView style="position:relative;" [items]="repositories" [loading]="loading" [pageSize]="pageSize"
|
||||
[currentPage]="currentPage" [totalCount]="totalCount" [expectScrollPercent]="90" [withAdmiral]="withAdmiral" (loadNextPageEvent)="loadNextPage()">
|
||||
<ng-template let-item="item">
|
||||
@ -90,5 +93,5 @@
|
||||
</a>
|
||||
</ng-template>
|
||||
</hbr-gridview>
|
||||
<confirmation-dialog #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
</div>
|
@ -22,6 +22,7 @@ import { PUSH_IMAGE_BUTTON_DIRECTIVES } from '../push-image/index';
|
||||
import { INLINE_ALERT_DIRECTIVES } from '../inline-alert/index';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
describe('RepositoryComponentGridview (inline template)', () => {
|
||||
|
||||
@ -115,7 +116,8 @@ describe('RepositoryComponentGridview (inline template)', () => {
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: RepositoryService, useClass: RepositoryDefaultService },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService }
|
||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ import { TagService, TagDefaultService } from '../service/tag.service';
|
||||
import { ChannelService } from '../channel/index';
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
import {LabelDefaultService, LabelService} from "../service/label.service";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
|
||||
class RouterStub {
|
||||
@ -174,6 +175,7 @@ describe('RepositoryComponent (inline template)', () => {
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: LabelService, useClass: LabelDefaultService},
|
||||
{ provide: ChannelService},
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -1,4 +1,4 @@
|
||||
<confirmation-dialog class="hidden-tag" #confirmationDialog [batchInfors]="batchDelectionInfos" (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<confirmation-dialog class="hidden-tag" #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||
<clr-modal class="hidden-tag" [(clrModalOpen)]="showTagManifestOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
||||
<div class="modal-body">
|
||||
|
@ -18,6 +18,7 @@ import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
import {CopyInputComponent} from "../push-image/copy-input.component";
|
||||
import {LabelPieceComponent} from "../label-piece/label-piece.component";
|
||||
import {LabelDefaultService, LabelService} from "../service/label.service";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
describe('TagComponent (inline template)', () => {
|
||||
|
||||
@ -112,7 +113,8 @@ describe('TagComponent (inline template)', () => {
|
||||
{ provide: SERVICE_CONFIG, useValue: config },
|
||||
{ provide: TagService, useClass: TagDefaultService },
|
||||
{ provide: ScanningResultService, useClass: ScanningResultDefaultService },
|
||||
{provide: LabelService, useClass: LabelDefaultService}
|
||||
{provide: LabelService, useClass: LabelDefaultService},
|
||||
{ provide: OperationService }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@ -53,11 +53,10 @@ import {
|
||||
clone,
|
||||
} from "../utils";
|
||||
|
||||
|
||||
import {CopyInputComponent} from "../push-image/copy-input.component";
|
||||
import {BatchInfo, BathInfoChanges} from "../confirmation-dialog/confirmation-batch-message";
|
||||
|
||||
import {LabelService} from "../service/label.service";
|
||||
import {operateChanges, OperateInfo, OperationState} from "../operation/operate";
|
||||
import {OperationService} from "../operation/operation.service";
|
||||
|
||||
export interface LabelState {
|
||||
iconsShow: boolean;
|
||||
@ -97,7 +96,6 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
staticBackdrop = true;
|
||||
closable = false;
|
||||
lastFilteredTagName: string;
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
inprogress: boolean;
|
||||
openLabelFilterPanel: boolean;
|
||||
openLabelFilterPiece: boolean;
|
||||
@ -146,6 +144,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
private labelService: LabelService,
|
||||
private translateService: TranslateService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private operationService: OperationService,
|
||||
private channel: ChannelService
|
||||
) { }
|
||||
|
||||
@ -566,12 +565,8 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
deleteTags(tags: Tag[]) {
|
||||
if (tags && tags.length) {
|
||||
let tagNames: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
tags.forEach(tag => {
|
||||
tagNames.push(tag.name);
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = tag.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
});
|
||||
|
||||
let titleKey: string, summaryKey: string, content: string, buttons: ConfirmationButtons;
|
||||
@ -598,7 +593,7 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
if (tags && tags.length) {
|
||||
let promiseLists: any[] = [];
|
||||
tags.forEach(tag => {
|
||||
promiseLists.push(this.delOperate(tag.signature, tag.name));
|
||||
promiseLists.push(this.delOperate(tag));
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
@ -609,34 +604,43 @@ export class TagComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(signature: any, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
if (signature) {
|
||||
delOperate(tag: Tag) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_TAG';
|
||||
operMessage.data.id = tag.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = tag.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
if (tag.signature) {
|
||||
Observable.forkJoin(this.translateService.get("BATCH.DELETED_FAILURE"),
|
||||
this.translateService.get("REPOSITORY.DELETION_SUMMARY_TAG_DENIED")).subscribe(res => {
|
||||
let wrongInfo: string = res[1] + "notary -s https://" + this.registryUrl + ":4443 -d ~/.docker/trust remove -p " +
|
||||
this.registryUrl + "/" + this.repoName + " " + name;
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, wrongInfo);
|
||||
let wrongInfo: string = res[1] + "notary -s https://" + this.registryUrl +
|
||||
":4443 -d ~/.docker/trust remove -p " +
|
||||
this.registryUrl + "/" + this.repoName +
|
||||
" " + name;
|
||||
operateChanges(operMessage, OperationState.failure, wrongInfo);
|
||||
});
|
||||
} else {
|
||||
return toPromise<number>(this.tagService
|
||||
.deleteTag(this.repoName, name))
|
||||
.deleteTag(this.repoName, tag.name))
|
||||
.then(
|
||||
response => {
|
||||
this.translateService.get("BATCH.DELETED_SUCCESS")
|
||||
.subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(error => {
|
||||
if (error.status === 503) {
|
||||
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
|
||||
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -32,7 +32,6 @@
|
||||
"clarity-icons": "^0.10.27",
|
||||
"clarity-ui": "^0.10.27",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.7.19-dev.8",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
@ -50,6 +49,7 @@
|
||||
"bootstrap": "4.0.0-alpha.5",
|
||||
"codelyzer": "~2.0.0-beta.4",
|
||||
"enhanced-resolve": "^3.0.0",
|
||||
"harbor-ui": "0.7.19-test-3",
|
||||
"jasmine-core": "2.4.1",
|
||||
"jasmine-spec-reporter": "2.5.0",
|
||||
"karma": "~1.7.0",
|
||||
|
@ -49,6 +49,7 @@
|
||||
</clr-vertical-nav-group-children>
|
||||
</clr-vertical-nav-group>
|
||||
</clr-vertical-nav>
|
||||
<hbr-operation-model *ngIf="isUserExisting"></hbr-operation-model>
|
||||
</div>
|
||||
</clr-main-container>
|
||||
<account-settings-modal></account-settings-modal>
|
||||
|
@ -34,9 +34,9 @@ import { StatisticHandler } from "../../shared/statictics/statistic-handler.serv
|
||||
import { ConfirmationDialogService } from "../../shared/confirmation-dialog/confirmation-dialog.service";
|
||||
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
||||
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message";
|
||||
import {BatchInfo, BathInfoChanges} from "../../shared/confirmation-dialog/confirmation-batch-message";
|
||||
import { SearchTriggerService } from "../../base/global-search/search-trigger.service";
|
||||
import {AppConfigService} from "../../app-config.service";
|
||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui";
|
||||
|
||||
import { Project } from "../project";
|
||||
import { ProjectService } from "../project.service";
|
||||
@ -52,7 +52,6 @@ export class ListProjectComponent implements OnDestroy {
|
||||
filteredType = 0; // All projects
|
||||
searchKeyword = "";
|
||||
selectedRow: Project[] = [];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
|
||||
@Output() addProject = new EventEmitter<void>();
|
||||
|
||||
@ -77,6 +76,7 @@ export class ListProjectComponent implements OnDestroy {
|
||||
private statisticHandler: StatisticHandler,
|
||||
private translate: TranslateService,
|
||||
private deletionDialogService: ConfirmationDialogService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
this.subscription = deletionDialogService.confirmationConfirm$.subscribe(message => {
|
||||
if (message &&
|
||||
@ -214,15 +214,10 @@ export class ListProjectComponent implements OnDestroy {
|
||||
|
||||
deleteProjects(p: Project[]) {
|
||||
let nameArr: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
if (p && p.length) {
|
||||
p.forEach(data => {
|
||||
nameArr.push(data.name);
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = data.name;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
});
|
||||
this.deletionDialogService.addBatchInfoList(this.batchDelectionInfos);
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"PROJECT.DELETION_TITLE",
|
||||
"PROJECT.DELETION_SUMMARY",
|
||||
@ -238,7 +233,7 @@ export class ListProjectComponent implements OnDestroy {
|
||||
let observableLists: any[] = [];
|
||||
if (projects && projects.length) {
|
||||
projects.forEach(data => {
|
||||
observableLists.push(this.delOperate(data.project_id, data.name));
|
||||
observableLists.push(this.delOperate(data));
|
||||
});
|
||||
Promise.all(observableLists).then(item => {
|
||||
let st: State = this.getStateAfterDeletion();
|
||||
@ -253,24 +248,31 @@ export class ListProjectComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(id: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(list => list.name === name);
|
||||
return this.proService.deleteProject(id)
|
||||
delOperate(project: Project) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_PROJECT';
|
||||
operMessage.data.id = project.project_id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = project.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return this.proService.deleteProject(project.project_id)
|
||||
.then(
|
||||
() => {
|
||||
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
if (error && error.status === 412) {
|
||||
Observable.forkJoin(this.translate.get("BATCH.DELETED_FAILURE"),
|
||||
this.translate.get("PROJECT.FAILED_TO_DELETE_PROJECT")).subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
|
||||
operateChanges(operMessage, OperationState.failure, res[1]);
|
||||
});
|
||||
} else {
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -34,8 +34,7 @@
|
||||
<clr-dg-row *clrDgItems="let m of members" [clrDgItem]="m">
|
||||
<clr-dg-cell>{{m.entity_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<span *ngIf="ChangeRoleOngoing(m.entity_name)" class="spinner spinner-inline"> Loading... </span>
|
||||
<span *ngIf="!ChangeRoleOngoing(m.entity_name)">{{roleInfo[m.role_id] | translate}}</span>
|
||||
<span>{{roleInfo[m.role_id] | translate}}</span>
|
||||
</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
|
@ -37,7 +37,7 @@ import { Subscription } from "rxjs/Subscription";
|
||||
|
||||
import { Project } from "../../project/project";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {BatchInfo, BathInfoChanges} from "../../shared/confirmation-dialog/confirmation-batch-message";
|
||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui";
|
||||
|
||||
@Component({
|
||||
templateUrl: "member.component.html",
|
||||
@ -62,8 +62,6 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
roleNum: number;
|
||||
isDelete = false;
|
||||
isChangeRole = false;
|
||||
batchActionInfos: BatchInfo[] = [];
|
||||
batchDeletionInfos: BatchInfo[] = [];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@ -73,6 +71,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private OperateDialogService: ConfirmationDialogService,
|
||||
private session: SessionService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
|
||||
this.delSub = OperateDialogService.confirmationConfirm$.subscribe(message => {
|
||||
@ -146,15 +145,6 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
this.isDelete = false;
|
||||
this.isChangeRole = true;
|
||||
this.roleNum = roleId;
|
||||
let nameArr: string[] = [];
|
||||
this.batchActionInfos = [];
|
||||
m.forEach(data => {
|
||||
nameArr.push(data.entity_name);
|
||||
let initBatchMessage = new BatchInfo();
|
||||
initBatchMessage.name = data.entity_name;
|
||||
this.batchActionInfos.push(initBatchMessage);
|
||||
});
|
||||
|
||||
this.changeOpe(m);
|
||||
}
|
||||
}
|
||||
@ -163,15 +153,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
if (members && members.length) {
|
||||
let promiseList: any[] = [];
|
||||
members.forEach(member => {
|
||||
if (member.entity_id === this.currentUser.user_id) {
|
||||
let foundMember = this.batchActionInfos.find(batchInfo => batchInfo.name === member.entity_name);
|
||||
this.translate.get("BATCH.SWITCH_FAILURE").subscribe(res => {
|
||||
this.messageHandlerService.handleError(res + ": " + foundMember.name);
|
||||
foundMember = BathInfoChanges(foundMember, res, false, true);
|
||||
});
|
||||
} else {
|
||||
promiseList.push(this.changeOperate(this.projectId, member.id, this.roleNum, member.entity_name));
|
||||
}
|
||||
promiseList.push(this.changeOperate(this.projectId, this.roleNum, member));
|
||||
});
|
||||
|
||||
Promise.all(promiseList).then(num => {
|
||||
@ -181,47 +163,45 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
changeOperate(projectId: number, memberId: number, roleId: number, username: string) {
|
||||
let foundMember = this.batchActionInfos.find(batchInfo => batchInfo.name === username);
|
||||
changeOperate(projectId: number, roleId: number, member: Member) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.SWITCH_ROLE';
|
||||
operMessage.data.id = member.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = member.entity_name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
if (member.entity_id === this.currentUser.user_id) {
|
||||
this.translate.get("BATCH.SWITCH_FAILURE").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return this.memberService
|
||||
.changeMemberRole(projectId, memberId, roleId)
|
||||
.changeMemberRole(projectId, member.id, roleId)
|
||||
.then(
|
||||
response => {
|
||||
this.translate.get("BATCH.SWITCH_SUCCESS").subscribe(res => {
|
||||
foundMember = BathInfoChanges(foundMember, res);
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.translate.get("BATCH.SWITCH_FAILURE").subscribe(res => {
|
||||
this.messageHandlerService.handleError(res + ": " + username);
|
||||
foundMember = BathInfoChanges(foundMember, res, false, true);
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
ChangeRoleOngoing(username: string) {
|
||||
if (this.batchActionInfos) {
|
||||
let memberActionInfo = this.batchActionInfos.find(batchInfo => batchInfo.name === username);
|
||||
return memberActionInfo && memberActionInfo.status === "pending";
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
deleteMembers(m: Member[]) {
|
||||
this.isDelete = true;
|
||||
this.isChangeRole = false;
|
||||
let nameArr: string[] = [];
|
||||
this.batchDeletionInfos = [];
|
||||
if (m && m.length) {
|
||||
m.forEach(data => {
|
||||
nameArr.push(data.entity_name);
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = data.entity_name;
|
||||
this.batchDeletionInfos.push(initBatchMessage);
|
||||
});
|
||||
this.OperateDialogService.addBatchInfoList(this.batchDeletionInfos);
|
||||
|
||||
let deletionMessage = new ConfirmationMessage(
|
||||
"MEMBER.DELETION_TITLE",
|
||||
@ -239,15 +219,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
if (members && members.length) {
|
||||
let promiseLists: any[] = [];
|
||||
members.forEach(member => {
|
||||
if (member.entity_id === this.currentUser.user_id) {
|
||||
let findedList = this.batchDeletionInfos.find(data => data.name === member.entity_name);
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
} else {
|
||||
promiseLists.push(this.delOperate(this.projectId, member.id, member.entity_name));
|
||||
}
|
||||
|
||||
promiseLists.push(this.delOperate(this.projectId, member));
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then(item => {
|
||||
@ -257,19 +229,33 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
delOperate(projectId: number, memberId: number, username: string) {
|
||||
let findedList = this.batchDeletionInfos.find(data => data.name === username);
|
||||
delOperate(projectId: number, member: Member) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_MEMBER';
|
||||
operMessage.data.id = member.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = member.entity_name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
if (member.entity_id === this.currentUser.user_id) {
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.memberService
|
||||
.deleteMember(projectId, memberId)
|
||||
.deleteMember(projectId, member.id)
|
||||
.then(
|
||||
response => {
|
||||
this.translate.get("BATCH.DELETED_SUCCESS").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.translate.get("BATCH.DELETED_FAILURE").subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -1,27 +0,0 @@
|
||||
|
||||
/**
|
||||
* Created by pengf on 11/22/2017.
|
||||
*/
|
||||
|
||||
export class BatchInfo {
|
||||
name: string;
|
||||
status: string;
|
||||
loading: boolean;
|
||||
errorState: boolean;
|
||||
errorInfo: string;
|
||||
constructor() {
|
||||
this.status = "pending";
|
||||
this.loading = false;
|
||||
this.errorState = false;
|
||||
this.errorInfo = "";
|
||||
}
|
||||
}
|
||||
|
||||
export function BathInfoChanges(list: BatchInfo, status: string, loading = false, errStatus = false, errorInfo = '') {
|
||||
list.status = status;
|
||||
list.loading = loading;
|
||||
list.errorState = errStatus;
|
||||
list.errorInfo = errorInfo;
|
||||
return list;
|
||||
}
|
||||
|
@ -5,18 +5,6 @@
|
||||
<clr-icon shape="warning" class="is-warning" size="64"></clr-icon>
|
||||
</div>
|
||||
<div class="confirmation-content">{{dialogContent}}</div>
|
||||
<div>
|
||||
<ul class="batchInfoUl">
|
||||
<li *ngFor="let info of resultLists">
|
||||
<span> <i class="spinner spinner-inline spinner-pos" [hidden]='!info.loading'></i> {{info.name}}</span>
|
||||
<span *ngIf="!info.errorInfo.length" [style.color]="colorChange(info)">{{info.status}}</span>
|
||||
<span *ngIf="info.errorInfo.length" [style.color]="colorChange(info)">
|
||||
<a (click)="toggleErrorTitle(errorInfo)" >{{info.status}}</a><br>
|
||||
<i #errorInfo style="display: none;">{{info.errorInfo}}</i>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" [ngSwitch]="buttons">
|
||||
<ng-template [ngSwitchCase]="0">
|
||||
@ -29,8 +17,7 @@
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="2">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="operate()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="!batchOverStatus" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.DELETE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
|
@ -19,7 +19,6 @@ import { ConfirmationDialogService } from './confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from './confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from './confirmation-state-message';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared.const';
|
||||
import {BatchInfo} from "./confirmation-batch-message";
|
||||
|
||||
@Component({
|
||||
selector: 'confiramtion-dialog',
|
||||
@ -32,32 +31,9 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
dialogTitle: string = "";
|
||||
dialogContent: string = "";
|
||||
message: ConfirmationMessage;
|
||||
resultLists: BatchInfo[] = [];
|
||||
annouceSubscription: Subscription;
|
||||
batchInfoSubscription: Subscription;
|
||||
buttons: ConfirmationButtons;
|
||||
isDelete: boolean = false;
|
||||
|
||||
get batchOverStatus(): boolean {
|
||||
if (this.resultLists.length) {
|
||||
return this.resultLists.every(item => item.loading === false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
colorChange(list: BatchInfo) {
|
||||
if (!list.loading && !list.errorState) {
|
||||
return 'green';
|
||||
} else if (!list.loading && list.errorState) {
|
||||
return 'red';
|
||||
} else {
|
||||
return '#666';
|
||||
}
|
||||
}
|
||||
toggleErrorTitle(errorSpan: any) {
|
||||
errorSpan.style.display = (errorSpan.style.display === 'none') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
constructor(
|
||||
private confirmationService: ConfirmationDialogService,
|
||||
private translate: TranslateService) {
|
||||
@ -71,19 +47,12 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
this.buttons = msg.buttons;
|
||||
this.open();
|
||||
});
|
||||
this.batchInfoSubscription = confirmationService.confirmationBatch$.subscribe(data => {
|
||||
this.resultLists = data;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.annouceSubscription) {
|
||||
this.annouceSubscription.unsubscribe();
|
||||
}
|
||||
if (this.batchInfoSubscription) {
|
||||
this.resultLists = [];
|
||||
this.batchInfoSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
open(): void {
|
||||
@ -91,7 +60,6 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.resultLists = [];
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
@ -113,26 +81,6 @@ export class ConfirmationDialogComponent implements OnDestroy {
|
||||
this.close();
|
||||
}
|
||||
|
||||
operate(): void {
|
||||
if (!this.message) {// Improper condition
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.resultLists.length) {
|
||||
this.resultLists.every(item => item.loading = true);
|
||||
this.isDelete = true;
|
||||
}
|
||||
|
||||
let data: any = this.message.data ? this.message.data : {};
|
||||
let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY;
|
||||
this.confirmationService.confirm(new ConfirmationAcknowledgement(
|
||||
ConfirmationState.CONFIRMED,
|
||||
data,
|
||||
target
|
||||
));
|
||||
}
|
||||
|
||||
confirm(): void {
|
||||
if (!this.message) {
|
||||
// Inproper condition
|
||||
|
@ -16,17 +16,14 @@ import { Subject } from 'rxjs/Subject';
|
||||
|
||||
import { ConfirmationMessage } from './confirmation-message';
|
||||
import { ConfirmationAcknowledgement } from './confirmation-state-message';
|
||||
import {BatchInfo} from "./confirmation-batch-message";
|
||||
|
||||
@Injectable()
|
||||
export class ConfirmationDialogService {
|
||||
confirmationAnnoucedSource = new Subject<ConfirmationMessage>();
|
||||
confirmationConfirmSource = new Subject<ConfirmationAcknowledgement>();
|
||||
confirmationBatchSource = new Subject<BatchInfo[]>();
|
||||
|
||||
confirmationAnnouced$ = this.confirmationAnnoucedSource.asObservable();
|
||||
confirmationConfirm$ = this.confirmationConfirmSource.asObservable();
|
||||
confirmationBatch$ = this.confirmationBatchSource.asObservable();
|
||||
|
||||
// User confirm the action
|
||||
public confirm(ack: ConfirmationAcknowledgement): void {
|
||||
@ -42,7 +39,4 @@ export class ConfirmationDialogService {
|
||||
public openComfirmDialog(message: ConfirmationMessage): void {
|
||||
this.confirmationAnnoucedSource.next(message);
|
||||
}
|
||||
public addBatchInfoList(data: BatchInfo[]): void {
|
||||
this.confirmationBatchSource.next(data);
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +20,13 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||
import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message';
|
||||
import {BatchInfo, BathInfoChanges} from '../shared/confirmation-dialog/confirmation-batch-message';
|
||||
import { MessageHandlerService } from '../shared/message-handler/message-handler.service';
|
||||
import { SessionService } from '../shared/session.service';
|
||||
import { AppConfigService } from '../app-config.service';
|
||||
|
||||
import { NewUserModalComponent } from './new-user-modal.component';
|
||||
import { UserService } from './user.service';
|
||||
import { User } from './user';
|
||||
import {operateChanges, OperateInfo, OperationService, OperationState} from "harbor-ui";
|
||||
/**
|
||||
* NOTES:
|
||||
* Pagination for this component is a temporary workaround solution. It will be replaced in future release.
|
||||
@ -51,7 +50,6 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
originalUsers: Promise<User[]>;
|
||||
selectedRow: User[] = [];
|
||||
ISADMNISTRATOR: string = "USER.ENABLE_ADMIN_ACTION";
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
|
||||
currentTerm: string;
|
||||
totalCount: number = 0;
|
||||
@ -72,6 +70,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
private msgHandler: MessageHandlerService,
|
||||
private session: SessionService,
|
||||
private appConfigService: AppConfigService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef) {
|
||||
this.deletionSubscription = deletionDialogService.confirmationConfirm$.subscribe(confirmed => {
|
||||
if (confirmed &&
|
||||
@ -228,19 +227,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
// Delete the specified user
|
||||
deleteUsers(users: User[]): void {
|
||||
let userArr: string[] = [];
|
||||
this.batchDelectionInfos = [];
|
||||
if (this.onlySelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (users && users.length) {
|
||||
users.forEach(user => {
|
||||
let initBatchMessage = new BatchInfo ();
|
||||
initBatchMessage.name = user.username;
|
||||
this.batchDelectionInfos.push(initBatchMessage);
|
||||
userArr.push(user.username);
|
||||
});
|
||||
this.deletionDialogService.addBatchInfoList(this.batchDelectionInfos);
|
||||
users.forEach(user => {
|
||||
userArr.push(user.username);
|
||||
});
|
||||
}
|
||||
// Confirm deletion
|
||||
let msg: ConfirmationMessage = new ConfirmationMessage(
|
||||
"USER.DELETION_TITLE",
|
||||
@ -252,21 +247,12 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
this.deletionDialogService.openComfirmDialog(msg);
|
||||
}
|
||||
}
|
||||
|
||||
delUser(users: User[]): void {
|
||||
// this.batchInfoDialog.open();
|
||||
let promiseLists: any[] = [];
|
||||
if (users && users.length) {
|
||||
users.forEach(user => {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === user.username);
|
||||
if (this.isMySelf(user.user_id)) {
|
||||
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
});
|
||||
} else {
|
||||
promiseLists.push(this.delOperate(user.user_id, user.username));
|
||||
}
|
||||
promiseLists.push(this.delOperate(user));
|
||||
});
|
||||
|
||||
Promise.all(promiseLists).then((item) => {
|
||||
@ -276,15 +262,31 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
}
|
||||
delOperate(id: number, name: string) {
|
||||
let findedList = this.batchDelectionInfos.find(data => data.name === name);
|
||||
return this.userService.deleteUser(id).then(() => {
|
||||
|
||||
delOperate(user: User) {
|
||||
// init operation info
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.DELETE_USER';
|
||||
operMessage.data.id = user.user_id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = user.username;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
if (this.isMySelf(user.user_id)) {
|
||||
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return this.userService.deleteUser(user.user_id).then(() => {
|
||||
this.translate.get('BATCH.DELETED_SUCCESS').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res);
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
});
|
||||
}).catch(error => {
|
||||
this.translate.get('BATCH.DELETED_FAILURE').subscribe(res => {
|
||||
findedList = BathInfoChanges(findedList, res, false, true);
|
||||
operateChanges(operMessage, OperationState.failure, res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -654,6 +654,27 @@
|
||||
"SATURDAY": "Saturday",
|
||||
"SUNDAY": "Sunday"
|
||||
},
|
||||
"OPERATION": {
|
||||
"LOCAL_EVENT": "Local Events",
|
||||
"ALL": "All",
|
||||
"RUNNING": "Running",
|
||||
"FAILED": "Failed",
|
||||
"DELETE_PROJECT": "Delete project",
|
||||
"DELETE_REPO": "Delete repository",
|
||||
"DELETE_TAG": "Delete tag",
|
||||
"DELETE_USER": "Delete user",
|
||||
"DELETE_REGISTRY": "Delete registry",
|
||||
"DELETE_REPLICATION": "Delete replication",
|
||||
"DELETE_MEMBER": "Delete member",
|
||||
"SWITCH_ROLE": "Switch role",
|
||||
"DELETE_LABEL": "Delete label",
|
||||
"REPLICATION": "Replication",
|
||||
"DAY_AGO": " day(s) ago",
|
||||
"HOUR_AGO": " hour(s) ago",
|
||||
"MINUTE_AGO": " minute(s) ago",
|
||||
"SECOND_AGO": "less 1 minute",
|
||||
"EVENT_LOG": "EVENT LOG"
|
||||
},
|
||||
"UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.",
|
||||
"UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.",
|
||||
"REPO_READ_ONLY": "Harbor is set to read-only mode, Deleting repository, tag and pushing image will be disabled under read-only mode.",
|
||||
|
@ -654,6 +654,27 @@
|
||||
"SATURDAY": "Saturday",
|
||||
"SUNDAY": "Sunday"
|
||||
},
|
||||
"OPERATION": {
|
||||
"LOCAL_EVENT": "Local Events",
|
||||
"ALL": "All",
|
||||
"RUNNING": "Running",
|
||||
"FAILED": "Failed",
|
||||
"DELETE_PROJECT": "Delete project",
|
||||
"DELETE_REPO": "Delete repository",
|
||||
"DELETE_TAG": "Delete tag",
|
||||
"DELETE_USER": "Delete user",
|
||||
"DELETE_REGISTRY": "Delete registry",
|
||||
"DELETE_REPLICATION": "Delete replication",
|
||||
"DELETE_MEMBER": "Delete member",
|
||||
"SWITCH_ROLE": "Switch role",
|
||||
"DELETE_LABEL": "Delete label",
|
||||
"REPLICATION": "Replication",
|
||||
"DAY_AGO": " day(s) ago",
|
||||
"HOUR_AGO": " hour(s) ago",
|
||||
"MINUTE_AGO": " minute(s) ago",
|
||||
"SECOND_AGO": "less 1 minute",
|
||||
"EVENT_LOG": "EVENT LOG"
|
||||
},
|
||||
"UNKNOWN_ERROR": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo más tarde.",
|
||||
"UNAUTHORIZED_ERROR": "La sesión no es válida o ha caducado. Necesita identificarse de nuevo para llevar a cabo esa acción.",
|
||||
"REPO_READ_ONLY": "Harbor is set to read-only mode, Deleting repository, tag and pushing image will be disabled under read-only mode.",
|
||||
|
@ -612,6 +612,27 @@
|
||||
"SATURDAY": "Saturday",
|
||||
"SUNDAY": "Sunday"
|
||||
},
|
||||
"OPERATION": {
|
||||
"LOCAL_EVENT": "Local Events",
|
||||
"ALL": "All",
|
||||
"RUNNING": "Running",
|
||||
"FAILED": "Failed",
|
||||
"DELETE_PROJECT": "Delete project",
|
||||
"DELETE_REPO": "Delete repository",
|
||||
"DELETE_TAG": "Delete tag",
|
||||
"DELETE_USER": "Delete user",
|
||||
"DELETE_REGISTRY": "Delete registry",
|
||||
"DELETE_REPLICATION": "Delete replication",
|
||||
"DELETE_MEMBER": "Delete member",
|
||||
"SWITCH_ROLE": "Switch role",
|
||||
"DELETE_LABEL": "Delete label",
|
||||
"REPLICATION": "Replication",
|
||||
"DAY_AGO": " day(s) ago",
|
||||
"HOUR_AGO": " hour(s) ago",
|
||||
"MINUTE_AGO": " minute(s) ago",
|
||||
"SECOND_AGO": "less 1 minute",
|
||||
"EVENT_LOG": "EVENT LOG"
|
||||
},
|
||||
"UNKNOWN_ERROR": "Des erreurs inconnues sont survenues. Veuillez réessayer plus tard.",
|
||||
"UNAUTHORIZED_ERROR": "Votre session est invalide ou a expiré. Vous devez vous connecter pour continuer votre action.",
|
||||
"REPO_READ_ONLY": "Harbor is set to read-only mode, Deleting repository, tag and pushing image will be disabled under read-only mode.",
|
||||
|
@ -654,6 +654,27 @@
|
||||
"SATURDAY": "周六",
|
||||
"SUNDAY": "周日"
|
||||
},
|
||||
"OPERATION": {
|
||||
"LOCAL_EVENT": "本地事件",
|
||||
"ALL": "所有",
|
||||
"RUNNING": "进行中",
|
||||
"FAILED": "失败",
|
||||
"DELETE_PROJECT": "删除项目",
|
||||
"DELETE_REPO": "删除仓库",
|
||||
"DELETE_TAG": "删除镜像标签",
|
||||
"DELETE_USER": "删除用户",
|
||||
"DELETE_REGISTRY": "Delete registry",
|
||||
"DELETE_REPLICATION": "删除复制",
|
||||
"DELETE_MEMBER": "删除成员",
|
||||
"SWITCH_ROLE": "切换角色",
|
||||
"DELETE_LABEL": "删除标签",
|
||||
"REPLICATION": "复制",
|
||||
"DAY_AGO": "天前",
|
||||
"HOUR_AGO": "小时前",
|
||||
"MINUTE_AGO": "分钟前",
|
||||
"SECOND_AGO": "少于一分钟",
|
||||
"EVENT_LOG": "事件日志"
|
||||
},
|
||||
"UNKNOWN_ERROR": "发生未知错误,请稍后再试。",
|
||||
"UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。",
|
||||
"REPO_READ_ONLY": "Harbor被设置为只读模式,在此模式下,不能删除仓库、标签及推送镜像。",
|
||||
|
@ -103,7 +103,6 @@ Delete Repo
|
||||
Sleep 1
|
||||
Click Element xpath=//clr-modal//button[2]
|
||||
Sleep 1
|
||||
Click Element xpath=//button[contains(.,"CLOSE")]
|
||||
|
||||
Delete Repo on CardView
|
||||
[Arguments] ${reponame}
|
||||
|
Loading…
Reference in New Issue
Block a user