Commit 2a1fa76c2f77ee535951ffc74da5bf355ee16fc9
Committed by
GitHub
1 parent
352b0730
Add new alias Current Tenant (#2604)
Showing
13 changed files
with
65 additions
and
49 deletions
@@ -480,6 +480,7 @@ export class EntityService { | @@ -480,6 +480,7 @@ export class EntityService { | ||
480 | entityTypes.push(EntityType.DASHBOARD); | 480 | entityTypes.push(EntityType.DASHBOARD); |
481 | if (useAliasEntityTypes) { | 481 | if (useAliasEntityTypes) { |
482 | entityTypes.push(AliasEntityType.CURRENT_CUSTOMER); | 482 | entityTypes.push(AliasEntityType.CURRENT_CUSTOMER); |
483 | + entityTypes.push(AliasEntityType.CURRENT_TENANT); | ||
483 | } | 484 | } |
484 | break; | 485 | break; |
485 | case Authority.CUSTOMER_USER: | 486 | case Authority.CUSTOMER_USER: |
@@ -1065,6 +1066,10 @@ export class EntityService { | @@ -1065,6 +1066,10 @@ export class EntityService { | ||
1065 | if (authUser.authority === Authority.CUSTOMER_USER) { | 1066 | if (authUser.authority === Authority.CUSTOMER_USER) { |
1066 | entityId.id = authUser.customerId; | 1067 | entityId.id = authUser.customerId; |
1067 | } | 1068 | } |
1069 | + } else if (entityType === AliasEntityType.CURRENT_TENANT){ | ||
1070 | + const authUser = getCurrentAuthUser(this.store); | ||
1071 | + entityId.entityType = EntityType.TENANT; | ||
1072 | + entityId.id = authUser.tenantId; | ||
1068 | } | 1073 | } |
1069 | return entityId; | 1074 | return entityId; |
1070 | } | 1075 | } |
@@ -14,11 +14,8 @@ | @@ -14,11 +14,8 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, Inject, InjectionToken, OnInit } from '@angular/core'; | ||
18 | -import { Timewindow } from '@shared/models/time/time.models'; | 17 | +import { Component, Inject, InjectionToken } from '@angular/core'; |
19 | import { AliasInfo, IAliasController } from '@core/api/widget-api.models'; | 18 | import { AliasInfo, IAliasController } from '@core/api/widget-api.models'; |
20 | -import { PageComponent } from '@shared/components/page.component'; | ||
21 | -import { TIMEWINDOW_PANEL_DATA, TimewindowPanelData } from '@shared/components/time/timewindow-panel.component'; | ||
22 | import { deepClone } from '@core/utils'; | 19 | import { deepClone } from '@core/utils'; |
23 | 20 | ||
24 | export const ALIASES_ENTITY_SELECT_PANEL_DATA = new InjectionToken<any>('AliasesEntitySelectPanelData'); | 21 | export const ALIASES_ENTITY_SELECT_PANEL_DATA = new InjectionToken<any>('AliasesEntitySelectPanelData'); |
@@ -15,8 +15,8 @@ | @@ -15,8 +15,8 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<form #entityAliasForm="ngForm" [formGroup]="entityAliasFormGroup" (ngSubmit)="save()" style="width: 600px;"> | ||
19 | - <mat-toolbar fxLayout="row" color="primary"> | 18 | +<form #entityAliasForm="ngForm" [formGroup]="entityAliasFormGroup" (ngSubmit)="save()" style="min-width: 550px;"> |
19 | + <mat-toolbar color="primary"> | ||
20 | <h2>{{ (isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2> | 20 | <h2>{{ (isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2> |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
22 | <button mat-button mat-icon-button | 22 | <button mat-button mat-icon-button |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | <div fxLayout="column" [formGroup]="entityFilterFormGroup" class="tb-entity-filter"> | 18 | <div fxLayout="column" [formGroup]="entityFilterFormGroup" class="tb-entity-filter"> |
19 | <mat-form-field class="mat-block"> | 19 | <mat-form-field class="mat-block"> |
20 | <mat-label translate>alias.filter-type</mat-label> | 20 | <mat-label translate>alias.filter-type</mat-label> |
21 | - <mat-select required matInput formControlName="type"> | 21 | + <mat-select required formControlName="type"> |
22 | <mat-option *ngFor="let type of aliasFilterTypes" [value]="type"> | 22 | <mat-option *ngFor="let type of aliasFilterTypes" [value]="type"> |
23 | {{aliasFilterTypeTranslations.get(type) | translate}} | 23 | {{aliasFilterTypeTranslations.get(type) | translate}} |
24 | </mat-option> | 24 | </mat-option> |
@@ -15,14 +15,7 @@ | @@ -15,14 +15,7 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; | 17 | import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; |
18 | -import { | ||
19 | - ControlValueAccessor, | ||
20 | - FormBuilder, FormControl, | ||
21 | - FormGroup, | ||
22 | - NG_VALUE_ACCESSOR, | ||
23 | - ValidatorFn, | ||
24 | - Validators | ||
25 | -} from '@angular/forms'; | 18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
26 | import { AliasFilterType, aliasFilterTypeTranslationMap, EntityAliasFilter } from '@shared/models/alias.models'; | 19 | import { AliasFilterType, aliasFilterTypeTranslationMap, EntityAliasFilter } from '@shared/models/alias.models'; |
27 | import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; | 20 | import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; |
28 | import { TranslateService } from '@ngx-translate/core'; | 21 | import { TranslateService } from '@ngx-translate/core'; |
@@ -14,18 +14,18 @@ | @@ -14,18 +14,18 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import {AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild} from '@angular/core'; | ||
18 | -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from '@angular/forms'; | ||
19 | -import {Observable} from 'rxjs'; | ||
20 | -import {map, mergeMap, startWith, tap, share} from 'rxjs/operators'; | ||
21 | -import {Store} from '@ngrx/store'; | ||
22 | -import {AppState} from '@app/core/core.state'; | ||
23 | -import {TranslateService} from '@ngx-translate/core'; | ||
24 | -import {AliasEntityType, EntityType} from '@shared/models/entity-type.models'; | ||
25 | -import {BaseData} from '@shared/models/base-data'; | ||
26 | -import {EntityId} from '@shared/models/id/entity-id'; | ||
27 | -import {EntityService} from '@core/http/entity.service'; | ||
28 | -import {coerceBooleanProperty} from '@angular/cdk/coercion'; | 17 | +import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; |
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||
19 | +import { Observable } from 'rxjs'; | ||
20 | +import { map, mergeMap, share, tap } from 'rxjs/operators'; | ||
21 | +import { Store } from '@ngrx/store'; | ||
22 | +import { AppState } from '@app/core/core.state'; | ||
23 | +import { TranslateService } from '@ngx-translate/core'; | ||
24 | +import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; | ||
25 | +import { BaseData } from '@shared/models/base-data'; | ||
26 | +import { EntityId } from '@shared/models/id/entity-id'; | ||
27 | +import { EntityService } from '@core/http/entity.service'; | ||
28 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
29 | 29 | ||
30 | @Component({ | 30 | @Component({ |
31 | selector: 'tb-entity-autocomplete', | 31 | selector: 'tb-entity-autocomplete', |
@@ -164,6 +164,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | @@ -164,6 +164,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | ||
164 | this.entityRequiredText = 'rulechain.rulechain-required'; | 164 | this.entityRequiredText = 'rulechain.rulechain-required'; |
165 | break; | 165 | break; |
166 | case EntityType.TENANT: | 166 | case EntityType.TENANT: |
167 | + case AliasEntityType.CURRENT_TENANT: | ||
167 | this.entityText = 'tenant.tenant'; | 168 | this.entityText = 'tenant.tenant'; |
168 | this.noEntitiesMatchingText = 'tenant.no-tenants-matching'; | 169 | this.noEntitiesMatchingText = 'tenant.no-tenants-matching'; |
169 | this.entityRequiredText = 'tenant.tenant-required'; | 170 | this.entityRequiredText = 'tenant.tenant-required'; |
@@ -226,10 +227,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | @@ -226,10 +227,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | ||
226 | this.searchText = ''; | 227 | this.searchText = ''; |
227 | if (value != null) { | 228 | if (value != null) { |
228 | if (typeof value === 'string') { | 229 | if (typeof value === 'string') { |
229 | - let targetEntityType = this.entityTypeValue; | ||
230 | - if (targetEntityType === AliasEntityType.CURRENT_CUSTOMER) { | ||
231 | - targetEntityType = EntityType.CUSTOMER; | ||
232 | - } | 230 | + const targetEntityType = this.checkEntityType(this.entityTypeValue); |
233 | this.entityService.getEntity(targetEntityType, value, {ignoreLoading: true}).subscribe( | 231 | this.entityService.getEntity(targetEntityType, value, {ignoreLoading: true}).subscribe( |
234 | (entity) => { | 232 | (entity) => { |
235 | this.modelValue = entity.id.id; | 233 | this.modelValue = entity.id.id; |
@@ -237,7 +235,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | @@ -237,7 +235,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | ||
237 | } | 235 | } |
238 | ); | 236 | ); |
239 | } else { | 237 | } else { |
240 | - const targetEntityType = value.entityType as EntityType; | 238 | + const targetEntityType = this.checkEntityType(value.entityType); |
241 | this.entityService.getEntity(targetEntityType, value.id, {ignoreLoading: true}).subscribe( | 239 | this.entityService.getEntity(targetEntityType, value.id, {ignoreLoading: true}).subscribe( |
242 | (entity) => { | 240 | (entity) => { |
243 | this.modelValue = entity.id.id; | 241 | this.modelValue = entity.id.id; |
@@ -276,10 +274,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | @@ -276,10 +274,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | ||
276 | 274 | ||
277 | fetchEntities(searchText?: string): Observable<Array<BaseData<EntityId>>> { | 275 | fetchEntities(searchText?: string): Observable<Array<BaseData<EntityId>>> { |
278 | this.searchText = searchText; | 276 | this.searchText = searchText; |
279 | - let targetEntityType = this.entityTypeValue; | ||
280 | - if (targetEntityType === AliasEntityType.CURRENT_CUSTOMER) { | ||
281 | - targetEntityType = EntityType.CUSTOMER; | ||
282 | - } | 277 | + const targetEntityType = this.checkEntityType(this.entityTypeValue); |
283 | return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, | 278 | return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, |
284 | 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe( | 279 | 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe( |
285 | map((data) => { | 280 | map((data) => { |
@@ -310,4 +305,12 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | @@ -310,4 +305,12 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit | ||
310 | }, 0); | 305 | }, 0); |
311 | } | 306 | } |
312 | 307 | ||
308 | + checkEntityType(entityType: EntityType | AliasEntityType): EntityType { | ||
309 | + if (entityType === AliasEntityType.CURRENT_CUSTOMER) { | ||
310 | + return EntityType.CUSTOMER; | ||
311 | + } else if (entityType === AliasEntityType.CURRENT_TENANT) { | ||
312 | + return EntityType.TENANT; | ||
313 | + } | ||
314 | + return entityType; | ||
315 | + } | ||
313 | } | 316 | } |
@@ -27,7 +27,7 @@ | @@ -27,7 +27,7 @@ | ||
27 | </tb-entity-type-select> | 27 | </tb-entity-type-select> |
28 | <tb-entity-autocomplete | 28 | <tb-entity-autocomplete |
29 | fxFlex | 29 | fxFlex |
30 | - *ngIf="modelValue.entityType" | 30 | + *ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT" |
31 | [required]="required" | 31 | [required]="required" |
32 | [entityType]="modelValue.entityType" | 32 | [entityType]="modelValue.entityType" |
33 | formControlName="entityId"> | 33 | formControlName="entityId"> |
@@ -14,15 +14,16 @@ | @@ -14,15 +14,16 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import {AfterViewInit, Component, forwardRef, Input, OnInit} from '@angular/core'; | ||
18 | -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from '@angular/forms'; | ||
19 | -import {Store} from '@ngrx/store'; | ||
20 | -import {AppState} from '@core/core.state'; | ||
21 | -import {TranslateService} from '@ngx-translate/core'; | ||
22 | -import {AliasEntityType, EntityType, entityTypeTranslations} from '@shared/models/entity-type.models'; | ||
23 | -import {EntityService} from '@core/http/entity.service'; | ||
24 | -import {EntityId} from '@shared/models/id/entity-id'; | ||
25 | -import {coerceBooleanProperty} from '@angular/cdk/coercion'; | 17 | +import { AfterViewInit, Component, forwardRef, Input, OnInit } from '@angular/core'; |
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||
19 | +import { Store } from '@ngrx/store'; | ||
20 | +import { AppState } from '@core/core.state'; | ||
21 | +import { TranslateService } from '@ngx-translate/core'; | ||
22 | +import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; | ||
23 | +import { EntityService } from '@core/http/entity.service'; | ||
24 | +import { EntityId } from '@shared/models/id/entity-id'; | ||
25 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
26 | +import { NULL_UUID } from '@shared/models/id/has-uuid'; | ||
26 | 27 | ||
27 | @Component({ | 28 | @Component({ |
28 | selector: 'tb-entity-select', | 29 | selector: 'tb-entity-select', |
@@ -60,6 +61,8 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | @@ -60,6 +61,8 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | ||
60 | 61 | ||
61 | displayEntityTypeSelect: boolean; | 62 | displayEntityTypeSelect: boolean; |
62 | 63 | ||
64 | + AliasEntityType = AliasEntityType; | ||
65 | + | ||
63 | private readonly defaultEntityType: EntityType | AliasEntityType = null; | 66 | private readonly defaultEntityType: EntityType | AliasEntityType = null; |
64 | 67 | ||
65 | private propagateChange = (v: any) => { }; | 68 | private propagateChange = (v: any) => { }; |
@@ -94,6 +97,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | @@ -94,6 +97,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | ||
94 | ngOnInit() { | 97 | ngOnInit() { |
95 | this.entitySelectFormGroup.get('entityType').valueChanges.subscribe( | 98 | this.entitySelectFormGroup.get('entityType').valueChanges.subscribe( |
96 | (value) => { | 99 | (value) => { |
100 | + if(value === AliasEntityType.CURRENT_TENANT){ | ||
101 | + this.modelValue.id = NULL_UUID; | ||
102 | + } | ||
97 | this.updateView(value, this.modelValue.id); | 103 | this.updateView(value, this.modelValue.id); |
98 | } | 104 | } |
99 | ); | 105 | ); |
@@ -139,7 +145,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | @@ -139,7 +145,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte | ||
139 | entityType, | 145 | entityType, |
140 | id: this.modelValue.entityType !== entityType ? null : entityId | 146 | id: this.modelValue.entityType !== entityType ? null : entityId |
141 | }; | 147 | }; |
142 | - if (this.modelValue.entityType && this.modelValue.id) { | 148 | + if (this.modelValue.entityType && (this.modelValue.id || this.modelValue.entityType === AliasEntityType.CURRENT_TENANT)) { |
143 | this.propagateChange(this.modelValue); | 149 | this.propagateChange(this.modelValue); |
144 | } else { | 150 | } else { |
145 | this.propagateChange(null); | 151 | this.propagateChange(null); |
@@ -49,7 +49,8 @@ export enum EntityType { | @@ -49,7 +49,8 @@ export enum EntityType { | ||
49 | } | 49 | } |
50 | 50 | ||
51 | export enum AliasEntityType { | 51 | export enum AliasEntityType { |
52 | - CURRENT_CUSTOMER = 'CURRENT_CUSTOMER' | 52 | + CURRENT_CUSTOMER = 'CURRENT_CUSTOMER', |
53 | + CURRENT_TENANT = 'CURRENT_TENANT' | ||
53 | } | 54 | } |
54 | 55 | ||
55 | export interface EntityTypeTranslation { | 56 | export interface EntityTypeTranslation { |
@@ -218,9 +219,16 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti | @@ -218,9 +219,16 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti | ||
218 | [ | 219 | [ |
219 | AliasEntityType.CURRENT_CUSTOMER, | 220 | AliasEntityType.CURRENT_CUSTOMER, |
220 | { | 221 | { |
221 | - type: 'entity.type-entity-view', | 222 | + type: 'entity.type-current-customer', |
222 | list: 'entity.type-current-customer' | 223 | list: 'entity.type-current-customer' |
223 | } | 224 | } |
225 | + ], | ||
226 | + [ | ||
227 | + AliasEntityType.CURRENT_TENANT, | ||
228 | + { | ||
229 | + type: 'entity.type-current-tenant', | ||
230 | + list: 'entity.type-current-tenant' | ||
231 | + } | ||
224 | ] | 232 | ] |
225 | ] | 233 | ] |
226 | ); | 234 | ); |
@@ -771,6 +771,7 @@ | @@ -771,6 +771,7 @@ | ||
771 | "list-of-rulenodes": "{ count, plural, 1 {Jeden uzel pravidla} other {Seznam # uzlů pravidel} }", | 771 | "list-of-rulenodes": "{ count, plural, 1 {Jeden uzel pravidla} other {Seznam # uzlů pravidel} }", |
772 | "rulenode-name-starts-with": "Uzly pravidel, jejichž název začíná '{{prefix}}'", | 772 | "rulenode-name-starts-with": "Uzly pravidel, jejichž název začíná '{{prefix}}'", |
773 | "type-current-customer": "Stávající zákazník", | 773 | "type-current-customer": "Stávající zákazník", |
774 | + "type-current-tenant": "Stávající tenant", | ||
774 | "search": "Vyhledat entity", | 775 | "search": "Vyhledat entity", |
775 | "selected-entities": "{ count, plural, 1 {1 entita} other {# entit} } zvoleno", | 776 | "selected-entities": "{ count, plural, 1 {1 entita} other {# entit} } zvoleno", |
776 | "entity-name": "Název entity", | 777 | "entity-name": "Název entity", |
@@ -830,6 +830,7 @@ | @@ -830,6 +830,7 @@ | ||
830 | "list-of-rulenodes": "{ count, plural, 1 {One rule node} other {List of # rule nodes} }", | 830 | "list-of-rulenodes": "{ count, plural, 1 {One rule node} other {List of # rule nodes} }", |
831 | "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", | 831 | "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", |
832 | "type-current-customer": "Current Customer", | 832 | "type-current-customer": "Current Customer", |
833 | + "type-current-tenant": "Current Tenant", | ||
833 | "search": "Search entities", | 834 | "search": "Search entities", |
834 | "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", | 835 | "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", |
835 | "entity-name": "Entity name", | 836 | "entity-name": "Entity name", |
@@ -810,6 +810,7 @@ | @@ -810,6 +810,7 @@ | ||
810 | "list-of-rulenodes": "{ count, plural, 1 {Одно правило} other {Список из # правил} }", | 810 | "list-of-rulenodes": "{ count, plural, 1 {Одно правило} other {Список из # правил} }", |
811 | "rulenode-name-starts-with": "Правила, чьи названия начинаются с '{{prefix}}'", | 811 | "rulenode-name-starts-with": "Правила, чьи названия начинаются с '{{prefix}}'", |
812 | "type-current-customer": "Текущий клиент", | 812 | "type-current-customer": "Текущий клиент", |
813 | + "type-current-tenant": "Текущий владелец", | ||
813 | "search": "Поиск объектов", | 814 | "search": "Поиск объектов", |
814 | "selected-entities": "Выбран(ы) { count, plural, 1 {1 объект} few {# объекта} other {# объектов} }", | 815 | "selected-entities": "Выбран(ы) { count, plural, 1 {1 объект} few {# объекта} other {# объектов} }", |
815 | "entity-name": "Название объекта", | 816 | "entity-name": "Название объекта", |
@@ -943,6 +943,7 @@ | @@ -943,6 +943,7 @@ | ||
943 | "list-of-rulenodes": "{ count, plural, 1 {Одне правило} other {Список # правил} }", | 943 | "list-of-rulenodes": "{ count, plural, 1 {Одне правило} other {Список # правил} }", |
944 | "rulenode-name-starts-with": "Список правил, імена яких починаються '{{prefix}}'", | 944 | "rulenode-name-starts-with": "Список правил, імена яких починаються '{{prefix}}'", |
945 | "type-current-customer": "Поточний клієнт", | 945 | "type-current-customer": "Поточний клієнт", |
946 | + "type-current-tenant": "Поточний власник", | ||
946 | "search": "Пошук сутностей", | 947 | "search": "Пошук сутностей", |
947 | "selected-entities": "{ count, plural, 1 {1 сутність} other {# сутності} } вибрано", | 948 | "selected-entities": "{ count, plural, 1 {1 сутність} other {# сутності} } вибрано", |
948 | "entity-name": "Ім'я сутності", | 949 | "entity-name": "Ім'я сутності", |