Merge pull request #6553 from zhoumeina/fix_retag_ui

show retag tooltip use regex, reset the dialog form when retagged
This commit is contained in:
Mia ZHOU 2018-12-19 13:32:07 +08:00 committed by GitHub
commit 2062c436d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 88 additions and 30 deletions

View File

@ -4,8 +4,13 @@
<div class="clr-control-container clr-col-xs-12 clr-col-md-8">
<div class="clr-input-wrapper" (mouseleave)="leaveProjectInput()">
<label aria-haspopup="true" role="tooltip" class="wrap-label tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='noProjectInfo'>
<input type="text" id="project-name" (keyup)='validateProjectName()' (blur)='blurProjectInput()' class="clr-input" formControlName="projectName" required minlength="2" pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" />
<span class="tooltip-content">{{noProjectInfo | translate}}</span>
<input type="text"
id="project-name"
class="clr-input"
(keyup)='validateProjectName()'
(blur)='blurProjectInput()'
formControlName="projectName"/>
<span *ngIf="noProjectInfo && (projectName.dirty || projectName.touched)" class="tooltip-content">{{noProjectInfo | translate}}</span>
</label>
<div class="select-box" [style.display]="selectedProjectList.length ? 'block' : 'none'">
<ul>
@ -20,8 +25,8 @@
<div class="clr-control-container clr-col-xs-12 clr-col-md-8">
<div class="clr-input-wrapper">
<label aria-haspopup="true" role="tooltip" class="wrap-label tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='repoName.invalid && (repoName.dirty || repoName.touched)'>
<input type="text" id="repo-name" class="clr-input" formControlName="repoName" required />
<span *ngIf="repoName.invalid && (repoName.dirty || repoName.touched)" class="tooltip-content">{{ 'TOOLTIP.NONEMPTY' | translate }}</span>
<input type="text" id="repo-name" class="clr-input" formControlName="repoName" />
<span *ngIf="repoName.invalid && (repoName.dirty || repoName.touched)" class="tooltip-content">{{ 'RETAG.TIP_REPO' | translate }}</span>
</label>
</div>
</div>
@ -31,8 +36,8 @@
<div class="clr-control-container clr-col-xs-12 clr-col-md-8">
<div class="clr-input-wrapper">
<label aria-haspopup="true" role="tooltip" class="wrap-label tooltip tooltip-validation tooltip-md tooltip-bottom-left" [class.invalid]='tagName.invalid && (tagName.dirty || tagName.touched)'>
<input type="text" id="tag-name" class="clr-input" formControlName="tagName" required />
<span *ngIf="repoName.invalid && (repoName.dirty || repoName.touched)" class="tooltip-content">{{ 'TOOLTIP.NONEMPTY' | translate }}</span>
<input type="text" id="tag-name" class="clr-input" formControlName="tagName" />
<span *ngIf="tagName.invalid && (tagName.dirty || tagName.touched)" class="tooltip-content">{{ 'RETAG.TIP_TAG' | translate }}</span>
</label>
</div>
</div>

View File

@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { Project } from "../project-policy-config/project";
import { Observable, Subject } from "rxjs/index";
import { Subject } from "rxjs/index";
import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { ProjectService } from "../service/project.service";
import { AbstractControl, FormBuilder, FormGroup, Validators } from "@angular/forms";
@ -16,6 +16,9 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
selectedProjectList: Project[] = [];
proNameChecker: Subject<string> = new Subject<string>();
imageNameForm: FormGroup;
public project: string;
public repo: string;
public tag: string;
constructor(
private fb: FormBuilder,
@ -23,9 +26,20 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
private proService: ProjectService,
) {
this.imageNameForm = this.fb.group({
projectName: ["", Validators.required],
repoName: ["", Validators.required],
tagName: ["", Validators.required],
projectName: ["", Validators.compose([
Validators.minLength(2),
Validators.required,
Validators.pattern('^[a-z0-9]+(?:[._-][a-z0-9]+)*$')
])],
repoName: ["", Validators.compose([
Validators.required,
Validators.maxLength(256),
Validators.pattern('^[a-z0-9]+(?:[._-][a-z0-9]+)*(/[a-z0-9]+(?:[._-][a-z0-9]+)*)*')
])],
tagName: ["", Validators.compose([
Validators.required,
Validators.pattern('^[\\w][\\w.-]{0,127}$')
])],
});
}
ngOnInit(): void {
@ -60,6 +74,23 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
});
}
validateProjectName(): void {
let cont = this.imageNameForm.controls["projectName"];
if (cont && cont.valid) {
this.proNameChecker.next(cont.value);
} else {
this.noProjectInfo = "PROJECT.NAME_TOOLTIP";
}
}
blurProjectInput(): void {
this.validateProjectName();
}
get form(): AbstractControl {
return this.imageNameForm;
}
get projectName(): AbstractControl {
return this.imageNameForm.get("projectName");
}
@ -78,19 +109,6 @@ export class ImageNameInputComponent implements OnInit, OnDestroy {
}
}
validateProjectName(): void {
let cont = this.imageNameForm.controls["projectName"];
if (cont && cont.valid) {
this.proNameChecker.next(cont.value);
} else {
this.noProjectInfo = "PROJECT.NAME_TOOLTIP";
}
}
blurProjectInput(): void {
this.validateProjectName();
}
leaveProjectInput(): void {
this.selectedProjectList = [];
}

View File

@ -11,7 +11,7 @@
<button type="button" class="btn btn-primary" [ngxClipboard]="digestTarget" (cbOnSuccess)="onSuccess($event)" (cbOnError)="onError($event)">{{'BUTTON.COPY' | translate}}</button>
</div>
</clr-modal>
<clr-modal class="hidden-tag" [(clrModalOpen)]="retagDialogOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="retagDialogClosable">
<clr-modal class="hidden-tag" [(clrModalOpen)]="retagDialogOpened" [clrModalStaticBackdrop]="staticBackdrop">
<h3 class="modal-title">{{ 'REPOSITORY.RETAG' | translate }}</h3>
<div class="modal-body retag-modal-body">
<div class="row col-md-12">

View File

@ -202,7 +202,7 @@
.retag-modal-body {
overflow-y: hidden;
min-height: 184px;
min-height: 450px;
padding-top: 16px;
}

View File

@ -22,7 +22,7 @@ import {
ElementRef, AfterViewInit
} from "@angular/core";
import {Subject, forkJoin} from "rxjs";
import { debounceTime , distinctUntilChanged} from 'rxjs/operators';
import { debounceTime , distinctUntilChanged, finalize} from 'rxjs/operators';
import { TranslateService } from "@ngx-translate/core";
import { State, Comparator } from "@clr/angular";
@ -97,7 +97,6 @@ export class TagComponent implements OnInit, AfterViewInit {
digestId: string;
staticBackdrop = true;
closable = false;
retagDialogClosable = true;
lastFilteredTagName: string;
inprogress: boolean;
openLabelFilterPanel: boolean;
@ -587,14 +586,21 @@ export class TagComponent implements OnInit, AfterViewInit {
}
onRetag() {
this.retagDialogOpened = false;
this.retagService.retag({
targetProject: this.imageNameInput.projectName.value,
targetRepo: this.imageNameInput.repoName.value,
targetTag: this.imageNameInput.tagName.value,
srcImage: this.retagSrcImage,
override: true
}).subscribe(response => {
})
.pipe(finalize(() => {
this.retagDialogOpened = false;
this.imageNameInput.form.reset();
}))
.subscribe(response => {
this.translateService.get('RETAG.MSG_SUCCESS').subscribe((res: string) => {
this.errorHandler.info(res);
});
}, error => {
this.errorHandler.error(error);
});

View File

@ -69,7 +69,7 @@
"RULE_USER_EXISTING": "Name is already in use.",
"EMPTY": "Name is required",
"NONEMPTY": "Can't be empty",
"REPO_TOOLTIP": "Users can not do any operations to the images in this mode."
"REPO_TOOLTIP": "Users can not do any operations to the images in this mode.",
},
"PLACEHOLDER": {
"CURRENT_PWD": "Enter current password",
@ -856,6 +856,11 @@
"MSG_SUCCESS": "Garbage Collection Successful",
"MSG_SCHEDULE_SET": "Garbage Collection schedule has been set",
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
},
"RETAG": {
"MSG_SUCCESS": "Retag successfully",
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
}
}

View File

@ -853,5 +853,11 @@
"MSG_SUCCESS": "Garbage Collection Successful",
"MSG_SCHEDULE_SET": "Garbage Collection schedule has been set",
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
},
"RETAG": {
"MSG_SUCCESS": "Retag successfully",
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
}
}

View File

@ -816,5 +816,11 @@
"MSG_SUCCESS": "Garbage Collection Successful",
"MSG_SCHEDULE_SET": "Garbage Collection schedule has been set",
"MSG_SCHEDULE_RESET": "Garbage Collection schedule has been reset"
},
"RETAG": {
"MSG_SUCCESS": "Retag successfully",
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
}
}

View File

@ -847,6 +847,12 @@
"MSG_SUCCESS":"Garbage Collection efetuado com sucesso",
"MSG_SCHEDULE_SET":"Agendamento de Garbage Collection efetuado",
"MSG_SCHEDULE_RESET":"Agendamento de Garbage Collection foi redefinido"
},
"RETAG": {
"MSG_SUCCESS": "Retag successfully",
"TIP_REPO": "A repository name is broken up into path components. A component of a repository name must be at least one lowercase, alpha-numeric characters, optionally separated by periods, dashes or underscores. More strictly, it must match the regular expression [a-z0-9]+(?:[._-][a-z0-9]+)*.If a repository name has two or more path components, they must be separated by a forward slash ('/').The total length of a repository name, including slashes, must be less the 256 characters.",
"TIP_TAG": "A tag is a label applied to a Docker image in a repository. Tags are how various images in a repository are distinguished from each other.It need to match Regex: (`[\\w][\\w.-]{0,127}`)"
}
}

View File

@ -851,5 +851,11 @@
"MSG_SUCCESS": "垃圾回收成功",
"MSG_SCHEDULE_SET": "垃圾回收定时任务设置成功",
"MSG_SCHEDULE_RESET": "垃圾回收定时任务已被重置"
},
"RETAG": {
"MSG_SUCCESS": "复制成功",
"TIP_REPO": "镜像仓库名被分解为路径组件。仓库名必须至少有一个小写字母、字母数字字符,可选句点、破折号或下划线分隔。严格意义上说,它必须匹配正则表达式[a-z0-9]+(?[.-][a-z0-9]+)*.如果仓库名有两个或多个路径组件,则它们必须用正斜杠('/')分隔。包括斜杠在内的仓库名的总长度必须小于256个字符。",
"TIP_TAG": "标签是应用于存储库中的Docker映像的一种标签它用于区分多种镜像。它需要匹配Regex([\\w][\\w.-]{0,127})"
}
}