-
-
-
-
- dashboard.states
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ 'dashboard.state-name' | translate }}
-
- {{ state.name }}
-
-
-
- {{ 'dashboard.state-id' | translate }}
-
- {{ state.id }}
-
-
-
- {{ 'dashboard.is-root-state' | translate }}
-
- {{state.root ? 'check_box' : 'check_box_outline_blank'}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ 'dashboard.no-states-text' }}
-
-
-
-
-
+
+ dashboard.manage-states
+
+
+
+
+
+
+
+ dashboard.states
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'dashboard.state-name' | translate }}
+
+ {{ state.name }}
+
+
+
+ {{ 'dashboard.state-id' | translate }}
+
+ {{ state.id }}
+
+
+
+ {{ 'dashboard.is-root-state' | translate }}
+
+ {{state.root ? 'check_box' : 'check_box_outline_blank'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ 'dashboard.no-states-text' }}
+
+
+
-
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.scss
index 862737c22b..98d48861bd 100644
--- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.scss
+++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.scss
@@ -13,27 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+@import "../scss/constants";
+
:host {
+ height: 100%;
+ display: grid;
+ grid-template-rows: min-content auto min-content;
+
.manage-dashboard-states {
- .tb-entity-table {
- .tb-entity-table-content {
- width: 100%;
- height: 100%;
- background: #fff;
+ .tb-entity-table-content {
+ width: 100%;
+ height: 100%;
+ background: #fff;
- .tb-entity-table-title {
- padding-right: 20px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
+ .tb-entity-table-title {
+ padding-right: 20px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
- .table-container {
- overflow: auto;
- }
+ .table-container {
+ overflow: auto;
}
}
}
+
+ @media #{$mat-sm} {
+ min-width: 470px;
+ }
+
+ @media #{$mat-gt-sm} {
+ min-width: 750px;
+ }
}
:host ::ng-deep {
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts
index 7dddc18dbd..6e74ee9b14 100644
--- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts
@@ -14,21 +14,10 @@
/// limitations under the License.
///
-import {
- AfterViewInit,
- Component,
- ElementRef,
- Inject,
- OnInit,
- SecurityContext,
- SkipSelf,
- ViewChild
-} from '@angular/core';
-import { ErrorStateMatcher } from '@angular/material/core';
+import { AfterViewInit, Component, ElementRef, Inject, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
-import { FormGroupDirective, NgForm, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component';
import { DashboardState } from '@app/shared/models/dashboard.models';
@@ -44,7 +33,7 @@ import { fromEvent, merge } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service';
-import { deepClone, isDefined } from '@core/utils';
+import { deepClone, isDefined, isEqual } from '@core/utils';
import {
DashboardStateDialogComponent,
DashboardStateDialogData
@@ -58,42 +47,42 @@ export interface ManageDashboardStatesDialogData {
widgets: {[id: string]: Widget };
}
+export interface ManageDashboardStatesDialogResult {
+ states: {[id: string]: DashboardState };
+ addWidgets?: {[id: string]: Widget };
+}
+
@Component({
selector: 'tb-manage-dashboard-states-dialog',
templateUrl: './manage-dashboard-states-dialog.component.html',
- providers: [{provide: ErrorStateMatcher, useExisting: ManageDashboardStatesDialogComponent}],
styleUrls: ['./manage-dashboard-states-dialog.component.scss']
})
export class ManageDashboardStatesDialogComponent
- extends DialogComponent
- implements OnInit, ErrorStateMatcher, AfterViewInit {
+ extends DialogComponent
+ implements OnInit, AfterViewInit {
- statesFormGroup: UntypedFormGroup;
+ @ViewChild('searchInput', {static: false}) searchInputField: ElementRef;
- states: {[id: string]: DashboardState };
- widgets: {[id: string]: Widget};
+ @ViewChild(MatPaginator, {static: false}) paginator: MatPaginator;
+ @ViewChild(MatSort, {static: false}) sort: MatSort;
+
+ isDirty = false;
displayedColumns: string[];
pageLink: PageLink;
textSearchMode = false;
dataSource: DashboardStatesDatasource;
- submitted = false;
+ private states: {[id: string]: DashboardState };
+ private widgets: {[id: string]: Widget};
- stateNames: Set = new Set();
-
- @ViewChild('searchInput') searchInputField: ElementRef;
-
- @ViewChild(MatPaginator) paginator: MatPaginator;
- @ViewChild(MatSort) sort: MatSort;
+ private stateNames: Set = new Set();
+ private addWidgets: {[id: string]: Widget} = {};
constructor(protected store: Store,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ManageDashboardStatesDialogData,
- @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
- public dialogRef: MatDialogRef,
- private fb: UntypedFormBuilder,
+ public dialogRef: MatDialogRef,
private translate: TranslateService,
private dialogs: DialogService,
private utils: UtilsService,
@@ -103,7 +92,6 @@ export class ManageDashboardStatesDialogComponent
this.states = this.data.states;
this.widgets = this.data.widgets;
- this.statesFormGroup = this.fb.group({});
Object.values(this.states).forEach(value => this.stateNames.add(value.name));
const sortOrder: SortOrder = { property: 'name', direction: Direction.ASC };
@@ -258,77 +246,65 @@ export class ManageDashboardStatesDialogComponent
}
duplicateState($event: Event, state: DashboardStateInfo) {
- const originalState = state;
- const newStateName = this.getNextDuplicatedName(state.name);
- if (newStateName) {
- const newStateId = newStateName.toLowerCase().replace(/\W/g, '_');
- if (this.states[newStateId]) {
- this.stateNames.add(newStateName);
- this.duplicateState(null, state);
+ const suffix = ` - ${this.translate.instant('action.copy')} `;
+ let counter = 0;
+ const maxAttempts = 1000;
+
+ while (counter++ < maxAttempts) {
+ const candidateName = `${state.name}${suffix}${counter}`;
+ if (this.stateNames.has(candidateName)) continue;
+
+ const candidateId = candidateName.toLowerCase().replace(/\W/g, '_');
+ if (this.states[candidateId]) {
+ continue;
}
- const duplicatedStates = deepClone(originalState);
- const duplicatedWidgets = deepClone(this.widgets);
+
+ const duplicatedState = deepClone(state);
const mainWidgets = {};
const rightWidgets = {};
- duplicatedStates.id = newStateId;
- duplicatedStates.name = newStateName;
- duplicatedStates.root = false;
- this.stateNames.add(duplicatedStates.name);
+ duplicatedState.id = candidateId;
+ duplicatedState.name = candidateName;
+ duplicatedState.root = false;
+ this.stateNames.add(duplicatedState.name);
- for (const [key, value] of Object.entries(duplicatedStates.layouts.main.widgets)) {
+ for (const [key, value] of Object.entries(duplicatedState.layouts.main.widgets)) {
const guid = this.utils.guid();
mainWidgets[guid] = value;
- duplicatedWidgets[guid] = this.widgets[key];
- duplicatedWidgets[guid].id = guid;
+ this.addWidgets[guid] = deepClone(this.widgets[key] ?? this.addWidgets[key]);
+ this.addWidgets[guid].id = guid;
}
- duplicatedStates.layouts.main.widgets = mainWidgets;
+ duplicatedState.layouts.main.widgets = mainWidgets;
- if (isDefined(duplicatedStates.layouts?.right)) {
- for (const [key, value] of Object.entries(duplicatedStates.layouts.right.widgets)) {
+ if (isDefined(duplicatedState.layouts?.right)) {
+ for (const [key, value] of Object.entries(duplicatedState.layouts.right.widgets)) {
const guid = this.utils.guid();
rightWidgets[guid] = value;
- duplicatedWidgets[guid] = this.widgets[key];
- duplicatedWidgets[guid].id = guid;
+ this.addWidgets[guid] = deepClone(this.widgets[key] ?? this.addWidgets[key]);
+ this.addWidgets[guid].id = guid;
}
- duplicatedStates.layouts.right.widgets = rightWidgets;
+ duplicatedState.layouts.right.widgets = rightWidgets;
}
- this.states[duplicatedStates.id] = duplicatedStates;
- this.widgets = duplicatedWidgets;
+ this.states[duplicatedState.id] = duplicatedState;
this.onStatesUpdated();
+ return;
}
}
- private getNextDuplicatedName(stateName: string): string {
- const suffix = ` - ${this.translate.instant('action.copy')} `;
- let counter = 0;
- while (++counter < Number.MAX_SAFE_INTEGER) {
- const newName = `${stateName}${suffix}${counter}`;
- if (!this.stateNames.has(newName)) {
- return newName;
- }
- }
-
- return null;
- }
-
private onStatesUpdated() {
- this.statesFormGroup.markAsDirty();
+ this.isDirty = true;
this.updateData(true);
}
- isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
- const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
- const customErrorState = !!(control && control.invalid && this.submitted);
- return originalErrorState || customErrorState;
- }
-
cancel(): void {
this.dialogRef.close(null);
}
save(): void {
- this.submitted = true;
- this.dialogRef.close({ states: this.states, widgets: this.widgets });
+ const result: ManageDashboardStatesDialogResult = {states: this.states};
+ if (!isEqual(this.addWidgets, {})) {
+ result.addWidgets = this.addWidgets;
+ }
+ this.dialogRef.close(result);
}
}
diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts
index ac7333079d..7eae04452d 100644
--- a/ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts
+++ b/ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.ts
@@ -128,6 +128,9 @@ export class EntityFilterViewComponent implements ControlValueAccessor {
{edgeTypes});
}
break;
+ case AliasFilterType.apiUsageState:
+ this.filterDisplayValue = this.translate.instant('alias.filter-type-apiUsageState');
+ break;
case AliasFilterType.entityViewType:
const entityViewTypesQuoted = [];
this.filter.entityViewTypes.forEach((entityViewType) => {
diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
index 92cd675ee8..e2d19b9e6c 100644
--- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
+++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
@@ -128,7 +128,7 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
entityTypes: [filter ? filter.entityTypes : []]
});
if (this.enableNotOption) {
- formGroup.addControl('negate', this.fb.control({value: filter ? filter.negate : false, disabled: true}));
+ formGroup.addControl('negate', this.fb.control({value: filter?.negate ?? false, disabled: !filter?.relationType}));
formGroup.get('relationType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts
index 1af25bd01e..d65d4067e5 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts
@@ -251,7 +251,6 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
}, 0);
}
} else {
- this.entityAliasList.push(newAlias);
this.modelValue = newAlias.id;
this.selectEntityAliasFormGroup.get('entityAlias').patchValue(newAlias, {emitEvent: true});
this.propagateChange(this.modelValue);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html
index 5dbeb8b646..5307a18a7b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html
@@ -116,7 +116,7 @@
dynamic-form.property.disable-on-property
-
+ {{ 'dynamic-form.property.disable-on-property-none' | translate }}
{{ prop }}
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts
index 98595de798..0f13409a3e 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts
@@ -40,6 +40,7 @@ export interface TemplateNotificationDialogData {
predefinedType?: NotificationType;
isAdd?: boolean;
isCopy?: boolean;
+ name?: string;
}
@Component({
@@ -85,6 +86,9 @@ export class TemplateNotificationDialogComponent
this.hideSelectType = true;
this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvent: false});
}
+ if (isDefinedAndNotNull(this.data?.name)) {
+ this.templateNotificationForm.get('name').setValue(this.data.name, {emitEvent: false});
+ }
if (data.isAdd || data.isCopy) {
this.dialogTitle = 'notification.add-notification-template';
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss
index e28d8ec14a..c8f678d679 100644
--- a/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss
@@ -24,7 +24,8 @@
}
.tb-widget-type-option-image-preview {
width: 36px;
- max-height: 100%;
+ height: 100%;
+ max-height: 36px;
object-fit: contain;
border-radius: 6px;
}
diff --git a/ui-ngx/src/app/shared/components/entity/entity-list.component.html b/ui-ngx/src/app/shared/components/entity/entity-list.component.html
index 9510f2a952..6bf7cdb78d 100644
--- a/ui-ngx/src/app/shared/components/entity/entity-list.component.html
+++ b/ui-ngx/src/app/shared/components/entity/entity-list.component.html
@@ -25,6 +25,7 @@
{{entity.name}}
diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html
index 595010fb19..6635921d7f 100644
--- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html
+++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html
@@ -22,6 +22,7 @@
{{customTranslate(entitySubtype)}}
diff --git a/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html b/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html
index d5e67a1f60..d712f11114 100644
--- a/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html
+++ b/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html
@@ -29,7 +29,7 @@
(click)="clear()">
close
-
diff --git a/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts b/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts
index 97c2a56bf5..3e703e5d6a 100644
--- a/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts
+++ b/ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts
@@ -212,13 +212,17 @@ export class TemplateAutocompleteComponent implements ControlValueAccessor, OnIn
}
createTemplate($event: Event, button: MatButton) {
- if ($event) {
- $event.stopPropagation();
- }
+ $event?.stopPropagation();
button._elementRef.nativeElement.blur();
+ this.createTemplateByName($event);
+ }
+
+ createTemplateByName($event: Event, name?: string) {
+ $event?.stopPropagation();
this.openNotificationTemplateDialog({
isAdd: true,
- predefinedType: this.notificationTypes
+ predefinedType: this.notificationTypes,
+ name
});
}
diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.html b/ui-ngx/src/app/shared/components/string-items-list.component.html
index d467fbe5f0..0469be2589 100644
--- a/ui-ngx/src/app/shared/components/string-items-list.component.html
+++ b/ui-ngx/src/app/shared/components/string-items-list.component.html
@@ -23,6 +23,7 @@