diff --git a/src/portal/src/app/all-pipes/all-pipes.module.ts b/src/portal/src/app/all-pipes/all-pipes.module.ts new file mode 100644 index 000000000..80f69f7df --- /dev/null +++ b/src/portal/src/app/all-pipes/all-pipes.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SelectArtifactIconPipe } from './select-artifact-icon/select-artifact-icon.pipe'; + + + +@NgModule({ + declarations: [SelectArtifactIconPipe], + imports: [ + CommonModule + ], + exports: [ + SelectArtifactIconPipe + ] +}) +export class AllPipesModule { } diff --git a/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.spec.ts b/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.spec.ts new file mode 100644 index 000000000..6928752ee --- /dev/null +++ b/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.spec.ts @@ -0,0 +1,19 @@ +import { SelectArtifactIconPipe } from './select-artifact-icon.pipe'; + +describe('SelectArtifactIconPipe', () => { + let mockTypeImage = "IMAGE"; + let mockTypeChart = "CHART"; + let mockTypecnab = "CNAB"; + it('create an instance', () => { + const pipe = new SelectArtifactIconPipe(); + expect(pipe).toBeTruthy(); + }); + it('it should success get adress of icon', () => { + const pipe = new SelectArtifactIconPipe(); + expect(pipe.transform(mockTypeImage, '')).toBe('images/artifact-image.svg'); + expect(pipe.transform(mockTypeChart, '')).toBe('images/artifact-chart.svg'); + expect(pipe.transform(mockTypecnab, '')).toBe('images/artifact-cnab.svg'); + expect(pipe.transform("", '')).toBe('images/artifact-default.svg'); + + }); +}); diff --git a/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.ts b/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.ts new file mode 100644 index 000000000..876007cfa --- /dev/null +++ b/src/portal/src/app/all-pipes/select-artifact-icon/select-artifact-icon.pipe.ts @@ -0,0 +1,18 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { artifactImages, artifactDefault } from '../../project/repository/artifact/artifact'; + +@Pipe({ + name: 'selectArtifactIcon' +}) +export class SelectArtifactIconPipe implements PipeTransform { + + transform(value: string, ...args: any[]): any { + + if (artifactImages.some(image => image === value)) { + return 'images/artifact-' + value.toLowerCase() + '.svg'; + } else { + return artifactDefault; + } + } + +} diff --git a/src/portal/src/app/app.module.ts b/src/portal/src/app/app.module.ts index f9413173c..a9b969856 100644 --- a/src/portal/src/app/app.module.ts +++ b/src/portal/src/app/app.module.ts @@ -45,7 +45,7 @@ import { LabelsComponent } from './labels/labels.component'; import { ProjectQuotasComponent } from './project-quotas/project-quotas.component'; import { HarborLibraryModule } from "../lib/harbor-library.module"; import { HTTP_INTERCEPTORS } from '@angular/common/http'; - +import { AllPipesModule } from './all-pipes/all-pipes.module'; registerLocaleData(zh, 'zh-cn'); registerLocaleData(es, 'es-es'); registerLocaleData(localeFr, 'fr-fr'); @@ -84,7 +84,8 @@ export function getCurrentLanguage(translateService: TranslateService) { DeveloperCenterModule, OidcOnboardModule, LicenseModule, - HarborLibraryModule + HarborLibraryModule, + AllPipesModule ], exports: [ ], diff --git a/src/portal/src/app/config/auth/config-auth.component.ts b/src/portal/src/app/config/auth/config-auth.component.ts index a8b1d4143..24fce6010 100644 --- a/src/portal/src/app/config/auth/config-auth.component.ts +++ b/src/portal/src/app/config/auth/config-auth.component.ts @@ -20,6 +20,8 @@ import { AppConfigService } from '../../services/app-config.service'; import { ConfigurationService } from '../config.service'; import { Configuration } from "../../../lib/components/config/config"; import { ErrorHandler } from "../../../lib/utils/error-handler"; +import { errorHandler as errorHandlerFn } from "../../../lib/utils/shared/shared.utils"; + import { SystemInfoService } from "../../../lib/services"; import { clone, isEmpty, getChanges as getChangesFunc } from "../../../lib/utils/utils"; import { CONFIG_AUTH_MODE } from "../../../lib/entities/shared.const"; @@ -142,7 +144,7 @@ export class ConfigurationAuthComponent implements OnChanges, OnInit { this.msgHandler.showSuccess('CONFIG.TEST_LDAP_SUCCESS'); }, error => { this.testingOnGoing = false; - let err = error.error; + let err = errorHandlerFn(error); if (!err || !err.trim()) { err = 'UNKNOWN'; } diff --git a/src/portal/src/app/project/project.module.ts b/src/portal/src/app/project/project.module.ts index d4af80b85..32a4bce80 100644 --- a/src/portal/src/app/project/project.module.ts +++ b/src/portal/src/app/project/project.module.ts @@ -63,6 +63,7 @@ import { import { ArtifactDefaultService, ArtifactService } from "./repository/artifact/artifact.service"; import { GridViewComponent } from "./repository/gridview/grid-view.component"; import { LastTriggerComponent } from "./webhook/last-trigger/last-trigger.component"; +import { AllPipesModule } from '../all-pipes/all-pipes.module'; @NgModule({ imports: [ @@ -73,6 +74,7 @@ import { LastTriggerComponent } from "./webhook/last-trigger/last-trigger.compon HelmChartModule, SummaryModule, TagFeatureIntegrationModule, + AllPipesModule ], declarations: [ ProjectComponent, diff --git a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html index f00cb114d..da27972fe 100644 --- a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html +++ b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html @@ -161,7 +161,7 @@
+ [src]="artifact.type | selectArtifactIcon" />     @@ -202,12 +202,14 @@
- + + + diff --git a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss index 1ba5837da..ce0fdda70 100644 --- a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss +++ b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss @@ -331,9 +331,12 @@ clr-datagrid { } .width-p-100 { width: 100%; + .lg { + width: 450px; + } } .w-rem-4 { - width: 4rem !important; + min-width: 4rem !important; } .tag-header-color { color: #fff; diff --git a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts index 5e4b64688..f0edf62b2 100644 --- a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts +++ b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts @@ -32,6 +32,7 @@ import { ChannelService } from "../../../../../../lib/services/channel.service"; import { OperationService } from "../../../../../../lib/components/operation/operation.service"; import { By } from "@angular/platform-browser"; import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service"; +import { AllPipesModule } from "../../../../../all-pipes/all-pipes.module"; describe("ArtifactListTabComponent (inline template)", () => { @@ -290,6 +291,7 @@ describe("ArtifactListTabComponent (inline template)", () => { SharedModule, BrowserAnimationsModule, HttpClientTestingModule, + AllPipesModule ], schemas: [ CUSTOM_ELEMENTS_SCHEMA diff --git a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts index 21a37ecc3..b38a76329 100644 --- a/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts +++ b/src/portal/src/app/project/repository/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts @@ -12,13 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. import { - AfterViewInit, Component, ElementRef, - EventEmitter, Input, OnDestroy, OnInit, - Output, ViewChild, } from "@angular/core"; @@ -27,11 +24,9 @@ import { catchError, debounceTime, distinctUntilChanged, finalize, map } from 'r import { TranslateService } from "@ngx-translate/core"; import { ClrLoadingState, ClrDatagridStateInterface, ClrDatagridComparatorInterface } from "@clr/angular"; -import { HttpParams } from "@angular/common/http"; import { ActivatedRoute, Router } from "@angular/router"; import { Comparator, Label, LabelService, ScanningResultService, - State, UserPermissionService, USERSTATICPERMISSION, VulnerabilitySummary } from "../../../../../../lib/services"; import { @@ -59,7 +54,7 @@ import { } from "../../../../../../lib/entities/shared.const"; import { operateChanges, OperateInfo, OperationState } from "../../../../../../lib/components/operation/operate"; import { errorHandler } from "../../../../../../lib/utils/shared/shared.utils"; -import { ArtifactFront as Artifact, mutipleFilter, artifactImages, ArtifactFront } from "../../../artifact/artifact"; +import { ArtifactFront as Artifact, mutipleFilter } from "../../../artifact/artifact"; import { Project } from "../../../../project"; import { ArtifactService as NewArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service"; import { ADDITIONS } from "../../../artifact/artifact-additions/models"; @@ -373,7 +368,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { })).subscribe(artifacts => { this.artifactList = artifacts; this.getArtifactAnnotationsArray(this.artifactList); - this.getArtifactIcon(this.artifactList); }, error => { this.errorHandlerService.error(error); }); @@ -401,7 +395,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } this.artifactList = res.body; this.getArtifactAnnotationsArray(this.artifactList); - this.getArtifactIcon(this.artifactList); }, error => { // error this.errorHandlerService.error(error); @@ -985,13 +978,4 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { get isFilterReadonly() { return this.filterByType === 'labels' ? 'readonly' : null; } - getArtifactIcon(artifacts: ArtifactFront[]) { - for (const artifact of artifacts) { - if (artifactImages.some(image => image === artifact.type)) { - artifact.showImage = 'images/artifact-' + artifact.type.toLowerCase() + '.svg'; - } else { - artifact.showImage = 'images/artifact-default.svg'; - } - } - } } diff --git a/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.html b/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.html index b5d6c84ea..3093876b7 100644 --- a/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.html +++ b/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.html @@ -1,8 +1,8 @@ -

{{'ARTIFACT.EXTRA_PROPERTIES' | translate}}

+

{{'ARTIFACT.OVERVIEW' | translate}}

- {{'ARTIFACT.EXTRA_PROPERTIES' | translate}} + {{'ARTIFACT.OVERVIEW' | translate}} {{item?.key}} {{item?.value}} diff --git a/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.ts b/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.ts index 9b21e46a3..b577d8c13 100644 --- a/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.ts +++ b/src/portal/src/app/project/repository/artifact/artifact-common-properties/artifact-common-properties.component.ts @@ -36,7 +36,7 @@ export class ArtifactCommonPropertiesComponent implements OnInit, OnChanges { Object.assign(this.commonProperties, this.artifactDetails.extra_attrs); for (let name in this.commonProperties) { if (this.commonProperties.hasOwnProperty(name)) { - if (isObject(this.commonProperties[name])) { + if (typeof (this.commonProperties[name]) === 'object') { this.commonProperties[name] = JSON.stringify(this.commonProperties[name]); } if (name === Types.CREATED) { diff --git a/src/portal/src/app/project/repository/artifact/artifact-summary.component.html b/src/portal/src/app/project/repository/artifact/artifact-summary.component.html index f8f90fedc..3205290fc 100644 --- a/src/portal/src/app/project/repository/artifact/artifact-summary.component.html +++ b/src/portal/src/app/project/repository/artifact/artifact-summary.component.html @@ -1,5 +1,5 @@
-

{{artifact?.digest | slice:0:15}}

+

{{artifact?.digest | slice:0:15}}

- - - + + + { @@ -66,6 +67,7 @@ describe('ArtifactSummaryComponent', () => { TestBed.configureTestingModule({ imports: [ ClarityModule, + AllPipesModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/portal/src/app/project/repository/artifact/artifact.ts b/src/portal/src/app/project/repository/artifact/artifact.ts index dd6a1e465..f2c3ae888 100644 --- a/src/portal/src/app/project/repository/artifact/artifact.ts +++ b/src/portal/src/app/project/repository/artifact/artifact.ts @@ -51,3 +51,4 @@ export const mutipleFilter = [ export const artifactImages = [ 'IMAGE', 'CHART', 'CNAB', 'OPENPOLICYAGENT' ]; + export const artifactDefault = "images/artifact-default.svg"; diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 8d93a7df7..a8b260097 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -1026,7 +1026,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "Additions", "ANNOTATION": "Annotations", - "EXTRA_PROPERTIES": "Extra Attributes", + "OVERVIEW": "Overview", "IMAGE": "IMAGE", "CHART": "CHART", "CNAB": "CNAB", @@ -1156,6 +1156,7 @@ "UNKNOWN": "n/a", "STATUS": "Status", "START_TIME": "Start Time", + "CREATION_TIME": "Creation Time", "UPDATE_TIME": "Update Time", "LOGS": "Logs", "PENDING": "Pending", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 9c266c514..3686fd91a 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -1025,7 +1025,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "Additions", "ANNOTATION": "Annotations", - "EXTRA_PROPERTIES": "Extra Attributes", + "OVERVIEW": "Overview", "IMAGE": "IMAGE", "CHART": "CHART", "CNAB": "CNAB", @@ -1153,6 +1153,7 @@ "UNKNOWN": "n/a", "STATUS": "Status", "START_TIME": "Start Time", + "CREATION_TIME": "Creation Time", "UPDATE_TIME": "Update Time", "LOGS": "Logs", "PENDING": "Pending", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index 771fe5083..8bdee8662 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -998,7 +998,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "Additions", "ANNOTATION": "Annotations", - "EXTRA_PROPERTIES": "Extra Attributes", + "OVERVIEW": "Overview", "IMAGE": "IMAGE", "CHART": "CHART", "CNAB": "CNAB", @@ -1124,6 +1124,7 @@ "UNKNOWN": "n. d.", "STATUS": "Status", "START_TIME": "Start Time", + "CREATION_TIME": "Creation Time", "UPDATE_TIME": "Update Time", "LOGS": "Logs", "PENDING": "Pending", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 32e30dcdb..e455dc54d 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -1021,7 +1021,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "Additions", "ANNOTATION": "Annotations", - "EXTRA_PROPERTIES": "Extra Attributes", + "OVERVIEW": "Overview", "IMAGE": "IMAGE", "CHART": "CHART", "CNAB": "CNAB", @@ -1147,6 +1147,7 @@ "UNKNOWN": "n/a", "STATUS": "Status", "START_TIME": "Início", + "CREATION_TIME": "Creation Time", "END_TIME": "Finalização", "DETAILS": "Detalhes", "PENDING": "Pendente", diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index bc5db36ae..ab593e220 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -1026,7 +1026,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "Additions", "ANNOTATION": "Annotations", - "EXTRA_PROPERTIES": "Extra Attributes", + "OVERVIEW": "Overview", "IMAGE": "IMAGE", "CHART": "CHART", "CNAB": "CNAB", @@ -1156,6 +1156,7 @@ "UNKNOWN": "n/a", "STATUS": "Durum", "START_TIME": "Başlama Zamanı", + "CREATION_TIME": "Creation Time", "UPDATE_TIME": "Güncelleme Zamanı", "LOGS": "Kayıtlar", "PENDING": "Askıda", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 7c42c4435..81e2d511b 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -1025,7 +1025,7 @@ "FILTER_FOR_ARTIFACTS": "Filter Artifacts", "ADDITIONS": "其他", "ANNOTATION": "注解", - "EXTRA_PROPERTIES": "额外属性", + "OVERVIEW": "属性总览", "IMAGE": "镜像", "CHART": "CHART", "CNAB": "CNAB", @@ -1236,8 +1236,8 @@ "DISABLE": "禁用", "ENABLE": "启用", "DELETE": "删除", - "ADD_TITLE": "添加Tag保留规则", - "ADD_SUBTITLE": "为当前项目指定tag保留规则。所有tag保留规则独立计算并且适用于所有符合条件的仓库。", + "ADD_TITLE": "添加 Tag 保留规则", + "ADD_SUBTITLE": "为当前项目指定 tag 保留规则。所有 tag 保留规则独立计算并且适用于所有符合条件的仓库。", "BY_WHAT": "以镜像或天数为条件", "RULE_TEMPLATE_1": "最近#天的镜像", "RULE_TEMPLATE_2": "最近活跃的#个镜像", @@ -1251,11 +1251,11 @@ "IN_REPOSITORIES": "应用到仓库", "REP_SEPARATOR": "使用逗号分隔repos,repo*和**", "TAGS": "Tags", - "INCLUDE_UNTAGGED": " 不含tag 的 artifacts", - "UNTAGGED": " 不含tag", - "MATCHES_TAGS": "匹配tags", - "MATCHES_EXCEPT_TAGS": "排除tags", - "TAG_SEPARATOR": "输入多个逗号分隔的Tags, Tag*或**。可通过勾选将未加 Tag 的镜像作为此策略的一部分。", + "INCLUDE_UNTAGGED": " 无 Tag 的 Artifacts", + "UNTAGGED": " 无 Tag", + "MATCHES_TAGS": "匹配 tags", + "MATCHES_EXCEPT_TAGS": "排除 tags", + "TAG_SEPARATOR": "输入多个逗号分隔的 Tags, Tag*或**。可通过勾选将未加 Tag 的镜像作为此策略的一部分。", "LABELS": "标签", "MATCHES_LABELS": "匹配标签", "MATCHES_EXCEPT_LABELS": "排除标签", @@ -1300,20 +1300,20 @@ "ACTION": "操作" }, "IMMUTABLE_TAG": { - "IMMUTABLE_RULES": "不可变的Tag规则", + "IMMUTABLE_RULES": "不可变的 Tag 规则", "ADD_RULE": "添加新规则", "ADD_RULE_HELP_1": "点击添加按钮可添加规则。", "EDIT": "修改", "DISABLE": "禁用", "ENABLE": "启用", "DELETE": "删除", - "ADD_TITLE": "添加不可变的Tag规则", - "ADD_SUBTITLE": "为此项目指定不可变的Tag规则。注意:所有不可变的Tag规则都将首先被独立计算,合并后最终获得不可变规则的集合。", + "ADD_TITLE": "添加不可变的 Tag 规则", + "ADD_SUBTITLE": "为此项目指定不可变的 Tag 规则。注意:所有不可变的 Tag 规则都将首先被独立计算,合并后最终获得不可变规则的集合。", "IN_REPOSITORIES": "应用到仓库", - "REP_SEPARATOR": "使用逗号分隔repos,repo*和**。", + "REP_SEPARATOR": "使用逗号分隔 repos,repo* 和 **。", "TAGS": "Tags", - "TAG_SEPARATOR": "使用逗号分割tags,tag*和**。", - "EDIT_TITLE": "编辑不改变的Tag规则", + "TAG_SEPARATOR": "使用逗号分割 tags,tag* 和 **。", + "EDIT_TITLE": "编辑不改变的 Tag 规则", "EXC": "排除", "MAT": "匹配", "AND": "且", diff --git a/src/portal/src/lib/components/config/gc/gc-history/gc-history.component.html b/src/portal/src/lib/components/config/gc/gc-history/gc-history.component.html index e7aab8a88..45bc82554 100644 --- a/src/portal/src/lib/components/config/gc/gc-history/gc-history.component.html +++ b/src/portal/src/lib/components/config/gc/gc-history/gc-history.component.html @@ -6,7 +6,7 @@ {{'GC.JOB_ID' | translate}} {{'GC.TRIGGER_TYPE' | translate}} {{'STATUS' | translate}} - {{'START_TIME' | translate}} + {{'CREATION_TIME' | translate}} {{'UPDATE_TIME' | translate}} {{'LOGS' | translate}}
{{'REPOSITORY.TAGS_COUNT' | translate | uppercase}} + {{'REPOSITORY.SIGNED' | translate | uppercase}} {{'REPOSITORY.PULL_TIME' | translate | uppercase}} @@ -217,6 +219,16 @@
{{tag.name}} + + {{tag.pull_time === availableTime ? '':(tag.pull_time | date: 'short')}} {{tag.push_time | date: 'short'}}