784823eacd1fefe11b3382f5f38e39a5f1dcf9b9
[sdc.git] / catalog-ui / src / app / ng2 / pages / home / home.component.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 'use strict';
21 import {Component as NgComponent, Inject, OnInit} from '@angular/core';
22 import {Component, ComponentMetadata, IConfigRoles, IUserProperties, Resource, Service} from 'app/models';
23 import {HomeFilter} from 'app/models/home-filter';
24 import {AuthenticationService, CacheService, HomeService, ResourceServiceNg2} from 'app/services-ng2';
25 import {ComponentState, ModalsHandler} from 'app/utils';
26 import {SdcUiServices} from 'onap-ui-angular';
27 import {CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, ResourceType} from '../../../utils/constants';
28 import {ImportVSPService} from '../../components/modals/onboarding-modal/import-vsp.service';
29 import {ISdcConfig, SdcConfigToken} from '../../config/sdc-config.config';
30 import {IAppMenu, SdcMenuToken} from '../../config/sdc-menu.config';
31 import {EntityFilterPipe} from '../../pipes/entity-filter.pipe';
32 import {TranslateService} from '../../shared/translator/translate.service';
33 import {FoldersItemsMenu, FoldersItemsMenuGroup, FoldersMenu} from './folders';
34 import {ImportVSPdata} from "../../components/modals/onboarding-modal/onboarding-modal.component";
35 import {DataTypeCatalogComponent} from "../../../models/data-type-catalog-component";
36
37 @NgComponent({
38     selector: 'home-page',
39     templateUrl: './home.component.html',
40     styleUrls: ['./home.component.less']
41 })
42 export class HomeComponent implements OnInit {
43     public numberOfItemToDisplay: number;
44     public homeItems: Component[];
45     public homeFilteredItems: Array<Component | DataTypeCatalogComponent>;
46     public homeFilteredSlicedItems: Array<Component | DataTypeCatalogComponent>;
47     public folders: FoldersMenu;
48     public roles: IConfigRoles;
49     public user: IUserProperties;
50     public showTutorial: boolean;
51     public isFirstTime: boolean;
52     public version: string;
53     public homeFilter: HomeFilter;
54     public vfcmtType: string;
55     public displayActions: boolean;
56
57     constructor(
58         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
59         @Inject(SdcMenuToken) public sdcMenu: IAppMenu,
60         @Inject('$state') private $state: ng.ui.IStateService,
61         private homeService: HomeService,
62         private authService: AuthenticationService,
63         private cacheService: CacheService,
64         private translateService: TranslateService,
65         private modalsHandler: ModalsHandler,
66         private modalService: SdcUiServices.ModalService,
67         private loaderService: SdcUiServices.LoaderService,
68         private importVSPService: ImportVSPService,
69         private resourceService: ResourceServiceNg2
70     ) { }
71
72     ngOnInit(): void {
73         this.initHomeComponentVars();
74         this.initFolders();
75         this.initEntities();
76
77         if (this.$state.params) {
78             if (this.$state.params.folder) {
79                 const folderName = this.$state.params.folder.replaceAll('_', ' ');
80
81                 const selectedFolder = this.folders.getFolders().find((tmpFolder: FoldersItemsMenu) => tmpFolder.text === folderName);
82                 if (selectedFolder) {
83                     this.setSelectedFolder(selectedFolder);
84                 }
85                 // Show the tutorial if needed when the dashboard page is opened.<script src="bower_components/angular-filter/dist/angular-filter.min.js"></script>
86                 // This is called from the welcome page.
87             } else if (this.$state.params.show === 'tutorial') {
88                 this.showTutorial = true;
89                 this.isFirstTime = true;
90             }
91         }
92     }
93
94     // Open onboarding modal
95     public notificationIconCallback(): void {
96         this.importVSPService.openOnboardingModal().subscribe((importVSPdata: ImportVSPdata) => {
97             const actualComponent = importVSPdata.previousComponent;
98             if (!actualComponent || actualComponent.csarVersion !== importVSPdata.componentCsar.csarVersion) {
99                 this.cacheService.set(CHANGE_COMPONENT_CSAR_VERSION_FLAG, importVSPdata.componentCsar.csarVersion);
100             }
101             const vfExistsAndIsNotCheckedOut: boolean = actualComponent && actualComponent.lifecycleState != ComponentState.NOT_CERTIFIED_CHECKOUT;
102             if (vfExistsAndIsNotCheckedOut) {
103                 this.checkoutAndRedirectToWorkspace(importVSPdata);
104                 return;
105             }
106             this.$state.go('workspace.general', {
107                 id: actualComponent && actualComponent.uniqueId,
108                 componentCsar: importVSPdata.componentCsar,
109                 type: importVSPdata.type
110             });
111         });
112     }
113
114     private checkoutAndRedirectToWorkspace(importVSPdata: ImportVSPdata) {
115         this.loaderService.activate();
116         this.resourceService.checkout(importVSPdata.previousComponent.uniqueId)
117         .subscribe((componentMetadata: ComponentMetadata) => {
118             this.$state.go('workspace.general', {
119                 id: componentMetadata.uniqueId,
120                 componentCsar: importVSPdata.componentCsar,
121                 type: importVSPdata.type
122             });
123             this.loaderService.deactivate();
124         }, () => {
125             this.loaderService.deactivate();
126         });
127         return;
128     }
129
130     public onImportVf(file: any): void {
131         if (file && file.filename) {
132             // Check that the file has valid extension.
133             const fileExtension: string = file.filename.split('.').pop();
134             if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
135                 this.$state.go('workspace.general', {
136                     type: ComponentType.RESOURCE.toLowerCase(),
137                     importedFile: file,
138                     resourceType: ResourceType.VF
139                 });
140             } else {
141                 const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE');
142                 const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS', {extensions: this.sdcConfig.csarFileExtension});
143                 this.modalService.openWarningModal(title, message, 'error-invalid-csar-ext');
144             }
145         }
146     }
147
148     public onImportVfc(file: any): void {
149         if (file && file.filename) {
150             // Check that the file has valid extension.
151             const fileExtension: string = file.filename.split('.').pop();
152             if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
153                 this.$state.go('workspace.general', {
154                     type: ComponentType.RESOURCE.toLowerCase(),
155                     importedFile: file,
156                     resourceType: ResourceType.VFC
157                 });
158             } else {
159                 const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE');
160                 const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS', {extensions: this.sdcConfig.toscaFileExtension});
161                 this.modalService.openWarningModal(title, message, 'error-invalid-tosca-ext');
162             }
163         }
164     }
165
166     public onImportService(file: any): void {
167         if (file && file.filename) {
168             // Check that the file has valid extension.
169             const fileExtension: string = file.filename.split(".").pop();
170             if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
171                 this.$state.go('workspace.general', {
172                     type: ComponentType.SERVICE.toLowerCase(),
173                     importedFile: file,
174                     serviceType: 'Service'
175                 });
176             } else {
177                 const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE');
178                 const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS', {extensions: this.sdcConfig.csarFileExtension});
179                 this.modalService.openWarningModal(title, message, 'error-invalid-csar-ext');
180             }
181         }
182     };
183
184     public openCreateModal(componentType: string, importedFile: any): void {
185         if (importedFile) {
186             this.initEntities(true); // Return from import
187         } else {
188             this.$state.go('workspace.general', {type: componentType.toLowerCase()});
189         }
190     }
191
192     public createPNF(): void {
193         this.$state.go('workspace.general', {
194             type: ComponentType.RESOURCE.toLowerCase(),
195             resourceType: ResourceType.PNF
196         });
197     }
198
199     public createCR(): void {
200         this.$state.go('workspace.general', {
201             type: ComponentType.RESOURCE.toLowerCase(),
202             resourceType: ResourceType.CR
203         });
204     }
205
206     public entitiesCount(folderItem: FoldersItemsMenu): any {
207         let total: number = 0;
208         if (folderItem.isGroup()) {
209             this.folders.getFolders().forEach((tmpFolder: FoldersItemsMenu) => {
210                 if (tmpFolder.group && tmpFolder.group === (folderItem as FoldersItemsMenuGroup).groupname) {
211                     total = total + this._getTotalCounts(tmpFolder);
212                 }
213             });
214         } else {
215             total = total + this._getTotalCounts(folderItem);
216         }
217         return total;
218     }
219
220     public updateFilter = () => {
221         this.$state.go('.', this.homeFilter.toUrlParam(), {location: 'replace', notify: false});
222         this.filterHomeItems();
223     }
224
225     public getCurrentFolderDistributed(): any[] {
226         const states = [];
227         if (this.folders) {
228             const folderItem: FoldersItemsMenu = this.folders.getCurrentFolder();
229             if (folderItem.isGroup()) {
230                 this.folders.getFolders().forEach((tmpFolder: FoldersItemsMenu) => {
231                     if (tmpFolder.group && tmpFolder.group === (folderItem as FoldersItemsMenuGroup).groupname) {
232                         this._setStates(tmpFolder, states);
233                     }
234                 });
235             } else {
236                 this._setStates(folderItem, states);
237             }
238         }
239         return states;
240     }
241
242     public setSelectedFolder(folderItem: FoldersItemsMenu): void {
243         this.folders.setSelected(folderItem);
244     }
245
246     public goToComponent(component: Component): void {
247         const loaderService = this.loaderService;
248         loaderService.activate();
249         this.$state.go('workspace.general', {id: component.uniqueId, type: component.componentType.toLowerCase()}).then(() => {
250             loaderService.deactivate();
251         });
252     }
253
254     public raiseNumberOfElementToDisplay(recalculate: boolean = false) {
255         const scrollPageAmount = 35;
256         if (!this.homeItems) {
257             this.numberOfItemToDisplay = 0;
258         } else if (this.homeItems.length > this.numberOfItemToDisplay || recalculate) {
259             let fullPagesAmount = Math.ceil(this.numberOfItemToDisplay / scrollPageAmount) * scrollPageAmount;
260             if (!recalculate || fullPagesAmount === 0) {  // TODO trigger infiniteScroll to check bottom and fire onBottomHit by itself (sdc-ui)
261                 fullPagesAmount += scrollPageAmount;
262             }
263             this.numberOfItemToDisplay = Math.min(this.homeItems.length, fullPagesAmount);
264             this.homeFilteredSlicedItems = this.homeFilteredItems.slice(0, this.numberOfItemToDisplay);
265         }
266     }
267
268     public changeCheckboxesFilter(checkboxesFilterArray: string[], checkboxValue: string, checked?: boolean) {
269         const checkboxIdx = checkboxesFilterArray.indexOf(checkboxValue);
270
271         checked = (checked !== undefined) ? checked : checkboxIdx === -1;
272         if (checked && checkboxIdx === -1) {
273             checkboxesFilterArray.push(checkboxValue);
274         } else if (!checked && checkboxIdx !== -1) {
275             checkboxesFilterArray.splice(checkboxIdx, 1);
276         }
277         this.updateFilter();
278     }
279
280     public changeFilterTerm(filterTerm: string): void {
281         this.homeFilter.search = { filterTerm };
282         this.updateFilter();
283     }
284
285     public setDisplayActions(display?: boolean) {
286         this.displayActions = display !== undefined ? display : !this.displayActions;
287     }
288
289     private _getTotalCounts(tmpFolder): number {
290         let total: number = 0;
291         if (tmpFolder.dist !== undefined) {
292             const distributions = tmpFolder.dist.split(',');
293             distributions.forEach((item: any) => {
294                 total = total + this.getEntitiesByStateDist(tmpFolder.state, item).length;
295             });
296         } else {
297             total = total + this.getEntitiesByStateDist(tmpFolder.state, tmpFolder.dist).length;
298         }
299         return total;
300     }
301
302     private _setStates(tmpFolder, states) {
303         if (tmpFolder.states !== undefined) {
304             tmpFolder.states.forEach((item: any) => {
305                 states.push({state: item.state, dist: item.dist});
306             });
307         } else {
308             states.push({state: tmpFolder.state, dist: tmpFolder.dist});
309         }
310     }
311
312     private initEntities(reload?: boolean) {
313         if (reload || this.componentShouldReload()) {
314             this.loaderService.activate();
315             this.homeService.getAllComponents(true).subscribe(
316                 (components: Component[]) => {
317                     this.cacheService.set('breadcrumbsComponentsState', this.$state.current.name);  // dashboard
318                     this.cacheService.set('breadcrumbsComponents', components);
319                     this.homeItems = components;
320                     this.loaderService.deactivate();
321                     this.filterHomeItems();
322                 }, (error) => { this.loaderService.deactivate(); });
323         } else {
324             this.homeItems = this.cacheService.get('breadcrumbsComponents');
325             this.filterHomeItems();
326         }
327     }
328
329     private isDefaultFilter = (): boolean => {
330         const defaultFilter = new HomeFilter();
331         return angular.equals(defaultFilter, this.homeFilter);
332     }
333
334     private componentShouldReload = (): boolean => {
335         const breadcrumbsValid: boolean = (this.$state.current.name === this.cacheService.get('breadcrumbsComponentsState') && this.cacheService.contains('breadcrumbsComponents'));
336         return !breadcrumbsValid || this.isDefaultFilter();
337     }
338
339     private getEntitiesByStateDist(state: string, dist: string): Component[] {
340         let gObj: Component[];
341         if (this.homeItems && (state || dist)) {
342             gObj = this.homeItems.filter((obj: Component) => {
343                 if (dist !== undefined && obj.distributionStatus === dist && obj.lifecycleState === state) {
344                     return true;
345                 } else if (dist === undefined && (obj.lifecycleState === state || obj.distributionStatus === state)) {
346                     return true;
347                 }
348                 return false;
349             });
350         } else {
351             gObj = [];
352         }
353         return gObj;
354     }
355
356     private filterHomeItems() {
357         this.homeFilteredItems = this.makeFilteredItems(this.homeItems, this.homeFilter);
358         this.raiseNumberOfElementToDisplay(true);
359         this.homeFilteredSlicedItems = this.homeFilteredItems.slice(0, this.numberOfItemToDisplay);
360     }
361
362     private makeFilteredItems(homeItems: Array<Component>, filter: HomeFilter) {
363         let filteredComponents: Array<Component | DataTypeCatalogComponent> = homeItems;
364
365         // filter: exclude all resources of type 'vfcmtType':
366             filteredComponents = filteredComponents.filter((c) =>
367                 !c.isResource() || (c as Resource).resourceType.indexOf(this.vfcmtType) === -1);
368
369         // common entity filter
370         // --------------------------------------------------------------------------
371         filteredComponents = EntityFilterPipe.transform(filteredComponents, filter);
372
373         return filteredComponents;
374     }
375
376     private initFolders = (): void => {
377         // Note: Do not use SdcUi.ChecklistComponent for folders checkboxes, since from the data structure
378         // it is not determined that all checkboxes under the same group are managed by the same selectedValues array.
379         if (this.user) {
380             this.folders = new FoldersMenu(this.roles[this.user.role].folder);
381         }
382     }
383
384     private initHomeComponentVars(): void {
385         this.version = this.cacheService.get('version');
386         this.numberOfItemToDisplay = 0;
387         this.displayActions = false;
388         this.user = this.authService.getLoggedinUser();
389         this.roles = this.sdcMenu.roles;
390         this.showTutorial = false;
391         this.isFirstTime = false;
392         this.vfcmtType = ResourceType.VFCMT;
393
394         // Checkboxes filter init
395         this.homeFilter = new HomeFilter(this.$state.params);
396
397         // bind callbacks that are transferred as inputs
398         this.notificationIconCallback = this.notificationIconCallback.bind(this);
399     }
400
401 }