UI: Prevent deleting an entity alias/filter that’s still used in map widgets
This commit is contained in:
parent
18b6a9557d
commit
429f9c7cc9
@ -31,7 +31,8 @@ import {
|
|||||||
DashboardLayoutsInfo,
|
DashboardLayoutsInfo,
|
||||||
DashboardState,
|
DashboardState,
|
||||||
DashboardStateLayouts,
|
DashboardStateLayouts,
|
||||||
GridSettings, LayoutType,
|
GridSettings,
|
||||||
|
LayoutType,
|
||||||
WidgetLayout
|
WidgetLayout
|
||||||
} from '@shared/models/dashboard.models';
|
} from '@shared/models/dashboard.models';
|
||||||
import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils';
|
import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils';
|
||||||
@ -61,6 +62,7 @@ import { MediaBreakpoints } from '@shared/models/constants';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models';
|
import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models';
|
||||||
import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models';
|
import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models';
|
||||||
|
import { findWidgetModelDefinition } from '@shared/models/widget/widget-model.definition';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -398,6 +400,14 @@ export class DashboardUtilsService {
|
|||||||
return datasources;
|
return datasources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWidgetDatasources(widget: Widget): Datasource[] {
|
||||||
|
const widgetDefinition = findWidgetModelDefinition(widget);
|
||||||
|
if (widgetDefinition) {
|
||||||
|
return widgetDefinition.datasources(widget);
|
||||||
|
}
|
||||||
|
return this.validateAndUpdateDatasources(widget.config.datasources);
|
||||||
|
}
|
||||||
|
|
||||||
public createDefaultLayoutData(): DashboardLayout {
|
public createDefaultLayoutData(): DashboardLayout {
|
||||||
return {
|
return {
|
||||||
widgets: {},
|
widgets: {},
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import { FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.mo
|
|||||||
import { RuleChainService } from '@core/http/rule-chain.service';
|
import { RuleChainService } from '@core/http/rule-chain.service';
|
||||||
import { RuleChainImport } from '@shared/models/rule-chain.models';
|
import { RuleChainImport } from '@shared/models/rule-chain.models';
|
||||||
import { Filter, FilterInfo, Filters, FiltersInfo, getFilterId } from '@shared/models/query/query.models';
|
import { Filter, FilterInfo, Filters, FiltersInfo, getFilterId } from '@shared/models/query/query.models';
|
||||||
import { getWidgetExportDefinition } from '@shared/models/widget/widget-export.models';
|
import { findWidgetModelDefinition } from '@shared/models/widget/widget-model.definition';
|
||||||
|
|
||||||
const WIDGET_ITEM = 'widget_item';
|
const WIDGET_ITEM = 'widget_item';
|
||||||
const WIDGET_REFERENCE = 'widget_reference';
|
const WIDGET_REFERENCE = 'widget_reference';
|
||||||
@ -142,7 +142,7 @@ export class ItemBufferService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let widgetExportInfo: any;
|
let widgetExportInfo: any;
|
||||||
const exportDefinition = getWidgetExportDefinition(widget);
|
const exportDefinition = findWidgetModelDefinition(widget);
|
||||||
if (exportDefinition) {
|
if (exportDefinition) {
|
||||||
widgetExportInfo = exportDefinition.prepareExportInfo(dashboard, widget);
|
widgetExportInfo = exportDefinition.prepareExportInfo(dashboard, widget);
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ export class ItemBufferService {
|
|||||||
let callFilterUpdateFunction = false;
|
let callFilterUpdateFunction = false;
|
||||||
let newEntityAliases: EntityAliases;
|
let newEntityAliases: EntityAliases;
|
||||||
let newFilters: Filters;
|
let newFilters: Filters;
|
||||||
const exportDefinition = getWidgetExportDefinition(widget);
|
const exportDefinition = findWidgetModelDefinition(widget);
|
||||||
if (exportDefinition && widgetExportInfo || aliasesInfo) {
|
if (exportDefinition && widgetExportInfo || aliasesInfo) {
|
||||||
newEntityAliases = deepClone(dashboard.configuration.entityAliases);
|
newEntityAliases = deepClone(dashboard.configuration.entityAliases);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core';
|
import { Component, DestroyRef, Inject, SkipSelf } from '@angular/core';
|
||||||
import { ErrorStateMatcher } from '@angular/material/core';
|
import { ErrorStateMatcher } from '@angular/material/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@ -60,7 +60,7 @@ export interface EntityAliasesDialogData {
|
|||||||
styleUrls: ['./entity-aliases-dialog.component.scss']
|
styleUrls: ['./entity-aliases-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesDialogComponent, EntityAliases>
|
export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesDialogComponent, EntityAliases>
|
||||||
implements OnInit, ErrorStateMatcher {
|
implements ErrorStateMatcher {
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
disableAdd: boolean;
|
disableAdd: boolean;
|
||||||
@ -107,8 +107,7 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
|
|||||||
this.addWidgetTitleToWidgetsMap(widget.config.alarmSource.entityAliasId, widget.config.title);
|
this.addWidgetTitleToWidgetsMap(widget.config.alarmSource.entityAliasId, widget.config.title);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const datasources = this.dashboardUtils.validateAndUpdateDatasources(widget.config.datasources);
|
this.dashboardUtils.getWidgetDatasources(widget).forEach((datasource) => {
|
||||||
datasources.forEach((datasource) => {
|
|
||||||
if ([DatasourceType.entity, DatasourceType.entityCount, DatasourceType.alarmCount].includes(datasource.type)
|
if ([DatasourceType.entity, DatasourceType.entityCount, DatasourceType.alarmCount].includes(datasource.type)
|
||||||
&& datasource.entityAliasId) {
|
&& datasource.entityAliasId) {
|
||||||
this.addWidgetTitleToWidgetsMap(datasource.entityAliasId, widget.config.title);
|
this.addWidgetTitleToWidgetsMap(datasource.entityAliasId, widget.config.title);
|
||||||
@ -143,7 +142,9 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
|
|||||||
widgetsTitleList = [];
|
widgetsTitleList = [];
|
||||||
this.aliasToWidgetsMap[aliasId] = widgetsTitleList;
|
this.aliasToWidgetsMap[aliasId] = widgetsTitleList;
|
||||||
}
|
}
|
||||||
widgetsTitleList.push(widgetTitle);
|
if (!widgetsTitleList.includes(widgetTitle)) {
|
||||||
|
widgetsTitleList.push(widgetTitle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createEntityAliasFormControl(aliasId: string, entityAlias: EntityAlias): AbstractControl {
|
private createEntityAliasFormControl(aliasId: string, entityAlias: EntityAlias): AbstractControl {
|
||||||
@ -166,9 +167,6 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
|
|||||||
return this.entityAliasesFormGroup.get('entityAliases') as UntypedFormArray;
|
return this.entityAliasesFormGroup.get('entityAliases') as UntypedFormArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||||
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
||||||
const customErrorState = !!(control && control.invalid && this.submitted);
|
const customErrorState = !!(control && control.invalid && this.submitted);
|
||||||
|
|||||||
@ -14,19 +14,19 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { Component, Inject, OnInit, SkipSelf } from '@angular/core';
|
import { Component, Inject, SkipSelf } from '@angular/core';
|
||||||
import { ErrorStateMatcher } from '@angular/material/core';
|
import { ErrorStateMatcher } from '@angular/material/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import {
|
import {
|
||||||
AbstractControl,
|
AbstractControl,
|
||||||
|
FormGroupDirective,
|
||||||
|
NgForm,
|
||||||
UntypedFormArray,
|
UntypedFormArray,
|
||||||
UntypedFormBuilder,
|
UntypedFormBuilder,
|
||||||
UntypedFormControl,
|
UntypedFormControl,
|
||||||
UntypedFormGroup,
|
UntypedFormGroup,
|
||||||
FormGroupDirective,
|
|
||||||
NgForm,
|
|
||||||
Validators
|
Validators
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@ -58,7 +58,7 @@ export interface FiltersDialogData {
|
|||||||
styleUrls: ['./filters-dialog.component.scss']
|
styleUrls: ['./filters-dialog.component.scss']
|
||||||
})
|
})
|
||||||
export class FiltersDialogComponent extends DialogComponent<FiltersDialogComponent, Filters>
|
export class FiltersDialogComponent extends DialogComponent<FiltersDialogComponent, Filters>
|
||||||
implements OnInit, ErrorStateMatcher {
|
implements ErrorStateMatcher {
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
disableAdd: boolean;
|
disableAdd: boolean;
|
||||||
@ -96,15 +96,16 @@ export class FiltersDialogComponent extends DialogComponent<FiltersDialogCompone
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.data.widgets.forEach((widget) => {
|
this.data.widgets.forEach((widget) => {
|
||||||
const datasources = this.dashboardUtils.validateAndUpdateDatasources(widget.config.datasources);
|
this.dashboardUtils.getWidgetDatasources(widget).forEach((datasource) => {
|
||||||
datasources.forEach((datasource) => {
|
if (datasource.type !== DatasourceType.function && datasource.filterId) {
|
||||||
if (datasource.type === DatasourceType.entity && datasource.filterId) {
|
|
||||||
widgetsTitleList = this.filterToWidgetsMap[datasource.filterId];
|
widgetsTitleList = this.filterToWidgetsMap[datasource.filterId];
|
||||||
if (!widgetsTitleList) {
|
if (!widgetsTitleList) {
|
||||||
widgetsTitleList = [];
|
widgetsTitleList = [];
|
||||||
this.filterToWidgetsMap[datasource.filterId] = widgetsTitleList;
|
this.filterToWidgetsMap[datasource.filterId] = widgetsTitleList;
|
||||||
}
|
}
|
||||||
widgetsTitleList.push(widget.config.title);
|
if (!widgetsTitleList.includes(widget.config.title)) {
|
||||||
|
widgetsTitleList.push(widget.config.title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -140,9 +141,6 @@ export class FiltersDialogComponent extends DialogComponent<FiltersDialogCompone
|
|||||||
return this.filtersFormGroup.get('filters') as UntypedFormArray;
|
return this.filtersFormGroup.get('filters') as UntypedFormArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||||
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
||||||
const customErrorState = !!(control && control.invalid && this.submitted);
|
const customErrorState = !!(control && control.invalid && this.submitted);
|
||||||
|
|||||||
@ -17,14 +17,15 @@
|
|||||||
import { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models';
|
import { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models';
|
||||||
import { FilterInfo, Filters, getFilterId } from '@shared/models/query/query.models';
|
import { FilterInfo, Filters, getFilterId } from '@shared/models/query/query.models';
|
||||||
import { Dashboard } from '@shared/models/dashboard.models';
|
import { Dashboard } from '@shared/models/dashboard.models';
|
||||||
import { DatasourceType, Widget } from '@shared/models/widget.models';
|
import { Datasource, DatasourceType, Widget } from '@shared/models/widget.models';
|
||||||
import {
|
import {
|
||||||
BaseMapSettings,
|
BaseMapSettings,
|
||||||
MapDataLayerSettings,
|
MapDataLayerSettings,
|
||||||
MapDataSourceSettings,
|
MapDataSourceSettings,
|
||||||
|
mapDataSourceSettingsToDatasource,
|
||||||
MapType
|
MapType
|
||||||
} from '@shared/models/widget/maps/map.models';
|
} from '@shared/models/widget/maps/map.models';
|
||||||
import { WidgetExportDefinition } from '@shared/models/widget/widget-export.models';
|
import { WidgetModelDefinition } from '@shared/models/widget/widget-model.definition';
|
||||||
|
|
||||||
interface AliasFilterPair {
|
interface AliasFilterPair {
|
||||||
alias?: EntityAliasInfo,
|
alias?: EntityAliasInfo,
|
||||||
@ -45,7 +46,7 @@ interface MapDatasourcesInfo {
|
|||||||
additionalDataSources?: ExportDataSourceInfo;
|
additionalDataSources?: ExportDataSourceInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapExportDefinition: WidgetExportDefinition<MapDatasourcesInfo> = {
|
export const MapModelDefinition: WidgetModelDefinition<MapDatasourcesInfo> = {
|
||||||
testWidget(widget: Widget): boolean {
|
testWidget(widget: Widget): boolean {
|
||||||
if (widget?.config?.settings) {
|
if (widget?.config?.settings) {
|
||||||
const settings = widget.config.settings;
|
const settings = widget.config.settings;
|
||||||
@ -103,6 +104,26 @@ export const MapExportDefinition: WidgetExportDefinition<MapDatasourcesInfo> = {
|
|||||||
if (info?.additionalDataSources) {
|
if (info?.additionalDataSources) {
|
||||||
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources);
|
updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
datasources(widget: Widget): Datasource[] {
|
||||||
|
const settings: BaseMapSettings = widget.config.settings as BaseMapSettings;
|
||||||
|
const datasources: Datasource[] = [];
|
||||||
|
if (settings.trips?.length) {
|
||||||
|
datasources.push(...getMapDataLayersDatasources(settings.trips));
|
||||||
|
}
|
||||||
|
if (settings.markers?.length) {
|
||||||
|
datasources.push(...getMapDataLayersDatasources(settings.markers));
|
||||||
|
}
|
||||||
|
if (settings.polygons?.length) {
|
||||||
|
datasources.push(...getMapDataLayersDatasources(settings.polygons));
|
||||||
|
}
|
||||||
|
if (settings.circles?.length) {
|
||||||
|
datasources.push(...getMapDataLayersDatasources(settings.circles));
|
||||||
|
}
|
||||||
|
if (settings.additionalDataSources?.length) {
|
||||||
|
datasources.push(...getMapDataLayersDatasources(settings.additionalDataSources));
|
||||||
|
}
|
||||||
|
return datasources;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -189,3 +210,16 @@ const prepareAliasAndFilterPair = (dashboard: Dashboard, settings: MapDataSource
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getMapDataLayersDatasources = (settings: MapDataLayerSettings[] | MapDataSourceSettings[]): Datasource[] => {
|
||||||
|
const datasources: Datasource[] = [];
|
||||||
|
settings.forEach((dsSettings) => {
|
||||||
|
datasources.push(mapDataSourceSettingsToDatasource(dsSettings));
|
||||||
|
if ((dsSettings as MapDataLayerSettings).additionalDataSources?.length) {
|
||||||
|
(dsSettings as MapDataLayerSettings).additionalDataSources.forEach((ds) => {
|
||||||
|
datasources.push(mapDataSourceSettingsToDatasource(ds));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return datasources;
|
||||||
|
};
|
||||||
@ -14,22 +14,23 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { Widget } from '@shared/models/widget.models';
|
import { Datasource, Widget } from '@shared/models/widget.models';
|
||||||
import { Dashboard } from '@shared/models/dashboard.models';
|
import { Dashboard } from '@shared/models/dashboard.models';
|
||||||
import { EntityAliases } from '@shared/models/alias.models';
|
import { EntityAliases } from '@shared/models/alias.models';
|
||||||
import { Filters } from '@shared/models/query/query.models';
|
import { Filters } from '@shared/models/query/query.models';
|
||||||
import { MapExportDefinition } from '@shared/models/widget/maps/map-export.models';
|
import { MapModelDefinition } from '@shared/models/widget/maps/map-model.definition';
|
||||||
|
|
||||||
export interface WidgetExportDefinition<T = any> {
|
export interface WidgetModelDefinition<T = any> {
|
||||||
testWidget(widget: Widget): boolean;
|
testWidget(widget: Widget): boolean;
|
||||||
prepareExportInfo(dashboard: Dashboard, widget: Widget): T;
|
prepareExportInfo(dashboard: Dashboard, widget: Widget): T;
|
||||||
updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void;
|
updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void;
|
||||||
|
datasources(widget: Widget): Datasource[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const widgetExportDefinitions: WidgetExportDefinition[] = [
|
const widgetModelRegistry: WidgetModelDefinition[] = [
|
||||||
MapExportDefinition
|
MapModelDefinition
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getWidgetExportDefinition = (widget: Widget): WidgetExportDefinition => {
|
export const findWidgetModelDefinition = (widget: Widget): WidgetModelDefinition => {
|
||||||
return widgetExportDefinitions.find(def => def.testWidget(widget));
|
return widgetModelRegistry.find(def => def.testWidget(widget));
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user