diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html index 34c43f240c..9339438726 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html @@ -16,33 +16,16 @@ -->
-
- - - -
- - {{ getName(queuesControl.value.name) }} - - - -
-
- - - - -
-
+
+ +
{ }; constructor(private store: Store, @@ -64,11 +66,17 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit this.tenantProfileDataFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { + this.valueChange$ = this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -87,7 +95,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit if (this.tenantProfileDataFormGroup.valid) { tenantProfileData = this.tenantProfileDataFormGroup.getRawValue(); } - this.propagateChange(tenantProfileData); + this.propagateChange(tenantProfileData.configuration); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss index c76509e9e3..f9940b3dae 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss @@ -35,6 +35,10 @@ width: fit-content; } } + + .mat-expansion-panel-header { + height: 48px; + } .expansion-panel-block { padding-bottom: 16px; } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index 6a57ee57d0..ed88fe9fba 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -23,6 +23,7 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityComponent } from '../entity/entity.component'; +import { guid } from '@core/utils'; @Component({ selector: 'tb-tenant-profile', @@ -54,6 +55,7 @@ export class TenantProfileComponent extends EntityComponent { buildForm(entity: TenantProfile): FormGroup { const mainQueue = [ { + id: guid(), consumerPerPartition: true, name: 'Main', packProcessingTimeout: 2000, @@ -70,7 +72,10 @@ export class TenantProfileComponent extends EntityComponent { batchSize: 1000, type: 'BURST' }, - topic: 'tb_rule_engine.main' + topic: 'tb_rule_engine.main', + additionalInfo: { + description: '' + } } ]; const formGroup = this.fb.group( diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index e13faf0f43..920ad2f162 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -15,161 +15,182 @@ limitations under the License. --> - -
- - admin.queue-name - - - {{ 'queue.name-required' | translate }} - - - - queue.poll-interval - - - {{ 'queue.poll-interval-required' | translate }} - - - {{ 'queue.poll-interval-min-value' | translate }} - - - - queue.partitions - - - {{ 'queue.partitions-required' | translate }} - - - {{ 'queue.partitions-min-value' | translate }} - - - -
{{ 'queue.consumer-per-partition' | translate }}
-
{{'queue.consumer-per-partition-hint' | translate}}
-
- - queue.processing-timeout - - - {{ 'queue.pack-processing-timeout-required' | translate }} - - - {{ 'queue.pack-processing-timeout-min-value' | translate }} - - - - - - - queue.submit-strategy - - - -
- - queue.submit-strategy - - - {{ strategy }} - - - - {{ 'queue.submit-strategy-type-required' | translate }} - - - - queue.batch-size - - - {{ 'queue.batch-size-required' | translate }} - - - {{ 'queue.batch-size-min-value' | translate }} - - -
-
-
- - - - queue.processing-strategy - - - -
- - queue.processing-strategy - - - {{ strategy }} - - - - {{ 'queue.processing-strategy-type-required' | translate }} - - - - queue.retries - - - {{ 'queue.retries-required' | translate }} - - - {{ 'queue.retries-min-value' | translate }} - - - - queue.failure-percentage - - - {{ 'queue.failure-percentage-required' | translate }} - - - {{ 'queue.failure-percentage-min-value' | translate }} - - - {{ 'queue.failure-percentage-max-value' | translate }} - - - - queue.pause-between-retries - - - {{ 'queue.pause-between-retries-required' | translate }} - - - {{ 'queue.pause-between-retries-min-value' | translate }} - - - - queue.max-pause-between-retries - - - {{ 'queue.max-pause-between-retries-required' | translate }} - - - {{ 'queue.max-pause-between-retries-min-value' | translate }} - - -
-
-
-
- - queue.description - - -
+ + +
+ + {{ queueTitle }} + + + +
+
+ +
+ + admin.queue-name + + + {{ 'queue.name-required' | translate }} + + + {{ 'queue.name-unique' | translate }} + + + + queue.poll-interval + + + {{ 'queue.poll-interval-required' | translate }} + + + {{ 'queue.poll-interval-min-value' | translate }} + + + + queue.partitions + + + {{ 'queue.partitions-required' | translate }} + + + {{ 'queue.partitions-min-value' | translate }} + + + +
{{ 'queue.consumer-per-partition' | translate }}
+
{{'queue.consumer-per-partition-hint' | translate}}
+
+ + queue.processing-timeout + + + {{ 'queue.pack-processing-timeout-required' | translate }} + + + {{ 'queue.pack-processing-timeout-min-value' | translate }} + + + + + + + queue.submit-strategy + + + +
+ + queue.submit-strategy + + + {{ strategy }} + + + + {{ 'queue.submit-strategy-type-required' | translate }} + + + + queue.batch-size + + + {{ 'queue.batch-size-required' | translate }} + + + {{ 'queue.batch-size-min-value' | translate }} + + +
+
+
+ + + + queue.processing-strategy + + + +
+ + queue.processing-strategy + + + {{ strategy }} + + + + {{ 'queue.processing-strategy-type-required' | translate }} + + + + queue.retries + + + {{ 'queue.retries-required' | translate }} + + + {{ 'queue.retries-min-value' | translate }} + + + + queue.failure-percentage + + + {{ 'queue.failure-percentage-required' | translate }} + + + {{ 'queue.failure-percentage-min-value' | translate }} + + + {{ 'queue.failure-percentage-max-value' | translate }} + + + + queue.pause-between-retries + + + {{ 'queue.pause-between-retries-required' | translate }} + + + {{ 'queue.pause-between-retries-min-value' | translate }} + + + + queue.max-pause-between-retries + + + {{ 'queue.max-pause-between-retries-required' | translate }} + + + {{ 'queue.max-pause-between-retries-min-value' | translate }} + + +
+
+
+
+ + queue.description + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index 38552bff25..6a437799b5 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -29,6 +29,7 @@ import { MatDialog } from '@angular/material/dialog'; import { UtilsService } from '@core/services/utils.service'; import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { Subscription } from 'rxjs'; @Component({ selector: 'tb-queue-form', @@ -47,7 +48,7 @@ import { isDefinedAndNotNull } from '@core/utils'; } ] }) -export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator { +export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { @Input() disabled: boolean; @@ -55,20 +56,28 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat @Input() newQueue = false; + @Input() + mainQueue = false; + @Input() systemQueue = false; - private modelValue: QueueInfo; + @Input() + expanded = false; + + @Output() + removeQueue = new EventEmitter(); queueFormGroup: FormGroup; - submitStrategies: string[] = []; processingStrategies: string[] = []; - + queueTitle = ''; hideBatchSize = false; + private modelValue: QueueInfo; private propagateChange = null; private propagateChangePending = false; + private valueChange$: Subscription = null; constructor(private dialog: MatDialog, private utils: UtilsService, @@ -114,10 +123,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat description: [''] }) }); - this.queueFormGroup.valueChanges.subscribe(() => { + this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); - this.queueFormGroup.get('name').valueChanges.subscribe((value) => this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`})); + this.queueFormGroup.get('name').valueChanges.subscribe((value) => { + this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}); + this.queueTitle = this.utils.customTranslation(value, value); + }); this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { this.submitStrategyTypeChanged(); }); @@ -128,6 +140,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat } } + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + this.valueChange$ = null; + } + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -141,6 +160,10 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat writeValue(value: QueueInfo): void { this.propagateChangePending = false; this.modelValue = value; + if (!this.modelValue.name) { + this.expanded = true; + } + this.queueTitle = this.utils.customTranslation(value.name, value.name); if (isDefinedAndNotNull(this.modelValue)) { this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts index 3b1773842d..7484d8b403 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts @@ -33,6 +33,8 @@ import { TenantProfileComponent } from '../../components/profile/tenant-profile. import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; import { DialogService } from '@core/services/dialog.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { map } from 'rxjs/operators'; +import { guid } from '@core/utils'; @Injectable() export class TenantProfilesTableConfigResolver implements Resolve> { @@ -84,7 +86,12 @@ export class TenantProfilesTableConfigResolver implements Resolve this.translate.instant('tenant-profile.delete-tenant-profiles-text'); this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink); - this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id); + this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id).pipe( + map(tenantProfile => ({ + ...tenantProfile, + profileData: {...tenantProfile.profileData, queueConfiguration: this.addId(tenantProfile.profileData.queueConfiguration)}, + })) + ); this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile); this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id); this.config.onEntityAction = action => this.onTenantProfileAction(action); @@ -93,6 +100,15 @@ export class TenantProfilesTableConfigResolver implements Resolve { + value.id = guid(); + queuesWithId.push(value); + }); + return queuesWithId; + } + resolve(): EntityTableConfig { this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles'); diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html index 471d191b61..c8af29fbd4 100644 --- a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html @@ -15,7 +15,7 @@ limitations under the License. --> - + - + [displayWith]="displayQueueFn" + > + - {{getDescription(queue)}} + {{getDescription(queue)}}
diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss new file mode 100644 index 0000000000..45a4ab9808 --- /dev/null +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.scss @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 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. + */ + +::ng-deep { + .queue-option { + .mat-option-text { + display: inline; + } + .queue-option-description { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } +} diff --git a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts index 0caa487d66..897e041649 100644 --- a/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts @@ -36,7 +36,7 @@ import { emptyPageData } from '@shared/models/page/page-data'; @Component({ selector: 'tb-queue-autocomplete', templateUrl: './queue-autocomplete.component.html', - styleUrls: [], + styleUrls: ['./queue-autocomplete.component.scss'], providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QueueAutocompleteComponent), @@ -207,7 +207,10 @@ export class QueueAutocompleteComponent implements ControlValueAccessor, OnInit getDescription(value) { return value.additionalInfo?.description ? value.additionalInfo.description : - `Submit Strategy: ${value.submitStrategy.type}, Processing Strategy: ${value.processingStrategy.type}`; + this.translate.instant( + 'queue.alt-description', + {submitStrategy: value.submitStrategy.type, processingStrategy: value.processingStrategy.type} + ); } clear() { diff --git a/ui-ngx/src/app/shared/models/queue.models.ts b/ui-ngx/src/app/shared/models/queue.models.ts index 86bd99a19d..6c67b4db1b 100644 --- a/ui-ngx/src/app/shared/models/queue.models.ts +++ b/ui-ngx/src/app/shared/models/queue.models.ts @@ -43,6 +43,7 @@ export enum QueueProcessingStrategyTypes { } export interface QueueInfo extends BaseData { + generatedId?: string; name: string; packProcessingTimeout: number; partitions: number; diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 14c0d232df..33d1367c01 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -98,7 +98,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan export interface TenantProfileData { configuration: TenantProfileConfiguration; - queueConfiguration?: QueueInfo; + queueConfiguration?: Array; } export interface TenantProfile extends BaseData { 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 93e1afe006..ff1f85b1f0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2773,6 +2773,7 @@ "select-name": "Select queue name", "name": "Name", "name-required": "Queue name is required!", + "name-unique": "Queue name is not unique!", "queue-required": "Queue is required!", "topic-required": "Queue topic is required!", "poll-interval-required": "Poll interval is required!", @@ -2819,7 +2820,8 @@ "delete": "Delete queue", "copyId": "Copy queue Id", "idCopiedMessage": "Queue Id has been copied to clipboard", - "description": "Description" + "description": "Description", + "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}" }, "tenant": { "tenant": "Tenant",