Merge pull request #10306 from rusikv/relation_query_not_option-ui

Relation query 'Not' option and redesign
This commit is contained in:
Andrew Shvayka 2024-05-22 15:52:53 +03:00 committed by GitHub
commit 78d5a57468
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 101 additions and 107 deletions

View File

@ -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">

View File

@ -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;
}
}
}
}

View File

@ -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 ||

View File

@ -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]);

View File

@ -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']"

View File

@ -18,6 +18,10 @@
flex: 1 1 50%;
}
.flex-18 {
flex: 1 1 18%;
}
.actions-header {
width: 40px
}

View File

@ -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() {

View File

@ -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"

View File

@ -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>

View File

@ -130,7 +130,6 @@ export interface RelationsQueryFilter {
filters?: Array<RelationEntityTypeFilter>;
maxLevel?: number;
fetchLastLevelOnly?: boolean;
negate?: boolean;
}
export interface EntitySearchQueryFilter {

View File

@ -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",

View File

@ -527,6 +527,9 @@
&.no-padding-right {
padding-right: 0;
}
&.align-start {
align-items: start;
}
@media #{$mat-gt-md} {
gap: 12px;
padding-left: 12px;