mirror of
https://github.com/goharbor/harbor.git
synced 2024-10-01 23:07:39 +02:00
Add stackview for repo-tags
This commit is contained in:
parent
963efc5253
commit
e7132c5252
@ -4,6 +4,8 @@ import { LOG_DIRECTIVES } from './log/index';
|
|||||||
import { FILTER_DIRECTIVES } from './filter/index';
|
import { FILTER_DIRECTIVES } from './filter/index';
|
||||||
import { ENDPOINT_DIRECTIVES } from './endpoint/index';
|
import { ENDPOINT_DIRECTIVES } from './endpoint/index';
|
||||||
import { REPOSITORY_DIRECTIVES } from './repository/index';
|
import { REPOSITORY_DIRECTIVES } from './repository/index';
|
||||||
|
import { REPOSITORY_STACKVIEW_DIRECTIVES } from './repository-stackview/index';
|
||||||
|
|
||||||
import { LIST_REPOSITORY_DIRECTIVES } from './list-repository/index';
|
import { LIST_REPOSITORY_DIRECTIVES } from './list-repository/index';
|
||||||
import { TAG_DIRECTIVES } from './tag/index';
|
import { TAG_DIRECTIVES } from './tag/index';
|
||||||
|
|
||||||
@ -135,6 +137,7 @@ export function initConfig(translateService: TranslateService, config: IServiceC
|
|||||||
FILTER_DIRECTIVES,
|
FILTER_DIRECTIVES,
|
||||||
ENDPOINT_DIRECTIVES,
|
ENDPOINT_DIRECTIVES,
|
||||||
REPOSITORY_DIRECTIVES,
|
REPOSITORY_DIRECTIVES,
|
||||||
|
REPOSITORY_STACKVIEW_DIRECTIVES,
|
||||||
LIST_REPOSITORY_DIRECTIVES,
|
LIST_REPOSITORY_DIRECTIVES,
|
||||||
TAG_DIRECTIVES,
|
TAG_DIRECTIVES,
|
||||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||||
@ -151,6 +154,7 @@ export function initConfig(translateService: TranslateService, config: IServiceC
|
|||||||
FILTER_DIRECTIVES,
|
FILTER_DIRECTIVES,
|
||||||
ENDPOINT_DIRECTIVES,
|
ENDPOINT_DIRECTIVES,
|
||||||
REPOSITORY_DIRECTIVES,
|
REPOSITORY_DIRECTIVES,
|
||||||
|
REPOSITORY_STACKVIEW_DIRECTIVES,
|
||||||
LIST_REPOSITORY_DIRECTIVES,
|
LIST_REPOSITORY_DIRECTIVES,
|
||||||
TAG_DIRECTIVES,
|
TAG_DIRECTIVES,
|
||||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||||
|
@ -7,6 +7,7 @@ export * from './log/index';
|
|||||||
export * from './filter/index';
|
export * from './filter/index';
|
||||||
export * from './endpoint/index';
|
export * from './endpoint/index';
|
||||||
export * from './repository/index';
|
export * from './repository/index';
|
||||||
|
export * from './repository-stackview/index';
|
||||||
export * from './tag/index';
|
export * from './tag/index';
|
||||||
export * from './replication/index';
|
export * from './replication/index';
|
||||||
export * from './vulnerability-scanning/index';
|
export * from './vulnerability-scanning/index';
|
6
src/ui_ng/lib/src/repository-stackview/index.ts
Normal file
6
src/ui_ng/lib/src/repository-stackview/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
import { RepositoryStackviewComponent } from './repository-stackview.component';
|
||||||
|
|
||||||
|
export const REPOSITORY_STACKVIEW_DIRECTIVES: Type<any>[] = [
|
||||||
|
RepositoryStackviewComponent
|
||||||
|
];
|
@ -0,0 +1,17 @@
|
|||||||
|
export const REPOSITORY_STACKVIEW_STYLES: string = `
|
||||||
|
.option-right {
|
||||||
|
padding-right: 16px;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-grid-custom {
|
||||||
|
position: relative;
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host >>> .datagrid .datagrid-body .datagrid-row {
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
`;
|
@ -0,0 +1,34 @@
|
|||||||
|
export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
|
||||||
|
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<div class="row flex-items-xs-right option-right">
|
||||||
|
<div class="flex-xs-middle">
|
||||||
|
<hbr-filter filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>
|
||||||
|
<a href="javascript:void(0)" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||||
|
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *clrDgItems="let r of repositories">
|
||||||
|
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
|
||||||
|
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||||
|
</clr-dg-action-overflow>
|
||||||
|
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
|
||||||
|
<hbr-tag style="min-height: 145px;" *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [sessionInfo]="sessionInfo" [projectId]="projectId" [isEmbeded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
|
||||||
|
{{pagination.totalItems}}{{'REPOSITORY.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,109 @@
|
|||||||
|
import { Component, Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
import { REPOSITORY_STACKVIEW_TEMPLATE } from './repository-stackview.component.html';
|
||||||
|
import { REPOSITORY_STACKVIEW_STYLES } from './repository-stackview.component.css';
|
||||||
|
|
||||||
|
import { Repository, SessionInfo } from '../service/interface';
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { RepositoryService } from '../service/repository.service';
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
|
||||||
|
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||||
|
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'hbr-repository-stackview',
|
||||||
|
template: REPOSITORY_STACKVIEW_TEMPLATE,
|
||||||
|
styles: [ REPOSITORY_STACKVIEW_STYLES ],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class RepositoryStackviewComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() sessionInfo: SessionInfo;
|
||||||
|
|
||||||
|
lastFilteredRepoName: string;
|
||||||
|
|
||||||
|
totalPage: number;
|
||||||
|
totalRecordCount: number;
|
||||||
|
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
|
repositories: Repository[];
|
||||||
|
|
||||||
|
@ViewChild('confirmationDialog')
|
||||||
|
confirmationDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private repositoryService: RepositoryService,
|
||||||
|
private ref: ChangeDetectorRef){}
|
||||||
|
|
||||||
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
||||||
|
if (message &&
|
||||||
|
message.source === ConfirmationTargets.REPOSITORY &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED) {
|
||||||
|
let repoName = message.data;
|
||||||
|
toPromise<number>(this.repositoryService
|
||||||
|
.deleteRepository(repoName))
|
||||||
|
.then(
|
||||||
|
response => {
|
||||||
|
this.refresh();
|
||||||
|
this.translateService.get('REPOSITORY.DELETED_REPO_SUCCESS')
|
||||||
|
.subscribe(res=>this.errorHandler.info(res));
|
||||||
|
}).catch(error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if(!this.projectId) {
|
||||||
|
this.errorHandler.error('Project ID cannot be unset.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!this.sessionInfo) {
|
||||||
|
this.errorHandler.error('Session info cannot be unset.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasProjectAdminRole = this.sessionInfo.hasProjectAdminRole || false;
|
||||||
|
this.lastFilteredRepoName = '';
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve(state?: State) {
|
||||||
|
toPromise<Repository[]>(this.repositoryService
|
||||||
|
.getRepositories(this.projectId, this.lastFilteredRepoName))
|
||||||
|
.then(
|
||||||
|
repos => this.repositories = repos,
|
||||||
|
error => this.errorHandler.error(error));
|
||||||
|
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
|
||||||
|
setTimeout(()=>clearInterval(hnd), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearchRepoNames(repoName: string) {
|
||||||
|
this.lastFilteredRepoName = repoName;
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRepo(repoName: string) {
|
||||||
|
let message = new ConfirmationMessage(
|
||||||
|
'REPOSITORY.DELETION_TITLE_REPO',
|
||||||
|
'REPOSITORY.DELETION_SUMMARY_REPO',
|
||||||
|
repoName,
|
||||||
|
repoName,
|
||||||
|
ConfirmationTargets.REPOSITORY,
|
||||||
|
ConfirmationButtons.DELETE_CANCEL);
|
||||||
|
this.confirmationDialog.open(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,17 @@
|
|||||||
export const TAG_STYLE = `
|
export const TAG_STYLE = `
|
||||||
.sub-header-title {
|
.sub-header-title {
|
||||||
margin-top: 12px;
|
margin: 12px 0;
|
||||||
}`;
|
}
|
||||||
|
.embeded-datagrid {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
.hidden-tag {
|
||||||
|
display: block; height: 0;
|
||||||
|
}
|
||||||
|
:host >>> .datagrid {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
:host >>> .datagrid-placeholder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
@ -1,6 +1,6 @@
|
|||||||
export const TAG_TEMPLATE = `
|
export const TAG_TEMPLATE = `
|
||||||
<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
<confirmation-dialog class="hidden-tag" #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
|
||||||
<clr-modal [(clrModalOpen)]="showTagManifestOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
<clr-modal class="hidden-tag" [(clrModalOpen)]="showTagManifestOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||||
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row col-md-12">
|
<div class="row col-md-12">
|
||||||
@ -11,8 +11,8 @@ export const TAG_TEMPLATE = `
|
|||||||
<button type="button" class="btn btn-primary" (click)="showTagManifestOpened = false">{{'BUTTON.OK' | translate}}</button>
|
<button type="button" class="btn btn-primary" (click)="showTagManifestOpened = false">{{'BUTTON.OK' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
<h2 class="sub-header-title">{{repoName}}</h2>
|
<h2 *ngIf="!isEmbeded" class="sub-header-title">{{repoName}}</h2>
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbeded">
|
||||||
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'REPOSITORY.PULL_COMMAND' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.PULL_COMMAND' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column *ngIf="withNotary">{{'REPOSITORY.SIGNED' | translate}}</clr-dg-column>
|
<clr-dg-column *ngIf="withNotary">{{'REPOSITORY.SIGNED' | translate}}</clr-dg-column>
|
||||||
@ -43,7 +43,7 @@ export const TAG_TEMPLATE = `
|
|||||||
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer *ngIf="!isEmbeded">
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
|
||||||
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10"></clr-dg-pagination>
|
<clr-dg-pagination #pagination [clrDgPageSize]="10"></clr-dg-pagination>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit, ViewChild, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
|
|
||||||
import { TagService } from '../service/tag.service';
|
import { TagService } from '../service/tag.service';
|
||||||
import { ErrorHandler } from '../error-handler/error-handler';
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
@ -43,6 +43,9 @@ export class TagComponent implements OnInit {
|
|||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() repoName: string;
|
@Input() repoName: string;
|
||||||
@Input() sessionInfo: SessionInfo;
|
@Input() sessionInfo: SessionInfo;
|
||||||
|
@Input() isEmbeded: boolean;
|
||||||
|
|
||||||
|
@Output() refreshRepo = new EventEmitter<boolean>();
|
||||||
|
|
||||||
hasProjectAdminRole: boolean;
|
hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
@ -123,6 +126,9 @@ export class TagComponent implements OnInit {
|
|||||||
.then(items => {
|
.then(items => {
|
||||||
this.tags = items;
|
this.tags = items;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
if(this.tags && this.tags.length === 0) {
|
||||||
|
this.refreshRepo.emit(true);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
|
Loading…
Reference in New Issue
Block a user