mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-25 18:18:04 +01:00
Merge pull request #4076 from pengpengshui/batchDelection
Modify create multiple replication rules with same name issue
This commit is contained in:
commit
f18ad3259e
@ -24,7 +24,7 @@ export const CONFIRMATION_DIALOG_STYLE: string = `
|
||||
.batchInfoUl li {line-height: 24px;border-bottom: 1px solid #e8e8e8;}
|
||||
.batchInfoUl li span:first-child {padding-right: 20px; width: 240px; display: inline-block; color:#666;
|
||||
text-overflow: ellipsis; overflow: hidden; vertical-align: middle;}
|
||||
.batchInfoUl li span:last-child {width: 230px; display: inline-block; color:#666;}
|
||||
.batchInfoUl li span:last-child {width: 220px; display: inline-block; color:#666;}
|
||||
.batchInfoUl li span i {display: inline-block; line-height: 1.2em; font-size: 0.8em; color: #999;}
|
||||
.batchInfoUl li span a{cursor: pointer; text-decoration: underline;}
|
||||
`;
|
@ -13,7 +13,7 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||
<clr-dg-column [clrDgField]="'projects'" *ngIf="!projectScope">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.MODE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.TRIGGER_MODE' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||
<clr-dg-cell (click)="selectRule(p)">{{p.name}}</clr-dg-cell>
|
||||
|
@ -40,7 +40,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)"><clr-dg-action-bar>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" [hidden]="!(jobs && jobs.length>0 && isSystemAdmin)" (click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!(jobs && jobs.length>0) || isStopOnGoing" (click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
|
@ -111,6 +111,7 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
rules: ReplicationRule[];
|
||||
loading: boolean;
|
||||
isStopOnGoing: boolean;
|
||||
|
||||
jobs: ReplicationJobItem[];
|
||||
batchDelectionInfos: BatchInfo[] = [];
|
||||
@ -362,8 +363,12 @@ export class ReplicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
stopJobs() {
|
||||
if (this.jobs && this.jobs.length) {
|
||||
this.isStopOnGoing = true;
|
||||
toPromise(this.replicationService.stopJobs(this.jobs[0].policy_id))
|
||||
.then(res => {this.refreshJobs(); })
|
||||
.then(res => {
|
||||
this.refreshJobs();
|
||||
this.isStopOnGoing = false;
|
||||
})
|
||||
.catch(error => this.errorHandler.error(error));
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ export const TAG_TEMPLATE = `
|
||||
<clr-dg-column style="width: 140px;" *ngIf="withClair">{{'REPOSITORY.VULNERABILITY' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 80px;" *ngIf="withNotary">{{'REPOSITORY.SIGNED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="min-width: 130px;">{{'REPOSITORY.AUTHOR' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 160px;"[clrDgSortBy]="createdComparator">{{'REPOSITORY.CREATED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 80px;" [clrDgField]="'docker_version'" *ngIf="!withClair">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 150px;"[clrDgSortBy]="createdComparator">{{'REPOSITORY.CREATED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column style="width: 140px;" [clrDgField]="'docker_version'" *ngIf="!withClair">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'TAG.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let t of tags" [clrDgItem]='t'>
|
||||
<clr-dg-cell class="truncated" style="width: 160px;" [ngSwitch]="withClair">
|
||||
@ -60,8 +60,8 @@ export const TAG_TEMPLATE = `
|
||||
</a>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell class="truncated" style="min-width: 130px;" title="{{t.author}}">{{t.author}}</clr-dg-cell>
|
||||
<clr-dg-cell style="width: 160px;">{{t.created | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell style="width: 80px;" *ngIf="!withClair">{{t.docker_version}}</clr-dg-cell>
|
||||
<clr-dg-cell style="width: 150px;">{{t.created | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell style="width: 140px;" *ngIf="!withClair">{{t.docker_version}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
|
||||
|
@ -31,7 +31,7 @@
|
||||
"clarity-icons": "^0.10.17",
|
||||
"clarity-ui": "^0.10.17",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.6.25",
|
||||
"harbor-ui": "0.6.28",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
@ -48,6 +48,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
inProgress: boolean = false;
|
||||
inNameChecking: boolean = false;
|
||||
isRuleNameExist: boolean = false;
|
||||
isSubmitOver: boolean = false;
|
||||
nameChecker: Subject<string> = new Subject<string>();
|
||||
|
||||
confirmSub: Subscription;
|
||||
@ -147,7 +148,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
get isVaild() {
|
||||
return !(this.isRuleNameExist || this.noProjectInfo || this.noEndpointInfo);
|
||||
return !(this.isRuleNameExist || this.noProjectInfo || this.noEndpointInfo || this.inProgress || this.isSubmitOver);
|
||||
}
|
||||
|
||||
createForm() {
|
||||
@ -412,6 +413,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
|
||||
onSubmit() {
|
||||
// add new Replication rule
|
||||
this.inProgress = true;
|
||||
let copyRuleForm: ReplicationRule = this.ruleForm.value;
|
||||
copyRuleForm.trigger = this.setTriggerVaule(copyRuleForm.trigger);
|
||||
if (!this.policyId) {
|
||||
@ -419,6 +421,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
.then(() => {
|
||||
this.msgHandler.showSuccess('REPLICATION.CREATED_SUCCESS');
|
||||
this.inProgress = false;
|
||||
this.isSubmitOver = true;
|
||||
setTimeout(() => {
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
if (this.projectId) {
|
||||
@ -426,7 +429,6 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
}else {
|
||||
this.router.navigate(['/harbor/replications']);
|
||||
}
|
||||
|
||||
}, 2000);
|
||||
|
||||
}).catch((error: any) => {
|
||||
@ -438,6 +440,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
.then(() => {
|
||||
this.msgHandler.showSuccess('REPLICATION.UPDATED_SUCCESS');
|
||||
this.inProgress = false;
|
||||
this.isSubmitOver = true;
|
||||
setTimeout(() => {
|
||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||
if (this.projectId) {
|
||||
@ -452,7 +455,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
||||
this.msgHandler.handleError(error);
|
||||
});
|
||||
}
|
||||
this.inProgress = true;
|
||||
|
||||
}
|
||||
|
||||
openModal() {
|
||||
|
@ -72,23 +72,32 @@
|
||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.MODE' | translate}}</label>
|
||||
<div formGroupName="trigger">
|
||||
<!--on trigger-->
|
||||
<div class="select floatSet">
|
||||
<div class="select floatSet pull-left">
|
||||
<select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)">
|
||||
<option *ngFor="let triggerName of triggerNames" value="{{triggerName}}">{{triggerName}}</option>
|
||||
<option value="Immediate">{{'REPLICATION.IMMEDIATE' | translate}}</option>
|
||||
<option value="Scheduled">{{'REPLICATION.SCHEDULE' | translate}}</option>
|
||||
<option value="Manual">{{'REPLICATION.MANUAL' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--on push-->
|
||||
<div style="float: left;" formGroupName="schedule_param">
|
||||
<div class="select floatSet" [hidden]="!isScheduleOpt">
|
||||
<div class="select floatSet pull-left" [hidden]="!isScheduleOpt">
|
||||
<select name="scheduleType" formControlName="type" (change)="selectSchedule($event)">
|
||||
<option *ngFor="let scheduleName of scheduleNames" value="{{scheduleName}}">{{scheduleName}}</option>
|
||||
<option value="Daily">{{'REPLICATION.DAILY' | translate}}</option>
|
||||
<option value="Weekly">{{'REPLICATION.WEEKLY' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--weekly-->
|
||||
<span style="float: left;" [hidden]="!weeklySchedule || !isScheduleOpt">on </span>
|
||||
<div [hidden]="!weeklySchedule || !isScheduleOpt" class="select floatSet">
|
||||
<div [hidden]="!weeklySchedule || !isScheduleOpt" class="select floatSet pull-left">
|
||||
<select name="scheduleDay" formControlName="weekday">
|
||||
<option *ngFor="let filter of weekly; let i = index" [value]="i+1">{{filter}}</option>
|
||||
<option value="1">{{'WEEKLY.MONDAY' | translate}}</option>
|
||||
<option value="2">{{'WEEKLY.TUESDAY' | translate}}</option>
|
||||
<option value="3">{{'WEEKLY.WEDNESDAY' | translate}}</option>
|
||||
<option value="4">{{'WEEKLY.THURSDAY' | translate}}</option>
|
||||
<option value="5">{{'WEEKLY.FRIDAY' | translate}}</option>
|
||||
<option value="6">{{'WEEKLY.SATURDAY' | translate}}</option>
|
||||
<option value="7">{{'WEEKLY.SUNDAY' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--daily/time-->
|
||||
|
@ -23,6 +23,6 @@
|
||||
.batchInfoUl li {line-height: 24px;border-bottom: 1px solid #e8e8e8;}
|
||||
.batchInfoUl li span:first-child {padding-right: 20px; width: 240px; display: inline-block; color:#666;
|
||||
text-overflow: ellipsis; overflow: hidden; vertical-align: middle;}
|
||||
.batchInfoUl li span:last-child {width: 230px; display: inline-block; color:#666;}
|
||||
.batchInfoUl li span:last-child {width: 220px; display: inline-block; color:#666;}
|
||||
.batchInfoUl li span i {display: inline-block; line-height: 1.2em; font-size: 0.8em; color: #999;}
|
||||
.batchInfoUl li span a{cursor: pointer; text-decoration: underline;}
|
||||
|
@ -326,10 +326,13 @@
|
||||
"SCHEDULE": "Scheduled",
|
||||
"MANUAL": "Manual",
|
||||
"IMMEDIATE": "Immediate",
|
||||
"DAILY": "Daily",
|
||||
"WEEKLY": "Weekly",
|
||||
"SETTING":"Options",
|
||||
"TRIGGER":"Triggering Condition",
|
||||
"TARGETS":"Target",
|
||||
"MODE": "Mode",
|
||||
"TRIGGER_MODE": "Trigger Mode",
|
||||
"SOURCE": "Source",
|
||||
"REPLICATE": "Replicate",
|
||||
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
|
||||
@ -606,6 +609,15 @@
|
||||
"COPY_ERROR": "Copy failed, please try to manually copy.",
|
||||
"FILTER_FOR_TAGS": "Filter Tags"
|
||||
},
|
||||
"WEEKLY": {
|
||||
"MONDAY": "Monday",
|
||||
"TUESDAY": "Tuesday",
|
||||
"WEDNESDAY": "Wednesday",
|
||||
"THURSDAY": "Thursday",
|
||||
"FRIDAY": "Friday",
|
||||
"SATURDAY": "Saturday",
|
||||
"SUNDAY": "Sunday"
|
||||
},
|
||||
"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.",
|
||||
"FORBIDDEN_ERROR": "You do not have the proper privileges to perform the action.",
|
||||
|
@ -326,10 +326,13 @@
|
||||
"SCHEDULE": "Scheduled",
|
||||
"MANUAL": "Manual",
|
||||
"IMMEDIATE": "Immediate",
|
||||
"DAILY": "Daily",
|
||||
"WEEKLY": "Weekly",
|
||||
"SETTING":"Options",
|
||||
"TRIGGER":"Triggering Condition",
|
||||
"TARGETS":"Target",
|
||||
"MODE": "Mode",
|
||||
"TRIGGER_MODE": "Trigger Mode",
|
||||
"SOURCE": "Source",
|
||||
"REPLICATE": "Replicate",
|
||||
"DELETE_REMOTE_IMAGES":"Delete remote images when locally deleted",
|
||||
@ -606,6 +609,15 @@
|
||||
"COPY_ERROR": "Copy failed, please try to manually copy.",
|
||||
"FILTER_FOR_TAGS": "Etiquetas de filtro"
|
||||
},
|
||||
"WEEKLY": {
|
||||
"MONDAY": "Monday",
|
||||
"TUESDAY": "Tuesday",
|
||||
"WEDNESDAY": "Wednesday",
|
||||
"THURSDAY": "Thursday",
|
||||
"FRIDAY": "Friday",
|
||||
"SATURDAY": "Saturday",
|
||||
"SUNDAY": "Sunday"
|
||||
},
|
||||
"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.",
|
||||
"FORBIDDEN_ERROR": "No tienes permisos para llevar a cabo esa acción.",
|
||||
|
@ -326,10 +326,13 @@
|
||||
"SCHEDULE": "定时",
|
||||
"MANUAL": "手动",
|
||||
"IMMEDIATE": "即刻",
|
||||
"DAILY": "每天",
|
||||
"WEEKLY": "每周",
|
||||
"SETTING":"设置",
|
||||
"TRIGGER":"触发条件",
|
||||
"TARGETS":"目标",
|
||||
"MODE": "模式",
|
||||
"TRIGGER_MODE": "触发模式",
|
||||
"SOURCE": "资源",
|
||||
"REPLICATE": "复制",
|
||||
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
|
||||
@ -606,6 +609,15 @@
|
||||
"COPY_ERROR": "拷贝失败,请尝试手动拷贝。",
|
||||
"FILTER_FOR_TAGS": "过滤项目"
|
||||
},
|
||||
"WEEKLY": {
|
||||
"MONDAY": "周一",
|
||||
"TUESDAY": "周二",
|
||||
"WEDNESDAY": "周三",
|
||||
"THURSDAY": "周四",
|
||||
"FRIDAY": "周五",
|
||||
"SATURDAY": "周六",
|
||||
"SUNDAY": "周日"
|
||||
},
|
||||
"UNKNOWN_ERROR": "发生未知错误,请稍后再试。",
|
||||
"UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。",
|
||||
"FORBIDDEN_ERROR": "当前操作被禁止,请确认你有合法的权限。",
|
||||
|
Loading…
Reference in New Issue
Block a user