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