Add labels on chart summary page

Add labels on chart summary

Signed-off-by: Qian Deng <dengq@vmware.com>
This commit is contained in:
Qian Deng 2018-10-19 10:28:30 +08:00
parent 7ffa135432
commit 6eb9fa53a7
16 changed files with 290 additions and 44 deletions

View File

@ -130,6 +130,26 @@
</tbody>
</table>
</div>
<div class="col-md-12 content-group">
<div>
<label>{{'HELM_CHART.LABELS' | translate }}</label>
</div>
<table class="table">
<tbody>
<tr>
<th class="left">{{'HELM_CHART.LABELS' | translate }}</th>
<th class="left">{{labels?.length}}</th>
</tr>
<tr>
<td></td>
<td class="left">
<hbr-label-piece *ngFor="let label of labels" [label]="label" [labelWidth]="90"> </hbr-label-piece>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -5,7 +5,7 @@ import {
Input
} from "@angular/core";
import { HelmChartMetaData, HelmChartSecurity } from "./../../service/interface";
import { HelmChartMetaData, HelmChartSecurity, Label } from "./../../service/interface";
import { downloadFile } from './../../utils';
import { HelmChartService } from "../../service/index";
import { ErrorHandler } from "./../../error-handler/error-handler";
@ -24,6 +24,7 @@ export class ChartDetailSummaryComponent implements OnInit {
@Input() chartName: string;
@Input() chartVersion: string;
@Input() readme: string;
@Input() labels: Label[];
copiedCMD = '';
addCMD: string;

View File

@ -28,7 +28,8 @@
[projectName]="project.name"
[chartVersion]="chartVersion"
[security]="chartDetail.security"
[readme]="chartDetail.files['README.md']"></hbr-chart-detail-summary>
[readme]="chartDetail.files['README.md']"
[labels]="chartDetail.labels"></hbr-chart-detail-summary>
</clr-tab-content>
</clr-tab>
<clr-tab>

View File

@ -57,7 +57,8 @@
[labels]="projectLabels"
[projectName]="projectName"
[resource]="selectedRows[0]"
[resourceType]="resourceType">
[resourceType]="resourceType"
(changeEvt)="onLabelChange(selectedRows[0])">
</hbr-resource-label-marker>
</clr-dropdown-menu>
</clr-dropdown>
@ -68,7 +69,7 @@
<clr-dg-column>{{'HELM_CHART.MAINTAINERS' | translate }}</clr-dg-column>
<clr-dg-column>{{'HELM_CHART.CREATED' | translate }}</clr-dg-column>
<clr-dg-column style="width: 140px;">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'HELM_CHART.NO_VERSION_PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let v of chartVersions" [clrDgItem]="v">
<clr-dg-cell>
@ -81,20 +82,9 @@
<clr-dg-cell>{{ v.engine }}</clr-dg-cell>
<clr-dg-cell>{{ getMaintainerString(v.maintainers) }}</clr-dg-cell>
<clr-dg-cell>{{ v.created | date}}</clr-dg-cell>
<clr-dg-cell style="width: 140px;">
<clr-dg-cell>
<hbr-label-piece *ngIf="v.labels?.length" [label]="v.labels[0]"> </hbr-label-piece>
<div class="signpost-item" [hidden]="v.labels?.length<=1">
<div class="trigger-item">
<clr-signpost>
<button class="btn btn-link" clrSignpostTrigger>...</button>
<clr-signpost-content [clrPosition]="'left-top'" *clrIfOpen>
<div>
<hbr-label-piece *ngFor="let label of v.labels" [label]="label"></hbr-label-piece>
</div>
</clr-signpost-content>
</clr-signpost>
</div>
</div>
<hbr-resource-label-signpost *ngIf="v.labels?.length>1" [labels]="v.labels"></hbr-resource-label-signpost>
</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>

View File

@ -39,6 +39,10 @@ clr-dg-action-bar {
}
}
hbr-resource-label-signpost {
display: inline-block;
}
.card-container {
margin-top: 21px;
.card-header {

View File

@ -1,4 +1,3 @@
import { Label } from './../../service/interface';
import {
Component,
Input,
@ -22,6 +21,7 @@ import {
HelmChartMaintainer,
LabelService
} from "./../../service/index";
import { Label } from './../../service/interface';
import { ErrorHandler } from "./../../error-handler/error-handler";
import { toPromise, DEFAULT_PAGE_SIZE, downloadFile } from "../../utils";
import { OperationService } from "./../../operation/operation.service";
@ -61,7 +61,6 @@ export class ChartVersionComponent implements OnInit {
lastFilteredVersionName: string;
chartVersions: HelmChartVersion[] = [];
versionsCopy: HelmChartVersion[] = [];
systemInfo: SystemInfo;
selectedRows: HelmChartVersion[] = [];
projectLabels: Label[] = [];
@ -85,7 +84,6 @@ export class ChartVersionComponent implements OnInit {
constructor(
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private systemInfoService: SystemInfoService,
private helmChartService: HelmChartService,
private resrouceLabelService: LabelService,
@ -116,7 +114,6 @@ export class ChartVersionComponent implements OnInit {
this.resrouceLabelService.getProjectLabels(this.projectId).subscribe(
(labels: Label[]) => {
this.projectLabels = labels;
console.log('chart version project labels', this.projectLabels);
}
);
}
@ -126,6 +123,7 @@ export class ChartVersionComponent implements OnInit {
this.helmChartService
.getChartVersions(this.projectName, this.chartName)
.pipe(finalize(() => {
this.selectedRows = [];
this.loading = false;
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 2000);
@ -133,7 +131,6 @@ export class ChartVersionComponent implements OnInit {
.subscribe(
versions => {
this.chartVersions = versions.filter(x => x.version.includes(this.lastFilteredVersionName));
this.versionsCopy = versions.map(x => Object.assign({}, x));
this.totalCount = versions.length;
},
err => {
@ -314,4 +311,14 @@ export class ChartVersionComponent implements OnInit {
return "HELM_CHART.ACTIVE";
}
}
onLabelChange(version: HelmChartVersion) {
this.resrouceLabelService.getChartVersionLabels(this.projectName, this.chartName, version.version)
.subscribe(labels => {
let versionIdx = this.chartVersions.findIndex(v => v.name === version.name);
this.chartVersions[versionIdx].labels = labels;
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 2000);
});
}
}

View File

@ -1,8 +1,10 @@
import { Type } from '@angular/core';
import { LabelComponent} from "./label.component";
import { LabelMarkerComponent } from './label-marker/label-marker.component';
import { LabelSignPostComponent } from './label-signpost/label-signpost.component';
export const LABEL_DIRECTIVES: Type<any>[] = [
LabelComponent,
LabelMarkerComponent
LabelMarkerComponent,
LabelSignPostComponent
];

View File

@ -1,28 +1,11 @@
// Copyright (c) 2018 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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, Input, Output, OnInit, EventEmitter, ChangeDetectorRef, ViewChild, ElementRef } from '@angular/core';
import { Observable, fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { finalize } from 'rxjs/operators';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, finalize } from 'rxjs/operators';
import { RepositoryItem, HelmChartVersion } from './../../service/interface';
import {Label} from "../../service/interface";
import { ResourceType } from '../../shared/shared.const';
import { LabelService } from '../../service/label.service';
import { TranslateService } from '@ngx-translate/core';
import { ErrorHandler } from '../../error-handler/error-handler';
@Component({
@ -37,6 +20,7 @@ export class LabelMarkerComponent implements OnInit {
@Input() projectName: string;
@Input() resource: RepositoryItem | HelmChartVersion;
@Input() resourceType: ResourceType;
@Output() changeEvt = new EventEmitter<any>();
labelFilter = '';
markedMap: Map<number, boolean> = new Map<number, boolean>();
@ -45,6 +29,8 @@ export class LabelMarkerComponent implements OnInit {
loading = false;
labelChangeDebouncer: Subject<any> = new Subject();
@ViewChild('filterInput') filterInputRef: ElementRef;
ngOnInit(): void {
@ -53,12 +39,13 @@ export class LabelMarkerComponent implements OnInit {
fromEvent(this.filterInputRef.nativeElement, 'keyup')
.pipe(debounceTime(500))
.subscribe(() => this.refresh());
this.labelChangeDebouncer.pipe(debounceTime(1000)).subscribe(() => this.changeEvt.emit());
}
constructor(
private labelService: LabelService,
private errorHandler: ErrorHandler,
private translateService: TranslateService,
private cdr: ChangeDetectorRef) {}
refresh() {
@ -75,7 +62,6 @@ export class LabelMarkerComponent implements OnInit {
}))
.subscribe( chartVersionLabels => {
for (let label of chartVersionLabels) {
console.log('marked label', label);
this.markedMap.set(label.id, true);
}
this.sortedLabels = this.getSortedLabels();
@ -102,6 +88,7 @@ export class LabelMarkerComponent implements OnInit {
() => {
this.markedMap.set(label.id, true);
this.refresh();
this.labelChangeDebouncer.next();
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
},
@ -128,6 +115,7 @@ export class LabelMarkerComponent implements OnInit {
() => {
this.markedMap.set(label.id, false);
this.refresh();
this.labelChangeDebouncer.next();
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
},

View File

@ -0,0 +1,12 @@
<div class="trigger-item">
<clr-signpost>
<button class="btn btn-link" clrSignpostTrigger>
...
</button>
<clr-signpost-content [clrPosition]="'left-top'" *clrIfOpen>
<div *ngFor="let label of labels">
<hbr-label-piece [label]="label" [labelWidth]="130"></hbr-label-piece>
</div>
</clr-signpost-content>
</clr-signpost>
</div>

View File

@ -0,0 +1,21 @@
clr-signpost {
button {
min-width: 0;
margin: 0;
height: 24px;
vertical-align: super;
}
clr-signpost-content {
::ng-deep {
.signpost-content-header {
display: none;
}
.signpost-content-body {
padding-bottom: 6px;
padding-top: 6px;
}
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2018 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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, Input, OnInit } from '@angular/core';
import {Label} from "../../service/interface";
@Component({
selector: 'hbr-resource-label-signpost',
templateUrl: './label-signpost.component.html',
styleUrls: ['./label-signpost.component.scss']
})
export class LabelSignPostComponent implements OnInit {
@Input() labels: Label[] = [];
sortedLabels: Label[] = [];
ngOnInit(): void {
}
constructor() {}
}

View File

@ -0,0 +1,159 @@
import { Observable} from "rxjs";
import { Label } from "./interface";
import { Inject, Injectable } from "@angular/core";
import { Http } from "@angular/http";
import { IServiceConfig, SERVICE_CONFIG } from "../service.config";
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from "../utils";
import { RequestQueryParams } from "./RequestQueryParams";
export abstract class LabelService {
abstract getLabels(
scope: string,
projectId?: number,
name?: string,
queryParams?: RequestQueryParams
): Observable<Label[]>;
abstract createLabel(
label: Label
): Observable<Label> | Promise<Label> | Label;
abstract getLabel(id: number): Observable<Label> | Promise<Label> | Label;
abstract updateLabel(
id: number,
param: Label
): Observable<any> | Promise<any> | any;
abstract deleteLabel(id: number): Observable<any> | Promise<any> | any;
}
@Injectable()
export class LabelDefaultService extends LabelService {
_labelUrl: string;
constructor(
@Inject(SERVICE_CONFIG) config: IServiceConfig,
private http: Http
) {
super();
this._labelUrl = config.labelEndpoint
? config.labelEndpoint
: "/api/labels";
}
getLabels(
scope: string,
projectId?: number,
name?: string,
queryParams?: RequestQueryParams
): Observable<Label[]> | Promise<Label[]> {
if (!queryParams) {
queryParams = new RequestQueryParams();
}
if (scope) {
queryParams.set("scope", scope);
}
if (projectId) {
queryParams.set("project_id", "" + projectId);
}
if (name) {
queryParams.set("name", "" + name);
}
return this.http
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
.toPromise()
.then(response => response.json())
.catch(error => Promise.reject(error));
}
getGLabels(
name?: string,
queryParams?: RequestQueryParams
): Observable<Label[]> | Promise<Label[]> {
if (!queryParams) {
queryParams = new RequestQueryParams();
}
queryParams.set("scope", "g");
if (name) {
queryParams.set("name", "" + name);
}
return this.http
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
.toPromise()
.then(response => response.json())
.catch(error => Promise.reject(error));
}
getPLabels(
projectId: number,
name?: string,
queryParams?: RequestQueryParams
): Observable<Label[]> | Promise<Label[]> {
if (!queryParams) {
queryParams = new RequestQueryParams();
}
queryParams.set("scope", "p");
if (projectId) {
queryParams.set("project_id", "" + projectId);
}
if (name) {
queryParams.set("name", "" + name);
}
return this.http
.get(this._labelUrl, buildHttpRequestOptions(queryParams))
.toPromise()
.then(response => response.json())
.catch(error => Promise.reject(error));
}
createLabel(label: Label): Observable<any> | Promise<any> | any {
if (!label) {
return Promise.reject("Invalid label.");
}
return this.http
.post(this._labelUrl, JSON.stringify(label), HTTP_JSON_OPTIONS)
.toPromise()
.then(response => response.status)
.catch(error => Promise.reject(error));
}
getLabel(id: number): Observable<Label> | Promise<Label> | Label {
if (!id || id <= 0) {
return Promise.reject("Bad request argument.");
}
let reqUrl = `${this._labelUrl}/${id}`;
return this.http
.get(reqUrl)
.toPromise()
.then(response => response.json())
.catch(error => Promise.reject(error));
}
updateLabel(id: number, label: Label): Observable<any> | Promise<any> | any {
if (!id || id <= 0) {
return Promise.reject("Bad request argument.");
}
if (!label) {
return Promise.reject("Invalid endpoint.");
}
let reqUrl = `${this._labelUrl}/${id}`;
return this.http
.put(reqUrl, JSON.stringify(label), HTTP_JSON_OPTIONS)
.toPromise()
.then(response => response.status)
.catch(error => Promise.reject(error));
}
deleteLabel(id: number): Observable<any> | Promise<any> | any {
if (!id || id <= 0) {
return Promise.reject("Bad request argument.");
}
let reqUrl = `${this._labelUrl}/${id}`;
return this.http
.delete(reqUrl)
.toPromise()
.then(response => response.status)
.catch(error => Promise.reject(error));
}
}

View File

@ -528,6 +528,7 @@
"PROV_FILE": "Prov File",
"READY": "Ready",
"NOT_READY": "Not Ready",
"LABELS": "labels",
"STATUS": "Status"
},
"ALERT": {

View File

@ -527,6 +527,7 @@
"PROV_FILE": "Prov File",
"READY": "Ready",
"NOT_READY": "Not Ready",
"LABELS": "labels",
"STATUS": "Status"
},
"ALERT": {

View File

@ -503,6 +503,7 @@
"PROV_FILE": "Prov File",
"READY": "Ready",
"NOT_READY": "Not Ready",
"LABELS": "labels",
"STATUS": "Status"
},
"ALERT": {

View File

@ -527,6 +527,7 @@
"PROV_FILE": "Prov 文件",
"READY": "就绪",
"NOT_READY": "未就绪",
"LABELS": "标签",
"STATUS": "状态"
},
"ALERT": {