Merge pull request #10306 from rusikv/relation_query_not_option-ui
Relation query 'Not' option and redesign
This commit is contained in:
commit
78d5a57468
@ -28,10 +28,9 @@
|
|||||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<fieldset [disabled]="isLoading$ | async">
|
<fieldset class="tb-form-panel no-padding no-border no-gap" [disabled]="isLoading$ | async">
|
||||||
<div fxFlex fxLayout="column">
|
<div class="tb-form-row column-xs align-start no-border no-gap no-padding tb-standard-fields">
|
||||||
<div fxLayout="row">
|
<mat-form-field class="flex">
|
||||||
<mat-form-field fxFlex class="mat-block">
|
|
||||||
<mat-label translate>alias.name</mat-label>
|
<mat-label translate>alias.name</mat-label>
|
||||||
<input matInput formControlName="alias" required>
|
<input matInput formControlName="alias" required>
|
||||||
<mat-error *ngIf="entityAliasFormGroup.get('alias').hasError('required')">
|
<mat-error *ngIf="entityAliasFormGroup.get('alias').hasError('required')">
|
||||||
@ -41,12 +40,11 @@
|
|||||||
{{ 'alias.duplicate-alias' | translate }}
|
{{ 'alias.duplicate-alias' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<section class="tb-resolve-multiple-switch" fxLayout="column" fxLayoutAlign="start center">
|
<div class="tb-resolve-multiple-switch" tb-hint-tooltip-icon="{{ 'alias.resolve-multiple-hint' | translate }}">
|
||||||
<label class="tb-small resolve-multiple-label" translate>alias.resolve-multiple</label>
|
<mat-slide-toggle class="mat-slide" formControlName="resolveMultiple">
|
||||||
<mat-slide-toggle class="resolve-multiple-switch"
|
{{ 'alias.resolve-multiple' | translate }}
|
||||||
formControlName="resolveMultiple">
|
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</section>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<tb-entity-filter formControlName="filter"
|
<tb-entity-filter formControlName="filter"
|
||||||
[resolveMultiple]="entityAliasFormGroup.get('resolveMultiple').value"
|
[resolveMultiple]="entityAliasFormGroup.get('resolveMultiple').value"
|
||||||
@ -55,7 +53,6 @@
|
|||||||
</tb-entity-filter>
|
</tb-entity-filter>
|
||||||
<tb-error [error]="entityAliasFormGroup.hasError('noEntityMatched')
|
<tb-error [error]="entityAliasFormGroup.hasError('noEntityMatched')
|
||||||
? translate.instant('alias.entity-filter-no-entity-matched') : ''"></tb-error>
|
? translate.instant('alias.entity-filter-no-entity-matched') : ''"></tb-error>
|
||||||
</div>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div mat-dialog-actions fxLayoutAlign="end center">
|
<div mat-dialog-actions fxLayoutAlign="end center">
|
||||||
|
|||||||
@ -13,16 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
@import '../../../../../scss/constants';
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
.tb-resolve-multiple-switch {
|
.tb-resolve-multiple-switch {
|
||||||
padding-left: 10px;
|
padding: 18px 0 0 18px;
|
||||||
|
@media #{$mat-xs} {
|
||||||
.resolve-multiple-switch {
|
padding: 0 0 18px 0;
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolve-multiple-label {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,45 +130,27 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template [ngSwitchCase]="aliasFilterType.relationsQuery">
|
<ng-template [ngSwitchCase]="aliasFilterType.relationsQuery">
|
||||||
<section fxLayout="column" id="relationsQueryFilter">
|
<section class="tb-form-panel no-border no-padding">
|
||||||
<label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
|
<div class="tb-form-panel stroked no-gap">
|
||||||
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
|
<div class="tb-form-panel-title" translate>alias.root-entity</div>
|
||||||
<mat-slide-toggle class="root-state-entity-switch"
|
<mat-slide-toggle class="mat-slide margin" formControlName="rootStateEntity">
|
||||||
formControlName="rootStateEntity">
|
{{ 'alias.root-state-entity' | translate }}
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
<div *ngIf="filterFormGroup.get('rootStateEntity').value" class="tb-form-panel no-border no-padding no-gap">
|
||||||
</section>
|
<mat-form-field>
|
||||||
<div fxFlex fxLayout="row" *ngIf="!filterFormGroup.get('rootStateEntity').value">
|
<mat-label translate>alias.state-entity-parameter-name</mat-label>
|
||||||
<tb-entity-select fxFlex
|
<input matInput formControlName="stateEntityParamName">
|
||||||
|
</mat-form-field>
|
||||||
|
<tb-entity-select useAliasEntityTypes="true" formControlName="defaultStateEntity">
|
||||||
|
</tb-entity-select>
|
||||||
|
</div>
|
||||||
|
<tb-entity-select *ngIf="!filterFormGroup.get('rootStateEntity').value"
|
||||||
required
|
required
|
||||||
useAliasEntityTypes="true"
|
useAliasEntityTypes="true"
|
||||||
formControlName="rootEntity">
|
formControlName="rootEntity">
|
||||||
</tb-entity-select>
|
</tb-entity-select>
|
||||||
</div>
|
<div class="tb-form-row no-border no-padding tb-standard-fields">
|
||||||
<div fxFlex fxLayout="column" fxLayout.gt-sm="row" *ngIf="filterFormGroup.get('rootStateEntity').value">
|
<mat-form-field class="flex">
|
||||||
<mat-form-field floatLabel="always" class="mat-block" style="margin-top: 24px; padding-right: 8px;">
|
|
||||||
<mat-label translate>alias.state-entity-parameter-name</mat-label>
|
|
||||||
<input matInput formControlName="stateEntityParamName"
|
|
||||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}">
|
|
||||||
</mat-form-field>
|
|
||||||
<div fxFlex fxLayout="column">
|
|
||||||
<label class="tb-small">{{ 'alias.default-state-entity' | translate }}</label>
|
|
||||||
<tb-entity-select fxFlex
|
|
||||||
useAliasEntityTypes="true"
|
|
||||||
formControlName="defaultStateEntity">
|
|
||||||
</tb-entity-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div fxFlex fxLayout="row">
|
|
||||||
<section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0;">
|
|
||||||
<mat-slide-toggle class="root-state-entity-switch"
|
|
||||||
formControlName="fetchLastLevelOnly">
|
|
||||||
</mat-slide-toggle>
|
|
||||||
<label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div fxFlex fxLayoutGap="8px" fxLayout="row" fxLayout.xs="column">
|
|
||||||
<mat-form-field class="mat-block" style="min-width: 100px;">
|
|
||||||
<mat-label translate>relation.direction</mat-label>
|
<mat-label translate>relation.direction</mat-label>
|
||||||
<mat-select required formControlName="direction">
|
<mat-select required formControlName="direction">
|
||||||
<mat-option *ngFor="let type of directionTypes" [value]="type">
|
<mat-option *ngFor="let type of directionTypes" [value]="type">
|
||||||
@ -176,7 +158,7 @@
|
|||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field fxFlex floatLabel="always" class="mat-block">
|
<mat-form-field class="flex" floatLabel="always">
|
||||||
<mat-label translate>alias.max-relation-level</mat-label>
|
<mat-label translate>alias.max-relation-level</mat-label>
|
||||||
<input matInput
|
<input matInput
|
||||||
type="number"
|
type="number"
|
||||||
@ -186,14 +168,19 @@
|
|||||||
formControlName="maxLevel">
|
formControlName="maxLevel">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-slide-toggle formControlName="negate" *ngIf="this.filterFormGroup.get('filters').value.length > 1">
|
<mat-slide-toggle *ngIf="filterFormGroup.get('maxLevel').value > 1 || !filterFormGroup.get('maxLevel').value"
|
||||||
{{ 'relation.negate-all-relations' | translate }}
|
class="mat-slide" formControlName="fetchLastLevelOnly">
|
||||||
|
{{ 'alias.last-level-relation' | translate }}
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<div class="mat-caption" style="padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>relation.relation-filters</div>
|
</div>
|
||||||
|
<div class="tb-form-panel stroked">
|
||||||
|
<div class="tb-form-panel-title" translate>relation.relation-filters</div>
|
||||||
<tb-relation-filters
|
<tb-relation-filters
|
||||||
|
enableNotOption
|
||||||
[allowedEntityTypes]="allowedEntityTypes"
|
[allowedEntityTypes]="allowedEntityTypes"
|
||||||
formControlName="filters">
|
formControlName="filters">
|
||||||
</tb-relation-filters>
|
</tb-relation-filters>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template [ngSwitchCase]="entityFilterFormGroup.get('type').value === aliasFilterType.assetSearchQuery ||
|
<ng-template [ngSwitchCase]="entityFilterFormGroup.get('type').value === aliasFilterType.assetSearchQuery ||
|
||||||
|
|||||||
@ -197,8 +197,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit, OnDe
|
|||||||
rootEntity: [filter ? filter.rootEntity : null, (filter && filter.rootStateEntity) ? [] : [Validators.required]],
|
rootEntity: [filter ? filter.rootEntity : null, (filter && filter.rootStateEntity) ? [] : [Validators.required]],
|
||||||
direction: [filter ? filter.direction : EntitySearchDirection.FROM, [Validators.required]],
|
direction: [filter ? filter.direction : EntitySearchDirection.FROM, [Validators.required]],
|
||||||
maxLevel: [filter ? filter.maxLevel : 1, []],
|
maxLevel: [filter ? filter.maxLevel : 1, []],
|
||||||
fetchLastLevelOnly: [filter ? filter.fetchLastLevelOnly : false, []],
|
fetchLastLevelOnly: [filter ? filter.fetchLastLevelOnly : false, []]
|
||||||
negate: [filter ? filter.negate : false, []]
|
|
||||||
});
|
});
|
||||||
const rootStateSubscription = this.filterFormGroup.get('rootStateEntity').valueChanges.subscribe((rootStateEntity: boolean) => {
|
const rootStateSubscription = this.filterFormGroup.get('rootStateEntity').valueChanges.subscribe((rootStateEntity: boolean) => {
|
||||||
this.filterFormGroup.get('rootEntity').setValidators(rootStateEntity ? [] : [Validators.required]);
|
this.filterFormGroup.get('rootEntity').setValidators(rootStateEntity ? [] : [Validators.required]);
|
||||||
|
|||||||
@ -18,13 +18,17 @@
|
|||||||
<div class="tb-form-panel no-border no-padding">
|
<div class="tb-form-panel no-border no-padding">
|
||||||
<div class="tb-form-table" [formGroup]="relationFiltersFormGroup">
|
<div class="tb-form-table" [formGroup]="relationFiltersFormGroup">
|
||||||
<div class="tb-form-table-header">
|
<div class="tb-form-table-header">
|
||||||
|
<div *ngIf="enableNotOption" class="tb-form-table-header-cell flex-18"></div>
|
||||||
<div class="tb-form-table-header-cell flex-50">{{ 'relation.relation-type' | translate }}</div>
|
<div class="tb-form-table-header-cell flex-50">{{ 'relation.relation-type' | translate }}</div>
|
||||||
<div class="tb-form-table-header-cell flex-50">{{ 'entity.entity-types' | translate }}</div>
|
<div class="tb-form-table-header-cell flex-50">{{ 'entity.entity-types' | translate }}</div>
|
||||||
<div class="tb-form-table-header-cell actions-header"></div>
|
<div class="tb-form-table-header-cell actions-header"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-table-body" formArrayName="relationFilters">
|
<div class="tb-form-table-body" formArrayName="relationFilters">
|
||||||
<div class="tb-form-table-row"
|
<div class="tb-form-table-row align-start"
|
||||||
*ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
|
*ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
|
||||||
|
<mat-chip-listbox *ngIf="enableNotOption" class="flex-18 center-stretch" [formControl]="relationFilterControl.get('negate')">
|
||||||
|
<mat-chip-option color="primary" [value]="true">{{ 'relation.not' | translate}}</mat-chip-option>
|
||||||
|
</mat-chip-listbox>
|
||||||
<tb-relation-type-autocomplete subscriptSizing="dynamic"
|
<tb-relation-type-autocomplete subscriptSizing="dynamic"
|
||||||
class="flex-50" showLabel="false"
|
class="flex-50" showLabel="false"
|
||||||
[additionalClasses]="['tb-inline-field']"
|
[additionalClasses]="['tb-inline-field']"
|
||||||
|
|||||||
@ -18,6 +18,10 @@
|
|||||||
flex: 1 1 50%;
|
flex: 1 1 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-18 {
|
||||||
|
flex: 1 1 18%;
|
||||||
|
}
|
||||||
|
|
||||||
.actions-header {
|
.actions-header {
|
||||||
width: 40px
|
width: 40px
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { Store } from '@ngrx/store';
|
|||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-relation-filters',
|
selector: 'tb-relation-filters',
|
||||||
@ -49,6 +50,10 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
|
|||||||
|
|
||||||
@Input() allowedEntityTypes: Array<EntityType | AliasEntityType>;
|
@Input() allowedEntityTypes: Array<EntityType | AliasEntityType>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
enableNotOption = false;
|
||||||
|
|
||||||
relationFiltersFormGroup: UntypedFormGroup;
|
relationFiltersFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
@ -112,18 +117,20 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
|
|||||||
public addFilter() {
|
public addFilter() {
|
||||||
const filter: RelationEntityTypeFilter = {
|
const filter: RelationEntityTypeFilter = {
|
||||||
relationType: null,
|
relationType: null,
|
||||||
entityTypes: [],
|
entityTypes: []
|
||||||
negate: false
|
|
||||||
};
|
};
|
||||||
this.relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter));
|
this.relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
private createRelationFilterFormGroup(filter: RelationEntityTypeFilter): AbstractControl {
|
private createRelationFilterFormGroup(filter: RelationEntityTypeFilter): AbstractControl {
|
||||||
return this.fb.group({
|
const formGroup = this.fb.group({
|
||||||
relationType: [filter ? filter.relationType : null],
|
relationType: [filter ? filter.relationType : null],
|
||||||
entityTypes: [filter ? filter.entityTypes : []],
|
entityTypes: [filter ? filter.entityTypes : []]
|
||||||
negate: [filter ? filter.negate : false],
|
|
||||||
});
|
});
|
||||||
|
if (this.enableNotOption) {
|
||||||
|
formGroup.addControl('negate', this.fb.control(filter ? filter.negate : false));
|
||||||
|
}
|
||||||
|
return formGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateModel() {
|
private updateModel() {
|
||||||
|
|||||||
@ -15,9 +15,8 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div fxLayout="row" class="tb-entity-select" [formGroup]="entitySelectFormGroup">
|
<div [formGroup]="entitySelectFormGroup">
|
||||||
<tb-entity-type-select
|
<tb-entity-type-select
|
||||||
style="min-width: 100px; padding-right: 8px;"
|
|
||||||
*ngIf="displayEntityTypeSelect"
|
*ngIf="displayEntityTypeSelect"
|
||||||
[showLabel]="true"
|
[showLabel]="true"
|
||||||
[required]="required"
|
[required]="required"
|
||||||
@ -26,7 +25,6 @@
|
|||||||
formControlName="entityType">
|
formControlName="entityType">
|
||||||
</tb-entity-type-select>
|
</tb-entity-type-select>
|
||||||
<tb-entity-autocomplete
|
<tb-entity-autocomplete
|
||||||
fxFlex
|
|
||||||
*ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT
|
*ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT
|
||||||
&& modelValue.entityType !== AliasEntityType.CURRENT_USER
|
&& modelValue.entityType !== AliasEntityType.CURRENT_USER
|
||||||
&& modelValue.entityType !== AliasEntityType.CURRENT_USER_OWNER"
|
&& modelValue.entityType !== AliasEntityType.CURRENT_USER_OWNER"
|
||||||
|
|||||||
@ -17,8 +17,7 @@
|
|||||||
-->
|
-->
|
||||||
<mat-form-field [formGroup]="entityTypeFormGroup">
|
<mat-form-field [formGroup]="entityTypeFormGroup">
|
||||||
<mat-label *ngIf="showLabel">{{ 'entity.type' | translate }}</mat-label>
|
<mat-label *ngIf="showLabel">{{ 'entity.type' | translate }}</mat-label>
|
||||||
<mat-select [required]="required"
|
<mat-select [required]="required" matInput formControlName="entityType">
|
||||||
class="tb-entity-type-select" matInput formControlName="entityType">
|
|
||||||
<mat-option *ngFor="let type of entityTypes" [value]="type">
|
<mat-option *ngFor="let type of entityTypes" [value]="type">
|
||||||
{{ displayEntityTypeFn(type) }}
|
{{ displayEntityTypeFn(type) }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|||||||
@ -130,7 +130,6 @@ export interface RelationsQueryFilter {
|
|||||||
filters?: Array<RelationEntityTypeFilter>;
|
filters?: Array<RelationEntityTypeFilter>;
|
||||||
maxLevel?: number;
|
maxLevel?: number;
|
||||||
fetchLastLevelOnly?: boolean;
|
fetchLastLevelOnly?: boolean;
|
||||||
negate?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntitySearchQueryFilter {
|
export interface EntitySearchQueryFilter {
|
||||||
|
|||||||
@ -638,6 +638,7 @@
|
|||||||
"filter-type-edge-search-query-description": "Edges with types {{edgeTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
|
"filter-type-edge-search-query-description": "Edges with types {{edgeTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
|
||||||
"entity-filter": "Entity filter",
|
"entity-filter": "Entity filter",
|
||||||
"resolve-multiple": "Resolve as multiple entities",
|
"resolve-multiple": "Resolve as multiple entities",
|
||||||
|
"resolve-multiple-hint": "Enable to render data from all filtered entities simultaneously. \nIf disabled, the widget shows data from the selected entity only.",
|
||||||
"filter-type": "Filter type",
|
"filter-type": "Filter type",
|
||||||
"filter-type-required": "Filter type is required.",
|
"filter-type-required": "Filter type is required.",
|
||||||
"entity-filter-no-entity-matched": "No entities matching specified filter were found.",
|
"entity-filter-no-entity-matched": "No entities matching specified filter were found.",
|
||||||
@ -3830,8 +3831,7 @@
|
|||||||
"additional-info": "Additional info (JSON)",
|
"additional-info": "Additional info (JSON)",
|
||||||
"invalid-additional-info": "Unable to parse additional info json.",
|
"invalid-additional-info": "Unable to parse additional info json.",
|
||||||
"no-relations-text": "No relations found",
|
"no-relations-text": "No relations found",
|
||||||
"negate-all-relations": "Negate all relations (NOT condition)",
|
"not": "Not"
|
||||||
"negate-relation": "Negate this relation (NOT condition)"
|
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"add": "Add resource",
|
"add": "Add resource",
|
||||||
|
|||||||
@ -527,6 +527,9 @@
|
|||||||
&.no-padding-right {
|
&.no-padding-right {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
&.align-start {
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
@media #{$mat-gt-md} {
|
@media #{$mat-gt-md} {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user