diff --git a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.html b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.html index 6864313a5..5bb0093c2 100644 --- a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.html +++ b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.html @@ -60,7 +60,7 @@ {{'ROBOT_ACCOUNT.ENABLED_STATE' | translate}} {{"SYSTEM_ROBOT.PROJECTS" | translate}} {{'ROBOT_ACCOUNT.CREATETION' | translate}} - {{'SYSTEM_ROBOT.EXPIRES_AT' | translate}} + {{'SYSTEM_ROBOT.EXPIRES_IN' | translate}} {{'ROBOT_ACCOUNT.DESCRIPTION' | translate}} {{ 'SYSTEM_ROBOT.NOT_FOUND' | translate @@ -96,7 +96,7 @@ {{r.creation_time | harborDatetime: 'short'}} - {{r.expires_at === -1?("ROBOT_ACCOUNT.NEVER_EXPIRED" | translate):(r.expires_at * 1000 | harborDatetime: 'short')}} + {{r.description}} diff --git a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.spec.ts b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.spec.ts index f74755356..ef3722561 100644 --- a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.spec.ts +++ b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.spec.ts @@ -12,7 +12,13 @@ import { ProjectService } from "../../../../../ng-swagger-gen/services/project.s import { MessageHandlerService } from "../../../shared/services/message-handler.service"; import { OperationService } from "../../../shared/components/operation/operation.service"; import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service"; -import { SharedTestingModule } from "../../../shared/shared.module"; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { CommonModule } from '@angular/common'; +import { ClarityModule } from '@clr/angular'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HarborDatetimePipe } from '../../../shared/pipes/harbor-datetime.pipe'; describe('SystemRobotAccountsComponent', () => { let component: SystemRobotAccountsComponent; @@ -119,10 +125,16 @@ describe('SystemRobotAccountsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - SharedTestingModule + TranslateModule.forRoot(), + CommonModule, + ClarityModule, + HttpClientTestingModule, + RouterTestingModule, + BrowserAnimationsModule, ], - declarations: [ SystemRobotAccountsComponent ], + declarations: [ SystemRobotAccountsComponent, HarborDatetimePipe], providers: [ + TranslateService, { provide: MessageHandlerService, useValue: fakedMessageHandlerService }, ConfirmationDialogService, OperationService, diff --git a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.ts b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.ts index 88a539a3c..743882b14 100644 --- a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.ts +++ b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-accounts.component.ts @@ -28,6 +28,7 @@ import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../ import { errorHandler } from "../../../shared/units/shared.utils"; import { ConfirmationMessage } from "../../global-confirmation-dialog/confirmation-message"; import { RobotPermission } from "../../../../../ng-swagger-gen/models/robot-permission"; +import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service'; const FIRST_PROJECTS_PAGE_SIZE: number = 100; @Component({ @@ -56,6 +57,7 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy { searchSub: Subscription; searchKey: string; subscription: Subscription; + deltaTime: number; // the different between server time and local time constructor(private robotService: RobotService, private projectService: ProjectService, private msgHandler: MessageHandlerService, @@ -63,8 +65,10 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy { private operationService: OperationService, private sanitizer: DomSanitizer, private translate: TranslateService, + private systemInfoService: SysteminfoService ) {} ngOnInit() { + this.getCurrenTime(); this.loadDataFromBackend(); if (!this.searchSub) { this.searchSub = this.filterComponent.filterTerms.pipe( @@ -124,6 +128,15 @@ export class SystemRobotAccountsComponent implements OnInit, OnDestroy { this.subscription = null; } } + getCurrenTime() { + this.systemInfoService.getSystemInfo().subscribe( + res => { + if (res?.current_time) { + this.deltaTime = new Date().getTime() - new Date(res?.current_time).getTime(); + } + } + ); + } loadDataFromBackend() { this.loadingData = true; this.addBtnState = ClrLoadingState.LOADING; diff --git a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts index 1b17b88f9..1597fa5f8 100644 --- a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts +++ b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts @@ -172,3 +172,9 @@ export function onlyHasPushPermission(access: Access[]): boolean { } return false; } + +export enum RobotTimeRemainColor { + GREEN = 'green', + WARNING = 'yellow', + EXPIRED = 'red' +} diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts index 7cfc5ad36..7946c87e9 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts @@ -31,6 +31,7 @@ import { Registry } from "../../../../../../../../../ng-swagger-gen/models/regis import { AppConfigService } from "../../../../../../../services/app-config.service"; import { ArtifactListPageService } from '../../artifact-list-page.service'; import { ClrLoadingState } from '@clr/angular'; +import { Accessory } from "ng-swagger-gen/models/accessory"; describe("ArtifactListTabComponent (inline template)", () => { @@ -263,6 +264,16 @@ describe("ArtifactListTabComponent (inline template)", () => { } }, + listAccessoriesResponse() { + const res: HttpResponse> = new HttpResponse>({ + headers: new HttpHeaders({'x-total-count': '0'}), + body: [] + }); + return of(res).pipe(delay(0)); + }, + listAccessories() { + return of(null).pipe(delay(0)); + }, listArtifactsResponse: () => { if (filtereName === 'sha256:3e33e3e3') { return of( diff --git a/src/portal/src/app/base/project/robot-account/robot-account.component.html b/src/portal/src/app/base/project/robot-account/robot-account.component.html index a2aef2618..1975c7f29 100644 --- a/src/portal/src/app/base/project/robot-account/robot-account.component.html +++ b/src/portal/src/app/base/project/robot-account/robot-account.component.html @@ -59,7 +59,7 @@ {{'ROBOT_ACCOUNT.ENABLED_STATE' | translate}} {{"SYSTEM_ROBOT.PERMISSION_COLUMN" | translate}} {{'ROBOT_ACCOUNT.CREATETION' | translate}} - {{'SYSTEM_ROBOT.EXPIRES_AT' | translate}} + {{'SYSTEM_ROBOT.EXPIRES_IN' | translate}} {{'ROBOT_ACCOUNT.DESCRIPTION' | translate}} {{ 'SYSTEM_ROBOT.NOT_FOUND' | translate @@ -89,7 +89,7 @@ {{r.creation_time | harborDatetime: 'short'}} - {{r.expires_at === -1?("ROBOT_ACCOUNT.NEVER_EXPIRED" | translate):(r.expires_at * 1000 | harborDatetime: 'short')}} + {{r.description}} diff --git a/src/portal/src/app/base/project/robot-account/robot-account.component.spec.ts b/src/portal/src/app/base/project/robot-account/robot-account.component.spec.ts index a01f6a243..5d10390c3 100644 --- a/src/portal/src/app/base/project/robot-account/robot-account.component.spec.ts +++ b/src/portal/src/app/base/project/robot-account/robot-account.component.spec.ts @@ -1,7 +1,7 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of, Subscription } from 'rxjs'; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, RouterModule } from "@angular/router"; import { MessageHandlerService } from "../../../shared/services/message-handler.service"; import { RobotAccountComponent } from './robot-account.component'; import { UserPermissionService } from "../../../shared/services"; @@ -12,7 +12,13 @@ import { Robot } from "../../../../../ng-swagger-gen/models/robot"; import { delay } from "rxjs/operators"; import { Action, PermissionsKinds, Resource } from "../../left-side-nav/system-robot-accounts/system-robot-util"; import { ConfirmationDialogService } from "../../global-confirmation-dialog/confirmation-dialog.service"; -import { SharedTestingModule } from "../../../shared/shared.module"; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { CommonModule } from '@angular/common'; +import { ClarityModule } from '@clr/angular'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HarborDatetimePipe } from '../../../shared/pipes/harbor-datetime.pipe'; describe('RobotAccountComponent', () => { let component: RobotAccountComponent; @@ -106,9 +112,15 @@ describe('RobotAccountComponent', () => { NO_ERRORS_SCHEMA ], imports: [ - SharedTestingModule, + TranslateModule.forRoot(), + CommonModule, + ClarityModule, + HttpClientTestingModule, + RouterTestingModule, + BrowserAnimationsModule, ], providers: [ + TranslateService, { provide: ActivatedRoute, useValue: { snapshot: { @@ -127,7 +139,7 @@ describe('RobotAccountComponent', () => { { provide: UserPermissionService, useValue: mockUserPermissionService }, { provide: RobotService, useValue: fakedRobotService}, ], - declarations: [RobotAccountComponent] + declarations: [RobotAccountComponent, HarborDatetimePipe] }).compileComponents(); })); diff --git a/src/portal/src/app/base/project/robot-account/robot-account.component.ts b/src/portal/src/app/base/project/robot-account/robot-account.component.ts index 13365e47d..1cebf9065 100644 --- a/src/portal/src/app/base/project/robot-account/robot-account.component.ts +++ b/src/portal/src/app/base/project/robot-account/robot-account.component.ts @@ -25,6 +25,7 @@ import { ConfirmationDialogService } from "../../global-confirmation-dialog/conf import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../shared/entities/shared.const"; import { errorHandler } from "../../../shared/units/shared.utils"; import { ConfirmationMessage } from "../../global-confirmation-dialog/confirmation-message"; +import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service'; @Component({ selector: "app-robot-account", @@ -55,6 +56,7 @@ export class RobotAccountComponent implements OnInit, OnDestroy { hasRobotReadPermission: boolean; projectId: number; projectName: string; + deltaTime: number; // the different between server time and local time constructor(private robotService: RobotService, private msgHandler: MessageHandlerService, private operateDialogService: ConfirmationDialogService, @@ -63,8 +65,10 @@ export class RobotAccountComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private translate: TranslateService, private sanitizer: DomSanitizer, + private systemInfoService: SysteminfoService ) {} ngOnInit() { + this.getCurrenTime(); this.projectId = +this.route.snapshot.parent.parent.params["id"]; let resolverData = this.route.snapshot.parent.parent.data; if (resolverData) { @@ -121,6 +125,15 @@ export class RobotAccountComponent implements OnInit, OnDestroy { ); } } + getCurrenTime() { + this.systemInfoService.getSystemInfo().subscribe( + res => { + if (res?.current_time) { + this.deltaTime = new Date().getTime() - new Date(res?.current_time).getTime(); + } + } + ); + } getPermissionsList(): void { let permissionsList = []; permissionsList.push(this.userPermissionService.getPermission(this.projectId, diff --git a/src/portal/src/app/shared/components/remaining-time/remaining-time.component.html b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.html new file mode 100644 index 000000000..69bb6af90 --- /dev/null +++ b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.html @@ -0,0 +1 @@ +{{timeRemain | translate}} diff --git a/src/portal/src/app/shared/components/remaining-time/remaining-time.component.scss b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/portal/src/app/shared/components/remaining-time/remaining-time.component.spec.ts b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.spec.ts new file mode 100644 index 000000000..a90b7ba88 --- /dev/null +++ b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.spec.ts @@ -0,0 +1,62 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RemainingTimeComponent } from './remaining-time.component'; +import { Component, ViewChild } from '@angular/core'; +import { Project } from '../../../../../ng-swagger-gen/models/project'; +import { RobotTimeRemainColor } from '../../../base/left-side-nav/system-robot-accounts/system-robot-util'; +import { SharedTestingModule } from '../../shared.module'; + + +describe('RemainingTimeComponent', () => { + let component: TestHostComponent; + let fixture: ComponentFixture; + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + SharedTestingModule + ], + declarations: [TestHostComponent, RemainingTimeComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestHostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + it('should show green color', () => { + fixture.detectChanges(); + expect(component?.remainingTimeComponent.color).toEqual(RobotTimeRemainColor.GREEN); + expect(component?.remainingTimeComponent.timeRemain).toEqual('ROBOT_ACCOUNT.NEVER_EXPIRED'); + }); + it('should show yellow color', () => { + component.deltaTime = 0; + component.expires_at = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 29).getTime() / 1000; + fixture.detectChanges(); + expect(component?.remainingTimeComponent.color).toEqual(RobotTimeRemainColor.WARNING); + expect(component?.remainingTimeComponent.timeRemain).toEqual('29d 0h 0m'); + }); + it('should show red color', () => { + component.deltaTime = 0; + component.expires_at = new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 31).getTime() / 1000; + fixture.detectChanges(); + expect(component?.remainingTimeComponent.color).toEqual(RobotTimeRemainColor.EXPIRED); + expect(component?.remainingTimeComponent.timeRemain).toEqual('SYSTEM_ROBOT.EXPIRED'); + }); +}); + + +// mock a TestHostComponent for ListProjectROComponent +@Component({ + template: ` + ` +}) +class TestHostComponent { + @ViewChild(RemainingTimeComponent) + remainingTimeComponent: RemainingTimeComponent; + expires_at: number = -1; + deltaTime: number = 100; +} diff --git a/src/portal/src/app/shared/components/remaining-time/remaining-time.component.ts b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.ts new file mode 100644 index 000000000..ca988f757 --- /dev/null +++ b/src/portal/src/app/shared/components/remaining-time/remaining-time.component.ts @@ -0,0 +1,83 @@ +// Copyright Project Harbor Authors +// +// 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, + OnDestroy, SimpleChanges, OnChanges, +} from '@angular/core'; +import { RobotTimeRemainColor } from '../../../base/left-side-nav/system-robot-accounts/system-robot-util'; +const SEC: number = 1000; +const MIN: number = 60 * 1000; +const DAY: number = 1000 * 60 * 60 * 24; +const HOUR: number = 1000 * 60 * 60; +const WARNING_DAYS = 30; +@Component({ + selector: 'app-remaining-time', + templateUrl: 'remaining-time.component.html', + styleUrls: ['./remaining-time.component.scss'] +}) +export class RemainingTimeComponent implements OnInit, OnDestroy, OnChanges { + color: string; + timeRemain: string; + @Input() + timeDiff: number; // the different between server time and local time, unit millisecond, localTime - serverTime + @Input() + deadline: number; // unit second + intelVal: any; + constructor() { } + + ngOnInit() { + if (!this.intelVal) { + this.intelVal = setInterval(() => { + this.refreshTimeRemain(); + }, MIN); + } + } + ngOnDestroy() { + if (this.intelVal) { + clearInterval(this.intelVal); + this.intelVal = null; + } + } + ngOnChanges(changes: SimpleChanges): void { + if (changes && changes["timeDiff"] && changes["deadline"]) { + this.refreshTimeRemain(); + } + } + refreshTimeRemain() { + if (this.timeDiff !== null && this.deadline !== null) { + if (this.deadline === -1) { + this.color = RobotTimeRemainColor.GREEN; + this.timeRemain = 'ROBOT_ACCOUNT.NEVER_EXPIRED'; + return; + } + const time = new Date(this.deadline * SEC).getTime() - new Date(new Date().getTime() - this.timeDiff).getTime(); + if (time > 0) { + const days = Math.floor(time / DAY); + const hours = Math.floor((time % DAY) / HOUR); + const minutes = Math.floor((time % HOUR) / MIN); + this.timeRemain = `${days}d ${hours}h ${minutes}m`; + if (days >= WARNING_DAYS) { + this.color = RobotTimeRemainColor.GREEN; + } else { + this.color = RobotTimeRemainColor.WARNING; + } + } else { + this.color = RobotTimeRemainColor.EXPIRED; + this.timeRemain = 'SYSTEM_ROBOT.EXPIRED'; + } + } + } +} diff --git a/src/portal/src/app/shared/shared.module.ts b/src/portal/src/app/shared/shared.module.ts index 79dd0a12b..c9c6b8120 100644 --- a/src/portal/src/app/shared/shared.module.ts +++ b/src/portal/src/app/shared/shared.module.ts @@ -78,6 +78,7 @@ import localeFr from '@angular/common/locales/fr'; import localePt from '@angular/common/locales/pt-PT'; import localeTr from '@angular/common/locales/tr'; import localeDe from '@angular/common/locales/de'; +import { RemainingTimeComponent } from './components/remaining-time/remaining-time.component'; // add locale data for supported languages ['en-us', 'zh-cn', 'zh-tw', 'es-es', 'fr-fr', 'pt-br', 'tr-tr', 'de-de']; // en-us defaulted supported registerLocaleData(zh_cn, 'zh-cn'); @@ -153,7 +154,8 @@ ClarityIcons.add({"robot-head": ` ListChartVersionRoComponent, DatePickerComponent, ImageNameInputComponent, - HarborDatetimePipe + HarborDatetimePipe, + RemainingTimeComponent ], exports: [ TranslateModule, @@ -191,7 +193,8 @@ ClarityIcons.add({"robot-head": ` ListChartVersionRoComponent, DatePickerComponent, ImageNameInputComponent, - HarborDatetimePipe + HarborDatetimePipe, + RemainingTimeComponent ], providers: [ {provide: EndpointService, useClass: EndpointDefaultService }, diff --git a/src/portal/src/i18n/lang/de-de-lang.json b/src/portal/src/i18n/lang/de-de-lang.json index e6b55a5b9..b5f8516cf 100644 --- a/src/portal/src/i18n/lang/de-de-lang.json +++ b/src/portal/src/i18n/lang/de-de-lang.json @@ -1704,7 +1704,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart Label" + "HELM_LABEL": "Helm Chart Label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index beba71ec8..004827e81 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -1704,7 +1704,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart label" + "HELM_LABEL": "Helm Chart label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index db864dd31..82179648d 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -1703,7 +1703,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart label" + "HELM_LABEL": "Helm Chart label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index 040e7df3a..2aa6d6d50 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -1672,7 +1672,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart label" + "HELM_LABEL": "Helm Chart label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 3a40c1db1..52c63b1da 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -1700,7 +1700,9 @@ "STOP": "Parar", "LIST": "Listar", "REPOSITORY": "Repositório", - "HELM_LABEL": "Marcador do Helm Chart" + "HELM_LABEL": "Marcador do Helm Chart", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index 60c2defec..8faa958a9 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -1704,7 +1704,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart label" + "HELM_LABEL": "Helm Chart label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index 436e3cf6e..e991c8c3f 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -1702,7 +1702,9 @@ "STOP": "停止", "LIST": "查询", "REPOSITORY": "仓库", - "HELM_LABEL": "Helm Chart 标签" + "HELM_LABEL": "Helm Chart 标签", + "EXPIRES_IN": "有效期剩余", + "EXPIRED": "已过期" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "删除附件确认", diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json index f063f52c9..8b9b576b7 100644 --- a/src/portal/src/i18n/lang/zh-tw-lang.json +++ b/src/portal/src/i18n/lang/zh-tw-lang.json @@ -1689,7 +1689,9 @@ "STOP": "Stop", "LIST": "List", "REPOSITORY": "Repository", - "HELM_LABEL": "Helm Chart label" + "HELM_LABEL": "Helm Chart label", + "EXPIRES_IN": "Expires in", + "EXPIRED": "Expired" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirm Accessory Deletion",