Commit 8c9328f9c5f3e2025a30e6ae59730b7aabd1656b

Authored by Vladyslav_Prykhodko
1 parent 76485dfd

UI: Improvement created device profile

... ... @@ -29,7 +29,7 @@
29 29 </mat-progress-bar>
30 30 <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
31 31 <div mat-dialog-content>
32   - <mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="selectedIndex = $event.selectedIndex">
  32 + <mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="changeStep($event)">
33 33 <mat-step [stepControl]="deviceProfileDetailsFormGroup">
34 34 <form [formGroup]="deviceProfileDetailsFormGroup" style="padding-bottom: 16px;">
35 35 <ng-template matStepLabel>{{ 'device-profile.device-profile-details' | translate }}</ng-template>
... ... @@ -63,7 +63,7 @@
63 63 </fieldset>
64 64 </form>
65 65 </mat-step>
66   - <mat-step [stepControl]="transportConfigFormGroup">
  66 + <mat-step [stepControl]="transportConfigFormGroup" [optional]="true">
67 67 <form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;">
68 68 <ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template>
69 69 <mat-form-field class="mat-block">
... ... @@ -73,6 +73,9 @@
73 73 {{deviceTransportTypeTranslations.get(type) | translate}}
74 74 </mat-option>
75 75 </mat-select>
  76 + <mat-hint *ngIf="transportConfigFormGroup.get('transportType').value">
  77 + {{deviceTransportTypeHints.get(transportConfigFormGroup.get('transportType').value) | translate}}
  78 + </mat-hint>
76 79 <mat-error *ngIf="transportConfigFormGroup.get('transportType').hasError('required')">
77 80 {{ 'device-profile.transport-type-required' | translate }}
78 81 </mat-error>
... ... @@ -83,7 +86,7 @@
83 86 </tb-device-profile-transport-configuration>
84 87 </form>
85 88 </mat-step>
86   - <mat-step [stepControl]="alarmRulesFormGroup">
  89 + <mat-step [stepControl]="alarmRulesFormGroup" [optional]="true">
87 90 <form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
88 91 <ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate:
89 92 {count: alarmRulesFormGroup.get('alarms').value ?
... ... @@ -95,19 +98,28 @@
95 98 </mat-step>
96 99 </mat-horizontal-stepper>
97 100 </div>
98   - <div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center">
99   - <button mat-button *ngIf="selectedIndex > 0"
100   - [disabled]="(isLoading$ | async)"
101   - (click)="previousStep()">{{ 'action.back' | translate }}</button>
102   - <span *ngIf="selectedIndex <= 0"></span>
103   - <div fxLayout="row wrap" fxLayoutGap="20px">
  101 + <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;">
  102 + <div fxFlex fxLayout="row" fxLayoutAlign="end">
  103 + <button mat-raised-button
  104 + *ngIf="showNext"
  105 + [disabled]="(isLoading$ | async)"
  106 + (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button>
  107 + </div>
  108 + <div fxFlex fxLayout="row">
104 109 <button mat-button
  110 + color="primary"
105 111 [disabled]="(isLoading$ | async)"
106 112 (click)="cancel()">{{ 'action.cancel' | translate }}</button>
107   - <button mat-raised-button
108   - [disabled]="(isLoading$ | async) || selectedForm().invalid"
109   - color="primary"
110   - (click)="nextStep()">{{ (selectedIndex === 2 ? 'action.add' : 'action.continue') | translate }}</button>
  113 + <span fxFlex></span>
  114 + <div fxLayout="row wrap" fxLayoutGap="8px">
  115 + <button mat-raised-button *ngIf="selectedIndex > 0"
  116 + [disabled]="(isLoading$ | async)"
  117 + (click)="previousStep()">{{ 'action.back' | translate }}</button>
  118 + <button mat-raised-button
  119 + [disabled]="(isLoading$ | async)"
  120 + color="primary"
  121 + (click)="add()">{{ 'action.add' | translate }}</button>
  122 + </div>
111 123 </div>
112 124 </div>
113 125 </div>
... ...
... ... @@ -13,26 +13,51 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -:host {
17   - .mat-dialog-content {
18   - display: flex;
19   - flex-direction: column;
20   - overflow: hidden;
  16 +@import "../../../../../scss/constants";
21 17
22   - .mat-stepper-horizontal {
23   - display: flex;
24   - flex-direction: column;
25   - overflow: hidden;
  18 +:host-context(.tb-fullscreen-dialog .mat-dialog-container) {
  19 + @media #{$mat-lt-sm} {
  20 + .mat-dialog-content {
  21 + max-height: 75vh;
26 22 }
27 23 }
28 24 }
29 25
30 26 :host ::ng-deep {
31 27 .mat-dialog-content {
  28 + display: flex;
  29 + flex-direction: column;
  30 + height: 100%;
  31 + padding: 24px 24px 8px !important;
  32 +
32 33 .mat-stepper-horizontal {
  34 + display: flex;
  35 + flex-direction: column;
  36 + height: 100%;
  37 + overflow: hidden;
  38 + @media #{$mat-lt-sm} {
  39 + .mat-step-label {
  40 + white-space: normal;
  41 + overflow: visible;
  42 + .mat-step-text-label {
  43 + overflow: visible;
  44 + }
  45 + }
  46 + }
33 47 .mat-horizontal-content-container {
34   - overflow: auto;
35   - min-height: 260px;
  48 + height: 350px;
  49 + max-height: 100%;
  50 + width: 100%;;
  51 + overflow-y: auto;
  52 + @media #{$mat-gt-sm} {
  53 + min-width: 800px;
  54 + }
  55 + }
  56 + .mat-horizontal-stepper-content[aria-expanded=true] {
  57 + height: 100%;
  58 + form {
  59 + height: 100%;
  60 + }
36 61 }
37 62 }
38 63 }
... ...
... ... @@ -36,13 +36,14 @@ import {
36 36 DeviceProfile,
37 37 DeviceProfileType,
38 38 deviceProfileTypeTranslationMap,
39   - DeviceTransportType,
  39 + DeviceTransportType, deviceTransportTypeHintMap,
40 40 deviceTransportTypeTranslationMap
41 41 } from '@shared/models/device.models';
42 42 import { DeviceProfileService } from '@core/http/device-profile.service';
43 43 import { EntityType } from '@shared/models/entity-type.models';
44 44 import { MatHorizontalStepper } from '@angular/material/stepper';
45 45 import { RuleChainId } from '@shared/models/id/rule-chain-id';
  46 +import { StepperSelectionEvent } from '@angular/cdk/stepper';
46 47
47 48 export interface AddDeviceProfileDialogData {
48 49 deviceProfileName: string;
... ... @@ -62,12 +63,16 @@ export class AddDeviceProfileDialogComponent extends
62 63
63 64 selectedIndex = 0;
64 65
  66 + showNext = true;
  67 +
65 68 entityType = EntityType;
66 69
67 70 deviceProfileTypes = Object.keys(DeviceProfileType);
68 71
69 72 deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
70 73
  74 + deviceTransportTypeHints = deviceTransportTypeHintMap;
  75 +
71 76 deviceTransportTypes = Object.keys(DeviceTransportType);
72 77
73 78 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
... ... @@ -150,25 +155,63 @@ export class AddDeviceProfileDialogComponent extends
150 155 }
151 156 }
152 157
153   - private add(): void {
154   - const deviceProfile: DeviceProfile = {
155   - name: this.deviceProfileDetailsFormGroup.get('name').value,
156   - type: this.deviceProfileDetailsFormGroup.get('type').value,
157   - transportType: this.transportConfigFormGroup.get('transportType').value,
158   - description: this.deviceProfileDetailsFormGroup.get('description').value,
159   - profileData: {
160   - configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
161   - transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
162   - alarms: this.alarmRulesFormGroup.get('alarms').value
  158 + add(): void {
  159 + if (this.allValid()) {
  160 + const deviceProfile: DeviceProfile = {
  161 + name: this.deviceProfileDetailsFormGroup.get('name').value,
  162 + type: this.deviceProfileDetailsFormGroup.get('type').value,
  163 + transportType: this.transportConfigFormGroup.get('transportType').value,
  164 + description: this.deviceProfileDetailsFormGroup.get('description').value,
  165 + profileData: {
  166 + configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
  167 + transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
  168 + alarms: this.alarmRulesFormGroup.get('alarms').value
  169 + }
  170 + };
  171 + if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) {
  172 + deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value);
163 173 }
164   - };
165   - if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) {
166   - deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value);
  174 + this.deviceProfileService.saveDeviceProfile(deviceProfile).subscribe(
  175 + (savedDeviceProfile) => {
  176 + this.dialogRef.close(savedDeviceProfile);
  177 + }
  178 + );
  179 + }
  180 + }
  181 +
  182 + getFormLabel(index: number): string {
  183 + switch (index) {
  184 + case 0:
  185 + return 'device-profile.device-profile-details';
  186 + case 1:
  187 + return 'device-profile.transport-configuration';
  188 + case 2:
  189 + return 'device-profile.alarm-rules';
  190 + }
  191 + }
  192 +
  193 + changeStep($event: StepperSelectionEvent): void {
  194 + this.selectedIndex = $event.selectedIndex;
  195 + if (this.selectedIndex === this.maxStepperIndex) {
  196 + this.showNext = false;
  197 + } else {
  198 + this.showNext = true;
167 199 }
168   - this.deviceProfileService.saveDeviceProfile(deviceProfile).subscribe(
169   - (savedDeviceProfile) => {
170   - this.dialogRef.close(savedDeviceProfile);
  200 + }
  201 +
  202 + private get maxStepperIndex(): number {
  203 + return this.addDeviceProfileStepper?._steps?.length - 1;
  204 + }
  205 +
  206 + allValid(): boolean {
  207 + return !this.addDeviceProfileStepper.steps.find((item, index) => {
  208 + if (item.stepControl.invalid) {
  209 + item.interacted = true;
  210 + this.addDeviceProfileStepper.selectedIndex = index;
  211 + return true;
  212 + } else {
  213 + return false;
171 214 }
172   - );
  215 + });
173 216 }
174 217 }
... ...
... ... @@ -48,7 +48,7 @@
48 48 </button>
49 49 </div>
50 50 <div *ngIf="!createAlarmRulesFormArray().controls.length">
51   - <span translate fxLayoutAlign="center center"
  51 + <span translate fxLayoutAlign="center center" style="margin: 16px 0"
52 52 class="tb-prompt">device-profile.no-create-alarm-rules</span>
53 53 </div>
54 54 <div fxLayout="row" *ngIf="!disabled">
... ... @@ -57,7 +57,7 @@
57 57 (click)="addCreateAlarmRule()"
58 58 matTooltip="{{ 'device-profile.add-create-alarm-rule' | translate }}"
59 59 matTooltipPosition="above">
60   - <mat-icon>add_circle_outline</mat-icon>
  60 + <mat-icon class="button-icon">add_circle_outline</mat-icon>
61 61 {{ 'device-profile.add-create-alarm-rule' | translate }}
62 62 </button>
63 63 </div>
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 :host {
18 18 .create-alarm-rule {
19   - border: 1px groove rgba(0, 0, 0, .25);
  19 + border: 2px groove rgba(0, 0, 0, .45);
20 20 border-radius: 4px;
21 21 padding: 8px;
22 22 }
... ... @@ -28,4 +28,9 @@
28 28 width: 160px;
29 29 }
30 30 }
  31 + .button-icon{
  32 + font-size: 20px;
  33 + width: 20px;
  34 + height: 20px;
  35 + }
31 36 }
... ...
... ... @@ -18,24 +18,11 @@
18 18 <mat-expansion-panel class="device-profile-alarm" fxFlex [formGroup]="alarmFormGroup" [(expanded)]="expanded">
19 19 <mat-expansion-panel-header>
20 20 <div fxFlex fxLayout="row" fxLayoutAlign="start center">
21   - <mat-panel-title [fxShow]="!expanded">
  21 + <mat-panel-title>
22 22 <div fxLayout="row" fxFlex fxLayoutAlign="start center">
23 23 {{ alarmFormGroup.get('alarmType').value }}
24 24 </div>
25 25 </mat-panel-title>
26   - <mat-form-field floatLabel="always"
27   - style="width: 600px;"
28   - [fxShow]="expanded"
29   - (keydown)="!disabled ? $event.stopPropagation() : null;"
30   - (click)="!disabled ? $event.stopPropagation() : null;">
31   - <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
32   - <input required matInput formControlName="alarmType" placeholder="Enter alarm type">
33   - <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
34   - {{ 'device-profile.alarm-type-required' | translate }}
35   - </mat-error>
36   - <mat-hint *ngIf="!disabled"
37   - innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
38   - </mat-form-field>
39 26 <span fxFlex></span>
40 27 <button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
41 28 type="button"
... ... @@ -46,6 +33,50 @@
46 33 </button>
47 34 </div>
48 35 </mat-expansion-panel-header>
  36 + <div fxLayout="column" fxLayoutGap="0.5em">
  37 + <mat-divider></mat-divider>
  38 + <mat-form-field fxFlex floatLabel="always">
  39 + <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
  40 + <input required matInput formControlName="alarmType" placeholder="Enter alarm type">
  41 + <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
  42 + {{ 'device-profile.alarm-type-required' | translate }}
  43 + </mat-error>
  44 + <mat-hint *ngIf="!disabled"
  45 + innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
  46 + </mat-form-field>
  47 + </div>
  48 + <mat-expansion-panel class="advanced-settings" [expanded]="false">
  49 + <mat-expansion-panel-header>
  50 + <mat-panel-title>
  51 + <div fxFlex fxLayout="row" fxLayoutAlign="end center">
  52 + <div class="tb-small" translate>device-profile.advanced-settings</div>
  53 + </div>
  54 + </mat-panel-title>
  55 + </mat-expansion-panel-header>
  56 + <mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;">
  57 + {{ 'device-profile.propagate-alarm' | translate }}
  58 + </mat-checkbox>
  59 + <section *ngIf="alarmFormGroup.get('propagate').value === true">
  60 + <mat-form-field floatLabel="always" class="mat-block">
  61 + <mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label>
  62 + <mat-chip-list #relationTypesChipList [disabled]="disabled">
  63 + <mat-chip
  64 + *ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;"
  65 + (removed)="removeRelationType(key)">
  66 + {{key}}
  67 + <mat-icon matChipRemove>close</mat-icon>
  68 + </mat-chip>
  69 + <input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}"
  70 + style="max-width: 200px;"
  71 + [matChipInputFor]="relationTypesChipList"
  72 + [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
  73 + (matChipInputTokenEnd)="addRelationType($event)"
  74 + [matChipInputAddOnBlur]="true">
  75 + </mat-chip-list>
  76 + <mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint>
  77 + </mat-form-field>
  78 + </section>
  79 + </mat-expansion-panel>
49 80 <div fxFlex fxLayout="column">
50 81 <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div>
51 82 <tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;">
... ... @@ -68,7 +99,7 @@
68 99 </button>
69 100 </div>
70 101 <div *ngIf="!alarmFormGroup.get('clearRule').value">
71   - <span translate fxLayoutAlign="center center"
  102 + <span translate fxLayoutAlign="center center" style="margin: 16px 0"
72 103 class="tb-prompt">device-profile.no-clear-alarm-rule</span>
73 104 </div>
74 105 <div fxLayout="row" *ngIf="!disabled"
... ... @@ -78,41 +109,9 @@
78 109 (click)="addClearAlarmRule()"
79 110 matTooltip="{{ 'device-profile.add-clear-alarm-rule' | translate }}"
80 111 matTooltipPosition="above">
81   - <mat-icon>add_circle_outline</mat-icon>
  112 + <mat-icon class="button-icon">add_circle_outline</mat-icon>
82 113 {{ 'device-profile.add-clear-alarm-rule' | translate }}
83 114 </button>
84 115 </div>
85 116 </div>
86   - <mat-expansion-panel class="advanced-settings" [expanded]="false">
87   - <mat-expansion-panel-header>
88   - <mat-panel-title>
89   - <div fxFlex fxLayout="row" fxLayoutAlign="end center">
90   - <div class="tb-small" translate>device-profile.advanced-settings</div>
91   - </div>
92   - </mat-panel-title>
93   - </mat-expansion-panel-header>
94   - <mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;">
95   - {{ 'device-profile.propagate-alarm' | translate }}
96   - </mat-checkbox>
97   - <section *ngIf="alarmFormGroup.get('propagate').value === true">
98   - <mat-form-field floatLabel="always" class="mat-block">
99   - <mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label>
100   - <mat-chip-list #relationTypesChipList [disabled]="disabled">
101   - <mat-chip
102   - *ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;"
103   - (removed)="removeRelationType(key)">
104   - {{key}}
105   - <mat-icon matChipRemove>close</mat-icon>
106   - </mat-chip>
107   - <input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}"
108   - style="max-width: 200px;"
109   - [matChipInputFor]="relationTypesChipList"
110   - [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
111   - (matChipInputTokenEnd)="addRelationType($event)"
112   - [matChipInputAddOnBlur]="true">
113   - </mat-chip-list>
114   - <mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint>
115   - </mat-form-field>
116   - </section>
117   - </mat-expansion-panel>
118 117 </mat-expansion-panel>
... ...
... ... @@ -17,7 +17,7 @@
17 17 :host {
18 18 display: block;
19 19 .clear-alarm-rule {
20   - border: 1px groove rgba(0, 0, 0, .25);
  20 + border: 2px groove rgba(0, 0, 0, .45);
21 21 border-radius: 4px;
22 22 padding: 8px;
23 23 }
... ... @@ -28,13 +28,16 @@
28 28 .mat-expansion-panel-header {
29 29 padding: 0 24px 0 8px;
30 30 &.mat-expanded {
31   - height: 80px;
  31 + height: 48px;
32 32 }
33 33 }
34 34 }
35 35 &.advanced-settings {
36 36 border: none;
37 37 padding: 0;
  38 + .mat-expansion-panel-header {
  39 + padding: 0 8px;
  40 + }
38 41 }
39 42 }
40 43 }
... ... @@ -43,7 +46,7 @@
43 46 .mat-expansion-panel {
44 47 &.device-profile-alarm {
45 48 .mat-expansion-panel-body {
46   - padding: 0 8px;
  49 + padding: 0 8px 8px;
47 50 }
48 51 }
49 52 &.advanced-settings {
... ... @@ -51,5 +54,10 @@
51 54 padding: 0;
52 55 }
53 56 }
  57 + .button-icon{
  58 + font-size: 20px;
  59 + width: 20px;
  60 + height: 20px;
  61 + }
54 62 }
55 63 }
... ...
... ... @@ -25,6 +25,9 @@
25 25 {{deviceTransportTypeTranslations.get(type) | translate}}
26 26 </mat-option>
27 27 </mat-select>
  28 + <mat-hint *ngIf="detailsForm.get('transportType').value">
  29 + {{deviceTransportTypeHints.get(detailsForm.get('transportType').value) | translate}}
  30 + </mat-hint>
28 31 <mat-error *ngIf="detailsForm.get('transportType').hasError('required')">
29 32 {{ 'device-profile.transport-type-required' | translate }}
30 33 </mat-error>
... ...
... ... @@ -18,7 +18,12 @@ import { Component } from '@angular/core';
18 18 import { Store } from '@ngrx/store';
19 19 import { AppState } from '@core/core.state';
20 20 import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
21   -import { DeviceProfile, DeviceTransportType, deviceTransportTypeTranslationMap } from '@shared/models/device.models';
  21 +import {
  22 + DeviceProfile,
  23 + DeviceTransportType,
  24 + deviceTransportTypeHintMap,
  25 + deviceTransportTypeTranslationMap
  26 +} from '@shared/models/device.models';
22 27
23 28 @Component({
24 29 selector: 'tb-device-profile-tabs',
... ... @@ -28,8 +33,11 @@ import { DeviceProfile, DeviceTransportType, deviceTransportTypeTranslationMap }
28 33 export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> {
29 34
30 35 deviceTransportTypes = Object.keys(DeviceTransportType);
  36 +
31 37 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
32 38
  39 + deviceTransportTypeHints = deviceTransportTypeHintMap;
  40 +
33 41 constructor(protected store: Store<AppState>) {
34 42 super(store);
35 43 }
... ...