Commit 7a19dee3eb4a441bf17dfd99e9978351bf5862fa
1 parent
cf6824cf
UI: Dynamic filter value configuration
Showing
9 changed files
with
90 additions
and
18 deletions
@@ -27,25 +27,33 @@ public class FilterPredicateValue<T> { | @@ -27,25 +27,33 @@ public class FilterPredicateValue<T> { | ||
27 | @Getter | 27 | @Getter |
28 | private final T defaultValue; | 28 | private final T defaultValue; |
29 | @Getter | 29 | @Getter |
30 | + private final T userValue; | ||
31 | + @Getter | ||
30 | private final DynamicValue<T> dynamicValue; | 32 | private final DynamicValue<T> dynamicValue; |
31 | 33 | ||
32 | public FilterPredicateValue(T defaultValue) { | 34 | public FilterPredicateValue(T defaultValue) { |
33 | - this(defaultValue, null); | 35 | + this(defaultValue, null, null); |
34 | } | 36 | } |
35 | 37 | ||
36 | @JsonCreator | 38 | @JsonCreator |
37 | public FilterPredicateValue(@JsonProperty("defaultValue") T defaultValue, | 39 | public FilterPredicateValue(@JsonProperty("defaultValue") T defaultValue, |
40 | + @JsonProperty("userValue") T userValue, | ||
38 | @JsonProperty("dynamicValue") DynamicValue<T> dynamicValue) { | 41 | @JsonProperty("dynamicValue") DynamicValue<T> dynamicValue) { |
39 | this.defaultValue = defaultValue; | 42 | this.defaultValue = defaultValue; |
43 | + this.userValue = userValue; | ||
40 | this.dynamicValue = dynamicValue; | 44 | this.dynamicValue = dynamicValue; |
41 | } | 45 | } |
42 | 46 | ||
43 | @JsonIgnore | 47 | @JsonIgnore |
44 | public T getValue() { | 48 | public T getValue() { |
45 | - if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) { | ||
46 | - return this.dynamicValue.getResolvedValue(); | 49 | + if (this.userValue != null) { |
50 | + return this.userValue; | ||
47 | } else { | 51 | } else { |
48 | - return defaultValue; | 52 | + if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) { |
53 | + return this.dynamicValue.getResolvedValue(); | ||
54 | + } else { | ||
55 | + return defaultValue; | ||
56 | + } | ||
49 | } | 57 | } |
50 | } | 58 | } |
51 | 59 |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 700px;"> | 18 | +<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 900px;"> |
19 | <mat-toolbar color="primary"> | 19 | <mat-toolbar color="primary"> |
20 | <h2 translate>filter.complex-filter</h2> | 20 | <h2 translate>filter.complex-filter</h2> |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup"> | 18 | <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup"> |
19 | - <div fxFlex fxLayout="column"> | 19 | + <div fxFlex fxLayout="column" [fxShow]="!dynamicMode"> |
20 | <div fxFlex fxLayout="column" [ngSwitch]="valueType"> | 20 | <div fxFlex fxLayout="column" [ngSwitch]="valueType"> |
21 | <ng-template [ngSwitchCase]="valueTypeEnum.STRING"> | 21 | <ng-template [ngSwitchCase]="valueTypeEnum.STRING"> |
22 | <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> | 22 | <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> |
@@ -43,6 +43,40 @@ | @@ -43,6 +43,40 @@ | ||
43 | </mat-checkbox> | 43 | </mat-checkbox> |
44 | </ng-template> | 44 | </ng-template> |
45 | </div> | 45 | </div> |
46 | - <div class="tb-hint">Default value</div> | 46 | + <div class="tb-hint" translate>filter.default-value</div> |
47 | </div> | 47 | </div> |
48 | + <div fxFlex fxLayout="column" [fxShow]="dynamicMode"> | ||
49 | + <div fxFlex formGroupName="dynamicValue" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> | ||
50 | + <div fxFlex fxLayout="column"> | ||
51 | + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> | ||
52 | + <mat-label></mat-label> | ||
53 | + <mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}"> | ||
54 | + <mat-option [value]="null"> | ||
55 | + {{'filter.no-dynamic-value' | translate}} | ||
56 | + </mat-option> | ||
57 | + <mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType"> | ||
58 | + {{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}} | ||
59 | + </mat-option> | ||
60 | + </mat-select> | ||
61 | + </mat-form-field> | ||
62 | + <div class="tb-hint" translate>filter.dynamic-source-type</div> | ||
63 | + </div> | ||
64 | + <div fxFlex fxLayout="column"> | ||
65 | + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> | ||
66 | + <mat-label></mat-label> | ||
67 | + <input matInput formControlName="sourceAttribute" placeholder="{{'filter.source-attribute' | translate}}"> | ||
68 | + </mat-form-field> | ||
69 | + <div class="tb-hint" translate>filter.source-attribute</div> | ||
70 | + </div> | ||
71 | + </div> | ||
72 | + </div> | ||
73 | + <button mat-icon-button | ||
74 | + class="mat-elevation-z1 tb-mat-32" | ||
75 | + color="primary" | ||
76 | + type="button" | ||
77 | + matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" | ||
78 | + matTooltipPosition="above" | ||
79 | + (click)="dynamicMode = !dynamicMode"> | ||
80 | + <mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon> | ||
81 | + </button> | ||
48 | </div> | 82 | </div> |
@@ -23,7 +23,12 @@ import { | @@ -23,7 +23,12 @@ import { | ||
23 | ValidatorFn, | 23 | ValidatorFn, |
24 | Validators | 24 | Validators |
25 | } from '@angular/forms'; | 25 | } from '@angular/forms'; |
26 | -import { EntityKeyValueType, FilterPredicateValue } from '@shared/models/query/query.models'; | 26 | +import { |
27 | + DynamicValueSourceType, | ||
28 | + dynamicValueSourceTypeTranslationMap, | ||
29 | + EntityKeyValueType, | ||
30 | + FilterPredicateValue | ||
31 | +} from '@shared/models/query/query.models'; | ||
27 | 32 | ||
28 | @Component({ | 33 | @Component({ |
29 | selector: 'tb-filter-predicate-value', | 34 | selector: 'tb-filter-predicate-value', |
@@ -46,8 +51,14 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | @@ -46,8 +51,14 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | ||
46 | 51 | ||
47 | valueTypeEnum = EntityKeyValueType; | 52 | valueTypeEnum = EntityKeyValueType; |
48 | 53 | ||
54 | + dynamicValueSourceTypes = Object.keys(DynamicValueSourceType); | ||
55 | + dynamicValueSourceTypeEnum = DynamicValueSourceType; | ||
56 | + dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; | ||
57 | + | ||
49 | filterPredicateValueFormGroup: FormGroup; | 58 | filterPredicateValueFormGroup: FormGroup; |
50 | 59 | ||
60 | + dynamicMode = false; | ||
61 | + | ||
51 | private propagateChange = null; | 62 | private propagateChange = null; |
52 | 63 | ||
53 | constructor(private fb: FormBuilder) { | 64 | constructor(private fb: FormBuilder) { |
@@ -83,6 +94,13 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | @@ -83,6 +94,13 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | ||
83 | } | 94 | } |
84 | ) | 95 | ) |
85 | }); | 96 | }); |
97 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( | ||
98 | + (sourceType) => { | ||
99 | + if (!sourceType) { | ||
100 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); | ||
101 | + } | ||
102 | + } | ||
103 | + ); | ||
86 | this.filterPredicateValueFormGroup.valueChanges.subscribe(() => { | 104 | this.filterPredicateValueFormGroup.valueChanges.subscribe(() => { |
87 | this.updateModel(); | 105 | this.updateModel(); |
88 | }); | 106 | }); |
@@ -106,8 +124,10 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | @@ -106,8 +124,10 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | ||
106 | 124 | ||
107 | writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void { | 125 | writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void { |
108 | this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false}); | 126 | this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false}); |
109 | - this.filterPredicateValueFormGroup.get('dynamicValue').patchValue(predicateValue.dynamicValue ? | ||
110 | - predicateValue.dynamicValue : { sourceType: null, sourceAttribute: null }, {emitEvent: false}); | 127 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').patchValue(predicateValue.dynamicValue ? |
128 | + predicateValue.dynamicValue.sourceType : null, {emitEvent: false}); | ||
129 | + this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(predicateValue.dynamicValue ? | ||
130 | + predicateValue.dynamicValue.sourceAttribute : null, {emitEvent: false}); | ||
111 | } | 131 | } |
112 | 132 | ||
113 | private updateModel() { | 133 | private updateModel() { |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <div fxLayout="column" class="mat-content mat-padding"> | 18 | <div fxLayout="column" class="mat-content mat-padding"> |
19 | - <div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue"> | 19 | + <div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue; let last = last"> |
20 | <div fxFlex fxLayout="row" fxLayoutAlign="start center"> | 20 | <div fxFlex fxLayout="row" fxLayoutAlign="start center"> |
21 | <mat-label fxFlex>{{filter.value.filter}}</mat-label> | 21 | <mat-label fxFlex>{{filter.value.filter}}</mat-label> |
22 | <button mat-icon-button color="primary" | 22 | <button mat-icon-button color="primary" |
@@ -28,6 +28,6 @@ | @@ -28,6 +28,6 @@ | ||
28 | <mat-icon>edit</mat-icon> | 28 | <mat-icon>edit</mat-icon> |
29 | </button> | 29 | </button> |
30 | </div> | 30 | </div> |
31 | - <mat-divider></mat-divider> | 31 | + <mat-divider *ngIf="!last"></mat-divider> |
32 | </div> | 32 | </div> |
33 | </div> | 33 | </div> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 700px;"> | 18 | +<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 900px;"> |
19 | <mat-toolbar color="primary"> | 19 | <mat-toolbar color="primary"> |
20 | <h2>{{(data.isAdd ? 'filter.add-key-filter' : 'filter.edit-key-filter') | translate}}</h2> | 20 | <h2>{{(data.isAdd ? 'filter.add-key-filter' : 'filter.edit-key-filter') | translate}}</h2> |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
@@ -33,10 +33,11 @@ import { DialogComponent } from '@app/shared/components/dialog.component'; | @@ -33,10 +33,11 @@ import { DialogComponent } from '@app/shared/components/dialog.component'; | ||
33 | import { TranslateService } from '@ngx-translate/core'; | 33 | import { TranslateService } from '@ngx-translate/core'; |
34 | import { | 34 | import { |
35 | EntityKeyValueType, | 35 | EntityKeyValueType, |
36 | - Filter, | 36 | + Filter, FilterPredicateValue, |
37 | filterToUserFilterInfoList, | 37 | filterToUserFilterInfoList, |
38 | UserFilterInputInfo | 38 | UserFilterInputInfo |
39 | } from '@shared/models/query/query.models'; | 39 | } from '@shared/models/query/query.models'; |
40 | +import { isDefinedAndNotNull } from '@core/utils'; | ||
40 | 41 | ||
41 | export interface UserFilterDialogData { | 42 | export interface UserFilterDialogData { |
42 | filter: Filter; | 43 | filter: Filter; |
@@ -81,15 +82,17 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC | @@ -81,15 +82,17 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC | ||
81 | } | 82 | } |
82 | 83 | ||
83 | private createUserInputFormControl(userInput: UserFilterInputInfo): AbstractControl { | 84 | private createUserInputFormControl(userInput: UserFilterInputInfo): AbstractControl { |
85 | + const predicateValue: FilterPredicateValue<string | number | boolean> = (userInput.info.keyFilterPredicate as any).value; | ||
86 | + const value = isDefinedAndNotNull(predicateValue.userValue) ? predicateValue.userValue : predicateValue.defaultValue; | ||
84 | const userInputControl = this.fb.group({ | 87 | const userInputControl = this.fb.group({ |
85 | label: [userInput.label], | 88 | label: [userInput.label], |
86 | valueType: [userInput.valueType], | 89 | valueType: [userInput.valueType], |
87 | - value: [(userInput.info.keyFilterPredicate as any).value.defaultValue, | 90 | + value: [value, |
88 | userInput.valueType === EntityKeyValueType.NUMERIC || | 91 | userInput.valueType === EntityKeyValueType.NUMERIC || |
89 | userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []] | 92 | userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []] |
90 | }); | 93 | }); |
91 | - userInputControl.get('value').valueChanges.subscribe(value => { | ||
92 | - (userInput.info.keyFilterPredicate as any).value.defaultValue = value; | 94 | + userInputControl.get('value').valueChanges.subscribe(userValue => { |
95 | + (userInput.info.keyFilterPredicate as any).value.userValue = userValue; | ||
93 | }); | 96 | }); |
94 | return userInputControl; | 97 | return userInputControl; |
95 | } | 98 | } |
@@ -255,6 +255,7 @@ export interface DynamicValue<T> { | @@ -255,6 +255,7 @@ export interface DynamicValue<T> { | ||
255 | 255 | ||
256 | export interface FilterPredicateValue<T> { | 256 | export interface FilterPredicateValue<T> { |
257 | defaultValue: T; | 257 | defaultValue: T; |
258 | + userValue?: T; | ||
258 | dynamicValue?: DynamicValue<T>; | 259 | dynamicValue?: DynamicValue<T>; |
259 | } | 260 | } |
260 | 261 |
@@ -1230,7 +1230,13 @@ | @@ -1230,7 +1230,13 @@ | ||
1230 | "time": "Time", | 1230 | "time": "Time", |
1231 | "current-tenant": "Current tenant", | 1231 | "current-tenant": "Current tenant", |
1232 | "current-customer": "Current customer", | 1232 | "current-customer": "Current customer", |
1233 | - "current-user": "Current user" | 1233 | + "current-user": "Current user", |
1234 | + "default-value": "Default value", | ||
1235 | + "dynamic-source-type": "Dynamic source type", | ||
1236 | + "no-dynamic-value": "No dynamic value", | ||
1237 | + "source-attribute": "Source attribute", | ||
1238 | + "switch-to-dynamic-value": "Switch to dynamic value", | ||
1239 | + "switch-to-default-value": "Switch to default value" | ||
1234 | }, | 1240 | }, |
1235 | "fullscreen": { | 1241 | "fullscreen": { |
1236 | "expand": "Expand to fullscreen", | 1242 | "expand": "Expand to fullscreen", |