Browse Source

UI: Added firmware autocomplete component; Add firmware to device and device profile

pull/4380/head
Vladyslav_Prykhodko 6 years ago
parent
commit
e31a95619c
  1. 9
      ui-ngx/src/app/core/http/entity.service.ts
  2. 7
      ui-ngx/src/app/core/http/firmware.service.ts
  3. 54
      ui-ngx/src/app/modules/home/components/firmware/firmware-autocomplete.component.html
  4. 236
      ui-ngx/src/app/modules/home/components/firmware/firmware-autocomplete.component.ts
  5. 7
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  6. 4
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  7. 8
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  8. 4
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  9. 6
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  10. 4
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  11. 4
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  12. 4
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  13. 2
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  14. 10
      ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html
  15. 12
      ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts
  16. 3
      ui-ngx/src/app/shared/models/device.models.ts
  17. 5
      ui-ngx/src/assets/locale/locale.constant-en_US.json

9
ui-ngx/src/app/core/http/entity.service.ts

@ -74,6 +74,7 @@ import {
StringOperation
} from '@shared/models/query/query.models';
import { alarmFields } from '@shared/models/alarm.models';
import { FirmwareService } from '@core/http/firmware.service';
@Injectable({
providedIn: 'root'
@ -93,6 +94,7 @@ export class EntityService {
private dashboardService: DashboardService,
private entityRelationService: EntityRelationService,
private attributeService: AttributeService,
private firmwareService: FirmwareService,
private utils: UtilsService
) { }
@ -128,6 +130,9 @@ export class EntityService {
case EntityType.ALARM:
console.error('Get Alarm Entity is not implemented!');
break;
case EntityType.FIRMWARE:
observable = this.firmwareService.getFirmwareInfo(entityId, config);
break;
}
return observable;
}
@ -326,6 +331,10 @@ export class EntityService {
case EntityType.ALARM:
console.error('Get Alarm Entities is not implemented!');
break;
case EntityType.FIRMWARE:
pageLink.sortOrder.property = 'title';
entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config);
break;
}
return entitiesObservable;
}

7
ui-ngx/src/app/core/http/firmware.service.ts

@ -23,8 +23,6 @@ import { PageData } from '@shared/models/page/page-data';
import { Firmware, FirmwareInfo } from '@shared/models/firmware.models';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { InterceptorHttpParams } from '@core/interceptors/interceptor-http-params';
import { InterceptorConfig } from '@core/interceptors/interceptor-config';
@Injectable({
providedIn: 'root'
@ -37,10 +35,11 @@ export class FirmwareService {
}
public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
let url = `/api/firmwares${pageLink.toQuery()}`;
let url = `/api/firmwares`;
if (isDefinedAndNotNull(hasData)) {
url += `&hasData=${hasData}`;
url += `/${hasData}`;
}
url += `${pageLink.toQuery()}`;
return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
}

54
ui-ngx/src/app/modules/home/components/firmware/firmware-autocomplete.component.html

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2021 The Thingsboard 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.
-->
<mat-form-field [formGroup]="firmwareFormGroup" class="mat-block">
<input matInput type="text" placeholder="{{ placeholderText | translate }}"
#firmwareInput
formControlName="firmwareId"
(focusin)="onFocus()"
[required]="required"
[matAutocomplete]="firmwareAutocomplete">
<button *ngIf="firmwareFormGroup.get('firmwareId').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-autocomplete class="tb-autocomplete"
#firmwareAutocomplete="matAutocomplete"
[displayWith]="displayFirmwareFn">
<mat-option *ngFor="let firmware of filteredFirmwares | async" [value]="firmware">
<span [innerHTML]="this.firmwareTitleText(firmware) | highlight:searchText"></span>
</mat-option>
<mat-option *ngIf="!(filteredFirmwares | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<span translate>firmware.no-firmware-text</span>
</div>
<ng-template #searchNotEmpty>
<span>
{{ translate.get('firmware.no-firmware-matching',
{entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
</ng-template>
</div>
</mat-option>
</mat-autocomplete>
<mat-error *ngIf="firmwareFormGroup.get('firmwareId').hasError('required')">
{{ requiredErrorText | translate }}
</mat-error>
</mat-form-field>

236
ui-ngx/src/app/modules/home/components/firmware/firmware-autocomplete.component.ts

@ -0,0 +1,236 @@
///
/// Copyright © 2016-2021 The Thingsboard 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, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { EntityId } from '@shared/models/id/entity-id';
import { EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data';
import { EntityService } from '@core/http/entity.service';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { FirmwareInfo } from '@shared/models/firmware.models';
import { FirmwareService } from '@core/http/firmware.service';
import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order';
@Component({
selector: 'tb-firmware-autocomplete',
templateUrl: './firmware-autocomplete.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FirmwareAutocompleteComponent),
multi: true
}]
})
export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnInit {
firmwareFormGroup: FormGroup;
modelValue: string | null;
@Input()
labelText: string;
@Input()
requiredText: string;
@Input()
useFullEntityId = false;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
@ViewChild('firmwareInput', {static: true}) firmwareInput: ElementRef;
@ViewChild('firmwareInput', {read: MatAutocompleteTrigger}) firmwareAutocomplete: MatAutocompleteTrigger;
filteredFirmwares: Observable<Array<FirmwareInfo>>;
searchText = '';
private dirty = false;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
public translate: TranslateService,
public truncate: TruncatePipe,
private entityService: EntityService,
private firmwareService: FirmwareService,
private fb: FormBuilder) {
this.firmwareFormGroup = this.fb.group({
firmwareId: [null]
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.filteredFirmwares = this.firmwareFormGroup.get('firmwareId').valueChanges
.pipe(
tap(value => {
let modelValue;
if (typeof value === 'string' || !value) {
modelValue = null;
} else {
modelValue = this.useFullEntityId ? value.id : value.id.id;
}
this.updateView(modelValue);
if (value === null) {
this.clear();
}
}),
map(value => value ? (typeof value === 'string' ? value : value.title) : ''),
mergeMap(name => this.fetchFirmware(name)),
share()
);
}
ngAfterViewInit(): void {
}
getCurrentEntity(): BaseData<EntityId> | null {
const currentRuleChain = this.firmwareFormGroup.get('firmwareId').value;
if (currentRuleChain && typeof currentRuleChain !== 'string') {
return currentRuleChain as BaseData<EntityId>;
} else {
return null;
}
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.firmwareFormGroup.disable({emitEvent: false});
} else {
this.firmwareFormGroup.enable({emitEvent: false});
}
}
textIsNotEmpty(text: string): boolean {
return (text && text.length > 0);
}
writeValue(value: string | EntityId | null): void {
this.searchText = '';
if (value != null && value !== '') {
let firmwareId = '';
if (typeof value === 'string') {
firmwareId = value;
} else if (value.entityType && value.id) {
firmwareId = value.id;
}
if (firmwareId !== '') {
this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe(
(entity) => {
this.modelValue = entity.id.id;
this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false});
},
() => {
this.modelValue = null;
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
if (value !== null) {
this.propagateChange(this.modelValue);
}
}
);
} else {
this.modelValue = null;
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
}
} else {
this.modelValue = null;
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
}
this.dirty = true;
}
onFocus() {
if (this.dirty) {
this.firmwareFormGroup.get('firmwareId').updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false;
}
}
reset() {
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
}
updateView(value: string | null) {
if (this.modelValue !== value) {
this.modelValue = value;
this.propagateChange(this.modelValue);
}
}
displayFirmwareFn(firmware?: FirmwareInfo): string | undefined {
return firmware ? `${firmware.title} (${firmware.version})` : undefined;
}
fetchFirmware(searchText?: string): Observable<Array<FirmwareInfo>> {
this.searchText = searchText;
const pageLink = new PageLink(50, 0, searchText, {
property: 'title',
direction: Direction.ASC
});
return this.firmwareService.getFirmwares(pageLink, true, {ignoreLoading: true}).pipe(
map((data) => data && data.data.length ? data.data : null)
);
}
clear() {
this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: true});
setTimeout(() => {
this.firmwareInput.nativeElement.blur();
this.firmwareInput.nativeElement.focus();
}, 0);
}
get placeholderText(): string {
return this.labelText || 'firmware.firmware';
}
get requiredErrorText(): string {
return this.requiredText || 'firmware.firmware-required';
}
firmwareTitleText(firmware: FirmwareInfo): string {
return `${firmware.title} (${firmware.version})`;
}
}

7
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -134,6 +134,7 @@ import { DashboardStateDialogComponent } from '@home/components/dashboard-page/s
import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component';
import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/embed-dashboard-dialog-token';
import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
import { FirmwareAutocompleteComponent } from '@home/components/firmware/firmware-autocomplete.component';
@NgModule({
declarations:
@ -247,7 +248,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
ManageDashboardStatesDialogComponent,
DashboardStateDialogComponent,
EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent
DisplayWidgetTypesPanelComponent,
FirmwareAutocompleteComponent
],
imports: [
CommonModule,
@ -351,7 +353,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
ManageDashboardStatesDialogComponent,
DashboardStateDialogComponent,
EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent
DisplayWidgetTypesPanelComponent,
FirmwareAutocompleteComponent
],
providers: [
WidgetComponentService,

4
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -60,6 +60,10 @@
{{ 'device-profile.type-required' | translate }}
</mat-error>
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>

8
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -48,7 +48,7 @@ import { MatHorizontalStepper } from '@angular/material/stepper';
import { RuleChainId } from '@shared/models/id/rule-chain-id';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { deepTrim } from '@core/utils';
import {ServiceType} from "@shared/models/queue.models";
import { ServiceType } from '@shared/models/queue.models';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
@ -72,13 +72,13 @@ export class AddDeviceProfileDialogComponent extends
entityType = EntityType;
deviceProfileTypes = Object.keys(DeviceProfileType);
deviceProfileTypes = Object.values(DeviceProfileType);
deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
deviceTransportTypeHints = deviceTransportTypeHintMap;
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypes = Object.values(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
@ -108,6 +108,7 @@ export class AddDeviceProfileDialogComponent extends
type: [DeviceProfileType.DEFAULT, [Validators.required]],
defaultRuleChainId: [null, []],
defaultQueueName: ['', []],
firmwareId: [null],
description: ['', []]
}
);
@ -186,6 +187,7 @@ export class AddDeviceProfileDialogComponent extends
transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey,
firmwareId: this.deviceProfileDetailsFormGroup.get('firmwareId').value,
description: this.deviceProfileDetailsFormGroup.get('description').value,
profileData: {
configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),

4
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -63,6 +63,10 @@
[queueType]="serviceType"
formControlName="defaultQueueName">
</tb-queue-type-list>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>

6
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -53,11 +53,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
entityType = EntityType;
deviceProfileTypes = Object.keys(DeviceProfileType);
deviceProfileTypes = Object.values(DeviceProfileType);
deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypes = Object.values(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
@ -109,6 +109,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}),
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
defaultQueueName: [entity ? entity.defaultQueueName : '', []],
firmwareId: [entity ? entity.firmwareId : null],
description: [entity ? entity.description : '', []],
}
);
@ -184,6 +185,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}}, {emitEvent: false});
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
}

4
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -48,6 +48,10 @@
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<mat-form-field class="mat-block" style="padding-bottom: 14px;">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>

4
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts

@ -70,7 +70,7 @@ export class DeviceWizardDialogComponent extends
entityType = EntityType;
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypes = Object.values(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
@ -107,6 +107,7 @@ export class DeviceWizardDialogComponent extends
this.deviceWizardFormGroup = this.fb.group({
name: ['', Validators.required],
label: [''],
firmwareId: [null],
gateway: [false],
overwriteActivityTime: [false],
transportType: [DeviceTransportType.DEFAULT, Validators.required],
@ -312,6 +313,7 @@ export class DeviceWizardDialogComponent extends
const device = {
name: this.deviceWizardFormGroup.get('name').value,
label: this.deviceWizardFormGroup.get('label').value,
firmwareId: this.deviceWizardFormGroup.get('firmwareId').value,
deviceProfileId: profileId,
additionalInfo: {
gateway: this.deviceWizardFormGroup.get('gateway').value,

4
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -95,6 +95,10 @@
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<tb-device-data
formControlName="deviceData"
required>

2
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -79,6 +79,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
{
name: [entity ? entity.name : '', [Validators.required]],
deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]],
firmwareId: [entity ? entity.firmwareId : null],
label: [entity ? entity.label : ''],
deviceData: [entity ? entity.deviceData : null, [Validators.required]],
additionalInfo: this.fb.group(
@ -95,6 +96,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
updateForm(entity: DeviceInfo) {
this.entityForm.patchValue({name: entity.name});
this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId});
this.entityForm.patchValue({firmwareId: entity.firmwareId});
this.entityForm.patchValue({label: entity.label});
this.entityForm.patchValue({deviceData: entity.deviceData});
this.entityForm.patchValue({

10
ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html

@ -28,6 +28,16 @@
[fxShow]="!hideDelete() && !isEdit">
{{'resource.delete' | translate }}
</button>
<div fxLayout="row" fxLayout.xs="column">
<button mat-raised-button
ngxClipboard
(cbOnSuccess)="onFirmwareIdCopied($event)"
[cbContent]="entity?.id?.id"
[fxShow]="!isEdit">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>firmware.copyId</span>
</button>
</div>
</div>
<div class="mat-padding" fxLayout="column">
<form [formGroup]="entityForm">

12
ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts

@ -24,6 +24,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { EntityComponent } from '@home/components/entity/entity.component';
import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
import { ActionNotificationShow } from '@core/notification/notification.actions';
@Component({
selector: 'tb-firmware',
@ -109,4 +110,15 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI
}
});
}
onFirmwareIdCopied($event) {
this.store.dispatch(new ActionNotificationShow(
{
message: this.translate.instant('firmware.idCopiedMessage'),
type: 'success',
duration: 750,
verticalPosition: 'bottom',
horizontalPosition: 'right'
}));
}
}

3
ui-ngx/src/app/shared/models/device.models.ts

@ -27,6 +27,7 @@ import { KeyFilter } from '@shared/models/query/query.models';
import { TimeUnit } from '@shared/models/time/time.models';
import * as _moment from 'moment';
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { FirmwareId } from '@shared/models/id/firmware-id';
export enum DeviceProfileType {
DEFAULT = 'DEFAULT'
@ -445,6 +446,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
provisionDeviceKey?: string;
defaultRuleChainId?: RuleChainId;
defaultQueueName?: string;
firmwareId?: FirmwareId;
profileData: DeviceProfileData;
}
@ -499,6 +501,7 @@ export interface Device extends BaseData<DeviceId> {
name: string;
type: string;
label: string;
firmwareId?: FirmwareId;
deviceProfileId?: DeviceProfileId;
deviceData?: DeviceData;
additionalInfo?: any;

5
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1698,6 +1698,7 @@
"checksum": "Checksum",
"checksum-required": "Checksum is required.",
"checksum-algorithm": "Checksum algorithm",
"copyId": "Copy firmware Id",
"description": "Description",
"delete": "Delete firmware",
"delete-firmware-text": "Be careful, after the confirmation the firmware will become unrecoverable.",
@ -1708,10 +1709,12 @@
"drop-file": "Drop a firmware file or click to select a file to upload.",
"empty": "Firmware is empty",
"export": "Export firmware",
"no-firmware-matching": "No firmware matching '{{firmware}}' were found.",
"idCopiedMessage": "Firmware Id has been copied to clipboard",
"no-firmware-matching": "No firmware matching '{{entity}}' were found.",
"no-firmware-text": "No firmwares found",
"firmware": "Firmware",
"firmware-details": "Firmware details",
"firmware-required": "Firmware is required.",
"search": "Search firmwares",
"selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected",
"title": "Title",

Loading…
Cancel
Save