mirror of
https://github.com/goharbor/harbor
synced 2025-04-15 21:43:40 +00:00
Add "expires in" column for robot UI (#16227)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
67bf1638d6
commit
7ff0bf188a
|
@ -60,7 +60,7 @@
|
|||
<clr-dg-column>{{'ROBOT_ACCOUNT.ENABLED_STATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{"SYSTEM_ROBOT.PROJECTS" | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'creation_time'">{{'ROBOT_ACCOUNT.CREATETION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'SYSTEM_ROBOT.EXPIRES_AT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'SYSTEM_ROBOT.EXPIRES_IN' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'ROBOT_ACCOUNT.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'SYSTEM_ROBOT.NOT_FOUND' | translate
|
||||
|
@ -96,7 +96,7 @@
|
|||
</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.creation_time | harborDatetime: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.expires_at === -1?("ROBOT_ACCOUNT.NEVER_EXPIRED" | translate):(r.expires_at * 1000 | harborDatetime: 'short')}}</clr-dg-cell>
|
||||
<clr-dg-cell><app-remaining-time [deadline]="r?.expires_at" [timeDiff]="deltaTime"></app-remaining-time></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.description}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -172,3 +172,9 @@ export function onlyHasPushPermission(access: Access[]): boolean {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export enum RobotTimeRemainColor {
|
||||
GREEN = 'green',
|
||||
WARNING = 'yellow',
|
||||
EXPIRED = 'red'
|
||||
}
|
||||
|
|
|
@ -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<Array<Accessory>> = new HttpResponse<Array<Accessory>>({
|
||||
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(
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<clr-dg-column>{{'ROBOT_ACCOUNT.ENABLED_STATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{"SYSTEM_ROBOT.PERMISSION_COLUMN" | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'creation_time'">{{'ROBOT_ACCOUNT.CREATETION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'SYSTEM_ROBOT.EXPIRES_AT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'SYSTEM_ROBOT.EXPIRES_IN' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'ROBOT_ACCOUNT.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'SYSTEM_ROBOT.NOT_FOUND' | translate
|
||||
|
@ -89,7 +89,7 @@
|
|||
</div>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.creation_time | harborDatetime: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{r.expires_at === -1?("ROBOT_ACCOUNT.NEVER_EXPIRED" | translate):(r.expires_at * 1000 | harborDatetime: 'short')}}</clr-dg-cell>
|
||||
<clr-dg-cell><app-remaining-time [deadline]="r?.expires_at" [timeDiff]="deltaTime"></app-remaining-time></clr-dg-cell>
|
||||
<clr-dg-cell>{{r.description}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
|
|
|
@ -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();
|
||||
}));
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<span [style.color]= 'color'>{{timeRemain | translate}}</span>
|
|
@ -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<TestHostComponent>;
|
||||
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: `
|
||||
<app-remaining-time [deadline]="expires_at" [timeDiff]="deltaTime"></app-remaining-time>`
|
||||
})
|
||||
class TestHostComponent {
|
||||
@ViewChild(RemainingTimeComponent)
|
||||
remainingTimeComponent: RemainingTimeComponent;
|
||||
expires_at: number = -1;
|
||||
deltaTime: number = 100;
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 },
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1702,7 +1702,9 @@
|
|||
"STOP": "停止",
|
||||
"LIST": "查询",
|
||||
"REPOSITORY": "仓库",
|
||||
"HELM_LABEL": "Helm Chart 标签"
|
||||
"HELM_LABEL": "Helm Chart 标签",
|
||||
"EXPIRES_IN": "有效期剩余",
|
||||
"EXPIRED": "已过期"
|
||||
},
|
||||
"ACCESSORY": {
|
||||
"DELETION_TITLE_ACCESSORY": "删除附件确认",
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue
Block a user