diff options
author | ys9693 <ys9693@att.com> | 2020-01-19 13:50:02 +0200 |
---|---|---|
committer | Ofir Sonsino <ofir.sonsino@intl.att.com> | 2020-01-22 12:33:31 +0000 |
commit | 16a9fce0e104a38371a9e5a567ec611ae3fc7f33 (patch) | |
tree | 03a2aff3060ddb5bc26a90115805a04becbaffc9 /catalog-ui/src/app/ng2/pages/home | |
parent | aa83a2da4f911c3ac89318b8e9e8403b072942e1 (diff) |
Catalog alignment
Issue-ID: SDC-2724
Signed-off-by: ys9693 <ys9693@att.com>
Change-Id: I52b4aacb58cbd432ca0e1ff7ff1f7dd52099c6fe
Diffstat (limited to 'catalog-ui/src/app/ng2/pages/home')
7 files changed, 994 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap b/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap new file mode 100644 index 0000000000..ae5445e546 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`home component should match current snapshot 1`] = ` +<home-page + $state={[Function Object]} + authService={[Function Object]} + cacheService={[Function Object]} + componentShouldReload={[Function Function]} + homeService={[Function Object]} + importVSPService={[Function Object]} + initFolders={[Function Function]} + isDefaultFilter={[Function Function]} + loaderService={[Function Object]} + modalService={[Function Object]} + modalsHandler={[Function Object]} + sdcConfig={[Function Object]} + sdcMenu={[Function Object]} + translateService={[Function Object]} + updateFilter={[Function Function]} +> + <div + class="sdc-catalog-container" + > + + <top-nav /> + </div> +</home-page> +`; diff --git a/catalog-ui/src/app/ng2/pages/home/folders.ts b/catalog-ui/src/app/ng2/pages/home/folders.ts new file mode 100644 index 0000000000..036ae329b7 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/folders.ts @@ -0,0 +1,93 @@ + +export interface IItemMenu { + +} + +export interface IMenuItemProperties { + text:string; + group:string; + state:string; + dist:string; + groupname:string; + states:Array<any>; +} + +export class FoldersMenu { + private _folders:Array<FoldersItemsMenu> = []; + + constructor(folders:Array<IMenuItemProperties>) { + let self = this; + folders.forEach(function (folder:IMenuItemProperties) { + if (folder.groupname) { + self._folders.push(new FoldersItemsMenuGroup(folder)); + } else { + self._folders.push(new FoldersItemsMenu(folder)); + } + }); + self._folders[0].setSelected(true); + } + + public getFolders():Array<FoldersItemsMenu> { + return this._folders; + } + + public getCurrentFolder():FoldersItemsMenu { + let menuItem:FoldersItemsMenu = undefined; + this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + if (tmpFolder.isSelected()) { + menuItem = tmpFolder; + } + }); + return menuItem; + } + + public setSelected(folder:FoldersItemsMenu):void { + this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + tmpFolder.setSelected(false); + }); + folder.setSelected(true); + } +} + +export class FoldersItemsMenu implements IItemMenu { + public text:string; + public group:string; + public state:string; + public dist:string; + public states:Array<any>; + + private selected:boolean = false; + + constructor(menuProperties:IMenuItemProperties) { + this.text = menuProperties.text; + this.group = menuProperties.group; + this.state = menuProperties.state; + this.states = menuProperties.states; + this.dist = menuProperties.dist; + } + + public isSelected():boolean { + return this.selected; + } + + public setSelected(value:boolean):void { + this.selected = value; + } + + public isGroup():boolean { + return false; + } +} + +export class FoldersItemsMenuGroup extends FoldersItemsMenu { + public groupname:string; + + constructor(menuProperties:IMenuItemProperties) { + super(menuProperties); + this.groupname = menuProperties.groupname; + } + + public isGroup():boolean { + return true; + } +} diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.html b/catalog-ui/src/app/ng2/pages/home/home.component.html new file mode 100644 index 0000000000..1c8c2b4373 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/home.component.html @@ -0,0 +1,88 @@ +<div class="sdc-catalog-container"> + <div class="w-sdc-main-container" *ngIf="user"> + + <div id="dashboard-main-scroll" infiniteScroll class="w-sdc-main-right-container" (infiniteScroll)="raiseNumberOfElementToDisplay()" [infiniteScrollDistance]="100"> + + <div class='w-sdc-row-flex-items'> + + <!-- ADD Component --> + <div *ngIf="user.role === 'DESIGNER'" class="w-sdc-dashboard-card-new" + (mouseleave)="setDisplayActions(false)" + (mouseover)="setDisplayActions(true)"> + <div class="w-sdc-dashboard-card-new-content" data-tests-id="AddButtonsArea"> + <div class="w-sdc-dashboard-card-new-content-plus" [hidden]="displayActions"></div> + <div class="sdc-dashboard-create-element-container" [hidden]="!displayActions"> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" testId="createResourceButton" size="medium" type="secondary" text="Add VF" (click)="openCreateModal('RESOURCE')"></sdc-button> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" testId="createCRButton" size="medium" type="secondary" text="Add CR" (click)="createCR()"></sdc-button> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" testId="createPNFButton" size="medium" type="secondary" text="Add PNF" (click)="createPNF()"></sdc-button> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" testId="createServiceButton" size="medium" type="secondary" text="Add Service" (click)="openCreateModal('SERVICE')"></sdc-button> + </div> + </div> + </div> + + <!-- Import Component --> + <div *ngIf="user.role === 'DESIGNER'" class="w-sdc-dashboard-card-new" + (mouseleave)="setDisplayActions(false)" + (mouseover)="setDisplayActions(true)"> + <div class="w-sdc-dashboard-card-new-content" data-tests-id="importButtonsArea" > + <div class="w-sdc-dashboard-card-import-content-plus" [hidden]="displayActions"></div> + <div class="sdc-dashboard-import-element-container" [hidden]="!displayActions"> + <sdc-button-file-opener + *ngIf="roles[user.role].dashboard.showCreateNew" + size="medium" + type="secondary" + text="Import VFC" + testId="importVFCbutton" + [extensions]="sdcConfig.toscaFileExtension" + (fileUpload)="onImportVfc($event)" + [convertToBase64]="true" + ></sdc-button-file-opener> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" data-tests-id="importButtonsVSP" size="medium" type="secondary" text="Import VSP" (click)="notificationIconCallback()"></sdc-button> + <sdc-button-file-opener + *ngIf="roles[user.role].dashboard.showCreateNew" + size="medium" + type="secondary" + text="Import DCAE" + testId="importDCAE" + [extensions]="sdcConfig.csarFileExtension" + (fileUpload)="onImportVf($event)" + [convertToBase64]="true" + ></sdc-button-file-opener> + </div> + </div> + </div> + + <!-- Tile new --> + <ui-tile *ngFor="let item of homeFilteredSlicedItems" + [component]="item" (onTileClick)="goToComponent(item)"></ui-tile> + <!-- Tile new --> + + </div> + + </div> + + <div class="w-sdc-left-sidebar"> + <div class="i-sdc-left-sidebar-item " + *ngFor="let folder of folders.getFolders()" + [ngClass]="{'category-title': folder.isGroup(), 'selectedLink': folder.isSelected()}"> + + <span *ngIf="folder.isGroup()" class="title-text">{{folder.text}}</span> + <sdc-checkbox *ngIf="!folder.isGroup() && !folder.dist" + [label]="folder.text" + [attr.data-tests-id]="'filter-' + folder.state" + [checked]="homeFilter.selectedStatuses.indexOf(folder.state) !== -1" + (checkedChange)="changeCheckboxesFilter(homeFilter.selectedStatuses, folder.state, $event)"></sdc-checkbox> + + <sdc-checkbox *ngIf="!folder.isGroup() && folder.dist" + [label]="folder.text" + [checked]="homeFilter.distributed.indexOf(folder.dist) !== -1" + (checkedChange)="changeCheckboxesFilter(homeFilter.distributed, folder.dist, $event)"></sdc-checkbox> + <span class="i-sdc-left-sidebar-item-state-count" [attr.data-tests-id]="'count-' + folder.state">{{entitiesCount(folder)}}</span> + </div> + </div> + + </div> + + <top-nav [topLvlSelectedIndex]="0" [version]="version" [searchTerm]="homeFilter.search.filterTerm" (searchTermChange)="changeFilterTerm($event)" [notificationIconCallback]="notificationIconCallback"></top-nav> + +</div> diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.less b/catalog-ui/src/app/ng2/pages/home/home.component.less new file mode 100644 index 0000000000..c5b73748ba --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/home.component.less @@ -0,0 +1,126 @@ +@import '../../../../assets/styles/mixins_old'; +@import '../../../../assets/styles/sprite'; +.w-sdc-left-sidebar-nav { + margin-top: 46px; +} + +.w-sdc-main-right-container { + height: 100%; + overflow-y: scroll; +} + +.w-sdc-main-right-container-element { + float: left; + height: 217px; + width: 217px; + margin: 10px; + position: relative; +} + +.w-sdc-main-right-container-element-details-container { + position: absolute; + top: 165px; + left: 50px; +} + +.w-sdc-main-right-container-element-name { + font-weight: bold; +} + +.i-sdc-left-sidebar-item{ + display: flex; + &.category-title .title-text, sdc-checkbox{ + flex-grow: 1; + } + &:not(.category-title).i-sdc-left-sidebar-item-state-count { + line-height: 14px; + } +} + + +//////////////////////////////Cards//////////////////// +.w-sdc-dashboard-card-new { + border: 2px dashed @color_m; + .border-radius(2px); + cursor: pointer; + display: inline-block; + height: 198px; + margin: 11px; + position: relative; + vertical-align: middle; + width: 202px; +} + +.w-sdc-dashboard-card-new-content { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + height: 100%; +} + +.w-sdc-dashboard-card-new-content-plus { + .sprite-new; + .add-icon; + position: relative; + margin-bottom: 20px; + + &:after { + .n_14_m; + content: 'ADD'; + position: absolute; + top: 25px; + left: -3px; + vertical-align: -50%; + } +} + +.w-sdc-dashboard-card-import-content-plus { + .sprite-new; + .import-icon; + position: relative; + margin-bottom: 20px; + + &:after { + .n_14_m; + content: 'IMPORT'; + position: absolute; + top: 25px; + left: -16px; + vertical-align: -50%; + } +} + +.sdc-dashboard-create-element-container, +.sdc-dashboard-import-element-container { + + width: 140px; + + sdc-button, + sdc-button-file-opener { + padding-bottom: 5px; + &:last-child{ + padding-bottom: 0; + } + } + + .import-file{ + position: relative; + file-opener{ + position: absolute; + top: 0; + /deep/ input[type="file"] { + .hand; + filter: alpha(opacity=0); + opacity: 0; + position: absolute; + top: 0; + left: 0; + width: 140px; + height: 36px; + } + } + } +} + + diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts b/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts new file mode 100644 index 0000000000..df854024fa --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts @@ -0,0 +1,270 @@ + +import { SdcConfigToken, ISdcConfig } from "../../config/sdc-config.config"; +import { SdcMenuToken, IAppMenu } from "../../config/sdc-menu.config"; + + +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { HomeComponent } from "./home.component"; +import {ConfigureFn, configureTests} from "../../../../jest/test-config.helper"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import { TranslateService } from "../../shared/translator/translate.service"; +import { HomeService, CacheService, AuthenticationService, ImportVSPService } from '../../../../app/services-ng2'; +import { ModalsHandler } from "../../../../app/utils"; +import { SdcUiServices } from "onap-ui-angular"; +import {ComponentType, ResourceType} from "../../../utils/constants"; +import { FoldersMenu, FoldersItemsMenu, FoldersItemsMenuGroup } from './folders'; +import { HomeFilter } from "../../../../app/models/home-filter"; +import {Component} from "../../../models/components/component"; + + + + +describe('home component', () => { + + // const mockedEvent = <MouseEvent>{ target: {} } + let fixture: ComponentFixture<HomeComponent>; + // let eventServiceMock: Partial<EventListenerService>; + + let importVspService: Partial<ImportVSPService>; + let mockStateService; + let modalServiceMock :Partial<SdcUiServices.ModalService>; + let translateServiceMock : Partial<TranslateService>; + let foldersItemsMenuMock; + let homeFilterMock :Partial<HomeFilter>; + let foldersMock; + let loaderServiceMock; + + + beforeEach( + async(() => { + modalServiceMock = { + openWarningModal: jest.fn() + } + + mockStateService = { + // go: jest.fn().mockReturnValue( new Promise.resolve((resolve, reject )=> resolve())) + go: jest.fn() + } + + translateServiceMock = { + translate: jest.fn() + } + + homeFilterMock = { + search: jest.fn, + toUrlParam: jest.fn() + } + + foldersMock = { + setSelected: jest.fn() + } + + loaderServiceMock = { + activate: jest.fn(), + deactivate: jest.fn() + } + + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [HomeComponent], + imports: [], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: SdcConfigToken, useValue: {"csarFileExtension":"csar", "toscaFileExtension":"yaml,yml"}}, + {provide: SdcMenuToken, useValue: {}}, + {provide: "$state", useValue: mockStateService}, + {provide: HomeService, useValue: {}}, + {provide: AuthenticationService, useValue: {}}, + {provide: CacheService, useValue: {}}, + {provide: TranslateService, useValue: translateServiceMock}, + {provide: ModalsHandler, useValue: {}}, + {provide: SdcUiServices.ModalService, useValue: modalServiceMock}, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock}, + {provide: ImportVSPService, useValue: {}} + ], + }); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(HomeComponent); + }); + }) + ); + + + it('should match current snapshot', () => { + expect(fixture).toMatchSnapshot(); + }); + + it('should call on home component openCreateModal with null imported file', () => { + const component = TestBed.createComponent(HomeComponent); + let componentType:string = 'test'; + let importedFile:any = null; + component.componentInstance.openCreateModal(componentType, importedFile); + expect(mockStateService.go).toBeCalledWith('workspace.general', {type: componentType.toLowerCase()}); + }); + + + it('should call on home component openCreateModal with imported file', () => { + const component = TestBed.createComponent(HomeComponent); + component.componentInstance.initEntities = jest.fn(); + let componentType:string = 'test'; + let importedFile:any = 'importedFile'; + component.componentInstance.openCreateModal(componentType, importedFile); + expect(component.componentInstance.initEntities).toBeCalledWith(true); + }); + + + it ('should call on home component onImportVf without file without extension', () => { + const component = TestBed.createComponent(HomeComponent); + let file:any = {filename : 'test'}; + let expectedTitle:string = translateServiceMock.translate("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE"); + let expectedMessage:string = translateServiceMock.translate("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS", {"csarFileExtension":"csar"}); + component.componentInstance.onImportVf(file); + expect(modalServiceMock.openWarningModal).toBeCalledWith(expectedTitle, expectedMessage , 'error-invalid-csar-ext'); + }); + + + it ('should call on home component onImportVf with file without extension' , () => { + const component = TestBed.createComponent(HomeComponent); + let file:any = {filename : 'test.csar'}; + component.componentInstance.onImportVf(file); + expect(mockStateService.go).toBeCalledWith('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VF + }); + }); + + + it ('should call on home component onImportVfc without file without extension', () => { + const component = TestBed.createComponent(HomeComponent); + let file:any = {filename : 'test'}; + let expectedTitle:string = translateServiceMock.translate("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE"); + let expectedMessage:string = translateServiceMock.translate("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS", {"toscaFileExtension":"yaml,yml"}); + component.componentInstance.onImportVfc(file); + expect(modalServiceMock.openWarningModal).toBeCalledWith(expectedTitle, expectedMessage , 'error-invalid-tosca-ext'); + }); + + it ('should call on home component onImportVfc with file without extension' , () => { + const component = TestBed.createComponent(HomeComponent); + let file:any = {filename : 'test.yml'}; + component.componentInstance.onImportVfc(file); + expect(mockStateService.go).toBeCalledWith('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VFC + }); + }); + + it ('should call on home component createPNF' , () => { + const component = TestBed.createComponent(HomeComponent); + component.componentInstance.createPNF(); + expect(mockStateService.go).toBeCalledWith('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + resourceType: ResourceType.PNF + }); + }); + + it ('should call on home component createCR' , () => { + const component = TestBed.createComponent(HomeComponent); + component.componentInstance.createCR(); + expect(mockStateService.go).toBeCalledWith('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + resourceType: ResourceType.CR + }); + }); + + + it ('should call on home component updateFilter' , () => { + const component = TestBed.createComponent(HomeComponent); + component.componentInstance.homeFilter = homeFilterMock; + component.componentInstance.filterHomeItems = jest.fn(); + component.componentInstance.updateFilter(); + + expect(mockStateService.go).toBeCalledWith('.', homeFilterMock.toUrlParam(), {location: 'replace', notify: false}); + // expect(spy).toHaveBeenCalledTimes(1); + + // let spy = spyOn(homeFilterMock, 'toUrlParam').and.returnValue({ + // 'filter.term': '', + // 'filter.distributed': '', + // 'filter.status':'' + // }); + }); + + // it ('should call on home component setSelectedFolder' , () => { + // const component = TestBed.createComponent(HomeComponent); + // let folderItem:Partial<FoldersItemsMenu> = { text:'someThing'}; + // let folderItem1:number; + // component.componentInstance.folders = foldersMock; + // expect(foldersMock.setSelected).toBeCalledWith(folderItem); + // }); + + // it ('should call on home component goToComponent' , () => { + // const component = TestBed.createComponent(HomeComponent); + // let componentParam:Partial<Component> = { uuid:'someThing', uniqueId:'uniqueID', componentType:'componentType'}; + // component.componentInstance.goToComponent(componentParam); + // expect(loaderServiceMock.activate).toHaveBeenCalled(); + // // expect(mockStateService.go).toBeCalledWith('workspace.general', {id: componentParam.uniqueId, type: componentParam.componentType.toLowerCase()}).then(function(){ + // // loaderServiceMock.deactivate(); + // // }); + // expect(mockStateService.go).toBeCalled(); + // }); + + // it ('should call on home component raiseNumberOfElementToDisplay so numberOfItemToDisplay will be 0' , () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.raiseNumberOfElementToDisplay(); + // expect(component.componentInstance.numberOfItemToDisplay).toEqual(0); + // }); + // + // it ('should call on home component raiseNumberOfElementToDisplay with min(2,70) so numberOfItemToDisplay will be 2' , () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.homeItems = ['item1', 'item2']; + // component.componentInstance.numberOfItemToDisplay = 70; + // component.componentInstance.raiseNumberOfElementToDisplay(true); + // expect(component.componentInstance.numberOfItemToDisplay).toEqual(2); + // }); + // + // it ('should call on home component raiseNumberOfElementToDisplay with min(3,35) so numberOfItemToDisplay will be 2 after fullPagesAmount is calculated' , () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.homeItems = ['item1', 'item2', 'item3']; + // component.componentInstance.numberOfItemToDisplay = 70; + // component.componentInstance.numberOfItemToDisplay = 0; + // component.componentInstance.raiseNumberOfElementToDisplay(false); + // expect(component.componentInstance.numberOfItemToDisplay).toEqual(3); + // }); + // + // + // it ('should call on home component changeFilterTerm' , () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.changeFilterTerm("testStr"); + // // expect ( "testStr" ).toEqual(homeFilterMock.search.) + // }); + + + + + + + // it ('should call on home component entitiesCount' , () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.entitiesCount("aaa"); + // expect(mockStateService.go).toBeCalledWith('workspace.general', { + // type: ComponentType.RESOURCE.toLowerCase(), + // resourceType: ResourceType.CR + // }); + // }); + + + // it('should call on home component notificationIconCallback', () => { + // const component = TestBed.createComponent(HomeComponent); + // component.componentInstance.initEntities = jest.fn(); + // component.componentInstance.notificationIconCallback(); + // expect(mockStateService.go).toBeCalledWith('workspace.general', {}); + // }); + + + + + +});
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.ts b/catalog-ui/src/app/ng2/pages/home/home.component.ts new file mode 100644 index 0000000000..1b69eba929 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/home.component.ts @@ -0,0 +1,358 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +'use strict'; +import { Component as NgComponent, Inject, OnInit } from '@angular/core'; +import { Component, IConfigRoles, IUserProperties, Resource } from 'app/models'; +import { HomeFilter } from 'app/models/home-filter'; +import { AuthenticationService, CacheService, HomeService } from 'app/services-ng2'; +import { ModalsHandler } from 'app/utils'; +import { SdcUiServices } from 'onap-ui-angular'; +import { CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, ResourceType } from '../../../utils/constants'; +import { ImportVSPService } from '../../components/modals/onboarding-modal/import-vsp.service'; +import { ISdcConfig, SdcConfigToken } from '../../config/sdc-config.config'; +import { IAppMenu, SdcMenuToken } from '../../config/sdc-menu.config'; +import { EntityFilterPipe } from '../../pipes/entity-filter.pipe'; +import { TranslateService } from '../../shared/translator/translate.service'; +import { FoldersItemsMenu, FoldersItemsMenuGroup, FoldersMenu } from './folders'; + +@NgComponent({ + selector: 'home-page', + templateUrl: './home.component.html', + styleUrls: ['./home.component.less'] +}) +export class HomeComponent implements OnInit { + public numberOfItemToDisplay: number; + public homeItems: Component[]; + public homeFilteredItems: Component[]; + public homeFilteredSlicedItems: Component[]; + public folders: FoldersMenu; + public roles: IConfigRoles; + public user: IUserProperties; + public showTutorial: boolean; + public isFirstTime: boolean; + public version: string; + public homeFilter: HomeFilter; + public vfcmtType: string; + public displayActions: boolean; + + constructor( + @Inject(SdcConfigToken) private sdcConfig: ISdcConfig, + @Inject(SdcMenuToken) public sdcMenu: IAppMenu, + @Inject('$state') private $state: ng.ui.IStateService, + private homeService: HomeService, + private authService: AuthenticationService, + private cacheService: CacheService, + private translateService: TranslateService, + private modalsHandler: ModalsHandler, + private modalService: SdcUiServices.ModalService, + private loaderService: SdcUiServices.LoaderService, + private importVSPService: ImportVSPService + ) {} + + ngOnInit(): void { + this.initHomeComponentVars(); + this.initFolders(); + this.initEntities(); + + if (this.$state.params) { + if (this.$state.params.folder) { + const folderName = this.$state.params.folder.replaceAll('_', ' '); + + const selectedFolder = this.folders.getFolders().find((tmpFolder: FoldersItemsMenu) => tmpFolder.text === folderName); + if (selectedFolder) { + this.setSelectedFolder(selectedFolder); + } + // Show the tutorial if needed when the dashboard page is opened.<script src="bower_components/angular-filter/dist/angular-filter.min.js"></script> + // This is called from the welcome page. + } else if (this.$state.params.show === 'tutorial') { + this.showTutorial = true; + this.isFirstTime = true; + } + } + } + + // Open onboarding modal + public notificationIconCallback(): void { + this.importVSPService.openOnboardingModal().subscribe((result) => { + if (!result.previousComponent || result.previousComponent.csarVersion !== result.componentCsar.csarVersion) { + this.cacheService.set(CHANGE_COMPONENT_CSAR_VERSION_FLAG, result.componentCsar.csarVersion); + } + this.$state.go('workspace.general', { + id: result.previousComponent && result.previousComponent.uniqueId, + componentCsar: result.componentCsar, + type: result.type + }); + }); + } + + public onImportVf(file: any): void { + if (file && file.filename) { + // Check that the file has valid extension. + const fileExtension: string = file.filename.split('.').pop(); + if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VF + }); + } else { + const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE'); + const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS', {extensions: this.sdcConfig.csarFileExtension}); + this.modalService.openWarningModal(title, message, 'error-invalid-csar-ext'); + } + } + } + + public onImportVfc(file: any): void { + if (file && file.filename) { + // Check that the file has valid extension. + const fileExtension: string = file.filename.split('.').pop(); + if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VFC + }); + } else { + const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE'); + const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS', {extensions: this.sdcConfig.toscaFileExtension}); + this.modalService.openWarningModal(title, message, 'error-invalid-tosca-ext'); + } + } + } + + public openCreateModal(componentType: string, importedFile: any): void { + if (importedFile) { + this.initEntities(true); // Return from import + } else { + this.$state.go('workspace.general', {type: componentType.toLowerCase()}); + } + } + + public createPNF(): void { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + resourceType: ResourceType.PNF + }); + } + + public createCR(): void { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + resourceType: ResourceType.CR + }); + } + + public entitiesCount(folderItem: FoldersItemsMenu): any { + let total: number = 0; + if (folderItem.isGroup()) { + this.folders.getFolders().forEach((tmpFolder: FoldersItemsMenu) => { + if (tmpFolder.group && tmpFolder.group === (folderItem as FoldersItemsMenuGroup).groupname) { + total = total + this._getTotalCounts(tmpFolder); + } + }); + } else { + total = total + this._getTotalCounts(folderItem); + } + return total; + } + + public updateFilter = () => { + this.$state.go('.', this.homeFilter.toUrlParam(), {location: 'replace', notify: false}); + this.filterHomeItems(); + } + + public getCurrentFolderDistributed(): any[] { + const states = []; + if (this.folders) { + const folderItem: FoldersItemsMenu = this.folders.getCurrentFolder(); + if (folderItem.isGroup()) { + this.folders.getFolders().forEach((tmpFolder: FoldersItemsMenu) => { + if (tmpFolder.group && tmpFolder.group === (folderItem as FoldersItemsMenuGroup).groupname) { + this._setStates(tmpFolder, states); + } + }); + } else { + this._setStates(folderItem, states); + } + } + return states; + } + + public setSelectedFolder(folderItem: FoldersItemsMenu): void { + this.folders.setSelected(folderItem); + } + + public goToComponent(component: Component): void { + const loaderService = this.loaderService; + loaderService.activate(); + this.$state.go('workspace.general', {id: component.uniqueId, type: component.componentType.toLowerCase()}).then(() => { + loaderService.deactivate(); + }); + } + + public raiseNumberOfElementToDisplay(recalculate: boolean = false) { + const scrollPageAmount = 35; + if (!this.homeItems) { + this.numberOfItemToDisplay = 0; + } else if (this.homeItems.length > this.numberOfItemToDisplay || recalculate) { + let fullPagesAmount = Math.ceil(this.numberOfItemToDisplay / scrollPageAmount) * scrollPageAmount; + if (!recalculate || fullPagesAmount === 0) { // TODO trigger infiniteScroll to check bottom and fire onBottomHit by itself (sdc-ui) + fullPagesAmount += scrollPageAmount; + } + this.numberOfItemToDisplay = Math.min(this.homeItems.length, fullPagesAmount); + this.homeFilteredSlicedItems = this.homeFilteredItems.slice(0, this.numberOfItemToDisplay); + } + } + + public changeCheckboxesFilter(checkboxesFilterArray: string[], checkboxValue: string, checked?: boolean) { + const checkboxIdx = checkboxesFilterArray.indexOf(checkboxValue); + + checked = (checked !== undefined) ? checked : checkboxIdx === -1; + if (checked && checkboxIdx === -1) { + checkboxesFilterArray.push(checkboxValue); + } else if (!checked && checkboxIdx !== -1) { + checkboxesFilterArray.splice(checkboxIdx, 1); + } + this.updateFilter(); + } + + public changeFilterTerm(filterTerm: string): void { + this.homeFilter.search = { filterTerm }; + this.updateFilter(); + } + + public setDisplayActions(display?: boolean) { + this.displayActions = display !== undefined ? display : !this.displayActions; + } + + private _getTotalCounts(tmpFolder): number { + let total: number = 0; + if (tmpFolder.dist !== undefined) { + const distributions = tmpFolder.dist.split(','); + distributions.forEach((item: any) => { + total = total + this.getEntitiesByStateDist(tmpFolder.state, item).length; + }); + } else { + total = total + this.getEntitiesByStateDist(tmpFolder.state, tmpFolder.dist).length; + } + return total; + } + + private _setStates(tmpFolder, states) { + if (tmpFolder.states !== undefined) { + tmpFolder.states.forEach((item: any) => { + states.push({state: item.state, dist: item.dist}); + }); + } else { + states.push({state: tmpFolder.state, dist: tmpFolder.dist}); + } + } + + private initEntities(reload?: boolean) { + if (reload || this.componentShouldReload()) { + this.loaderService.activate(); + this.homeService.getAllComponents(true).subscribe( + (components: Component[]) => { + this.cacheService.set('breadcrumbsComponentsState', this.$state.current.name); // dashboard + this.cacheService.set('breadcrumbsComponents', components); + this.homeItems = components; + this.loaderService.deactivate(); + this.filterHomeItems(); + }, (error) => { this.loaderService.deactivate(); }); + } else { + this.homeItems = this.cacheService.get('breadcrumbsComponents'); + this.filterHomeItems(); + } + } + + private isDefaultFilter = (): boolean => { + const defaultFilter = new HomeFilter(); + return angular.equals(defaultFilter, this.homeFilter); + } + + private componentShouldReload = (): boolean => { + const breadcrumbsValid: boolean = (this.$state.current.name === this.cacheService.get('breadcrumbsComponentsState') && this.cacheService.contains('breadcrumbsComponents')); + return !breadcrumbsValid || this.isDefaultFilter(); + } + + private getEntitiesByStateDist(state: string, dist: string): Component[] { + let gObj: Component[]; + if (this.homeItems && (state || dist)) { + gObj = this.homeItems.filter((obj: Component) => { + if (dist !== undefined && obj.distributionStatus === dist && obj.lifecycleState === state) { + return true; + } else if (dist === undefined && (obj.lifecycleState === state || obj.distributionStatus === state)) { + return true; + } + return false; + }); + } else { + gObj = []; + } + return gObj; + } + + private filterHomeItems() { + this.homeFilteredItems = this.makeFilteredItems(this.homeItems, this.homeFilter); + this.raiseNumberOfElementToDisplay(true); + this.homeFilteredSlicedItems = this.homeFilteredItems.slice(0, this.numberOfItemToDisplay); + } + + private makeFilteredItems(homeItems: Component[], filter: HomeFilter) { + let filteredComponents: Component[] = homeItems; + + // filter: exclude all resources of type 'vfcmtType': + filteredComponents = filteredComponents.filter((c) => + !c.isResource() || (c as Resource).resourceType.indexOf(this.vfcmtType) === -1); + + // common entity filter + // -------------------------------------------------------------------------- + filteredComponents = EntityFilterPipe.transform(filteredComponents, filter); + + return filteredComponents; + } + + private initFolders = (): void => { + // Note: Do not use SdcUi.ChecklistComponent for folders checkboxes, since from the data structure + // it is not determined that all checkboxes under the same group are managed by the same selectedValues array. + if (this.user) { + this.folders = new FoldersMenu(this.roles[this.user.role].folder); + } + } + + private initHomeComponentVars(): void { + this.version = this.cacheService.get('version'); + this.numberOfItemToDisplay = 0; + this.displayActions = false; + this.user = this.authService.getLoggedinUser(); + this.roles = this.sdcMenu.roles; + this.showTutorial = false; + this.isFirstTime = false; + this.vfcmtType = ResourceType.VFCMT; + + // Checkboxes filter init + this.homeFilter = new HomeFilter(this.$state.params); + + // bind callbacks that are transferred as inputs + this.notificationIconCallback = this.notificationIconCallback.bind(this); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/home/home.module.ts b/catalog-ui/src/app/ng2/pages/home/home.module.ts new file mode 100644 index 0000000000..3e7c0cd312 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/home/home.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { HomeComponent } from "./home.component"; +import { LayoutModule } from "../../components/layout/layout.module"; +import { UiElementsModule } from "../../components/ui/ui-elements.module"; +import { GlobalPipesModule } from "../../pipes/global-pipes.module"; +import { TranslateModule } from "../../shared/translator/translate.module"; +import { SdcUiComponentsModule } from "onap-ui-angular"; + +@NgModule({ + declarations: [ + HomeComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + LayoutModule, + UiElementsModule, + GlobalPipesModule, + TranslateModule + ], + exports: [ + HomeComponent + ], + entryComponents: [ + HomeComponent + ], + providers: [] +}) +export class HomeModule { +} |