Fix Bug 1975: Map view Issue in VIM account
[osm/NG-UI.git] / src / app / vim-accounts / vim-account-details / VimAccountDetailsComponent.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
4  Licensed under the Apache License, Version 2.0 (the 'License');
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17 */
18 /**
19  * @file Vim Account Component.
20  */
21 import { Component, Injector, OnInit } from '@angular/core';
22 import { Router } from '@angular/router';
23 import { TranslateService } from '@ngx-translate/core';
24 import { CONFIGCONSTANT, CONSTANTNUMBER, ERRORDATA, VIM_TYPES } from 'CommonModel';
25 import { DataService } from 'DataService';
26 import { environment } from 'environment';
27 import { LocalDataSource } from 'ng2-smart-table';
28 import { NSInstanceDetails } from 'NSInstanceModel';
29 import Feature from 'ol/Feature';
30 import Point from 'ol/geom/Point';
31 import { defaults as defaultInteractions } from 'ol/interaction';
32 import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
33 import Map from 'ol/Map';
34 import Overlay from 'ol/Overlay';
35 import { fromLonLat } from 'ol/proj.js';
36 import OSM from 'ol/source/OSM';
37 import VectorSource from 'ol/source/Vector';
38 import { Icon, Style } from 'ol/style';
39 import View from 'ol/View';
40 import { RestService } from 'RestService';
41 import { Subscription } from 'rxjs';
42 import { SharedService } from 'SharedService';
43 import { VimAccountDetails, VIMData } from 'VimAccountModel';
44 import { VimAccountsActionComponent } from 'VimAccountsAction';
45 /**
46  * Creating component
47  * @Component takes VimAccountDetailsComponent.html as template url
48  */
49 @Component({
50     selector: 'app-vim-account-details',
51     templateUrl: './VimAccountDetailsComponent.html',
52     styleUrls: ['./VimAccountDetailsComponent.scss']
53 })
54 /** Exporting a class @exports VimAccountDetailsComponent */
55 export class VimAccountDetailsComponent implements OnInit {
56     /** To inject services @public */
57     public injector: Injector;
58     /** handle translate @public */
59     public translateService: TranslateService;
60     /** initially show the map container@public */
61     public showMap: boolean;
62     /**  hide and show popup @public */
63     public popupShow: boolean = false;
64     /** Data of smarttable populate through LocalDataSource @public */
65     public dataSource: LocalDataSource = new LocalDataSource();
66     /** Columns list of the smart table @public */
67     public columnLists: object = {};
68     /** Settings for smarttable to populate the table with columns @public */
69     public settings: object = {};
70     /** initially hide the list@private */
71     public showList: boolean;
72     /** to store locations name @public */
73     public getLocation: GetLocation[];
74     /** Contains content for map popup @public */
75     public popupData: string;
76     /** Check the loading results @public */
77     public isLoadingResults: boolean = true;
78     /** Give the message for the loading @public */
79     public message: string = 'PLEASEWAIT';
80     /** Class for empty and present data @public */
81     public checkDataClass: string;
82     /** Formation of appropriate Data for LocalDatasource @public */
83     public vimData: VIMData[];
84     /** operational State init data @public */
85     public operationalStateFirstStep: string = CONFIGCONSTANT.vimOperationalStateFirstStep;
86     /** operational State running data @public */
87     public operationalStateSecondStep: string = CONFIGCONSTANT.vimOperationalStateStateSecondStep;
88     /** operational State failed data @public */
89     public operationalStateThirdStep: string = CONFIGCONSTANT.vimOperationalStateThirdStep;
90     /** NS Instances operational State failed data @public */
91     public nsinstancesoperationalStateRunning: string = CONFIGCONSTANT.operationalStateSecondStep;
92     /** Instance of the rest service @private */
93     private restService: RestService;
94     /** dataService to pass the data from one component to another @private */
95     private dataService: DataService;
96     /** Contains all methods related to shared @private */
97     private sharedService: SharedService;
98     /** Holds the instance of router class @private */
99     private router: Router;
100     /** ns INstance Data @private */
101     private nsData: NSInstanceDetails[];
102     /** map object @private */
103     private map: Map;
104     /**  used to bind marker @private */
105     private vectorSource: VectorSource;
106     /** used to bind vectorSource @private */
107     private vectorLayer: VectorLayer;
108     /** marker @private */
109     private marker: Feature;
110     /** latitude @private */
111     private lat: number;
112     /** longitude @private */
113     private lng: number;
114     /**  each vector layer of marker is pushed to layers array @private */
115     private layers: VectorLayer[] = [];
116     /** locationData @private */
117     private locationData: VimAccountDetails[];
118     /** popup array @private */
119     private overLay: Overlay[] = [];
120     /** Instance of subscriptions @private */
121     private generateDataSub: Subscription;
122
123     constructor(injector: Injector) {
124         this.injector = injector;
125         this.restService = this.injector.get(RestService);
126         this.dataService = this.injector.get(DataService);
127         this.sharedService = this.injector.get(SharedService);
128         this.router = this.injector.get(Router);
129         this.translateService = this.injector.get(TranslateService);
130     }
131     /** Lifecyle Hooks the trigger before component is instantiate @public */
132     public ngOnInit(): void {
133         this.osmMapView();
134         this.listView();
135         this.generateColumns();
136         this.generateSettings();
137         this.generateData();
138         this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
139     }
140
141     /** smart table Header Colums @public */
142     public generateColumns(): void {
143         this.columnLists = {
144             name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
145             identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' },
146             type: {
147                 title: this.translateService.instant('TYPE'), width: '15%',
148                 filter: {
149                     type: 'list',
150                     config: {
151                         selectText: 'Select',
152                         list: VIM_TYPES
153                     }
154                 }
155             },
156             operationalState: {
157                 title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html',
158                 filter: {
159                     type: 'list',
160                     config: {
161                         selectText: 'Select',
162                         list: [
163                             { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
164                             { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
165                             { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
166                         ]
167                     }
168                 },
169                 valuePrepareFunction: (cell: VIMData, row: VIMData): string => {
170                     if (row.operationalState === this.operationalStateFirstStep) {
171                         return `<span class="icon-label" title="${row.operationalState}">
172                         <i class="fas fa-clock text-warning"></i>
173                         </span>`;
174                     } else if (row.operationalState === this.operationalStateSecondStep) {
175                         return `<span class="icon-label" title="${row.operationalState}">
176                         <i class="fas fa-check-circle text-success"></i>
177                         </span>`;
178                     } else if (row.operationalState === this.operationalStateThirdStep) {
179                         return `<span class="icon-label" title="${row.operationalState}">
180                         <i class="fas fa-times-circle text-danger"></i>
181                         </span>`;
182                     } else {
183                         return `<span>${row.operationalState}</span>`;
184                     }
185                 }
186             },
187             description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
188             Actions: {
189                 name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
190                 valuePrepareFunction: (cell: VIMData, row: VIMData): VIMData => row,
191                 renderComponent: VimAccountsActionComponent
192             }
193         };
194     }
195
196     /** smart table Data Settings @public */
197     public generateSettings(): void {
198         this.settings = {
199             edit: {
200                 editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
201                 confirmSave: true
202             },
203             delete: {
204                 deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
205                 confirmDelete: true
206             },
207             columns: this.columnLists,
208             actions: {
209                 add: false,
210                 edit: false,
211                 delete: false,
212                 position: 'right'
213             },
214             attr: this.sharedService.tableClassConfig(),
215             pager: this.sharedService.paginationPagerConfig(),
216             noDataMessage: this.translateService.instant('NODATAMSG')
217         };
218     }
219
220     /** smart table listing manipulation @public */
221     public onChange(perPageValue: number): void {
222         this.dataSource.setPaging(1, perPageValue, true);
223     }
224
225     /** smart table listing manipulation @public */
226     public onUserRowSelect(event: MessageEvent): void {
227         Object.assign(event.data, { page: 'vim-account' });
228         this.dataService.changeMessage(event.data);
229     }
230
231     /** on Navigate to Composer Page @public */
232     public composeVIM(): void {
233         this.router.navigate(['vim/new']).catch(() => {
234             //empty block
235         });
236     }
237
238     /** To show map conatainer @public */
239     public mapView(): void {
240         this.showList = true;
241         this.showMap = false;
242     }
243     /** To show listview @public */
244     public listView(): void {
245         this.showMap = true;
246         this.showList = false;
247     }
248     /** Load the datasource appropriatetype @public */
249     public loadDatasource(getdata: VIMData[]): void {
250         if (getdata.length > 0) {
251             this.checkDataClass = 'dataTables_present';
252         } else {
253             this.checkDataClass = 'dataTables_empty';
254         }
255         this.dataSource.load(getdata).then((data: boolean) => {
256             //empty block
257         }).catch(() => {
258             //empty block
259         });
260     }
261
262     /** Generate generateVIMData object from loop and return for the datasource @public */
263     public generateVIMData(vimAccountData: VimAccountDetails): VIMData {
264         return {
265             name: vimAccountData.name,
266             identifier: vimAccountData._id,
267             type: vimAccountData.vim_type,
268             operationalState: vimAccountData._admin.operationalState,
269             description: vimAccountData.description,
270             instancesData: this.nsData,
271             resources: vimAccountData.resources !== undefined ? vimAccountData.resources : null
272         };
273     }
274
275     /**
276      * Lifecyle hook which get trigger on component destruction
277      */
278     public ngOnDestroy(): void {
279         this.generateDataSub.unsubscribe();
280     }
281
282     /** Fetching the data from server to Load in the smarttable @protected */
283     private generateData(): void {
284         this.isLoadingResults = true;
285         this.vimData = [];
286         this.getNSData().then((): void => {
287             this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccountsData: VimAccountDetails[]) => {
288                 this.locationData = vimAccountsData;
289                 vimAccountsData.forEach((vimAccountData: VimAccountDetails) => {
290                     const vimDataObj: VIMData = this.generateVIMData(vimAccountData);
291                     this.vimData.push(vimDataObj);
292                 });
293                 this.loadDatasource(this.vimData);
294                 this.removeLayersOverLay();
295                 this.arrayOfLocation();
296                 this.isLoadingResults = false;
297             }, (error: ERRORDATA) => {
298                 this.restService.handleError(error, 'get');
299                 this.isLoadingResults = false;
300             });
301         }).catch((error: ERRORDATA): void => {
302             this.restService.handleError(error, 'get');
303             this.isLoadingResults = false;
304         });
305     }
306
307     /** fetching the nsdata @private */
308     private async getNSData(): Promise<Boolean> {
309         return new Promise<Boolean>((resolve: Function, reject: Function): void => {
310             this.nsData = [];
311             this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => {
312                 const nsRunningInstancesData: NSInstanceDetails[] = nsdInstancesData.filter((instancesData: NSInstanceDetails) =>
313                     instancesData['operational-status'] === this.nsinstancesoperationalStateRunning);
314                 this.nsData = nsRunningInstancesData;
315                 resolve(true);
316             }, (error: ERRORDATA) => {
317                 this.restService.handleError(error, 'get');
318                 resolve(true);
319             });
320         });
321     }
322
323     /** create map view @private */
324     private osmMapView(): void {
325         this.map = new Map({
326             target: 'map',
327             layers: [new TileLayer({
328                 source: new OSM()
329             })],
330             interactions: defaultInteractions({
331                 mouseWheelZoom: true
332             }),
333             view: new View({
334                 center: fromLonLat([CONSTANTNUMBER.osmapviewlong, CONSTANTNUMBER.osmapviewlat]),
335                 zoom: 3,
336                 minZoom: 1.5
337             })
338         });
339     }
340
341     /** remove the layers and overlay @private */
342     private removeLayersOverLay(): void {
343         this.layers.forEach((layer: VectorLayer) => {
344             this.map.removeLayer(layer);
345         });
346         this.overLay.forEach((lay: Overlay) => {
347             this.map.removeOverlay(lay);
348         });
349     }
350
351     /** filter locations from vimaccounts @private */
352     private arrayOfLocation(): void {
353         this.getLocation = [];
354         this.locationData.filter((item: VimAccountDetails) => {
355             if (item.hasOwnProperty('config')) {
356                 if (item.config.hasOwnProperty('location')) {
357                     this.getLocation.push({ name: item.name, location: item.config.location, id: item._id });
358                 }
359             }
360         });
361         if (this.getLocation !== []) {
362             this.getLocation.filter((loc: GetLocation) => {
363                 if (loc.location !== '') {
364                     if (loc.location !== ',,') {
365                         const getLatLong: string[] = loc.location.split(',');
366                         this.lng = +getLatLong[CONSTANTNUMBER.splitLongitude];
367                         this.lat = +getLatLong[CONSTANTNUMBER.splitLatitude];
368                         this.addMarker(getLatLong[0], loc.id, loc.name);
369                     }
370                 }
371             });
372         }
373     }
374     /** add markers on map @private */
375     private addMarker(loc: string, id: string, name: string): void {
376         const container: HTMLElement = document.getElementById('popup');
377         const closer: HTMLElement = document.getElementById('popup-closer');
378         this.popupShow = true;
379         const overlay: Overlay = this.addOverlay(container);
380         this.marker = this.addFeature(loc, id, name);
381         this.setStyleMarker();
382         this.setVectorSource();
383         this.setVectorLayer();
384         this.map.addLayer(this.vectorLayer);
385         this.layers.push(this.vectorLayer);
386         if (this.layers.length === 1) {
387             this.markerClickEvent(closer, overlay);
388         }
389     }
390     /** Create an overlay to anchor the popup to the map @private */
391     private addOverlay(container: HTMLElement): Overlay {
392         return new Overlay({
393             element: container,
394             autoPan: true,
395             autoPanAnimation: {
396                 duration: 250
397             }
398         });
399     }
400     /** Return the Feature of creating a marker in the map @private */
401     private addFeature(loc: string, id: string, name: string): Feature {
402         return new Feature({
403             geometry: new Point(fromLonLat([this.lng, this.lat])),
404             location: loc,
405             Id: id,
406             vimName: name
407         });
408     }
409     /** Set the style of the marker @private */
410     private setStyleMarker(): void {
411         this.marker.setStyle(new Style({
412             image: new Icon(({
413                 crossOrigin: 'anonymous',
414                 src: 'assets/images/map-icon.png'
415             }))
416         }));
417     }
418     /** Set the map vector source @private */
419     private setVectorSource(): void {
420         this.vectorSource = new VectorSource({
421             features: [this.marker]
422         });
423     }
424     /** Set the map vector layer @private */
425     private setVectorLayer(): void {
426         this.vectorLayer = new VectorLayer({
427             source: this.vectorSource
428         });
429     }
430     /** Add a click handler to the map to render the popup. @private */
431     private markerClickEvent(closer: HTMLElement, overlay: Overlay): void {
432         // tslint:disable-next-line: no-any
433         this.map.on('singleclick', (evt: any) => {
434             const feature: Feature = this.map.forEachFeatureAtPixel(evt.pixel,
435                 (f: Feature) => {
436                     return f;
437                 });
438             if (feature) {
439                 this.setCoordinates(feature, overlay);
440             } else {
441                 this.map.removeOverlay(overlay);
442             }
443         });
444         /** Handle close event for overlay */
445         closer.onclick = (): boolean => {
446             overlay.setPosition(undefined);
447             closer.blur();
448             return false;
449         };
450     }
451     /** Set the coordinates point if the feature is available @private */
452     // tslint:disable-next-line: no-any
453     private setCoordinates(feature: any, overlay: Overlay): void {
454         this.popupData = '';
455         this.popupData += '<h3 class="popover-header">' + feature.values_.vimName + '- (' + feature.values_.location + ')</h3>';
456         this.popupData += '<ul class="list-unstyled m-2">';
457         const instnaceData: NSInstanceDetails[] = this.nsData.filter((item: NSInstanceDetails) => {
458             if (item.datacenter === feature.values_.Id) {
459                 this.popupData += '<li class="m-2"><a class="d-block text-truncate" target="_parent" href="instances/ns/' + item._id + '"><i class="fa-sitemap fas icons mr-1"></i>'
460                     + item.name + '</a></li>';
461                 return item;
462             }
463         });
464         if (instnaceData.length === 0) {
465             this.popupData += '<li class="m-2">' + this.translateService.instant('PAGE.DASHBOARD.NOINSTANCES') + '</li>';
466         }
467         this.popupData += '</ul>';
468         const coordinates: number[] = feature.getGeometry().getCoordinates();
469         overlay.setPosition(coordinates);
470         this.overLay.push(overlay);
471         this.map.addOverlay(overlay);
472     }
473 }
474
475 /** interface for get location */
476 interface GetLocation {
477     location: string;
478     id: string;
479     name?: string;
480 }