From fa3bbb2851a443d088ceb86e08f1daa41111e54a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 19:03:31 +0300 Subject: [PATCH 1/8] UI: And confirm dialog after change ota package in device profile --- .../app/core/http/device-profile.service.ts | 58 +++++++++++++++---- .../src/app/core/http/ota-package.service.ts | 4 ++ .../entity/entity-details-panel.component.ts | 2 +- ...device-profile-autocomplete.component.html | 2 +- .../device-profile-dialog.component.ts | 2 +- .../entity/entities-table-config.models.ts | 4 +- .../device-profiles-table-config.resolver.ts | 3 +- .../ota-update/ota-update.component.html | 1 + .../assets/locale/locale.constant-en_US.json | 4 +- 9 files changed, 62 insertions(+), 18 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 7e3183255a..f5c5625ba2 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -14,16 +14,21 @@ /// limitations under the License. /// -import {Injectable} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {PageLink} from '@shared/models/page/page-link'; -import {defaultHttpOptionsFromConfig, RequestConfig} from './http-utils'; -import {Observable} from 'rxjs'; -import {PageData} from '@shared/models/page/page-data'; -import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models'; -import {isDefinedAndNotNull, isEmptyStr} from '@core/utils'; -import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; -import {SortOrder} from '@shared/models/page/sort-order'; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { forkJoin, Observable, of, throwError } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { SortOrder } from '@shared/models/page/sort-order'; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; +import { mergeMap } from 'rxjs/operators'; +import { DialogService } from '@core/services/dialog.service'; +import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' @@ -31,7 +36,10 @@ import {SortOrder} from '@shared/models/page/sort-order'; export class DeviceProfileService { constructor( - private http: HttpClient + private http: HttpClient, + private otaPackageService: OtaPackageService, + private dialogService: DialogService, + private translate: TranslateService ) { } @@ -70,6 +78,34 @@ export class DeviceProfileService { ); } + public saveDeviceProfileAndConfirmOtaChange(originDeviceProfile: DeviceProfile, deviceProfile: DeviceProfile, + config?: RequestConfig): Observable { + const tasks: Observable[] = []; + if (originDeviceProfile?.id?.id && originDeviceProfile.firmwareId?.id !== deviceProfile.firmwareId?.id) { + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.FIRMWARE, deviceProfile.id.id)); + } else { + tasks.push(of(0)); + } + if (originDeviceProfile?.id?.id && originDeviceProfile.softwareId?.id !== deviceProfile.softwareId?.id) { + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.SOFTWARE, deviceProfile.id.id)); + } else { + tasks.push(of(0)); + } + return forkJoin(tasks).pipe( + mergeMap(([deviceFirmwareUpdate, deviceSoftwareUpdate]) => { + let text = ''; + if (deviceFirmwareUpdate > 0) { + text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate}); + } + if (deviceSoftwareUpdate > 0) { + text += text.length ? '
' : ''; + text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate}); + } + return text !== '' ? this.dialogService.confirm('', text) : of(true); + }), + mergeMap((update) => update ? this.saveDeviceProfile(deviceProfile, config) : throwError('Canceled saving device profiles'))); + } + public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable { return this.http.post('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 34993e5318..3ae1490a88 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -120,4 +120,8 @@ export class OtaPackageService { return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); } + public countUpdateDeviceAfterChangePackage(type: OtaUpdateType, deviceProfileId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/devices/count/${type}?deviceProfileId=${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts index cba32ddcaf..d6378620f9 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts @@ -280,7 +280,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV editingEntity.additionalInfo = mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo); } - this.entitiesTableConfig.saveEntity(editingEntity).subscribe( + this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe( (entity) => { this.entity = entity; this.entityComponent.entity = entity; diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index bd6f92b175..20f271d660 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -66,5 +66,5 @@ {{ 'device-profile.device-profile-required' | translate }} - {{ hint | translate }} + {{ hint | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts index 0efcad9608..58433f01c4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts @@ -90,7 +90,7 @@ export class DeviceProfileDialogComponent extends this.submitted = true; if (this.deviceProfileComponent.entityForm.valid) { this.deviceProfile = {...this.deviceProfile, ...this.deviceProfileComponent.entityFormValue()}; - this.deviceProfileService.saveDeviceProfile(this.deviceProfile).subscribe( + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(this.deviceProfile, this.deviceProfile).subscribe( (deviceProfile) => { this.dialogRef.close(deviceProfile); } diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index e6de5dbf84..4b057d8fef 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -37,7 +37,7 @@ export type EntityStringFunction> = (entity: T) => str export type EntityVoidFunction> = (entity: T) => void; export type EntityIdsVoidFunction> = (ids: HasUUID[]) => void; export type EntityCountStringFunction = (count: number) => string; -export type EntityTwoWayOperation> = (entity: T) => Observable; +export type EntityTwoWayOperation> = (entity: T, originalEntity?: T) => Observable; export type EntityByIdOperation> = (id: HasUUID) => Observable; export type EntityIdOneWayOperation = (id: HasUUID) => Observable; export type EntityActionFunction> = (action: EntityAction) => boolean; @@ -173,7 +173,7 @@ export class EntityTableConfig, P extends PageLink = P deleteEntitiesTitle: EntityCountStringFunction = () => ''; deleteEntitiesContent: EntityCountStringFunction = () => ''; loadEntity: EntityByIdOperation = () => of(); - saveEntity: EntityTwoWayOperation = (entity) => of(entity); + saveEntity: EntityTwoWayOperation = (entity, originalEntity) => of(entity); deleteEntity: EntityIdOneWayOperation = () => of(); entitiesFetchFunction: EntitiesFetchFunction = () => of(emptyPageData()); onEntityAction: EntityActionFunction = () => false; diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts index 49a256dd55..98281c1f88 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts @@ -104,7 +104,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve this.deviceProfileService.getDeviceProfiles(pageLink); this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id); - this.config.saveEntity = deviceProfile => this.deviceProfileService.saveDeviceProfile(deviceProfile); + this.config.saveEntity = (deviceProfile, originDeviceProfile) => + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(originDeviceProfile, deviceProfile); this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id); this.config.onEntityAction = action => this.onDeviceProfileAction(action); this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index 77a68f72e5..8ea3a19801 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -69,6 +69,7 @@ Date: Thu, 3 Jun 2021 18:58:31 +0300 Subject: [PATCH 2/8] UI: OTA updates add support external URL --- .../app/core/http/device-profile.service.ts | 4 +- .../ota-update-table-config.resolve.ts | 42 +++++++- .../ota-update/ota-update.component.html | 96 ++++++++++++------- .../pages/ota-update/ota-update.component.ts | 42 +++++--- .../app/shared/models/ota-package.models.ts | 1 + .../assets/locale/locale.constant-en_US.json | 13 ++- 6 files changed, 141 insertions(+), 57 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index f5c5625ba2..729ba7869b 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -98,10 +98,10 @@ export class DeviceProfileService { text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate}); } if (deviceSoftwareUpdate > 0) { - text += text.length ? '
' : ''; + text += text.length ? ' ' : ''; text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate}); } - return text !== '' ? this.dialogService.confirm('', text) : of(true); + return text !== '' ? this.dialogService.confirm('', text, null, this.translate.instant('common.proceed')) : of(true); }), mergeMap((update) => update ? this.saveDeviceProfile(deviceProfile, config) : throwError('Canceled saving device profiles'))); } diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index b22d43da46..d56b4e4541 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -35,6 +35,10 @@ import { PageLink } from '@shared/models/page/page-link'; import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; +import { ClipboardService } from 'ngx-clipboard'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; @Injectable() export class OtaUpdateTableConfigResolve implements Resolve> { @@ -44,8 +48,10 @@ export class OtaUpdateTableConfigResolve implements Resolve, private otaPackageService: OtaPackageService, - private fileSize: FileSizePipe) { + private fileSize: FileSizePipe, + private clipboardService: ClipboardService) { this.config.entityType = EntityType.OTA_PACKAGE; this.config.entityComponent = OtaUpdateComponent; this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE); @@ -62,14 +68,20 @@ export class OtaUpdateTableConfigResolve implements Resolve('fileName', 'ota-update.file-name', '25%'), new EntityTableColumn('dataSize', 'ota-update.file-size', '70px', entity => { - return this.fileSize.transform(entity.dataSize || 0); + return entity.dataSize ? this.fileSize.transform(entity.dataSize) : ''; }), new EntityTableColumn('checksum', 'ota-update.checksum', '540px', entity => { - return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; + return entity.checksum ? `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}` : ''; }, () => ({}), false) ); this.config.cellActionDescriptors.push( + { + name: this.translate.instant('ota-update.copy-checksum'), + icon: 'content_copy', + isEnabled: (otaPackage) => !!otaPackage.checksum, + onAction: ($event, entity) => this.copyPackageChecksum($event, entity) + }, { name: this.translate.instant('ota-update.download'), icon: 'file_download', @@ -101,7 +113,26 @@ export class OtaUpdateTableConfigResolve implements Resolve): boolean { @@ -109,6 +140,9 @@ export class OtaUpdateTableConfigResolve implements Resolve @@ -49,7 +49,7 @@
-
+
ota-update.title @@ -83,44 +83,68 @@ -
ota-update.warning-after-save-no-edit
-
- - ota-update.checksum-algorithm - - - {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} - - - - - ota-update.checksum - - -
- - +
ota-update.warning-after-save-no-edit
+ + Upload binary file + Use external URL +
-
-
+
+
+ + + + {{ 'ota-update.auto-generate-checksum' | translate }} + +
+
- ota-update.file-name - + ota-update.checksum-algorithm + + + {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} + + - ota-update.file-size-bytes - - - - ota-update.content-type - + ota-update.checksum + + ota-update.checksum-hint
+
+
+ + ota-update.file-name + + + + ota-update.file-size-bytes + + + + ota-update.content-type + + +
+
+
+
+ + ota-update.url + + + ota-update.url-required + +
diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index fd4cd7ae27..42e0ffc33c 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -30,6 +30,8 @@ import { OtaUpdateTypeTranslationMap } from '@shared/models/ota-package.models'; import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { filter, takeUntil } from 'rxjs/operators'; +import { isNotEmptyStr } from '@core/utils'; @Component({ selector: 'tb-ota-update', @@ -52,6 +54,26 @@ export class OtaUpdateComponent extends EntityComponent implements O super(store, fb, entityValue, entitiesTableConfigValue); } + ngOnInit() { + super.ngOnInit(); + this.entityForm.get('resource').valueChanges.pipe( + filter(() => this.isAdd), + takeUntil(this.destroy$) + ).subscribe((resource) => { + if (resource === 'file') { + this.entityForm.get('url').clearValidators(); + this.entityForm.get('file').setValidators(Validators.required); + this.entityForm.get('url').updateValueAndValidity({emitEvent: false}); + this.entityForm.get('file').updateValueAndValidity({emitEvent: false}); + } else { + this.entityForm.get('file').clearValidators(); + this.entityForm.get('url').setValidators(Validators.required); + this.entityForm.get('file').updateValueAndValidity({emitEvent: false}); + this.entityForm.get('url').updateValueAndValidity({emitEvent: false}); + } + }); + } + ngOnDestroy() { super.ngOnDestroy(); this.destroy$.next(); @@ -74,6 +96,8 @@ export class OtaUpdateComponent extends EntityComponent implements O deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)], + url: [entity ? entity.url : ''], + resource: ['file'], additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], @@ -82,6 +106,7 @@ export class OtaUpdateComponent extends EntityComponent implements O }); if (this.isAdd) { form.addControl('file', this.fb.control(null, Validators.required)); + form.addControl('generateChecksum', this.fb.control(true)); } else { form.addControl('fileName', this.fb.control(null)); form.addControl('dataSize', this.fb.control(null)); @@ -101,6 +126,8 @@ export class OtaUpdateComponent extends EntityComponent implements O fileName: entity.fileName, dataSize: entity.dataSize, contentType: entity.contentType, + url: entity.url, + resource: isNotEmptyStr(entity.url) ? 'url' : 'file', additionalInfo: { description: entity.additionalInfo ? entity.additionalInfo.description : '' } @@ -108,8 +135,6 @@ export class OtaUpdateComponent extends EntityComponent implements O if (!this.isAdd && this.entityForm.enabled) { this.entityForm.disable({emitEvent: false}); this.entityForm.get('additionalInfo').enable({emitEvent: false}); - // this.entityForm.get('dataSize').disable({emitEvent: false}); - // this.entityForm.get('contentType').disable({emitEvent: false}); } } @@ -124,14 +149,9 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } - onPackageChecksumCopied() { - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant('ota-update.checksum-copied-message'), - type: 'success', - duration: 750, - verticalPosition: 'bottom', - horizontalPosition: 'right' - })); + prepareFormValue(formValue: any): any { + delete formValue.resource; + delete formValue.generateChecksum; + return super.prepareFormValue(formValue); } } diff --git a/ui-ngx/src/app/shared/models/ota-package.models.ts b/ui-ngx/src/app/shared/models/ota-package.models.ts index 22efc881b0..b6fecfc2c5 100644 --- a/ui-ngx/src/app/shared/models/ota-package.models.ts +++ b/ui-ngx/src/app/shared/models/ota-package.models.ts @@ -87,6 +87,7 @@ export interface OtaPackageInfo extends BaseData { title?: string; version?: string; hasData?: boolean; + url?: string; fileName: string; checksum?: string; checksumAlgorithm?: ChecksumAlgorithm; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b4f8c32101..732535dafe 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -575,7 +575,8 @@ "enter-password": "Enter password", "enter-search": "Enter search", "created-time": "Created time", - "loading": "Loading..." + "loading": "Loading...", + "proceed": "Proceed" }, "content-type": { "json": "Json", @@ -2152,12 +2153,14 @@ "assign-firmware-required": "Assigned firmware is required", "assign-software": "Assigned software", "assign-software-required": "Assigned software is required", + "auto-generate-checksum": "Auto-generate checksum", "checksum": "Checksum", + "checksum-hint": "If checksum is empty, it will be generated automatically", "checksum-algorithm": "Checksum algorithm", "checksum-copied-message": "Package checksum has been copied to clipboard", - "change-firmware": "You have changed the firmware. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.", - "change-software": "You have changed the software. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.", - "chose-compatible-device-profile": "Choose compatible device profile. The uploaded package will be available only for devices with the chosen profile.", + "change-firmware": "Change of the firmware may cause update of { count, plural, 1 {1 device} other {# devices} }.", + "change-software": "Change of the software may cause update of { count, plural, 1 {1 device} other {# devices} }.", + "chose-compatible-device-profile": "The uploaded package will be available only for devices with the chosen profile.", "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", "chose-software-distributed-device": "Choose software that will be distributed to the devices", "content-type": "Content type", @@ -2193,6 +2196,8 @@ "firmware": "Firmware", "software": "Software" }, + "url": "Direct URL", + "url-required": "Direct URL is required", "version": "Version", "version-required": "Version is required.", "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." From 195ed51afa9ba44d2fc93ca2a680303aa96a9efe Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 4 Jun 2021 20:49:01 +0300 Subject: [PATCH 3/8] UI: Add copy button checksum --- .../src/app/core/http/ota-package.service.ts | 2 +- .../entity/entities-table.component.html | 28 +++++- .../entity/entities-table.component.ts | 4 +- .../entity/entities-table-config.models.ts | 13 ++- .../ota-update-table-config.resolve.ts | 66 +++++++------ .../ota-update/ota-update.component.html | 8 +- .../pages/ota-update/ota-update.component.ts | 11 +++ .../button/copy-button.component.html | 29 ++++++ .../button/copy-button.component.scss | 30 ++++++ .../button/copy-button.component.ts | 95 +++++++++++++++++++ .../ota-package-autocomplete.component.ts | 2 +- ui-ngx/src/app/shared/shared.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 1 + 13 files changed, 256 insertions(+), 40 deletions(-) create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.html create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.scss create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.ts diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 0042f67b24..7e7f09726e 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -39,7 +39,7 @@ export class OtaPackageService { } public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, - hasData = true, config?: RequestConfig): Observable> { + config?: RequestConfig): Observable> { const url = `/api/otaPackages/${deviceProfileId}/${type}${pageLink.toQuery()}`; return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index f4ecd52437..83f221f9e1 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -163,8 +163,34 @@ *matCellDef="let entity; let row = index" [matTooltip]="cellTooltip(entity, column, row)" matTooltipPosition="above" - [innerHTML]="cellContent(entity, column, row)" [ngStyle]="cellStyle(entity, column, row)"> + + + + + + + + + + + + diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 40a6b0cece..df164adda4 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -43,7 +43,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseData, HasId } from '@shared/models/base-data'; import { ActivatedRoute } from '@angular/router'; import { - CellActionDescriptor, + CellActionDescriptor, CellActionDescriptorType, EntityActionTableColumn, EntityColumn, EntityTableColumn, @@ -104,6 +104,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn timewindow: Timewindow; dataSource: EntitiesDataSource>; + cellActionType = CellActionDescriptorType; + isDetailsOpen = false; detailsPanelOpened = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 4b057d8fef..8fcd2aa070 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -48,6 +48,9 @@ export type CellContentFunction> = (entity: T, key: st export type CellTooltipFunction> = (entity: T, key: string) => string | undefined; export type HeaderCellStyleFunction> = (key: string) => object; export type CellStyleFunction> = (entity: T, key: string) => object; +export type CopyCellContent> = (entity: T, key: string, length: number) => object; + +export enum CellActionDescriptorType { 'DEFAULT', 'COPY_BUTTON'} export interface CellActionDescriptor> { name: string; @@ -56,7 +59,8 @@ export interface CellActionDescriptor> { mdiIcon?: string; style?: any; isEnabled: (entity: T) => boolean; - onAction: ($event: MouseEvent, entity: T) => void; + onAction: ($event: MouseEvent, entity: T) => any; + type?: CellActionDescriptorType; } export interface GroupActionDescriptor> { @@ -95,7 +99,8 @@ export class EntityTableColumn> extends BaseEntityTabl public sortable: boolean = true, public headerCellStyleFunction: HeaderCellStyleFunction = () => ({}), public cellTooltipFunction: CellTooltipFunction = () => undefined, - public isNumberColumn: boolean = false) { + public isNumberColumn: boolean = false, + public actionCell: CellActionDescriptor = {isEnabled: () => false, name: null, onAction: () => ({})}) { super('content', key, title, width, sortable); } } @@ -187,3 +192,7 @@ export class EntityTableConfig, P extends PageLink = P export function checkBoxCell(value: boolean): string { return `${value ? 'check_box' : 'check_box_outline_blank'}`; } + +export function copyCellContent(value: string): string { + return `${value ? 'check_box' : 'check_box_outline_blank'}`; +} diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index d56b4e4541..dc7c36371d 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -17,6 +17,7 @@ import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; import { + CellActionDescriptorType, DateEntityTableColumn, EntityTableColumn, EntityTableConfig @@ -61,27 +62,46 @@ export class OtaUpdateTableConfigResolve implements Resolve('createdTime', 'common.created-time', this.datePipe, '150px'), - new EntityTableColumn('title', 'ota-update.title', '25%'), - new EntityTableColumn('version', 'ota-update.version', '25%'), - new EntityTableColumn('type', 'ota-update.package-type', '25%', entity => { + new EntityTableColumn('title', 'ota-update.title', '20%'), + new EntityTableColumn('version', 'ota-update.version', '20%'), + new EntityTableColumn('type', 'ota-update.package-type', '20%', entity => { return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); }), - new EntityTableColumn('fileName', 'ota-update.file-name', '25%'), + new EntityTableColumn('url', 'ota-update.url', '20%', entity => { + return entity.url && entity.url.length > 20 ? `${entity.url.slice(0, 20)}…` : ''; + }, () => ({}), true, () => ({}), () => undefined, false, + { + name: this.translate.instant('ota-update.copy-checksum'), + icon: 'content_paste', + style: { + 'font-size': '16px', + color: 'rgba(0,0,0,.87)' + }, + isEnabled: (otaPackage) => !!otaPackage.url, + onAction: ($event, entity) => entity.url, + type: CellActionDescriptorType.COPY_BUTTON + }), + new EntityTableColumn('fileName', 'ota-update.file-name', '20%'), new EntityTableColumn('dataSize', 'ota-update.file-size', '70px', entity => { return entity.dataSize ? this.fileSize.transform(entity.dataSize) : ''; }), - new EntityTableColumn('checksum', 'ota-update.checksum', '540px', entity => { - return entity.checksum ? `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}` : ''; - }, () => ({}), false) + new EntityTableColumn('checksum', 'ota-update.checksum', '220px', entity => { + return entity.checksum ? this.checksumText(entity) : ''; + }, () => ({}), true, () => ({}), () => undefined, false, + { + name: this.translate.instant('ota-update.copy-checksum'), + icon: 'content_paste', + style: { + 'font-size': '16px', + color: 'rgba(0,0,0,.87)' + }, + isEnabled: (otaPackage) => !!otaPackage.checksum, + onAction: ($event, entity) => entity.checksum, + type: CellActionDescriptorType.COPY_BUTTON + }) ); this.config.cellActionDescriptors.push( - { - name: this.translate.instant('ota-update.copy-checksum'), - icon: 'content_copy', - isEnabled: (otaPackage) => !!otaPackage.checksum, - onAction: ($event, entity) => this.copyPackageChecksum($event, entity) - }, { name: this.translate.instant('ota-update.download'), icon: 'file_download', @@ -120,19 +140,12 @@ export class OtaUpdateTableConfigResolve implements Resolve 20) { + text = `${text.slice(0, 20)}…`; } - this.clipboardService.copy(otaPackageInfo?.checksum); - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant('ota-update.checksum-copied-message'), - type: 'success', - duration: 750, - verticalPosition: 'bottom', - horizontalPosition: 'right' - })); + return text; } onPackageAction(action: EntityAction): boolean { @@ -140,9 +153,6 @@ export class OtaUpdateTableConfigResolve implements Resolve diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index 42e0ffc33c..9193b37c8e 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -149,6 +149,17 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } + onPackageChecksumCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('ota-update.checksum-copied-message'), + type: 'success', + duration: 750, + verticalPosition: 'bottom', + horizontalPosition: 'right' + })); + } + prepareFormValue(formValue: any): any { delete formValue.resource; delete formValue.generateChecksum; diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html new file mode 100644 index 0000000000..55f3008cf7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -0,0 +1,29 @@ + + diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.scss b/ui-ngx/src/app/shared/components/button/copy-button.component.scss new file mode 100644 index 0000000000..3cf3757c03 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.scss @@ -0,0 +1,30 @@ +/** + * 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. + */ +:host { + .mat-icon-button{ + height: 32px; + width: 32px; + line-height: 32px; + .mat-icon.copied{ + color: #00C851!important; + } + } + &:hover{ + .mat-icon{ + color: #28567E !important; + } + } +} diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts new file mode 100644 index 0000000000..e8e4d8e4b8 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -0,0 +1,95 @@ +/// +/// 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 { ChangeDetectorRef, Component, Input, ViewChild } from '@angular/core'; +import { ClipboardService } from 'ngx-clipboard'; +import { MatTooltip, TooltipPosition } from '@angular/material/tooltip/tooltip'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-copy-button', + styleUrls: ['copy-button.component.scss'], + templateUrl: './copy-button.component.html' +}) +export class CopyButtonComponent { + + private copedIcon = ''; + private timer; + + copied = false; + + @Input() + copyText: string; + + @Input() + disabled = false; + + @Input() + mdiIcon: string; + + @Input() + icon: string; + + @Input() + tooltipText: string; + + @Input() + tooltipPosition: TooltipPosition; + + @Input() + style: {[key: string]: any} = {}; + + constructor(private clipboardService: ClipboardService, + private translate: TranslateService, + private cd: ChangeDetectorRef) { + } + + @ViewChild('tooltip', {static: true}) tooltip: MatTooltip; + + copy($event: Event): void { + $event.stopPropagation(); + $event.preventDefault(); + if (this.timer) { + clearTimeout(this.timer); + } + this.clipboardService.copy(this.copyText); + this.copedIcon = 'done'; + this.copied = true; + this.tooltip.show(); + this.timer = setTimeout(() => { + this.copedIcon = null; + this.copied = false; + this.tooltip.hide(); + this.cd.detectChanges(); + }, 1500); + } + + get iconSymbol(): string { + return this.copedIcon || this.icon; + } + + get mdiIconSymbol(): string { + return this.copedIcon ? '' : this.mdiIcon; + } + + get matTooltipText(): string { + return this.copied ? this.translate.instant('ota-update.copied') : this.tooltipText; + } + + get matTooltipPosition(): TooltipPosition { + return this.copied ? 'below' : this.tooltipPosition; + } +} diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 25df909a8c..bb3be21fa8 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -216,7 +216,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On direction: Direction.ASC }); return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, - true, {ignoreLoading: true}).pipe( + {ignoreLoading: true}).pipe( map((data) => data && data.data.length ? data.data : null) ); } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 5e71d4c44a..f2bcdc97a4 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -143,6 +143,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ota-package-autocomplete.component'; import { MAT_DATE_LOCALE } from '@angular/material/core'; +import { CopyButtonComponent } from '@shared/components/button/copy-button.component'; @NgModule({ providers: [ @@ -240,7 +241,8 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; EntityGatewaySelectComponent, ContactComponent, OtaPackageAutocompleteComponent, - WidgetsBundleSearchComponent + WidgetsBundleSearchComponent, + CopyButtonComponent ], imports: [ CommonModule, @@ -412,7 +414,8 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; EntityGatewaySelectComponent, ContactComponent, OtaPackageAutocompleteComponent, - WidgetsBundleSearchComponent + WidgetsBundleSearchComponent, + CopyButtonComponent ] }) export class SharedModule { } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6f2618dd7a..da35b00d3a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2166,6 +2166,7 @@ "content-type": "Content type", "copy-checksum": "Copy checksum", "copyId": "Copy package Id", + "copied": "Copied!", "delete": "Delete package", "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", From cf899b6ffd49a8736213ea021ef0bbe80f34ab4e Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Sun, 6 Jun 2021 10:30:05 +0300 Subject: [PATCH 4/8] UI: Improvement tb0copy-button component --- .../components/button/copy-button.component.html | 4 ++-- .../components/button/copy-button.component.scss | 2 +- .../components/button/copy-button.component.ts | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html index 55f3008cf7..6b38870d3e 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.html +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -20,8 +20,8 @@ [disabled]="disabled" [matTooltip]="matTooltipText" [matTooltipPosition]="matTooltipPosition" - (mouseenter)="copied ? $event.stopImmediatePropagation():''" - (mouseleave)="copied ? $event.stopImmediatePropagation():''" + (mouseenter)="immediatePropagation($event)" + (mouseleave)="immediatePropagation($event)" (click)="copy($event)"> {{ iconSymbol }} diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.scss b/ui-ngx/src/app/shared/components/button/copy-button.component.scss index 3cf3757c03..0e5f5de8d9 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.scss +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.scss @@ -19,7 +19,7 @@ width: 32px; line-height: 32px; .mat-icon.copied{ - color: #00C851!important; + color: #00C851 !important; } } &:hover{ diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts index e8e4d8e4b8..9e73ad9734 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { ClipboardService } from 'ngx-clipboard'; -import { MatTooltip, TooltipPosition } from '@angular/material/tooltip/tooltip'; +import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; import { TranslateService } from '@ngx-translate/core'; @Component({ @@ -52,6 +52,9 @@ export class CopyButtonComponent { @Input() style: {[key: string]: any} = {}; + @Output() + successCopied: EventEmitter; + constructor(private clipboardService: ClipboardService, private translate: TranslateService, private cd: ChangeDetectorRef) { @@ -61,11 +64,11 @@ export class CopyButtonComponent { copy($event: Event): void { $event.stopPropagation(); - $event.preventDefault(); if (this.timer) { clearTimeout(this.timer); } this.clipboardService.copy(this.copyText); + this.successCopied.emit(this.copyText); this.copedIcon = 'done'; this.copied = true; this.tooltip.show(); @@ -92,4 +95,8 @@ export class CopyButtonComponent { get matTooltipPosition(): TooltipPosition { return this.copied ? 'below' : this.tooltipPosition; } + + immediatePropagation($event: Event): void { + this.copied ? $event.stopImmediatePropagation(): ''; + } } From b67d9930d3da0580680ba29c03d49a7ba638a44b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 7 Jun 2021 10:49:04 +0300 Subject: [PATCH 5/8] UI: Refactoring --- .../ota-update/ota-update-table-config.resolve.ts | 9 +++------ .../pages/ota-update/ota-update.component.html | 14 +++++++++++--- .../home/pages/ota-update/ota-update.component.ts | 11 +++++++++++ .../components/button/copy-button.component.html | 3 --- .../components/button/copy-button.component.ts | 13 +++---------- .../src/assets/locale/locale.constant-en_US.json | 6 ++++-- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index dc7c36371d..19b84cef15 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -36,8 +36,6 @@ import { PageLink } from '@shared/models/page/page-link'; import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; -import { ClipboardService } from 'ngx-clipboard'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -51,8 +49,7 @@ export class OtaUpdateTableConfigResolve implements Resolve, private otaPackageService: OtaPackageService, - private fileSize: FileSizePipe, - private clipboardService: ClipboardService) { + private fileSize: FileSizePipe) { this.config.entityType = EntityType.OTA_PACKAGE; this.config.entityComponent = OtaUpdateComponent; this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE); @@ -67,11 +64,11 @@ export class OtaUpdateTableConfigResolve implements Resolve('type', 'ota-update.package-type', '20%', entity => { return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); }), - new EntityTableColumn('url', 'ota-update.url', '20%', entity => { + new EntityTableColumn('url', 'ota-update.direct-url', '20%', entity => { return entity.url && entity.url.length > 20 ? `${entity.url.slice(0, 20)}…` : ''; }, () => ({}), true, () => ({}), () => undefined, false, { - name: this.translate.instant('ota-update.copy-checksum'), + name: this.translate.instant('ota-update.copy-direct-url'), icon: 'content_paste', style: { 'font-size': '16px', diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index 35b4fcba08..bde274c28f 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -41,10 +41,18 @@ ngxClipboard (cbOnSuccess)="onPackageChecksumCopied()" [cbContent]="entity?.checksum" - [fxShow]="!isEdit"> + [fxShow]="!isEdit && entity?.checksum"> ota-update.copy-checksum +
@@ -137,12 +145,12 @@
- ota-update.url + ota-update.direct-url - ota-update.url-required + ota-update.direct-url-required
diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index 9193b37c8e..1182e6c40a 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -160,6 +160,17 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } + onPackageDirectUrlCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('ota-update.checksum-copied-message'), + type: 'success', + duration: 750, + verticalPosition: 'bottom', + horizontalPosition: 'right' + })); + } + prepareFormValue(formValue: any): any { delete formValue.resource; delete formValue.generateChecksum; diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html index 6b38870d3e..20dd7d06ed 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.html +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -16,12 +16,9 @@ -->