diff --git a/layered-map-widget-plugin/layer-config/layer-modal.component.ts b/layered-map-widget-plugin/layer-config/layer-modal.component.ts index a03f646..f360cad 100644 --- a/layered-map-widget-plugin/layer-config/layer-modal.component.ts +++ b/layered-map-widget-plugin/layer-config/layer-modal.component.ts @@ -86,7 +86,7 @@ export class LayerModalComponent { // called if cancel is pressed onDismiss(): void { - this.closeSubject.next(null); + this.closeSubject.next(undefined); } // called if save is pressed diff --git a/layered-map-widget-plugin/layered-map-widget-config.component.ts b/layered-map-widget-plugin/layered-map-widget-config.component.ts index a7d090d..c7ffdac 100644 --- a/layered-map-widget-plugin/layered-map-widget-config.component.ts +++ b/layered-map-widget-plugin/layered-map-widget-config.component.ts @@ -22,7 +22,9 @@ export type WidgetConfigMode = 'CREATE' | 'UPDATE'; templateUrl: './layered-map-widget-config.component.html', }) export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBeforeSave { - @Input() config: ILayeredMapWidgetConfig = {}; + @Input() config: ILayeredMapWidgetConfig = { + layers: [], + }; ng1FormRef?: any; items: IManagedObject[] = []; mode: WidgetConfigMode; @@ -44,18 +46,18 @@ export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBefor async openLayerModal(layer?: LayerConfig) { const modalRef = this.bsModalService.show(LayerModalComponent, {}); - const close = modalRef.content.closeSubject.pipe(take(1)).toPromise(); + const close = modalRef.content?.closeSubject.pipe(take(1)).toPromise(); if (!layer) { // create mode const created = await close; - if (isDeviceFragmentLayerConfig(created) || isQueryLayerConfig(created)) { - this.config.layers.push({ config: created, active: true }); + if (!!created && (isDeviceFragmentLayerConfig(created) || isQueryLayerConfig(created))) { + this.config.layers?.push({ config: created, active: true }); this.config.layers = [...this.config.layers]; } } else { // edit mode const original = cloneDeep(layer.config); - modalRef.content.setLayer(layer.config); + modalRef.content?.setLayer(layer.config); const updated = await close; if (!updated) { layer.config = original; @@ -66,10 +68,10 @@ export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBefor async openPopoverModal(layer: LayerConfig) { const modalRef = this.bsModalService.show(PopoverModalComponent, {}); if (layer.config.popoverConfig) { - modalRef.content.setConfig(clone(layer.config.popoverConfig)); + modalRef.content?.setConfig(clone(layer.config.popoverConfig)); } - const close = modalRef.content.closeSubject.pipe(take(1)).toPromise(); + const close = modalRef.content?.closeSubject.pipe(take(1)).toPromise(); const popoverConfig = await close; if (popoverConfig) { layer.config.popoverConfig = popoverConfig; @@ -90,19 +92,23 @@ export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBefor async openEventTrackCreatorModal() { const modalRef = this.bsModalService.show(EventLineCreatorModalComponent, {}); - modalRef.content.items = clone(this.config.devices); - const openExportTemplateModal = modalRef.content.closeSubject.pipe(take(1)).toPromise(); + modalRef.content!.items = clone(this.config.devices ?? []); + const openExportTemplateModal = modalRef.content?.closeSubject.pipe(take(1)).toPromise(); const track = await openExportTemplateModal; - this.addTrackToConfig(track); + if (track) { + this.addTrackToConfig(track); + } } async openDrawTrackCreatorModal() { const modalRef = this.bsModalService.show(DrawLineCreatorModalComponent, { class: 'modal-lg', }); - const openExportTemplateModal = modalRef.content.closeSubject.pipe(take(1)).toPromise(); + const openExportTemplateModal = modalRef.content!.closeSubject.pipe(take(1)).toPromise(); const track = await openExportTemplateModal; - this.addTrackToConfig(track); + if (track) { + this.addTrackToConfig(track); + } } private addTrackToConfig(track: ITrack | null): void { @@ -116,9 +122,9 @@ export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBefor } deleteTrack(track: ITrack): void { - this.config.tracks = this.config.tracks.filter((t) => t.name !== track.name); + this.config.tracks = this.config.tracks?.filter((t) => t.name !== track.name); if (this.config.selectedTrack === track.name) { - this.config.selectedTrack = null; + this.config.selectedTrack = undefined; } } @@ -128,11 +134,15 @@ export class LayeredMapWidgetConfig implements OnInit, DynamicComponent, OnBefor // check and select a new element (automatically unchecks other ones) this.config.selectedTrack = track.name; } else if (track.name === this.config.selectedTrack) { - this.config.selectedTrack = null; + this.config.selectedTrack = undefined; } } async onBeforeSave(config?: ILayeredMapWidgetConfig): Promise { + if (!config) { + return false; + } + if (config.layers.find((l) => isDeviceFragmentLayerConfig(l)) && isEmpty(this.config.device)) { this.alert.danger('Device Fragment layer requires you to select a group or device!'); return false; diff --git a/layered-map-widget-plugin/layered-map-widget.component.ts b/layered-map-widget-plugin/layered-map-widget.component.ts index e47296c..8c83b5d 100644 --- a/layered-map-widget-plugin/layered-map-widget.component.ts +++ b/layered-map-widget-plugin/layered-map-widget.component.ts @@ -7,6 +7,7 @@ import { MapOptions, polyline, Polyline, + Popup, tileLayer, } from 'leaflet'; import { LayeredMapWidgetService } from './service/layered-map-widget.service'; @@ -73,7 +74,7 @@ export class LayeredMapWidgetComponent implements AfterViewInit, OnDestroy { }; private layerSubs: Map = new Map(); - private positionUpdateSub: Subscription = null; + private positionUpdateSub: Subscription | null = null; circuit: Polyline; constructor( @@ -116,7 +117,7 @@ export class LayeredMapWidgetComponent implements AfterViewInit, OnDestroy { layerControl.addBaseLayer(osm, 'Open Street Map'); osm.addTo(this.map); - if (!isEmpty(config.layers)) { + if (config.layers && !isEmpty(config.layers)) { const layers = this.layerService.createLayers(config.layers, devices); for (const layer of layers) { layerControl.addOverlay(layer.group, layer.config.name); @@ -128,15 +129,18 @@ export class LayeredMapWidgetComponent implements AfterViewInit, OnDestroy { } this.map.on('popupopen', (event) => { - const popup = event.popup; - const ref: ComponentRef = get(popup, 'ref'); + const popup = event.popup as Popup & { ref: ComponentRef }; + const ref = get(popup, 'ref'); ref.instance.onShow(); - this.map.setView(popup.getLatLng(), 13); + const latLng = popup.getLatLng(); + if (latLng) { + this.map.setView(latLng, 13); + } }); this.map.on('popupclose', (event) => { - const popup = event.popup; - const ref: ComponentRef = get(popup, 'ref'); + const popup = event.popup as Popup & { ref: ComponentRef }; + const ref = get(popup, 'ref'); ref.instance.onHide(); }); @@ -153,11 +157,11 @@ export class LayeredMapWidgetComponent implements AfterViewInit, OnDestroy { private updateRealtimeSubs(layers: MyLayer[], config: ILayeredMapWidgetConfig): void { for (const layer of layers) { if (this.layerSubs.has(layer)) { - this.layerSubs.get(layer).unsubscribe(); + this.layerSubs.get(layer)!.unsubscribe(); this.layerSubs.delete(layer); } const cfg = layer.config; - if (isDeviceFragmentLayerConfig(cfg)) { + if (config.device && isDeviceFragmentLayerConfig(cfg)) { const query = `(bygroupid(${config.device.id}) or id eq '${config.device.id}') and has(c8y_Position) and ${cfg.fragment} eq '${cfg.value}'`; const sub = this.inventoryPollingService .createPolling$({ query }, layer) @@ -219,6 +223,6 @@ export class LayeredMapWidgetComponent implements AfterViewInit, OnDestroy { if (!isEmpty(this.layerSubs)) { this.layerSubs.forEach((sub) => sub.unsubscribe()); } - this.positionUpdateSub.unsubscribe(); + this.positionUpdateSub?.unsubscribe(); } } diff --git a/layered-map-widget-plugin/layered-map-widget.model.ts b/layered-map-widget-plugin/layered-map-widget.model.ts index d94b397..13f0e16 100644 --- a/layered-map-widget-plugin/layered-map-widget.model.ts +++ b/layered-map-widget-plugin/layered-map-widget.model.ts @@ -62,7 +62,7 @@ export class MyLayer implements LayerAttributes { devices: string[] = []; coordinates = new Map(); markerCache = new Map>(); - group: LayerGroup = null; + group = new LayerGroup(); active = true; } @@ -77,7 +77,7 @@ export interface ILayeredMapWidgetConfig { selectedTrack?: string; tracks?: ITrack[]; saved?: boolean; - layers?: LayerConfig[]; + layers: LayerConfig[]; } export interface ITrack { diff --git a/layered-map-widget-plugin/popover-config/popover-modal.component.ts b/layered-map-widget-plugin/popover-config/popover-modal.component.ts index 815ec81..db2bad9 100644 --- a/layered-map-widget-plugin/popover-config/popover-modal.component.ts +++ b/layered-map-widget-plugin/popover-config/popover-modal.component.ts @@ -22,7 +22,7 @@ interface Tab { }) export class PopoverModalComponent { title = 'Popover config'; - closeSubject: Subject = new Subject(); + closeSubject: Subject = new Subject(); labels: ModalLabels = { ok: 'Save', cancel: 'Cancel' }; SAMPLE_TEMPLATES_C8Y = SAMPLE_TEMPLATES_C8Y; @@ -46,7 +46,7 @@ export class PopoverModalComponent { }, ]; - currentTab: Tab['id'] = this.tabs.find((t) => t.active).id; + currentTab: Tab['id'] = this.tabs.find((t) => t.active)?.id ?? this.tabs[2].id; form = new FormGroup({}); fields: FormlyFieldConfig[] = [ @@ -71,7 +71,7 @@ export class PopoverModalComponent { isActionsFormCollapsed = true; jsonEditorData: object = clone(SAMPLE_TEMPLATES_C8Y.OPERATION); - jsonErrorMessage: string; + jsonErrorMessage?: string; constructor(public bsModalRef: BsModalRef) {} diff --git a/layered-map-widget-plugin/service/alarm-polling.service.ts b/layered-map-widget-plugin/service/alarm-polling.service.ts index 36c1661..5437b27 100644 --- a/layered-map-widget-plugin/service/alarm-polling.service.ts +++ b/layered-map-widget-plugin/service/alarm-polling.service.ts @@ -85,7 +85,7 @@ export class AlarmPollingService { let res = await this.inventory.list({ ...filter, withTotalPages: true }); while (res.data.length) { mos.push(...res.data); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); @@ -109,7 +109,7 @@ export class AlarmPollingService { .map((alarm) => alarm.source.id); ids.forEach((id) => result.add(id)); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); diff --git a/layered-map-widget-plugin/service/event-polling.service.ts b/layered-map-widget-plugin/service/event-polling.service.ts index 63f5c29..41d0adf 100644 --- a/layered-map-widget-plugin/service/event-polling.service.ts +++ b/layered-map-widget-plugin/service/event-polling.service.ts @@ -85,7 +85,7 @@ export class EventPollingService { let res = await this.inventory.list({ ...filter, withTotalPages: true }); while (res.data.length) { mos.push(...res.data); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); @@ -109,7 +109,7 @@ export class EventPollingService { .map((event) => event.source.id); ids.forEach((id) => result.add(id)); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); diff --git a/layered-map-widget-plugin/service/inventory-polling.service.ts b/layered-map-widget-plugin/service/inventory-polling.service.ts index 1b1256c..204e169 100644 --- a/layered-map-widget-plugin/service/inventory-polling.service.ts +++ b/layered-map-widget-plugin/service/inventory-polling.service.ts @@ -81,7 +81,7 @@ export class InventoryPollingService { while (res.data.length) { res.data.forEach((mo) => result.push(mo)); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); diff --git a/layered-map-widget-plugin/service/layer.service.ts b/layered-map-widget-plugin/service/layer.service.ts index 4438ab7..74087e8 100644 --- a/layered-map-widget-plugin/service/layer.service.ts +++ b/layered-map-widget-plugin/service/layer.service.ts @@ -30,7 +30,6 @@ export class LayerService { const layer = Object.assign(new MyLayer(), setup); if (isQueryLayerConfig(setup.config)) { - layer.group = new LayerGroup(); const config = setup.config; if (config.type === 'Alarm') { this.queryLayerService.fetchByAlarmQuery(config.filter).then((devices) => { @@ -86,7 +85,7 @@ export class LayerService { classNames = 'status warning'; } - const marker = layer.markerCache.get(deviceId); + const marker = layer.markerCache.get(deviceId)!; const icon = this.markerIconService.getIcon(layer.config.icon, classNames); marker.setIcon(icon); } @@ -119,7 +118,7 @@ export class LayerService { layer.coordinates.delete(toDeleteId); } if (layer.markerCache.has(toDeleteId)) { - const markerToDelete = layer.markerCache.get(toDeleteId); + const markerToDelete = layer.markerCache.get(toDeleteId)!; layer.group.removeLayer(markerToDelete); layer.markerCache.delete(toDeleteId); } @@ -128,7 +127,7 @@ export class LayerService { createLayerGroup(layer: MyLayer): void { const markers = [...layer.coordinates.keys()].map((key) => { - const coord = layer.coordinates.get(key); + const coord = layer.coordinates.get(key)!; const marker = this.createMarker(key, coord, layer); layer.markerCache.set(key, marker); return marker; @@ -168,9 +167,9 @@ export class LayerService { layer.markerCache.set(id, marker); layer.group.addLayer(marker); } else { - const oldCoord = layer.coordinates.get(id); + const oldCoord = layer.coordinates.get(id)!; const newCoord = latLng(position); - marker = layer.markerCache.get(id); + marker = layer.markerCache.get(id)!; if (oldCoord.distanceTo(newCoord) > 0) { layer.coordinates.set(id, newCoord); marker.setLatLng(newCoord); diff --git a/layered-map-widget-plugin/service/layered-map-widget.service.ts b/layered-map-widget-plugin/service/layered-map-widget.service.ts index 7c80793..a7bdcda 100644 --- a/layered-map-widget-plugin/service/layered-map-widget.service.ts +++ b/layered-map-widget-plugin/service/layered-map-widget.service.ts @@ -18,16 +18,16 @@ export interface ILocationUpdateEvent extends IEvent { export class LayeredMapWidgetService { constructor(private eventService: EventService) {} - getTrack(config: ILayeredMapWidgetConfig): ITrack | null { + getTrack(config: ILayeredMapWidgetConfig): ITrack | undefined { if ( has(config, 'selectedTrack') && has(config, 'tracks') && config.selectedTrack !== null && !isEmpty(config.tracks) ) { - return config.tracks.find((t) => t.name === config.selectedTrack); + return config.tracks?.find((t) => t.name === config.selectedTrack); } - return null; + return undefined; } createLines(coords: LatLng[]): Polyline[] { diff --git a/layered-map-widget-plugin/service/popover-action.service.ts b/layered-map-widget-plugin/service/popover-action.service.ts index c9baa7d..1cfe499 100644 --- a/layered-map-widget-plugin/service/popover-action.service.ts +++ b/layered-map-widget-plugin/service/popover-action.service.ts @@ -49,9 +49,9 @@ export class PopoverActionService { let newAlarm: IAlarm = { source: mo, time: new Date().toISOString(), - severity: partial.severity, - type: partial.type, - text: partial.text, + severity: partial.severity!, + type: partial.type!, + text: partial.text!, }; // add potential custom fragments newAlarm = { ...newAlarm, ...partial }; @@ -64,8 +64,8 @@ export class PopoverActionService { let newEvent: IEvent = { source: mo, time: new Date().toISOString(), - type: partial.type, - text: partial.text, + type: partial.type!, + text: partial.text!, }; // add potential custom fragments newEvent = { ...newEvent, ...partial }; diff --git a/layered-map-widget-plugin/service/query-layer.service.ts b/layered-map-widget-plugin/service/query-layer.service.ts index 26055a3..05f4d51 100644 --- a/layered-map-widget-plugin/service/query-layer.service.ts +++ b/layered-map-widget-plugin/service/query-layer.service.ts @@ -12,7 +12,7 @@ export class QueryLayerService { private event: EventService ) {} - async fetchByAlarmQuery(params: object) { + async fetchByAlarmQuery(params: object): Promise { const result = new Map(); const filter = { withTotalPages: true, @@ -34,14 +34,14 @@ export class QueryLayerService { ) ); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); } await Promise.all(resolvers); - return [...result.values()].filter((mo) => !!mo); + return [...result.values()].filter((mo) => mo !== null) as IManagedObject[]; } async fetchByInventoryQuery(params: object) { @@ -55,10 +55,10 @@ export class QueryLayerService { let res = await this.inventory.list(filter); while (res.data.length) { result.push(...res.data); - if (res.data.length < res.paging.pageSize) { + if (res.data.length < (res.paging?.pageSize ?? -1)) { break; } - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); @@ -66,7 +66,7 @@ export class QueryLayerService { return result; } - async fetchByEventQuery(params: object) { + async fetchByEventQuery(params: object): Promise { const result = new Map(); const filter = { withTotalPages: true, @@ -86,13 +86,13 @@ export class QueryLayerService { mos.data.forEach((mo) => result.set(mo.id, mo)) ) ); - if (!res.paging.nextPage) { + if (!res.paging?.nextPage) { break; } res = await res.paging.next(); } await Promise.all(resolvers); - return [...result.values()].filter((mo) => !!mo); + return [...result.values()].filter((mo) => mo !== null) as IManagedObject[]; } resolveManagedObjects(ids: string[]) {