diff options
Diffstat (limited to 'catalog-ui/src/app/ng2')
136 files changed, 3978 insertions, 411 deletions
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index c3cd06043b..c949a73248 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -18,51 +18,61 @@ * ============LICENSE_END========================================================= */ -import {BrowserModule} from '@angular/platform-browser'; -import {NgModule, APP_INITIALIZER} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {forwardRef} from '@angular/core'; -import {AppComponent} from './app.component'; -import {UpgradeAdapter} from '@angular/upgrade'; -import {UpgradeModule} from '@angular/upgrade/static'; -import {PropertiesAssignmentModule} from './pages/properties-assignment/properties-assignment.module'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { forwardRef } from '@angular/core'; +import { AppComponent } from './app.component'; +import { UpgradeAdapter } from '@angular/upgrade'; +import { UpgradeModule } from '@angular/upgrade/static'; +import { SdcUiComponentsModule, SdcUiComponents } from "sdc-ui/lib/angular"; +import { PropertiesAssignmentModule } from './pages/properties-assignment/properties-assignment.module'; import { DataTypesServiceProvider, SharingServiceProvider, CookieServiceProvider, StateServiceFactory, StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory, - NotificationServiceProvider + NotificationServiceProvider, ComponentFactoryProvider } from "./utils/ng1-upgraded-provider"; -import {ConfigService} from "./services/config.service"; -import {HttpModule} from '@angular/http'; -import {HttpService} from './services/http.service'; -import {AuthenticationService} from './services/authentication.service'; -import {Cookie2Service} from "./services/cookie.service"; -import {ComponentServiceNg2} from "./services/component-services/component.service"; -import {ComponentServiceFactoryNg2} from "./services/component-services/component.service.factory"; -import {ServiceServiceNg2} from "./services/component-services/service.service"; -import {ComponentInstanceServiceNg2} from "./services/component-instance-services/component-instance.service"; -import {ModalService} from "./services/modal.service"; -import {UiElementsModule} from "./components/ui/ui-elements.module"; -import {ConnectionWizardModule} from "./pages/connection-wizard/connection-wizard.module"; +import { ConfigService } from "./services/config.service"; +import { HttpModule } from '@angular/http'; +import { HttpService } from './services/http.service'; +import { AuthenticationService } from './services/authentication.service'; +import { Cookie2Service } from "./services/cookie.service"; +import { ComponentServiceNg2 } from "./services/component-services/component.service"; +import { ComponentServiceFactoryNg2 } from "./services/component-services/component.service.factory"; +import { ServiceServiceNg2 } from "./services/component-services/service.service"; +import { ComponentInstanceServiceNg2 } from "./services/component-instance-services/component-instance.service"; +import { ModalService } from "./services/modal.service"; +import { UiElementsModule } from "./components/ui/ui-elements.module"; +import { ConnectionWizardModule } from "./pages/connection-wizard/connection-wizard.module"; import {InterfaceOperationModule} from "./pages/interface-operation/interface-operation.module"; import {OperationCreatorModule} from "./pages/interface-operation/operation-creator/operation-creator.module"; -import {LayoutModule} from "./components/layout/layout.module"; -import {UserService} from "./services/user.service"; -import {PoliciesService} from "./services/policies.service"; -import {DynamicComponentService} from "./services/dynamic-component.service"; -import {SdcConfig} from "./config/sdc-config.config"; +import { LayoutModule } from "./components/layout/layout.module"; +import { UserService } from "./services/user.service"; +import { DynamicComponentService } from "./services/dynamic-component.service"; +import { SdcConfig } from "./config/sdc-config.config"; +import { SdcMenu } from "./config/sdc-menu.config"; import { TranslateModule } from "./shared/translator/translate.module"; import { TranslationServiceConfig } from "./config/translation.service.config"; -import {ServicePathCreatorModule} from './pages/service-path-creator/service-path-creator.module'; -import {ServicePathsListModule} from './pages/service-paths-list/service-paths-list.module'; +import { MultilineEllipsisModule } from "./shared/multiline-ellipsis/multiline-ellipsis.module"; +import { ServicePathCreatorModule } from './pages/service-path-creator/service-path-creator.module'; +import { ServicePathsListModule } from './pages/service-paths-list/service-paths-list.module'; +import { ServicePathModule } from 'app/ng2/components/logic/service-path/service-path.module'; +import { ServicePathSelectorModule } from 'app/ng2/components/logic/service-path-selector/service-path-selector.module'; +import { CompositionPanelModule } from 'app/ng2/pages/composition/panel/panel.module'; +import { WindowRef } from "./services/window.service"; +import {ArchiveService} from "./services/archive.service"; +import { ModalsHandlerProvider } from './utils/ng1-upgraded-provider'; import {PluginFrameModule} from "./components/ui/plugin/plugin-frame.module"; import {PluginsService} from "./services/plugins.service"; import {EventBusService} from "./services/event-bus.service"; -import {ServicePathModule} from 'app/ng2/components/logic/service-path/service-path.module'; -import {ServicePathSelectorModule} from 'app/ng2/components/logic/service-path-selector/service-path-selector.module'; +import {GroupsService} from "./services/groups.service"; +import {PoliciesService} from "./services/policies.service"; +import {AutomatedUpgradeService} from "./pages/automated-upgrade/automated-upgrade.service"; +import {AutomatedUpgradeModule} from "./pages/automated-upgrade/automated-upgrade.module"; export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule)); -export function configServiceFactory(config:ConfigService) { +export function configServiceFactory(config: ConfigService) { return () => { return Promise.all([ config.loadValidationConfiguration(), @@ -83,8 +93,11 @@ export function configServiceFactory(config:ConfigService) { HttpModule, LayoutModule, TranslateModule, + MultilineEllipsisModule, UiElementsModule, - + CompositionPanelModule, + SdcUiComponentsModule, + AutomatedUpgradeModule, //We need to import them here since we use them in angular1 ConnectionWizardModule, PropertiesAssignmentModule, @@ -97,10 +110,15 @@ export function configServiceFactory(config:ConfigService) { ServicePathSelectorModule ], exports: [], - entryComponents: [], + entryComponents: [ + // *** sdc-ui components to be used as downgraded: + // SdcUiComponents.ButtonComponent + ], providers: [ + WindowRef, DataTypesServiceProvider, SharingServiceProvider, + ComponentFactoryProvider, CookieServiceProvider, StateServiceFactory, StateParamsServiceFactory, @@ -108,6 +126,7 @@ export function configServiceFactory(config:ConfigService) { CacheServiceProvider, EventListenerServiceProvider, NotificationServiceProvider, + ModalsHandlerProvider, AuthenticationService, Cookie2Service, ConfigService, @@ -115,14 +134,18 @@ export function configServiceFactory(config:ConfigService) { ComponentServiceFactoryNg2, ModalService, ServiceServiceNg2, + AutomatedUpgradeService, HttpService, UserService, PoliciesService, + GroupsService, DynamicComponentService, SdcConfig, + SdcMenu, ComponentInstanceServiceNg2, TranslationServiceConfig, PluginsService, + ArchiveService, EventBusService, { provide: APP_INITIALIZER, @@ -130,13 +153,13 @@ export function configServiceFactory(config:ConfigService) { deps: [ConfigService], multi: true }, - ], + ], bootstrap: [AppComponent] }) export class AppModule { + constructor(public upgrade: UpgradeModule, public eventBusService:EventBusService) { - constructor(public upgrade:UpgradeModule, eventBusService:EventBusService) { } } diff --git a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.html b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.html index 55c4bf0460..78f311112e 100644 --- a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.html +++ b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.html @@ -20,7 +20,7 @@ <li [ngClass]="{'selected': $last }"> <a (click)="menuItemClick(groupItem, groupItem.menuItems[groupItem.selectedIndex])" [attr.data-tests-id]="'breadcrumbs-button-' + $index"> - {{groupItem.menuItems[groupItem.selectedIndex].text}} + {{groupItem.menuItems[groupItem.selectedIndex]?.text}} </a> </li> <li class="triangle-dropdown" diff --git a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts index a0b6b2b543..881a91613d 100644 --- a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts +++ b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts @@ -41,6 +41,8 @@ export class TopNavComponent { @Input() public hideSearch:boolean; @Input() public searchTerm:string; @Input() public notificationIconCallback:Function; + @Input() public unsavedChanges: boolean; + @Input() public unsavedChangesCallback: (completeCallback:Function)=> Promise<any>; @Output() public searchTermChange:EventEmitter<string> = new EventEmitter<string>(); emitSearchTerm(event:string) { this.searchTermChange.emit(event); @@ -80,17 +82,21 @@ export class TopNavComponent { return true; }); - //if it's a different state , checking previous state param + //if it's a different state if (result === -1) { - this.topLvlMenu.menuItems.forEach((item:MenuItem, index:number)=> { - if (item.state === this.$state.params['previousState']) { - result = index; - } - }); - } + //if in 'workspace' - checking previous state param + if (this.$state.includes('workspace')) { + // if previous state is 'dashboard' or 'catalog', then select it - otherwise, use 'catalog' as default for 'workspace' + const selectedStateName = (['dashboard', 'catalog'].indexOf(this.$state.params['previousState']) !== -1) + ? this.$state.params['previousState'] + : 'catalog'; + result = this.topLvlMenu.menuItems.findIndex((item:MenuItem) => item.state === selectedStateName); + } - if (result === -1) { - result = 0; + //if yet, none is selected, then select the first as default + if (result === -1) { + result = 0; + } } return result; @@ -151,8 +157,21 @@ export class TopNavComponent { } menuItemClick(itemGroup:MenuItemGroup, item:MenuItem) { - itemGroup.itemClick = false; + let onSuccessFunction = () => { + this.navigate(itemGroup, item); + } + if (this.unsavedChanges && this.unsavedChangesCallback){ + this.unsavedChangesCallback(onSuccessFunction).then((onSuccess)=> { + this.navigate(itemGroup, item); + }, (onReject) => {}); + } else { + this.navigate(itemGroup, item); + } + } + + navigate(itemGroup:MenuItemGroup, item:MenuItem) { + itemGroup.itemClick = false; let onSuccess = ():void => { itemGroup.selectedIndex = itemGroup.menuItems.indexOf(item); }; @@ -165,4 +184,5 @@ export class TopNavComponent { this[item.action](item.state, item.params).then(onSuccess, onFailed); } } + } diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-display-options.ts b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-display-options.ts index c8d4566653..36257ca94e 100644 --- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-display-options.ts +++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-display-options.ts @@ -23,10 +23,16 @@ export class HierarchyDisplayOptions { valueProperty: string; childrenProperty: string; searchText:string; - constructor(idProperty:string, valueProperty:string, childrenProperty?:string, searchText?:string) { + archived:boolean; + + iconProperty: string; + constructor(idProperty:string, valueProperty:string, childrenProperty?:string, searchText?:string, iconProperty?:string, archived?:boolean) { + this.idProperty = idProperty; this.valueProperty = valueProperty; this.childrenProperty = childrenProperty; this.searchText = searchText; + this.archived = archived; + this.iconProperty = iconProperty; } } diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.html b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.html index c3f9e5ac74..aa60337f84 100644 --- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.html +++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.html @@ -1,7 +1,12 @@ <div class="navigation-wrapper"> <div class="node-item" *ngFor="let item of displayData" (click)="onClick($event, item)"> <div class="node-data-wrapper" [ngClass]="{'selected': selectedItem && selectedItem === item[displayOptions.idProperty]}"> - <span class="node-data" [ngClass]="{'mark':item[displayOptions.valueProperty] === displayOptions.searchText}" [attr.data-tests-id]="item[displayOptions.valueProperty]">{{item[displayOptions.valueProperty]}}</span> + <span class="node-data" [ngClass]="{'mark':item[displayOptions.valueProperty] === displayOptions.searchText}" [attr.data-tests-id]="item[displayOptions.valueProperty]"> + + <span *ngIf="displayOptions.iconProperty" [ngClass]="['node-data-icon', item[displayOptions.iconProperty], 'small']"></span> + {{item[displayOptions.valueProperty]}} + <span class="sprite-new archive-label" *ngIf="item.originArchived"></span> + </span> </div> <div class="children-node" *ngIf="item[displayOptions.childrenProperty]"> <hierarchy-navigation class="children-hierarchy" [displayData]="item[displayOptions.childrenProperty]" diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.less b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.less index 4befa2c797..33ffb49537 100644 --- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.less +++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.less @@ -1,3 +1,5 @@ +@import './../../../../../assets/styles/mixins.less'; + .navigation-wrapper { text-align: left; } @@ -30,6 +32,25 @@ .node-data { margin-left: 10px; margin-right: 10px; + align-items: center; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 300px; + .archive-label{ + margin-left: 5px; + } +} + +.node-data-icon { + vertical-align: middle; + margin-right: 7px; + + &.defaulticon.small { + background-color: #999; + border-radius: 14px; + } } .node-data-wrapper.selected { @@ -50,3 +71,20 @@ +.icon-group { + .square-icon(); + background-color: @main_color_a; + + &::before { + content: "G"; + } +} +.icon-policy { + .square-icon(); + background-color: @main_color_r; + + &::before { + content: "P"; + } +} + diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html index b7cde7eb23..daffc9efea 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html @@ -17,8 +17,8 @@ tooltip="{{input.description}}" tooltipDelay="0"></span> </div> <div class="table-cell col3"> - <div class="inner-cell-div" tooltip="{{instanceNamesMap[input.instanceUniqueId]}}"> - <span>{{instanceNamesMap[input.instanceUniqueId]}}</span> + <div class="inner-cell-div" tooltip="{{instanceNamesMap[input.instanceUniqueId]?.name}}"> + <span>{{instanceNamesMap[input.instanceUniqueId]?.name}}</span> </div> </div> <div class="table-cell col2"> @@ -34,7 +34,8 @@ [type]="input.type" [name]="input.name" (elementChanged)="onInputChanged(input, $event)" - [readonly]="readonly"> + [readonly]="readonly" + [testId]="'input-' + input.name"> </dynamic-element> <div class="delete-button-container"> <span *ngIf="input.instanceUniqueId && !readonly" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span> diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less index d709f3f0c5..5fbb62f7fb 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less @@ -85,8 +85,8 @@ border-right:#d2d2d2 solid 1px; } &.col1 { - flex: 1 0 210px; - max-width:210px; + flex: 1 0 120px; + max-width:120px; display: flex; justify-content: space-between; @@ -112,14 +112,7 @@ } &.valueCol { - flex: 1 0 auto; - min-width: 350px; - display: flex; - justify-content: flex-end; - padding: 0px; - align-items: center; - - .value-input { + .value-input { flex: 1; border: none; background-color: inherit; @@ -142,7 +135,7 @@ padding: 0px; .delete-button-container { - padding: 3px 5px 0 0 ; + padding: 0 8px 0 0 ; } } } diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts index ebecbc9390..0c7fc2a24c 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts @@ -24,6 +24,7 @@ import {Component, Input, Output, EventEmitter} from "@angular/core"; import {InputFEModel} from "app/models"; import {ModalService} from "../../../services/modal.service"; +import { InstanceFeDetails } from "app/models/instance-fe-details"; @Component({ selector: 'inputs-table', @@ -33,7 +34,7 @@ import {ModalService} from "../../../services/modal.service"; export class InputsTableComponent { @Input() inputs: Array<InputFEModel>; - @Input() instanceNamesMap: Map<string, string>; + @Input() instanceNamesMap: Map<string, InstanceFeDetails>; @Input() readonly:boolean; @Input() isLoading:boolean; @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>(); diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html index 4805875d83..b5ae7a8f66 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html @@ -19,6 +19,7 @@ [name]="property.name" (elementChanged)="mapKeyChanged.emit($event.value)" [readonly]="readonly" + [testId]="'prop-key-' + propertyTestsId" ></dynamic-element> </div> </ng-container> @@ -27,12 +28,13 @@ <div class="table-cell"> <dynamic-element class="value-input" pattern="validationUtils.getValidationPattern(property.type)" - [value]="property.valueObj" + [value]="property.isDeclared ? property.value : property.valueObj" [type]="property.isDeclared ? 'string' : property.type" [name]="property.name" [path]="property.propertiesName" (elementChanged)="onElementChanged($event)" [readonly]="readonly || property.isDeclared || property.isDisabled" + [testId]="'prop-' + propertyTestsId" ></dynamic-element> </div> </ng-container> @@ -45,9 +47,9 @@ </ng-container> <!-- ICONS: add, delete, and expand --> <ng-container *ngIf="!property.isDeclared"> - <a *ngIf="(propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && !property.isChildOfListOrMap" class="property-icon add-item" (click)="createNewChildProperty();" [ngClass]="{'disabled':readonly || preventInsertItem(property)}">Add value to list</a> - <span *ngIf="property.isChildOfListOrMap" (click)="deleteItem.emit(property);" class="property-icon sprite-new delete-item-icon" [ngClass]="{'disabled':readonly}"></span> - <span *ngIf="!isPropertyFEModel && (propType == derivedPropertyTypes.COMPLEX || ((propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && hasChildren))" (click)="expandChildById(propPath)" class="property-icon sprite-new round-expand-icon" [class.open]="expandedChildId.indexOf(propPath) == 0"></span> + <a *ngIf="(propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && !property.isChildOfListOrMap" class="property-icon add-item" (click)="createNewChildProperty();" [ngClass]="{'disabled':readonly || preventInsertItem(property)}" [attr.data-tests-id]="'add-to-list-' + propertyTestsId">Add value to list</a> + <span *ngIf="property.isChildOfListOrMap" (click)="deleteItem.emit(property);" class="property-icon sprite-new delete-item-icon" [ngClass]="{'disabled':readonly}" [attr.data-tests-id]="'delete-from-list-' + propertyTestsId"></span> + <span *ngIf="!isPropertyFEModel && (propType == derivedPropertyTypes.COMPLEX || ((propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && hasChildren))" (click)="expandChildById(propPath)" class="property-icon sprite-new round-expand-icon" [class.open]="expandedChildId.indexOf(propPath) == 0" [attr.data-tests-id]="'expand-' + propertyTestsId"></span> </ng-container> </div> @@ -59,6 +61,7 @@ [hasDeclareOption]="hasDeclareOption" [canBeDeclared]="hasDeclareOption && prop.canBeDeclared" [property]="prop" + [rootProperty]="rootProperty || property" [expandedChildId]="expandedChildId" [propertyNameSearchText]="propertyNameSearchText" [readonly]="readonly" diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts index 6f7e57b643..6e19c95003 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts @@ -41,6 +41,7 @@ export class DynamicPropertyComponent { propPath: string; isPropertyFEModel: boolean; nestedLevel: number; + propertyTestsId: string; @Input() canBeDeclared: boolean; @Input() property: PropertyFEModel | DerivedFEProperty; @@ -50,6 +51,7 @@ export class DynamicPropertyComponent { @Input() readonly: boolean; @Input() hasChildren: boolean; @Input() hasDeclareOption:boolean; + @Input() rootProperty: PropertyFEModel; @Output('propertyChanged') emitter: EventEmitter<void> = new EventEmitter<void>(); @Output() expandChild: EventEmitter<string> = new EventEmitter<string>(); @@ -69,6 +71,8 @@ export class DynamicPropertyComponent { this.propType = this.property.derivedDataType; this.propPath = (this.property instanceof PropertyFEModel) ? this.property.name : this.property.propertiesName; this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length; + this.rootProperty = (this.rootProperty) ? this.rootProperty : <PropertyFEModel>this.property; + this.propertyTestsId = this.getPropertyTestsId(); } ngDoCheck() { @@ -105,6 +109,10 @@ export class DynamicPropertyComponent { }).length > 1; } + getPropertyTestsId = () => { + return [this.rootProperty.name].concat(this.rootProperty.getParentNamesArray(this.property.propertiesName, [], true)).join('.'); + }; + onElementChanged = (event: IUiElementChangeEvent) => { this.property.updateValueObj(event.value, event.isValid); this.emitter.emit(); diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html index ecfa7e7c5e..b574b552ae 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html @@ -9,8 +9,14 @@ <div class="table-body" [ngClass]="{'view-mode': readonly}"> <div class="no-data" *ngIf="!fePropertiesMap || !(fePropertiesMap | keys).length">No data to display</div> - <ng-container *ngFor="let instanceId of fePropertiesMap | keys; trackBy:instanceId"> - <div class="table-rows-header white-sub-header" *ngIf="feInstanceNamesMap">{{feInstanceNamesMap[instanceId]}}</div> + <ng-container *ngFor="let instanceId of fePropertiesMap | keys; trackBy:vspId"> + <div class="table-rows-header white-sub-header" *ngIf="feInstanceNamesMap"> + + + <span [ngClass]="['prop-instance-icon', feInstanceNamesMap[instanceId].iconClass, 'small']"></span> + {{feInstanceNamesMap[instanceId].name}} + <div class="sprite-new archive-label" *ngIf="feInstanceNamesMap[instanceId].originArchived == true" ></div> + </div> <div class="table-row" *ngFor="let property of fePropertiesMap[instanceId] | searchFilter:'name':searchTerm; trackBy:property?.name" @@ -22,8 +28,8 @@ <checkbox *ngIf="hasDeclareOption" [(checked)]="property.isSelected" [disabled]="property.isDisabled || property.isDeclared || readonly" (checkedChange)="propertyChecked(property)" [attr.data-tests-id]="property.name"></checkbox> - <div class="inner-cell-div" tooltip="{{property.name}}"> - <span>{{property.name}}</span> + <div class="inner-cell-div-multiline" tooltip="{{property.name}}"> + <multiline-ellipsis className="table-cell-multiline-ellipsis" [lines]="2">{{property.name}}</multiline-ellipsis> </div> </div> <span *ngIf="property.description" class="property-description-icon sprite-new show-desc" tooltip="{{property.description}}" tooltipDelay="0"></span> diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.less b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.less index 20da0b6ec2..72f67e434e 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.less +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.less @@ -1,4 +1,4 @@ -@import './../../../../../assets/styles/variables.less'; +@import './../../../../../assets/styles/mixins.less'; @import '../../../../../assets/styles/sprite'; @smaller-screen: ~"only screen and (max-width: 1580px)"; @@ -12,13 +12,16 @@ text-align:left; - .inner-cell-div{ - max-width: 100%; + .inner-cell-div { text-overflow: ellipsis; overflow: hidden; height: 20px; } + .inner-cell-div-multiline { + max-width: 100%; + } + .table-header { display: flex; flex-direction:row; @@ -36,6 +39,11 @@ .table-rows-header { border: #d2d2d2 solid 1px; border-top:none; + display: flex; + align-items: center; + .archive-label{ + margin-left: 10px; + } } .table-body { @@ -120,7 +128,7 @@ .property-description-icon { float: right; margin-top: 4px; - margin-left: 5px; + margin-left: 15px; flex: 0 0 auto; } } @@ -177,4 +185,53 @@ } } + .table-row { + /deep/ .table-cell-multiline-ellipsis .multiline-ellipsis-dots { + background: linear-gradient(to right, transparent 0%, #ffffff 80%); + padding-left: 1em; + } + + &.selected /deep/ .table-cell-multiline-ellipsis .multiline-ellipsis-dots { + background: linear-gradient(to right, transparent 0%, #e6f6fb 80%); + padding-left: 1em; + } + + &.readonly /deep/ .table-cell-multiline-ellipsis .multiline-ellipsis-dots { + background: linear-gradient(to right, transparent 0%, #f8f8f8 80%); + padding-left: 1em; + } + + &:hover:not(.selected) /deep/ .table-cell-multiline-ellipsis .multiline-ellipsis-dots { + background: linear-gradient(to right, transparent 0%, #f8f8f8 80%); + padding-left: 1em; + } + } + + .prop-instance-icon { + vertical-align: middle; + margin-right: 7px; + &.defaulticon.small { + background-color: @main_color_q; + border-radius:14px; + } + // square icons + + &.icon-group { + .square-icon(); + background-color: @main_color_a; + + &::before { + content: "G"; + } + } + &.icon-policy { + .square-icon(); + background-color: @main_color_r; + + &::before { + content: "P"; + } + } + + } } diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts index 093fae1684..da1fb82ba0 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts @@ -21,6 +21,7 @@ import { Component, Input, Output, EventEmitter} from "@angular/core"; import {PropertyFEModel, DerivedFEProperty, InstanceFePropertiesMap} from "app/models"; import {PropertiesService} from "../../../services/properties.service"; +import { InstanceFeDetails } from "../../../../models/instance-fe-details"; @Component({ selector: 'properties-table', @@ -30,7 +31,7 @@ import {PropertiesService} from "../../../services/properties.service"; export class PropertiesTableComponent { @Input() fePropertiesMap: InstanceFePropertiesMap; - @Input() feInstanceNamesMap: Map<string, string>; + @Input() feInstanceNamesMap: Map<string, InstanceFeDetails>; @Input() selectedPropertyId: string; @Input() propertyNameSearchText:string; @Input() searchTerm:string; diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/property-table.module.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/property-table.module.ts index 91f33485a9..91baaf1bc1 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/property-table.module.ts +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/property-table.module.ts @@ -8,6 +8,7 @@ import {HttpModule} from "@angular/http"; import {FilterChildPropertiesPipe} from "./pipes/filterChildProperties.pipe"; import {GlobalPipesModule} from "../../../pipes/global-pipes.module"; import {PropertiesService} from "../../../services/properties.service"; +import {MultilineEllipsisModule} from "../../../shared/multiline-ellipsis/multiline-ellipsis.module"; @NgModule({ imports: [ @@ -15,7 +16,8 @@ import {PropertiesService} from "../../../services/properties.service"; HttpModule, CommonModule, GlobalPipesModule, - UiElementsModule + UiElementsModule, + MultilineEllipsisModule ], declarations: [ FilterChildPropertiesPipe, diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html index 6e0f93f750..9d570f036a 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html @@ -9,7 +9,7 @@ </radio-buttons> <label class="select-type-label">Select type:</label> <div class="select-type"> - <ui-element-dropdown [values]="types" [(value)]="selectedType" (valueChange)="onTypeSelected($event)"></ui-element-dropdown> + <ui-element-dropdown [values]="types" [(value)]="selectedType" (valueChange)="onTypeSelected($event)" testId="select"></ui-element-dropdown> </div> <div class="table-and-list-container"> diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html index 72e083534c..af7a470495 100644 --- a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html @@ -1,5 +1,5 @@ <div class="service-path-selector"> - <label>Service Paths:</label> + <label>Service Flows:</label> <ui-element-dropdown class="path-dropdown" data-tests-id="service-path-selector" diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts index be9966acef..e09001fc6c 100644 --- a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts @@ -1,4 +1,3 @@ -import * as _ from "lodash"; import {Component, Input, KeyValueDiffer, IterableDiffers, KeyValueDiffers, DoCheck} from '@angular/core'; import {Service} from "app/models/components/service"; import {TranslateService} from "app/ng2/shared/translator/translate.service"; diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html index ff7902e2b9..75f064230c 100644 --- a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html @@ -1,22 +1,15 @@ <div class='service-path'> <button class='zoom-icons create-path-button' data-tests-id="pathsMenuBtn" (click)="showServicePathMenu = !showServicePathMenu">...</button> - <div class="service-path-menu" - *ngIf="showServicePathMenu" > + <div class="service-path-menu" *ngIf="showServicePathMenu"> <div > <ul> - <li><div class="hand" (click)="onCreateServicePath()" data-tests-id="createPathMenuItem"> - Create Service Path + <li *ngIf='!isViewOnly'><div class="hand" (click)="onCreateServicePath()" data-tests-id="createPathMenuItem"> + Create Service Flow </div></li> <li><div class="hand" (click)="onListServicePath()" data-tests-id="pathsListMenuItem"> - Service Paths List + Service Flows List </div></li> </ul> </div> </div> - <!-- TODO - ask Orit about positioning issues and styling issues --> - <!-- - <menu-list [open]="showServicePathMenu" [position]="menuPos" > - <menu-item [action]="onCreateServicePath">Create Path</menu-item> - <menu-item [action]="onListServicePath">Paths' List</menu-item> - </menu-list> --> </div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts index 4a6209fb6f..d66c5f0132 100644 --- a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts @@ -39,6 +39,7 @@ export class ServicePathComponent { @Input() service: Service; @Input() onCreate: Function; @Input() onSave: Function; + @Input() isViewOnly:boolean; constructor(private ModalServiceNg2: ModalService) {} @@ -46,7 +47,7 @@ export class ServicePathComponent { this.showServicePathMenu = false; let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal); let saveButton: ButtonModel = new ButtonModel('Create', 'blue', this.createPath, this.getDisabled ); - let modalModel: ModalModel = new ModalModel('l', 'Create Service Path', '', [saveButton, cancelButton], 'standard', true); + let modalModel: ModalModel = new ModalModel('l', 'Create Service Flow', '', [saveButton, cancelButton], 'standard', true); this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service}); this.modalInstance.instance.open(); @@ -55,10 +56,10 @@ export class ServicePathComponent { onListServicePath = ():void => { this.showServicePathMenu = false; let cancelButton: ButtonModel = new ButtonModel('Close', 'outline white', this.ModalServiceNg2.closeCurrentModal); - let modalModel: ModalModel = new ModalModel('md', 'Service Paths List','', [cancelButton], 'standard', true); + let modalModel: ModalModel = new ModalModel('md', 'Service Flows List','', [cancelButton], 'standard', true); this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathsListComponent, {service: this.service, - onCreateServicePath: this.onCreateServicePath, onEditServicePath: this.onEditServicePath}); + onCreateServicePath: this.onCreateServicePath, onEditServicePath: this.onEditServicePath, isViewOnly: this.isViewOnly}); this.modalInstance.instance.open(); }; @@ -70,14 +71,14 @@ export class ServicePathComponent { onEditServicePath = (id:string):void => { let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal); let saveButton: ButtonModel = new ButtonModel('Save', 'blue', this.createPath, this.getDisabled ); - let modalModel: ModalModel = new ModalModel('l', 'Edit Path', '', [saveButton, cancelButton]); + let modalModel: ModalModel = new ModalModel('l', 'Edit Path', '', [saveButton, cancelButton], 'standard', true); this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service, pathId: id}); this.modalInstance.instance.open(); }; getDisabled = ():boolean => { - return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit(); + return this.isViewOnly || !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit(); }; } diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html index e279e3f704..981efbb58e 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html @@ -1,11 +1,11 @@ -<div class="sdc-canvas-zone {{class}}-zone" [class.minimized]="minifyZone" [class.hidden]="!showZone"> - <div class="sdc-canvas-zone__header" (click)="unminifyZone()" > +<div class="sdc-canvas-zone {{class}}-zone" [class.minimized]="minimized" [class.hidden]="!visible" (click)="backgroundClicked()"> + <div class="sdc-canvas-zone__header" (click)="unminifyZone(); $event.stopPropagation();" > <div class="sdc-canvas-zone__title">{{title}} <span class="sdc-canvas-zone__counter">{{count}}</span> </div> <span class="sdc-canvas-zone__state-button">–</span> </div> - <div class="sdc-canvas-zone__container"> + <div class="sdc-canvas-zone__container" #scrollDiv > <ng-content></ng-content> </div> </div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less index 3e77c5ca3b..02880a9202 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less @@ -1,5 +1,5 @@ .sdc-canvas-zone { - width: 280px; + width: 285px; max-height:186px; display:flex; flex-direction:column; diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts index 7e60cb37a6..4059ad6cae 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts @@ -1,6 +1,5 @@ -import { Component, Input, Output, ViewEncapsulation, EventEmitter } from '@angular/core'; -import { EventListenerService } from 'app/services'; -import { GRAPH_EVENTS } from 'app/utils'; +import { Component, Input, Output, ViewEncapsulation, EventEmitter, OnInit } from '@angular/core'; +import { ZoneInstanceType } from '../../../../models/graph/zones/zone-instance'; @Component({ selector: 'zone-container', @@ -9,17 +8,28 @@ import { GRAPH_EVENTS } from 'app/utils'; encapsulation: ViewEncapsulation.None }) -export class ZoneContainerComponent { +export class ZoneContainerComponent implements OnInit { @Input() title:string; - @Input() class:string; - @Input() count:number; - @Input() showZone:boolean; - @Input() minifyZone:boolean; - constructor(private eventListenerService:EventListenerService) {} + @Input() type:ZoneInstanceType; + @Input() count:number; + @Input() visible:boolean; + @Input() minimized:boolean; + @Output() minimize: EventEmitter<any> = new EventEmitter<any>(); + @Output() backgroundClick: EventEmitter<void> = new EventEmitter<void>(); + private class:string; + + constructor() {} + + ngOnInit() { + this.class = ZoneInstanceType[this.type].toLowerCase(); + } private unminifyZone = () => { - this.minifyZone = !this.minifyZone; - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE); + this.minimize.emit(); + } + + private backgroundClicked = () => { + this.backgroundClick.emit(); } }
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html index d36b7aec6f..031b081323 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html @@ -1,8 +1,10 @@ -<div class="zone-child mode-{{config.mode}}" [class.locked]="activeInstanceMode > MODE.HOVER" - (mouseenter)="setMode(MODE.HOVER)" (mouseleave)="setMode(MODE.NONE)" (click)="setMode(MODE.SELECTED)"> - <div class="zone-child__handle" (click)="setMode(MODE.TAG, $event)">+</div> - <div class="zone-child__body"> - <div class="zone-child__body-content">{{config.count || defaultIconText}}</div> +<div tooltip="{{zoneInstance.instanceData.name}}" #currentComponent + class="zone-instance mode-{{zoneInstance.mode}}" [class.locked]="activeInstanceMode > MODE.HOVER" [class.hiding]="hidden" + (mouseenter)="setMode(MODE.HOVER)" (mouseleave)="setMode(MODE.NONE)" (click)="setMode(MODE.SELECTED, $event)"> + <div *ngIf="zoneInstance.handle" class="target-handle {{zoneInstance.handle}}" (click)="tagHandleClicked($event)"></div> + <div *ngIf="!isViewOnly" class="zone-instance__handle" (click)="setMode(MODE.TAG, $event)">+</div> + <div class="zone-instance__body"> + <div class="zone-instance__body-content">{{zoneInstance.assignments.length || defaultIconText}}</div> </div> - <div class="zone-child__name">{{config.name}}</div> + <div class="zone-instance__name">{{zoneInstance.instanceData.name}}</div> </div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less index a1d56df96e..b562c08514 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less @@ -1,9 +1,12 @@ -.zone-child { +@import '../../../../../../assets/styles/variables'; + +.zone-instance { position:relative; width:76px; margin:5px; + opacity:1; - .zone-child__handle { + .zone-instance__handle { display:none; position:absolute; right:4px; @@ -11,33 +14,34 @@ width:22px; height:22px; cursor:pointer; - border: solid white 1px; + border: solid @main_color_p 1px; border-radius: 2px; text-align: center; font-weight:bold; } - .zone-child__body { + .zone-instance__body { margin:0 auto; width:43px; height:43px; display:flex; padding:3px; - } - .zone-child__body-content { + .zone-instance__body-content { border-radius: 2px; flex:1; - color:white; - font-size:18px; + color:@main_color_p; + font-size:16px; text-align:center; display:flex; align-items: center; justify-content: center; + box-shadow:none; + transition:box-shadow 5s; } - .zone-child__name { + .zone-instance__name { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -45,66 +49,86 @@ } /* Dynamic classes below */ + .target-handle { + position:absolute; + width:18px; + height:18px; + display:block; + top: -4px; + right: 10px; + background-size: 100% 100%; + cursor: url("../../../../../../assets/styles/images/canvas-tagging-icons/policy_2.svg"), pointer; + + &.tagged-policy { + background-image: url('../../../../../../assets/styles/images/canvas-tagging-icons/policy_added.svg'); + } + + &.tag-available { + background-image: url('../../../../../../assets/styles/images/canvas-tagging-icons/indication.svg'); + } + } + + &.mode-1, &.mode-2, &.mode-3 { //hover, selected, tag - .zone-child__body { + .zone-instance__body { border:solid 2px; border-radius: 2px; padding:2px; cursor:pointer; } - .zone-child__handle{ + } + + &.mode-1, &.mode-2:hover{ + .zone-instance__handle{ display:block; - cursor:pointer; } } &.locked { - cursor: default; + cursor: inherit; } - - // &:not(.locked):hover .zone-child__handle{ - // display:block; - // } - .zone-child__body { - cursor: default; + + &.hiding { + opacity:0; + .zone-instance__body-content { + box-shadow: #CCC 0px 0px 15px; + } } - &.mode-3 .zone-child__handle { + + + &.mode-3 .zone-instance__handle { width:24px; height:24px; right:3px; top:9px; display:block; background-image: linear-gradient(-140deg, #009E98 0%, #97D648 100%); - border: 2px solid #FFFFFF; + border: 2px solid @main_color_p; border-radius: 2px; box-shadow: inset 2px -2px 3px 0 #007A3E; - cursor: pointer; } } .sdc-canvas-zone.group-zone { - .zone-child__handle { - background-color:#009FDB; - } - .zone-child__body { - border-color:#009FDB; + .zone-instance__handle { + background-color:@main_color_a; } - .zone-child__body-content { - background: #009FDB; + .zone-instance__body { + border-color:@main_color_a; + .zone-instance__body-content { + background: @main_color_a; + } } } .sdc-canvas-zone.policy-zone { - .zone-child__handle { - background-color:#0568AE; + .zone-instance__handle { + background-color:@main_color_r; } - .zone-child__body { - border-color:#1287D9; - .zone-child__body-content { - background: #1287D9; + .zone-instance__body { + border-color:@main_color_r; + .zone-instance__body-content { + background: @main_color_r; } } - .zone-child__body-content { - background: #0568AE; - } }
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts index 8057ae908a..3c2dd45db5 100644 --- a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts @@ -1,5 +1,14 @@ -import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; -import { ZoneInstanceConfig, ZoneInstanceMode } from 'app/models/graph/zones/zone-child'; +import { Component, Input, Output, EventEmitter, ViewEncapsulation, OnInit, SimpleChange, ElementRef, ViewChild, SimpleChanges } from '@angular/core'; +import { + ZoneInstance, ZoneInstanceMode, ZoneInstanceType, + IZoneInstanceAssignment +} from 'app/models/graph/zones/zone-instance'; +import { PoliciesService } from '../../../../services/policies.service'; +import { GroupsService } from '../../../../services/groups.service'; +import { IZoneService } from "../../../../../models/graph/zones/zone"; +import { EventListenerService } from 'app/services'; +import { GRAPH_EVENTS } from '../../../../../utils'; +import { Subject, Observable } from 'rxjs'; @Component({ selector: 'zone-instance', @@ -7,22 +16,95 @@ import { ZoneInstanceConfig, ZoneInstanceMode } from 'app/models/graph/zones/zon styleUrls: ['./zone-instance.component.less'], encapsulation: ViewEncapsulation.None }) -export class ZoneInstanceComponent { +export class ZoneInstanceComponent implements OnInit { - @Input() config:ZoneInstanceConfig; + @Input() zoneInstance:ZoneInstance; @Input() defaultIconText:string; @Input() isActive:boolean; + @Input() isViewOnly:boolean; @Input() activeInstanceMode: ZoneInstanceMode; + @Input() hidden:boolean; + @Input() forceSave:Subject<Function>; @Output() modeChange: EventEmitter<any> = new EventEmitter<any>(); + @Output() assignmentSaveStart: EventEmitter<void> = new EventEmitter<void>(); + @Output() assignmentSaveComplete: EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() tagHandleClick: EventEmitter<ZoneInstance> = new EventEmitter<ZoneInstance>(); + @ViewChild('currentComponent') currentComponent: ElementRef; private MODE = ZoneInstanceMode; + private zoneService:IZoneService; - private setMode = (mode:ZoneInstanceMode, event?:any):void => { - if(!this.isActive || this.isActive && mode == ZoneInstanceMode.TAG){ //when active, do not allow hover/select mode toggling - this.modeChange.emit({newMode: mode, instance: this.config}); + constructor(private policiesService:PoliciesService, private groupsService:GroupsService, private eventListenerService:EventListenerService){} + + ngOnInit(){ + if(this.zoneInstance.type == ZoneInstanceType.POLICY){ + this.zoneService = this.policiesService; + } else { + this.zoneService = this.groupsService; } - if(event){ - event.stopPropagation(); + this.forceSave.subscribe((afterSaveFunction:Function) => { + this.setMode(ZoneInstanceMode.TAG, null, afterSaveFunction); + }) + } + + ngOnChanges(changes:SimpleChanges) { + if(changes.hidden){ + this.currentComponent.nativeElement.scrollIntoView({behavior: "smooth", block: "nearest", inline:"end"}); } } + ngOnDestroy() { + this.forceSave.unsubscribe(); + } + + private setMode = (mode:ZoneInstanceMode, event?:any, afterSaveCallback?:Function):void => { + + if(event){ //prevent event from handle and then repeat event from zone instance + event.stopPropagation(); + } + + if(!this.isActive && this.activeInstanceMode === ZoneInstanceMode.TAG) { + return; //someone else is tagging. No events allowed + } + + if(this.isActive && this.zoneInstance.mode === ZoneInstanceMode.TAG){ + if(mode !== ZoneInstanceMode.TAG) { + return; //ignore all other events. The only valid option is saving changes. + } + + let oldAssignments:Array<IZoneInstanceAssignment> = this.zoneInstance.instanceData.getSavedAssignments(); + if(this.zoneInstance.isZoneAssignmentChanged(oldAssignments, this.zoneInstance.assignments)) { + + this.assignmentSaveStart.emit(); + + this.zoneService.updateZoneInstanceAssignments(this.zoneInstance.parentComponentType, this.zoneInstance.parentComponentID, this.zoneInstance.instanceData.uniqueId, this.zoneInstance.assignments).subscribe( + (success) => { + this.zoneInstance.instanceData.setSavedAssignments(this.zoneInstance.assignments); + if(this.zoneInstance.type === ZoneInstanceType.POLICY){ + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, this.zoneInstance.instanceData); + } else { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, this.zoneInstance.instanceData); + } + this.assignmentSaveComplete.emit(true); + if(afterSaveCallback) afterSaveCallback(); + }, (error) => { + this.zoneInstance.assignments = oldAssignments; + this.assignmentSaveComplete.emit(false); + }); + } else { + if(afterSaveCallback) afterSaveCallback(); + } + this.modeChange.emit({newMode: ZoneInstanceMode.NONE, instance: this.zoneInstance}); + + } else { + this.modeChange.emit({newMode: mode, instance: this.zoneInstance}); + } + + + } + + private tagHandleClicked = (event:Event) => { + this.tagHandleClick.emit(this.zoneInstance); + event.stopPropagation(); + }; + }
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts index d1e68f3088..c15f92ccb8 100644 --- a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts @@ -53,6 +53,7 @@ export class DynamicElementComponent { @ViewChild('target', { read: ViewContainerRef }) target: any; @Input() type: any; @Input() name: string; + @Input() testId: string; @Input() readonly:boolean; @Input() path:string;//optional param. used only for for subnetpoolid type @@ -111,6 +112,7 @@ export class DynamicElementComponent { if (this.cmpRef) { this.cmpRef.instance.name = this.name; this.cmpRef.instance.type = this.type; + this.cmpRef.instance.testId = this.testId; this.cmpRef.instance.value = this.value; this.cmpRef.instance.readonly = this.readonly; } @@ -133,7 +135,7 @@ export class DynamicElementComponent { case DynamicElementComponentCreatorIdentifier.FLOAT: this.createComponent(UiElementIntegerInputComponent); - this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/; + this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/.source; break; case DynamicElementComponentCreatorIdentifier.STRING: @@ -156,10 +158,10 @@ export class DynamicElementComponent { case DynamicElementComponentCreatorIdentifier.DEFAULT: default: this.createComponent(UiElementInputComponent); - console.log("ERROR: No ui component to handle type: " + this.type); + console.log("ERROR: No ui-models component to handle type: " + this.type); } - // Subscribe to change event of of ui-element-basic and fire event to change the value + // Subscribe to change event of of ui-models-element-basic and fire event to change the value this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); }); this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); }); } diff --git a/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.html b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.html new file mode 100644 index 0000000000..83daca2ae5 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.html @@ -0,0 +1,13 @@ +<h1 class="w-sdc-designer-sidebar-section-title" + tooltip="{{titleTooltip}}" + [ngClass]="{'expanded': state == 0, 'collapsed': state == 1}" + (click)="toggleState()"> + {{caption}}<span class="w-sdc-designer-sidebar-section-title-icon"></span> + <ng-content select="header"></ng-content> + <span class="w-sdc-designer-sidebar-section-title-icon"></span> +</h1> + +<div class="expand-collapse-content" [ngClass]="{'visible': state === 0, 'hidden': state === 1}"> + <ng-content></ng-content> + <ng-content select="content"></ng-content> +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.less b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.less new file mode 100644 index 0000000000..e5dd2527c1 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.less @@ -0,0 +1,7 @@ +.ellipsis-directive-more-less { + float: right; + margin-right: 10px; + line-height: 23px; + text-decoration: underline; + text-align: left; +} diff --git a/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.ts b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.ts new file mode 100644 index 0000000000..0fa0d33de9 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/expand-collapse/expand-collapse.component.ts @@ -0,0 +1,56 @@ +/*- + * ============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========================================================= + */ + +import { Component, Input, Output, ViewEncapsulation, AfterViewInit } from '@angular/core'; + +export enum ExpandState { + EXPANDED, + COLLAPSED +} + +@Component({ + selector: 'ng2-expand-collapse', + templateUrl: './expand-collapse.component.html', + styleUrls: ['./expand-collapse.component.less'], + encapsulation: ViewEncapsulation.None +}) + +export class ExpandCollapseComponent implements AfterViewInit { + @Input() caption: String; + @Input() state: ExpandState; + @Input() titleTooltip: String; + + constructor() { + + } + + toggleState():void { + if (this.state == ExpandState.EXPANDED) { + this.state = ExpandState.COLLAPSED; + } else { + this.state = ExpandState.EXPANDED; + } + } + + ngAfterViewInit(): void { + + } + +} diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html index 805e5ac295..c564abc092 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html @@ -1,3 +1,3 @@ -<select name='{{name}}' [(ngModel)]="value" (change)="onChange()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType"> +<select name='{{name}}' [(ngModel)]="value" (change)="onChange()" [ngClass]="{'disabled':readonly}" [attr.data-tests-id]="'value-' + testId"> <option *ngFor="let ddvalue of values" [ngValue]="ddvalue.label != undefined ? ddvalue.value : ddvalue">{{ddvalue.label||ddvalue}}</option> </select> diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html index 057e731ada..fdba850e93 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html @@ -11,4 +11,5 @@ [formControl]="control" tooltip="{{value}}" [readonly]="readonly" + [attr.data-tests-id]="'value-' + testId" /> diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html index e1555e87fd..04307e4c2d 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html @@ -11,4 +11,5 @@ [formControl]="control" tooltip="{{value}}" [readonly]="readonly" + [attr.data-tests-id]="'value-' + testId" /> diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.html index 3bd51b4e36..3bc94de1e4 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.html @@ -7,7 +7,7 @@ [value]="value!=undefined?value:''" disabled /> - <button [popover]="popoverForm" [ngClass]="{'disabled':readonly}">Edit</button> + <button [popover]="popoverForm" (onShown)="setEditValue()" [ngClass]="{'disabled':readonly}" [attr.data-tests-id] ="'edit-button-' + testId">Edit</button> </div> <popover-content #popoverForm [title]="name" [buttons]="buttonsArray" [placement]="'top'" [closeOnClickOutside]="true"> @@ -16,11 +16,12 @@ #textArea class="subnet-value" [ngClass]="{'error': control.invalid}" - [(ngModel)]="value" + [(ngModel)]="editValue" [attr.maxlength]="validation.propertyValue.max" [attr.minlength]="validation.propertyValue.min" [pattern]="pattern" [formControl]="control" + [attr.data-tests-id]="'value-' + testId" ></textarea> </div> </popover-content> diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts index 525cd1742c..f485d270fa 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -import {Component, ViewChild, ElementRef, Input} from '@angular/core'; +import {Component, ViewChild, ElementRef} from '@angular/core'; import {ButtonsModelMap, ButtonModel} from "app/models"; import {PopoverContentComponent} from "../../popover/popover-content.component"; import {UiElementBase, UiElementBaseInterface} from "../ui-element-base.component"; @@ -32,13 +32,14 @@ export class UiElementPopoverInputComponent extends UiElementBase implements UiE @ViewChild('textArea') textArea: ElementRef; @ViewChild('popoverForm') popoverContentComponent: PopoverContentComponent; + editValue: any; saveButton: ButtonModel; buttonsArray: ButtonsModelMap; constructor() { super(); // Create Save button and insert to buttons map - this.saveButton = new ButtonModel('save', 'blue', this.onChange); + this.saveButton = new ButtonModel('Set', 'blue', this.onChange.bind(this)); this.buttonsArray = { 'test': this.saveButton }; // Define the regex pattern for this controller @@ -47,4 +48,15 @@ export class UiElementPopoverInputComponent extends UiElementBase implements UiE // Disable / Enable button according to validation //this.control.valueChanges.subscribe(data => this.saveButton.disabled = this.control.invalid); } + + public setEditValue() { + // copy value to edit + this.editValue = angular.copy(this.value); + } + + public onChange() { + this.popoverContentComponent.hide(); + this.value = this.editValue; + super.onChange(); + } } diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts index b4e9e7d36a..31fa7c3442 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts @@ -46,9 +46,12 @@ export class UiElementBase { @Input() name: string; @Input() type: string; + @Input() path: string; @Input() pattern: any; @Input() readonly:boolean; + @Input() testId:string; + constructor() { //this.control = new FormControl('', [Validators.required]); this.control = new FormControl('', []); diff --git a/catalog-ui/src/app/ng2/components/ui/forms/modal-forms.module.ts b/catalog-ui/src/app/ng2/components/ui/forms/modal-forms.module.ts new file mode 100644 index 0000000000..34404e50a5 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/modal-forms.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from '@angular/common'; +import { SdcUiComponentsModule } from "sdc-ui/lib/angular"; +import { ValueEditComponent } from './value-edit/value-edit.component'; +import { UnsavedChangesComponent } from "./unsaved-changes/unsaved-changes.component"; +import { UiElementsModule } from "../ui-elements.module"; + + + +@NgModule({ + declarations: [ + ValueEditComponent, + UnsavedChangesComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + UiElementsModule + ], + exports: [ValueEditComponent, UnsavedChangesComponent], + entryComponents: [ UnsavedChangesComponent + ], + providers: [] +}) +export class ModalFormsModule { + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.html b/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.html new file mode 100644 index 0000000000..bdf21dec62 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.html @@ -0,0 +1,6 @@ +<div class="unsaved-changes-modal"> + <div class="message"> + <p>Your changes {{isValidChangedData ? '' : '(invalid)'}} have not been saved.</p> + <p>Do you want to {{isValidChangedData ? 'save' : 'discard'}} them?</p> + </div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.ts b/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.ts new file mode 100644 index 0000000000..b8fdeaf659 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: 'unsaved-changes', + templateUrl: './unsaved-changes.component.html', + styleUrls: [] +}) +export class UnsavedChangesComponent { + + @Input() isValidChangedData:boolean; + + constructor(){ + } + + + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.html b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.html new file mode 100644 index 0000000000..464b756a3f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.html @@ -0,0 +1,12 @@ +<div class="name-update-container"> + <sdc-input #updateNameInput + label="Instance Name" + required="true" + [maxLength]="50" + [(value)]="name" + testId="instanceName"></sdc-input> + <sdc-validation [validateElement]="updateNameInput" (validityChanged)="validityChanged($event)"> + <sdc-required-validator message="Name is required."></sdc-required-validator> + <sdc-regex-validator message="Special characters not allowed." [pattern]="pattern"></sdc-regex-validator> + </sdc-validation> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.less b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.less new file mode 100644 index 0000000000..b958ca17b7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.less @@ -0,0 +1,3 @@ +.name-update-container { + min-height: 90px; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.ts b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.ts new file mode 100644 index 0000000000..08bc0586c7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/forms/value-edit/value-edit.component.ts @@ -0,0 +1,25 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: 'value-edit', + templateUrl: './value-edit.component.html', + styleUrls: ['./value-edit.component.less'] +}) +export class ValueEditComponent { + + @Input() name:String; + @Input() validityChangedCallback: Function; + + private pattern:string = "^[\\s\\w\&_.:-]{1,1024}$" + constructor(){ + } + + private validityChanged = (value):void => { + if(this.validityChangedCallback) { + this.validityChangedCallback(value); + } + } + + + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.html b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.html new file mode 100644 index 0000000000..ba897198d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.html @@ -0,0 +1,30 @@ +<div class="component-add-element"> + <div class="add-element-selection-container"> + <sdc-dropdown class="add-element-selection" selectedOption="selectedElement" placeHolder="Please choose option" + (changed)="onElementSelected($event)" [options]="dropdownOptions"></sdc-dropdown> + <svg-icon-label class="add-element-button" [class.disabled]="!selectedElement" + name="plus-circle-o" + mode="primary" + size="medium" + label="ADD" + labelPlacement="right" + clickable="{{!!selectedElement}}" + (click)="addElement()"> + </svg-icon-label> + </div> + + <div class="elements-list"> + <ul> + <li *ngFor="let element of existingElements" class="elements-list-item"> + <span>{{element.name}}</span> + <svg-icon-label name="trash-o" clickable="true" size="small" class="elements-list-item-delete" + data-tests-id="delete_target" (click)="removeElement(element)"></svg-icon-label> + </li> + </ul> + <div *ngIf="existingElements.length===0" class="add-element-no-data"> + <div class="add-element-no-data-title">No {{elementName}} have been added yet.</div> + <div class="add-element-no-data-content">Begin typing or select {{elementName}} above</div> + </div> + </div> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.less b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.less new file mode 100644 index 0000000000..522483dfb7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.less @@ -0,0 +1,61 @@ +@import "./../../../../../../assets/styles/variables-old"; + +.component-add-element { + + .add-element-selection-container { + + display: flex; + flex-direction: row; + .add-element-selection { + flex: 2; + } + + .add-element-button { + padding: 0px 0px 0px 15px; + &:not(.disabled) { + cursor:pointer; + } + } + + } + + .elements-list { + border: 1px solid #d2d2d2; + margin-top: 10px; + height: 300px; + overflow: auto; + + .elements-list-item { + padding: 10px 20px 10px 20px; + + &:first-child { + padding-top: 15px; + } + + &:hover { + background-color: #f8f8f8; + .elements-list-item-delete { + visibility: visible; + } + } + } + } + + .elements-list-item-delete { + float:right; + cursor: pointer; + visibility: hidden; + } + + + .add-element-no-data { + margin: 0 auto; + padding-top: 30px; + text-align: center; + + .add-element-no-data-title { + font-family: @font-opensans-bold; + } + + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.ts b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.ts new file mode 100644 index 0000000000..1d05b27d68 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.component.ts @@ -0,0 +1,65 @@ +/** + * Created by ob0695 on 11.04.2018. + */ +import {Component, Input} from "@angular/core"; +import {UiBaseObject} from "../../../../../models/ui-models/ui-base-object"; +import {IDropDownOption} from "sdc-ui/lib/angular/form-elements/dropdown/dropdown-models"; + +@Component({ + selector: 'add-elements', + templateUrl: './add-elements.component.html', + styleUrls: ['./add-elements.component.less'] +}) + +export class AddElementsComponent { + + @Input() elementsToAdd:Array<UiBaseObject>; + @Input() elementName: string; + + private existingElements:Array<UiBaseObject>; + private dropdownOptions:Array<IDropDownOption>; + private selectedElement:IDropDownOption; + + ngOnInit() { + this.existingElements = []; + this.convertElementToDropdownDisplay(); + + } + + private convertElementToDropdownDisplay = () => { + this.dropdownOptions = []; + _.forEach(this.elementsToAdd, (elementToAdd:UiBaseObject) =>{ + this.dropdownOptions.push({label:elementToAdd.name, value: elementToAdd.uniqueId }) + }); + } + + onElementSelected(selectedElement:IDropDownOption):void { + this.selectedElement = selectedElement + } + + addElement():void { + + if(this.selectedElement){ + this.dropdownOptions = _.reject(this.dropdownOptions, (option: IDropDownOption) => { // remove from dropDown + return option.value === this.selectedElement.value; + }); + + let selected = _.find(this.elementsToAdd, (element:UiBaseObject) => { + return this.selectedElement.value === element.uniqueId; + }); + + this.elementsToAdd =_.without(this.elementsToAdd, selected); // remove from optional elements to add + this.existingElements.push(selected); // add to existing element list + this.selectedElement = undefined; + } else { + console.log("no element selected"); //TODO:show error? + } + } + + removeElement(element:UiBaseObject):void { + + this.existingElements =_.without(this.existingElements, element); // remove from optional elements to add + this.dropdownOptions.push({label:element.name, value: element.uniqueId }); + this.elementsToAdd.push(element); + } +} diff --git a/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.module.ts b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.module.ts new file mode 100644 index 0000000000..a1c34f5686 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal/add-elements/add-elements.module.ts @@ -0,0 +1,30 @@ +/** + * Created by ob0695 on 11.04.2018. + */ +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; +import {AddElementsComponent} from "./add-elements.component"; +import {CommonModule} from "@angular/common"; + +/** + * Created by ob0695 on 9.04.2018. + */ +@NgModule({ + declarations: [ + AddElementsComponent + ], + + imports: [ + CommonModule, + SdcUiComponentsModule + ], + + entryComponents: [ + AddElementsComponent + ], + exports: [], + providers: [] +}) +export class AddElementsModule { + +} diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html index 6fc55d19e7..1708fc4c23 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html @@ -1,6 +1,5 @@ <div class="custom-modal {{input.size}}"> <div class="ng2-modal-content" - ngDraggable [ngDraggable]="input.isMovable" [handle]="ModalHandle" [bounds]="ModalBounds" diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts index 609a1fc5e1..7e45b9e55b 100644 --- a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts @@ -24,6 +24,7 @@ import { setTimeout } from 'core-js/library/web/timers'; import { EventListenerService } from 'app/services'; import { GRAPH_EVENTS } from 'app/utils'; import { Point } from 'app/models'; +import { ZoneInstanceType, ZoneInstance } from '../../../../models/graph/zones/zone-instance'; @@ -37,8 +38,9 @@ export class PaletteAnimationComponent { @Input() from : Point; @Input() to : Point; + @Input() type: ZoneInstanceType; @Input() iconName : string; - @Input() data : any; + @Input() zoneInstance : ZoneInstance; public animation; private visible:boolean = false; @@ -47,6 +49,11 @@ export class PaletteAnimationComponent { constructor(private eventListenerService:EventListenerService) {} + + ngOnDestroy(){ + this.zoneInstance.hidden = false; //if animation component is destroyed before animation is complete + } + public runAnimation() { this.visible = true; let positionDiff:Point = new Point(this.to.x - this.from.x, this.to.y - this.from.y); @@ -57,7 +64,7 @@ export class PaletteAnimationComponent { public animationComplete = (e) => { this.visible = false; - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE); + this.zoneInstance.hidden = false; }; diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts index d30d5f6178..a10ca7dc81 100644 --- a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts @@ -35,7 +35,8 @@ export class PalettePopupPanelComponent implements OnInit { public addZoneInstance(): void { if(this.displayComponent) { - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, this.component, this.displayComponent, this.popupPanelPosition); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE, this.component, this.displayComponent, this.popupPanelPosition); + this.hidePopupPanel(); } } diff --git a/catalog-ui/src/app/ng2/components/ui/tile/tile.component.html b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.html new file mode 100644 index 0000000000..a7a7b2c5b5 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.html @@ -0,0 +1,28 @@ +<div class="sdc-tile sdc-tile-fix-width"> + + <div class='sdc-tile-header' [ngClass]="{'purple': component.isResource(), 'blue': !component.isResource()}"> + <div *ngIf="component.isResource()" data-tests-id="asset-type">{{component.getComponentSubType()}}</div> + <div *ngIf="component.isService()">S</div> + </div> + + <div class='sdc-tile-content' data-tests-id="dashboard-Elements" (click)="tileClicked()"> + <div class='sdc-tile-content-icon'> + <div [ngClass]="[component.iconSprite, component.icon]" [ngClass]="{'sprite-resource-icons': component.isResource(), 'sprite-services-icons': component.isService()}" + [attr.data-tests-id]="component.name"></div> + </div> + + <div class='sdc-tile-content-info'> + <div class="sdc-tile-info-line title" [attr.data-tests-id]="component.name | resourceName" [tooltip]="component.name | resourceName" [tooltipDisabled]="!hasEllipsis"> + <multiline-ellipsis className="w-sdc-tile-multiline-ellipsis" [lines]="3" (hasEllipsisChanged)="hasEllipsis = $event">{{component.name | resourceName}}</multiline-ellipsis> + </div> + <div class="sdc-tile-info-line subtitle" [attr.data-tests-id]="component.name+'Version'">V {{component.version}}</div> + </div> + </div> + + <div class='sdc-tile-footer'> + <div class="sdc-tile-footer-content"> + <div class='sdc-tile-footer-text'>{{component.getStatus(sdcMenu)}}</div> + </div> + </div> + +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/tile/tile.component.less b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.less new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.less diff --git a/catalog-ui/src/app/ng2/components/ui/tile/tile.component.ts b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.ts new file mode 100644 index 0000000000..b6f63584be --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/tile/tile.component.ts @@ -0,0 +1,24 @@ +import {Component, Input, Output, Inject, EventEmitter} from '@angular/core'; +import {Component as ComponentModel} from 'app/models'; +import {SdcMenuToken, IAppMenu} from "../../../config/sdc-menu.config"; + +@Component({ + selector: 'ui-tile', + templateUrl: './tile.component.html', + styleUrls: ['./tile.component.less'] +}) +export class TileComponent { + @Input() public component: ComponentModel; + @Output() public onTileClick: EventEmitter<ComponentModel>; + + public hasEllipsis: boolean; + + constructor(@Inject(SdcMenuToken) public sdcMenu:IAppMenu) { + this.onTileClick = new EventEmitter<ComponentModel>(); + this.hasEllipsis = false; + } + + public tileClicked() { + this.onTileClick.emit(this.component); + } +} diff --git a/catalog-ui/src/app/ng2/components/ui/tile/tile.module.ts b/catalog-ui/src/app/ng2/components/ui/tile/tile.module.ts new file mode 100644 index 0000000000..55b34400d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/tile/tile.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { TileComponent } from './tile.component'; +import { GlobalPipesModule } from "../../../pipes/global-pipes.module"; +import { TooltipModule } from "../tooltip/tooltip.module"; +import {MultilineEllipsisModule} from "../../../shared/multiline-ellipsis/multiline-ellipsis.module"; + + +@NgModule({ + imports: [BrowserModule, GlobalPipesModule, TooltipModule, MultilineEllipsisModule], + declarations: [TileComponent], + exports: [TileComponent], + entryComponents: [TileComponent] +}) +export class TileModule { } diff --git a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts index 44314734c3..e905db73a6 100644 --- a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts +++ b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts @@ -20,26 +20,28 @@ import { NgModule } from '@angular/core'; import { NavbarModule } from "./navbar/navbar.module"; -import {DynamicElementModule} from "./dynamic-element/dynamic-element.module"; -import {FormElementsModule} from "./form-components/form-elements.module"; -import {LoaderComponent} from "./loader/loader.component"; -import {ModalModule} from "./modal/modal.module"; -import {PopoverModule} from "./popover/popover.module"; -import {SearchBarComponent} from "./search-bar/search-bar.component"; -import {SearchWithAutoCompleteComponent} from "./search-with-autocomplete/search-with-autocomplete.component"; -import {PalettePopupPanelComponent} from "./palette-popup-panel/palette-popup-panel.component"; -import {ZoneContainerComponent} from "./canvas-zone/zone-container.component"; -import {ZoneInstanceComponent } from "./canvas-zone/zone-instance/zone-instance.component"; -import {PaletteAnimationComponent} from "./palette-animation/palette-animation.component" -import {TabModule} from "./tabs/tabs.module"; -import {TooltipModule} from "./tooltip/tooltip.module"; -import {CommonModule} from "@angular/common"; -import {FormsModule} from "@angular/forms"; -import {BrowserModule} from "@angular/platform-browser"; -import {MultiStepsWizardModule} from "./multi-steps-wizard/multi-steps-wizard.module"; -import {MenuListModule} from "./menu/menu-list.module"; -import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-ng2.module"; -//import {SdcUiComponentsModule} from "sdc-ui/lib/angular"; +import { DynamicElementModule } from "./dynamic-element/dynamic-element.module"; +import { FormElementsModule } from "./form-components/form-elements.module"; +import { LoaderComponent } from "./loader/loader.component"; +import { ModalModule } from "./modal/modal.module"; +import { PopoverModule } from "./popover/popover.module"; +import { SearchBarComponent } from "./search-bar/search-bar.component"; +import { SearchWithAutoCompleteComponent } from "./search-with-autocomplete/search-with-autocomplete.component"; +import { PalettePopupPanelComponent } from "./palette-popup-panel/palette-popup-panel.component"; +import { ZoneContainerComponent } from "./canvas-zone/zone-container.component"; +import { ZoneInstanceComponent } from "./canvas-zone/zone-instance/zone-instance.component"; +import { PaletteAnimationComponent } from "./palette-animation/palette-animation.component" +import { TabModule } from "./tabs/tabs.module"; +import { TooltipModule } from "./tooltip/tooltip.module"; +import { CommonModule } from "@angular/common"; +import { FormsModule } from "@angular/forms"; +import { BrowserModule } from "@angular/platform-browser"; +import { MultiStepsWizardModule } from "./multi-steps-wizard/multi-steps-wizard.module"; +import { MenuListModule } from "./menu/menu-list.module"; +import { MenuListNg2Module } from "../downgrade-wrappers/menu-list-ng2/menu-list-ng2.module"; +import { ExpandCollapseComponent } from './expand-collapse/expand-collapse.component'; +import { SdcUiComponentsModule } from "sdc-ui/lib/angular"; +import { TileModule } from "./tile/tile.module"; @NgModule({ declarations: [ @@ -49,11 +51,12 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n PalettePopupPanelComponent, ZoneContainerComponent, ZoneInstanceComponent, - PaletteAnimationComponent + PaletteAnimationComponent, + ExpandCollapseComponent ], - + imports: [ - //SdcUiComponentsModule, + SdcUiComponentsModule, BrowserModule, FormsModule, CommonModule, @@ -66,7 +69,8 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n TooltipModule, MultiStepsWizardModule, MenuListModule, - MenuListNg2Module + MenuListNg2Module, + TileModule ], exports: [ LoaderComponent, @@ -85,7 +89,9 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n TooltipModule, MenuListModule, MenuListNg2Module, - PaletteAnimationComponent + PaletteAnimationComponent, + ExpandCollapseComponent, + TileModule ], entryComponents: [SearchWithAutoCompleteComponent, PalettePopupPanelComponent, ZoneContainerComponent, ZoneInstanceComponent, PaletteAnimationComponent] }) diff --git a/catalog-ui/src/app/ng2/config/sdc-menu.config.factory.ts b/catalog-ui/src/app/ng2/config/sdc-menu.config.factory.ts new file mode 100644 index 0000000000..4417c32f27 --- /dev/null +++ b/catalog-ui/src/app/ng2/config/sdc-menu.config.factory.ts @@ -0,0 +1,6 @@ +import {IAppMenu} from "app/models"; + +export function getSdcMenu() : IAppMenu{ + const sdcMenu:IAppMenu = require('./../../../../configurations/menu.js'); + return sdcMenu; +} diff --git a/catalog-ui/src/app/ng2/config/sdc-menu.config.ts b/catalog-ui/src/app/ng2/config/sdc-menu.config.ts new file mode 100644 index 0000000000..07680b5bc9 --- /dev/null +++ b/catalog-ui/src/app/ng2/config/sdc-menu.config.ts @@ -0,0 +1,12 @@ +import {Provider, OpaqueToken} from "@angular/core"; +import {getSdcMenu} from "./sdc-menu.config.factory"; +import {IAppMenu} from "app/models"; + +export { IAppMenu }; + +export const SdcMenuToken = new OpaqueToken('SdcMenuToken'); + +export const SdcMenu:Provider = { + provide: SdcMenuToken, + useFactory: getSdcMenu +}; diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-models/ui-component-to-upgrade.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-models/ui-component-to-upgrade.ts new file mode 100644 index 0000000000..97fb71e210 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-models/ui-component-to-upgrade.ts @@ -0,0 +1,103 @@ +import {ComponentState} from "../../../../utils/constants"; +import {IDependenciesServerResponse} from "../../../services/responses/dependencies-server-response"; +import {UiBaseObject} from "../../../../models/ui-models/ui-base-object"; + +/** + * Created by ob0695 on 5/1/2018. + */ +export enum AutomatedUpgradeInstanceType { + VF, SERVICE_PROXY, ALLOTTED_RESOURCE +} +export class ServiceContainerToUpgradeUiObject extends UiBaseObject { + + icon:string; + version:string; + isLock:boolean; // true if service is in check-out or ceritification-in-progress + vspInstances:Array<VspInstanceUiObject>; // list of instances of the vsp contain in the service - intances can be vf, proxy or allotted + isAlreadyUpgrade:boolean; // true if all instances is in latest version + + constructor(componentToUpgrade:IDependenciesServerResponse) { + super(componentToUpgrade.uniqueId, componentToUpgrade.type, componentToUpgrade.name); + this.icon = componentToUpgrade.icon; + this.version = componentToUpgrade.version; + this.isAlreadyUpgrade = true; + this.isLock = componentToUpgrade.state === ComponentState.CERTIFICATION_IN_PROGRESS || componentToUpgrade.state === ComponentState.NOT_CERTIFIED_CHECKOUT; + this.vspInstances = []; + } + + public addVfInstance = (vsp: IDependenciesServerResponse, latestVersion:string):void => { + let isNeededUpgrade = parseInt(vsp.version) < parseInt(latestVersion); + this.vspInstances.push(new VspInstanceUiObject(vsp.uniqueId, vsp.name, vsp.version, vsp.icon)); + if (isNeededUpgrade) { + this.isAlreadyUpgrade = false; + } + } + + public addProxyInstance = (vsp: IDependenciesServerResponse, isNeededUpgrade:boolean, instanceName:string):void => { + this.vspInstances.push(new ProxyVspInstanceUiObject(vsp.uniqueId, vsp.name, vsp.version, vsp.icon, instanceName)); + if (isNeededUpgrade) { + this.isAlreadyUpgrade = false; + } + } + + public addAllottedResourceInstance = (vsp: IDependenciesServerResponse, isNeededUpgrade:boolean, instanceName:string, vfName:string, vfId:string):void => { + this.vspInstances.push(new AllottedResourceInstanceUiObject(vsp.uniqueId, vsp.name, vsp.version, vsp.icon, instanceName, vfName, vfId)); + if (isNeededUpgrade) { + this.isAlreadyUpgrade = false; + } + } + + public addMultipleInstances = (vsp: IDependenciesServerResponse, vspLatestVersion:string, instancesNames:Array<string>, allottedOriginVf: IDependenciesServerResponse):void => { + _.forEach(instancesNames, (instanceName:string) => { + let isNeededUpgrade = parseInt(vsp.version) < parseInt(vspLatestVersion); + if (allottedOriginVf) { + this.addAllottedResourceInstance(vsp, isNeededUpgrade, instanceName, allottedOriginVf.name, allottedOriginVf.uniqueId); + } else { + this.addProxyInstance(vsp, isNeededUpgrade, instanceName); + } + }) + } +} + +export class VspInstanceUiObject { + + vspName:string; + vspVersion:string; + vspId:string; + icon:string; + instanceType:AutomatedUpgradeInstanceType; + + constructor(uniqueId:string, vspName:string, vspVersion:string, icon:string) { + this.vspId = uniqueId; + this.vspName = vspName; + this.vspVersion = vspVersion; + this.icon = icon; + this.instanceType = AutomatedUpgradeInstanceType.VF; + } +} + +export class ProxyVspInstanceUiObject extends VspInstanceUiObject { + + instanceName:string; + + constructor(uniqueId:string, vspName:string, vspVersion:string, icon:string, instanceName: string) { + super(uniqueId, vspName, vspVersion, icon); + this.instanceName = instanceName; + this.instanceType = AutomatedUpgradeInstanceType.SERVICE_PROXY; + } +} + +export class AllottedResourceInstanceUiObject extends VspInstanceUiObject { + + instanceName:string; + originVfName:string; + originVfId:string; + + constructor(uniqueId:string, vspName:string, vspVersion:string, icon:string, instanceName:string, originVfName:string, originVfId:string) { + super(uniqueId, vspName, vspVersion, icon) + this.instanceName = instanceName; + this.originVfId = originVfId; + this.originVfName = originVfName; + this.instanceType = AutomatedUpgradeInstanceType.ALLOTTED_RESOURCE; + } +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.html new file mode 100644 index 0000000000..12a3b72b92 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.html @@ -0,0 +1,10 @@ +<div class="automated-upgrade-component"> + <span innerHTML="{{statusText}}"> </span> + <div class="components-to-upgrade-list"> + <ul> + <li class="components-to-upgrade-list-item " *ngFor="let component of upgradedComponentsList"> + <upgrade-list-status-item [upgradedComponent]="component" [upgradeComponentStatus]="upgradeStatusMap[component.name]"></upgrade-list-status-item> + </li> + </ul> + </div> +</div> diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.ts new file mode 100644 index 0000000000..0bc342ce65 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-status/automated-upgrade-status.component.ts @@ -0,0 +1,24 @@ +/** + * Created by ob0695 on 4/24/2018. + */ +import {Component, Input} from "@angular/core"; +import Dictionary = _.Dictionary; +import {AutomatedUpgradeStatusResponse} from "../../../services/responses/automated-upgrade-response"; +import {ServiceContainerToUpgradeUiObject} from "../automated-upgrade-models/ui-component-to-upgrade"; + +@Component({ + selector: 'automated-upgrade-status', + templateUrl: './automated-upgrade-status.component.html', + styleUrls: ['./../automated-upgrade.component.less'] +}) +export class AutomatedUpgradeStatusComponent { + + @Input() upgradedComponentsList: Array<ServiceContainerToUpgradeUiObject>; + @Input() upgradeStatusMap: Dictionary<AutomatedUpgradeStatusResponse>; + @Input() statusText: string; + + constructor () { + + } + +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.html new file mode 100644 index 0000000000..2a03d94c8c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.html @@ -0,0 +1,25 @@ +<ul class="list-item-inner-content"> + <li *ngFor="let vspInstance of vspInstances" class="first-line"> + <div [ngSwitch]="vspInstance.instanceType"> + + <div *ngSwitchCase="automatedUpgradeType.VF"> + <upgrade-line-item arrowName="arrow2-right-child" text="{{vspInstance.vspName}} ({{vspInstance.vspVersion}})"></upgrade-line-item> + </div> + + <div *ngSwitchCase="automatedUpgradeType.SERVICE_PROXY"> + <upgrade-line-item arrowName="arrow2-right-child" prefix="Service Proxy: " text="{{vspInstance.instanceName}}"></upgrade-line-item> + <div class="second-line"> + <upgrade-line-item arrowName="arrow2-right-child" icon="{{vspInstance.icon}}" text="{{ vspInstance.vspName }} ({{vspInstance.vspVersion}})"></upgrade-line-item> + </div> + </div> + + <div *ngSwitchCase="automatedUpgradeType.ALLOTTED_RESOURCE"> + <upgrade-line-item class="allotted-resource-line" arrowName="arrow2-right-child" prefix=" VNF: " text="{{vspInstance.originVfName}}"></upgrade-line-item> + <upgrade-line-item arrowName="arrow2-right" prefix=" VFC: " text="{{vspInstance.instanceName}}"></upgrade-line-item> + <div class="second-line"> + <upgrade-line-item arrowName="arrow2-right-child" icon="vspInstance.icon" prefix=" VFC: " text="{{ vspInstance.vspName }} ({{vspInstance.vspVersion}})"></upgrade-line-item> + </div> + </div> + </div> + </li> +</ul>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.less b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.less new file mode 100644 index 0000000000..24f8107e84 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.less @@ -0,0 +1,25 @@ +@import "./../../../../../../assets/styles/override"; + +.list-item-inner-content { + .first-line { + + padding: 2px 20px 2px 65px; + border-bottom: 1px solid @sdcui_color_silver; + + &:last-child { + border-bottom: none; + } + } + + .second-line { + padding-left: 45px; + font-weight: bold; + } + + .allotted-resource-line { + + float: left; + margin-right: 15px; + + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.ts new file mode 100644 index 0000000000..0fa467c3ae --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component.ts @@ -0,0 +1,24 @@ +/** + * Created by ob0695 on 5/7/2018. + */ +/** + * Created by ob0695 on 5/2/2018. + */ +import {Component, Input} from "@angular/core"; +import { + VspInstanceUiObject, + AutomatedUpgradeInstanceType +} from "../../automated-upgrade-models/ui-component-to-upgrade"; + + +@Component({ + selector: 'upgrade-list-item-inner-content', + templateUrl: './list-item-inner-content.component.html', + styleUrls: ['./list-item-inner-content.component.less'] +}) +export class UpgradeListItemInnerContent { + + @Input() vspInstances:Array<VspInstanceUiObject>; + + automatedUpgradeType = AutomatedUpgradeInstanceType; +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-order-pipe/list-item-order-pipe.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-order-pipe/list-item-order-pipe.ts new file mode 100644 index 0000000000..abebe0bdd8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/list-item-order-pipe/list-item-order-pipe.ts @@ -0,0 +1,42 @@ +import {Pipe, PipeTransform} from "@angular/core"; +import {ServiceContainerToUpgradeUiObject} from "../../automated-upgrade-models/ui-component-to-upgrade"; + +/* + This filter needs to return all not upgraded components sorted by name first, after that all upgraded components sorted by name + And in the end all the locked components sorted by name + */ + +@Pipe({ + name: 'upgradeListItemOrderBy' +}) +export class UpgradeListItemOrderPipe implements PipeTransform { + + private orderByName = (firstName:string, secondName:string):number => { + var textA = firstName.toLocaleLowerCase(); + var textB = secondName.toLocaleLowerCase(); + return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; + } + + transform(array:Array<ServiceContainerToUpgradeUiObject>):Array<ServiceContainerToUpgradeUiObject> { + array.sort((first:ServiceContainerToUpgradeUiObject, second:ServiceContainerToUpgradeUiObject) => { + if (first.isLock && second.isLock) { + return this.orderByName(first.name, second.name); + } else if (first.isLock) { + return 1; + } else if (second.isLock) { + return -1; + } else { + if (first.isAlreadyUpgrade && second.isAlreadyUpgrade) { + return this.orderByName(first.name, second.name); + } else if (first.isAlreadyUpgrade) { + return 1; + } else if (second.isAlreadyUpgrade) { + return -1; + } else { + return this.orderByName(first.name, second.name); + } + } + }); + return array; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.html new file mode 100644 index 0000000000..e848283f67 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.html @@ -0,0 +1,6 @@ +<div class="upgrade-line-item"> + <svg-icon *ngIf="arrowName" name="{{arrowName}}" size="medium" mode="secondary"></svg-icon> + <div *ngIf="icon" class="line-item-icon small sprite-services-icons {{icon}}"></div> + <span *ngIf="prefix" class="line-item-prefix">{{prefix}}</span> + <span class="line-item-text">{{text}}</span> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.less b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.less new file mode 100644 index 0000000000..d558e881c2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.less @@ -0,0 +1,17 @@ +.upgrade-line-item{ + + display: flex; + align-items: center; + + .line-item-icon { + margin-left: 10px; + } + + .line-item-prefix { + margin-left: 7px; + } + + .line-item-text { + margin-left: 7px; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.ts new file mode 100644 index 0000000000..b256d7efe8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component.ts @@ -0,0 +1,19 @@ +import {Component, Input} from "@angular/core"; + +@Component({ + selector: 'upgrade-line-item', + templateUrl: './upgrade-line-item.component.html', + styleUrls: ['./upgrade-line-item.component.less'] +}) + +export class UpgradeLineItemComponent { + + @Input() arrowName:string; + @Input() icon:string; + @Input() prefix:string; + @Input() text:string; + + constructor() { + + } +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.html new file mode 100644 index 0000000000..c6d3add7e6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.html @@ -0,0 +1,13 @@ +<div class="components-to-upgrade-list-item"> + <div class="component-to-upgrade-data"> + <div class="component-to-upgrade-icon small sprite-services-icons {{upgradedComponent.icon}}"></div> + <span class="component-to-upgrade-name">{{upgradedComponent.name}} ({{upgradedComponent.version}})</span> + <svg-icon-label class="upgraded-component-status" *ngIf="upgradeComponentStatus.status === 'OK'" + name="success-circle-o" mode="success" size="medium" clickable="false"></svg-icon-label> + <svg-icon-label class="upgraded-component-status" *ngIf="upgradeComponentStatus.status !== 'OK'" + name="x-circle-o" mode="error" + size="medium" [label]="'Update Failed'" clickable="false" disabled="false" labelPlacement="left"></svg-icon-label> + </div> + + <upgrade-list-item-inner-content [vspInstances]="upgradedComponent.vspInstances"></upgrade-list-item-inner-content> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.ts new file mode 100644 index 0000000000..726bc67fcf --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component.ts @@ -0,0 +1,19 @@ +import {Component, Input} from "@angular/core"; +import {AutomatedUpgradeStatusResponse} from "../../../../services/responses/automated-upgrade-response"; +import {ServiceContainerToUpgradeUiObject} from "../../automated-upgrade-models/ui-component-to-upgrade"; + +@Component({ + selector: 'upgrade-list-status-item', + templateUrl: './upgrade-list-status-item.component.html', + styleUrls: ['./../upgrade-list-item.component.less'] +}) +export class UpgradeListItemStatusComponent { + + @Input() upgradedComponent: ServiceContainerToUpgradeUiObject; + @Input() upgradeComponentStatus: AutomatedUpgradeStatusResponse; + + constructor () { + + } + +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item.component.less b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item.component.less new file mode 100644 index 0000000000..4507929b7f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item.component.less @@ -0,0 +1,44 @@ +@import "./../../../../../assets/styles/override"; + +.components-to-upgrade-list-item { + border: 1px solid @sdcui_color_light-gray; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.11); + margin-bottom: 10px; + line-height: 40px; + + .component-to-upgrade-data { + display: flex; + background-color: @sdcui_color_light-silver; + height: 40px; + padding: 0 10px; + align-items: center; + + .component-to-upgrade-icon { + margin-left: 26px; + } + + .component-to-upgrade-name { + margin-left: 8px; + } + + .component-to-upgrade-checkbox { + height: 24px; // Workaround can not vertical center + } + + .upgraded-component-status { + margin-left: auto; + } + } + + .vsp-data { + align-items: center; + display: flex; + border-bottom: 1px solid @sdcui_color_silver; + padding: 0 10px 0 64px; + .vsp-data-label { + color: @sdcui_color_text-black; + margin-left: 7px; + font-weight: 600; + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.html new file mode 100644 index 0000000000..6e6af0cd1c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.html @@ -0,0 +1,19 @@ +<div class="components-to-upgrade-list-item "> + <div class="component-to-upgrade-data"> + <sdc-checkbox class="component-to-upgrade-checkbox" + *ngIf="!componentToUpgrade.isAlreadyUpgrade && !componentToUpgrade.isLock" + [checked]="true" + [disabled]="disabled" + (checkedChange)="onComponentChecked(componentToUpgrade.uniqueId)"> + </sdc-checkbox> + <svg-icon *ngIf="componentToUpgrade.isAlreadyUpgrade" name="success-circle-o" mode="success" + size="medium"></svg-icon> + <svg-icon *ngIf="!componentToUpgrade.isAlreadyUpgrade && componentToUpgrade.isLock" name="locked" mode="info" + size="small"></svg-icon> + <div class="component-to-upgrade-icon small sprite-services-icons {{componentToUpgrade.icon}}"></div> + <span class="component-to-upgrade-name">{{componentToUpgrade.name}} ({{componentToUpgrade.version}})</span> + </div> + + <upgrade-list-item-inner-content [vspInstances]="componentToUpgrade.vspInstances"></upgrade-list-item-inner-content> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.ts new file mode 100644 index 0000000000..806b83126f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component.ts @@ -0,0 +1,23 @@ +import {Component, Input, Output, EventEmitter} from "@angular/core"; +import {ServiceContainerToUpgradeUiObject, AutomatedUpgradeInstanceType} from "../../automated-upgrade-models/ui-component-to-upgrade"; + +@Component({ + selector: 'upgrade-list-item', + templateUrl: './upgrade-list-item.component.html', + styleUrls: ['./../upgrade-list-item.component.less'] +}) +export class UpgradeListItemComponent { + + @Input() componentToUpgrade:ServiceContainerToUpgradeUiObject; + @Input() disabled: boolean; + @Output() onCheckedChange:EventEmitter<any> = new EventEmitter<any>(); + + constructor() { + } + + onComponentChecked = ():void => { + this.onCheckedChange.emit(); + } + + +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.html b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.html new file mode 100644 index 0000000000..67e7f08436 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.html @@ -0,0 +1,25 @@ +<div class="automated-upgrade-component"> + + <div *ngIf="certificationStatusText" class="certification-status"> + <svg-icon-label + name="success-circle-o" + mode="success" + size="medium" + clickable="false" + disabled="false" + labelPlacement="right"> + </svg-icon-label> + <span class="certification-status-text">{{certificationStatusText}}</span> + </div> + + <div> + <span innerHTML="{{informationText}}"> </span> + <div class="components-to-upgrade-list"> + <ul> + <li *ngFor="let component of componentsToUpgrade | upgradeListItemOrderBy"> + <upgrade-list-item (onCheckedChange)= "onComponentSelected(component.uniqueId)" [disabled]="disabled" [componentToUpgrade]="component"></upgrade-list-item> + </li> + </ul> + </div> + </div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.less b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.less new file mode 100644 index 0000000000..2ab21303d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.less @@ -0,0 +1,23 @@ +.automated-upgrade-component { + + min-height: 280px; + .certification-status { + border: 1px solid #4ca90c; + border-left: 5px solid #4ca90c; + margin-bottom: 20px; + padding: 5px 5px 5px 10px; + font-weight: bold; + display: flex; + line-height: 21px; + + .certification-status-text { + padding-left: 5px; + } + } + .components-to-upgrade-list { + + overflow: auto; + max-height: 300px; + margin-top: 15px; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.ts new file mode 100644 index 0000000000..9ae73497ef --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.component.ts @@ -0,0 +1,65 @@ +/** + * Created by ob0695 on 4/18/2018. + */ +/*- + * ============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========================================================= + */ + +import {Component, Input, Inject, forwardRef} from "@angular/core"; +import {TranslateService} from "../../shared/translator/translate.service"; +import {ServiceContainerToUpgradeUiObject} from "./automated-upgrade-models/ui-component-to-upgrade"; +import {AutomatedUpgradeService} from "./automated-upgrade.service"; + +@Component({ + selector: 'upgrade-vsp', + templateUrl: './automated-upgrade.component.html', + styleUrls: ['./automated-upgrade.component.less'], + providers: [TranslateService] +}) +export class AutomatedUpgradeComponent { + + @Input() componentsToUpgrade: Array<ServiceContainerToUpgradeUiObject>; + @Input() certificationStatusText: string; + @Input() informationText: string; + @Input() disabled: string; + private selectedComponentsToUpgrade: Array<string> = []; + + constructor (@Inject(forwardRef(() => AutomatedUpgradeService)) private automatedUpgradeService: AutomatedUpgradeService) { + } + + ngOnInit(): void { // We need to check all elements that needed upgrade as default + this.selectedComponentsToUpgrade = _.filter(this.componentsToUpgrade, (componentToUpgrade:ServiceContainerToUpgradeUiObject) => { + return !componentToUpgrade.isAlreadyUpgrade && !componentToUpgrade.isLock; + }).map(element => element.uniqueId) + } + + onComponentSelected = (uniqueId:string):void => { + + if(this.selectedComponentsToUpgrade.indexOf(uniqueId) > -1) { + this.selectedComponentsToUpgrade = _.without(this.selectedComponentsToUpgrade, uniqueId); + } else { + this.selectedComponentsToUpgrade.push(uniqueId); + } + if(this.selectedComponentsToUpgrade.length === 0) { + this.automatedUpgradeService.changeUpgradeButtonState(true); + } else { + this.automatedUpgradeService.changeUpgradeButtonState(false); + } + } +} diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.module.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.module.ts new file mode 100644 index 0000000000..19f6412071 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.module.ts @@ -0,0 +1,34 @@ +/** + * Created by ob0695 on 4/18/2018. + */ +import { NgModule } from "@angular/core"; +import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; +import {CommonModule} from "@angular/common"; +import {AutomatedUpgradeStatusComponent} from "./automated-upgrade-status/automated-upgrade-status.component"; +import {AutomatedUpgradeComponent} from "./automated-upgrade.component"; +import {UpgradeListItemComponent} from "./automated-upgrade-ui-components/upgrade-list-item/upgrade-list-item.component"; +import {UpgradeListItemStatusComponent} from "./automated-upgrade-ui-components/upgrade-list-item-status/upgrade-list-status-item.component"; +import {TranslateService} from "../../shared/translator/translate.service"; +import {UpgradeListItemInnerContent} from "./automated-upgrade-ui-components/list-item-inner-content/list-item-inner-content.component"; +import {UpgradeLineItemComponent} from "./automated-upgrade-ui-components/upgrade-line-item/upgrade-line-item.component"; +import {UpgradeListItemOrderPipe} from "./automated-upgrade-ui-components/list-item-order-pipe/list-item-order-pipe"; + +@NgModule({ + declarations: [ + AutomatedUpgradeStatusComponent, + UpgradeListItemComponent, + UpgradeListItemStatusComponent, + AutomatedUpgradeComponent, + UpgradeListItemInnerContent, + UpgradeLineItemComponent, + UpgradeListItemOrderPipe + ], + imports: [CommonModule, SdcUiComponentsModule], + exports: [], + entryComponents: [ + AutomatedUpgradeComponent, AutomatedUpgradeStatusComponent + ], + providers: [TranslateService] +}) +export class AutomatedUpgradeModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.service.ts b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.service.ts new file mode 100644 index 0000000000..0acfececaa --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/automated-upgrade/automated-upgrade.service.ts @@ -0,0 +1,279 @@ +import {SdcUiComponents} from "sdc-ui/lib/angular"; +import {Injectable, Inject} from "@angular/core"; +import {IModalConfig} from "sdc-ui/lib/angular/modals/models/modal-config"; +import {AutomatedUpgradeComponent} from "./automated-upgrade.component"; +import {Component} from "../../../models/components/component"; +import {ComponentServiceNg2} from "../../services/component-services/component.service"; +import {GeneralStatus, ComponentType} from "../../../utils/constants"; +import {IDependenciesServerResponse} from "../../services/responses/dependencies-server-response"; +import {AutomatedUpgradeStatusComponent} from "./automated-upgrade-status/automated-upgrade-status.component"; +import {AutomatedUpgradeStatusResponse} from "../../services/responses/automated-upgrade-response"; +import Dictionary = _.Dictionary; +import {TranslateService, ITranslateArgs} from "../../shared/translator/translate.service"; +import { + ServiceContainerToUpgradeUiObject, + AllottedResourceInstanceUiObject, VspInstanceUiObject +} from "./automated-upgrade-models/ui-component-to-upgrade"; + +export interface IAutomatedUpgradeRequestObj { + serviceId:string; + resourceId?:string; +} + +export enum Placement { + left = "left" +} + +@Injectable() +export class AutomatedUpgradeService { + + private vspComponent:Component; + private uiComponentsToUpgrade:Array<ServiceContainerToUpgradeUiObject>; + private componentType:string; + + constructor(private modalService:SdcUiComponents.ModalService, + private componentService:ComponentServiceNg2, + private translateService:TranslateService) { + } + + + public convertToServerRequest = (selectedServices:Array<string>):Array<IAutomatedUpgradeRequestObj> => { + + let automatedRequest:Array<IAutomatedUpgradeRequestObj> = []; + _.forEach(selectedServices, (serviceId:string) => { + let serviceToUpgrade:ServiceContainerToUpgradeUiObject = _.find(this.uiComponentsToUpgrade, (service:ServiceContainerToUpgradeUiObject) => { + return serviceId === service.uniqueId; + }); + + if (serviceToUpgrade.vspInstances[0] instanceof AllottedResourceInstanceUiObject) { // If this is allotted resource instances, we need to take the origin vf id (all the instances have the save origin vspId + automatedRequest.push({ + serviceId: serviceId, + resourceId: (<AllottedResourceInstanceUiObject> serviceToUpgrade.vspInstances[0]).originVfId + }); + } else { + automatedRequest.push({serviceId: serviceId}); + } + }); + return automatedRequest; + } + + private getStatusText = (statusMap:Dictionary<AutomatedUpgradeStatusResponse>):string => { + let failedUpgraded = _.filter(_.flatMap(statusMap), (upgradeStatus:AutomatedUpgradeStatusResponse) => { + return upgradeStatus.status !== GeneralStatus.OK + }); + + if (failedUpgraded.length > 0) { + return this.getTextByComponentType("_UPGRADE_STATUS_FAIL"); + } + return this.getTextByComponentType("_UPGRADE_STATUS_SUCCESS"); + } + + private disabledAllModalButtons = ():void => { + this.modalService.getCurrentInstance().innerModalContent.instance.disabled = true; + this.modalService.getCurrentInstance().buttons[0].show_spinner = true; + this.modalService.getCurrentInstance().buttons[1].disabled = true; + } + + public changeUpgradeButtonState = (isDisabled:boolean):void => { + if (this.modalService.getCurrentInstance().buttons[0].disabled !== isDisabled) { + this.modalService.getCurrentInstance().buttons[0].disabled = isDisabled; + } + } + + //TODO We will need to replace this function after sdc-ui modal new design, this is just a workaround + public automatedUpgrade = ():void => { + + let selectedServices = this.modalService.getCurrentInstance().innerModalContent.instance.selectedComponentsToUpgrade; + this.disabledAllModalButtons(); + this.componentService.automatedUpgrade(this.vspComponent.componentType, this.vspComponent.uniqueId, this.convertToServerRequest(selectedServices)).subscribe((automatedUpgradeStatus:any) => { + + if (automatedUpgradeStatus.status === GeneralStatus.OK) { + + let statusMap:Dictionary<AutomatedUpgradeStatusResponse> = _.keyBy(automatedUpgradeStatus.componentToUpgradeStatus, 'name'); + // In the status modal we only showing the upgraded component that the user selected, not the entire list + let upgradedComponent:Array<ServiceContainerToUpgradeUiObject> = _.filter(this.uiComponentsToUpgrade, (component:ServiceContainerToUpgradeUiObject) => { + return selectedServices.indexOf(component.uniqueId) > -1; + }); + + _.forEach(upgradedComponent, (upgradedComponent:ServiceContainerToUpgradeUiObject) => { // If upgrade success we need to upgrade the version all success + if (statusMap[upgradedComponent.name].status === GeneralStatus.OK) { + upgradedComponent.version = statusMap[upgradedComponent.name].version; + _.forEach(upgradedComponent.vspInstances, (instance:VspInstanceUiObject) => { + instance.vspVersion = this.vspComponent.version; + }); + } + }); + + let statusModalTitle = this.getTextByComponentType("_UPGRADE_STATUS_TITLE"); + this.modalService.getCurrentInstance().setTitle(statusModalTitle); + this.modalService.getCurrentInstance().getButtons().splice(0, 1); // Remove the upgrade button + this.modalService.getCurrentInstance().buttons[0].disabled = false; // enable close again + this.modalService.getCurrentInstance().innerModalContent.destroy(); + this.modalService.createInnnerComponent(AutomatedUpgradeStatusComponent, { + upgradedComponentsList: upgradedComponent, + upgradeStatusMap: statusMap, + statusText: this.getStatusText(statusMap) + }); + } + }); + } + + public isAlreadyAdded = (uniqueId:string):ServiceContainerToUpgradeUiObject => { + let componentToUpgrade = _.find(this.uiComponentsToUpgrade, (componentToUpgrade:ServiceContainerToUpgradeUiObject) => { + return componentToUpgrade.uniqueId === uniqueId; + }); + return componentToUpgrade; + } + + public initVfUpgradeData = (serviceToUpgrade:IDependenciesServerResponse, vsp:IDependenciesServerResponse) => { + + let existed = this.isAlreadyAdded(serviceToUpgrade.uniqueId); + if (existed) { // We will take the VF with the lower version existed - only one exist all the time in vf upgrade + if (vsp.version < existed.vspInstances[0].vspVersion) { + existed.vspInstances = []; + existed.addVfInstance(vsp, this.vspComponent.version); + } + } else { + let dependencyUiObj:ServiceContainerToUpgradeUiObject = new ServiceContainerToUpgradeUiObject(serviceToUpgrade); + dependencyUiObj.addVfInstance(vsp, this.vspComponent.version); + this.uiComponentsToUpgrade.push(dependencyUiObj); + } + } + + // Service data will create instances of proxy or allotted resources + public initServiceUpgradeData = (serviceToUpgrade:IDependenciesServerResponse, vsp:IDependenciesServerResponse, instanceNames:Array<string>, allottedOriginVf?:IDependenciesServerResponse) => { + + let existedService = this.isAlreadyAdded(serviceToUpgrade.uniqueId); + if (existedService) { + existedService.addMultipleInstances(vsp, this.vspComponent.version, instanceNames, allottedOriginVf); + } + else { + let dependencyUiObj:ServiceContainerToUpgradeUiObject = new ServiceContainerToUpgradeUiObject(serviceToUpgrade); + dependencyUiObj.addMultipleInstances(vsp, this.vspComponent.version, instanceNames, allottedOriginVf); + this.uiComponentsToUpgrade.push(dependencyUiObj); + } + } + + /* + The server return response of 3 level nested object + First level - Vsp data by version + Each vsp have a decencies (the services contains the vsp - By default this is vf upgrade + If instancesNames exist - this can be proxy or allotted + If we have second layer of dependencies than this is allotted + Since we display the data the opposite way the BE return, this function will order the data in order to display it + */ + public convertToComponentsToUpgradeUiObjArray = (dependenciesServerResponse:Array<IDependenciesServerResponse>):void => { + + this.uiComponentsToUpgrade = []; + + _.forEach(dependenciesServerResponse, (vsp:IDependenciesServerResponse) => { // 3 nested levels - 1 level for vf, 2 level proxy, 3 levels allotted + if (vsp.dependencies) { + _.forEach(vsp.dependencies, (dependency:IDependenciesServerResponse) => { + if (dependency.instanceNames) { // Init service upgrade data + if (dependency.dependencies) { + _.forEach(dependency.dependencies, (serviceContainer:IDependenciesServerResponse) => { // Initiate allotted_resource instances + this.initServiceUpgradeData(serviceContainer, vsp, dependency.instanceNames, dependency); + }); + } else { //Init service_proxy instances + this.initServiceUpgradeData(dependency, vsp, dependency.instanceNames); + } + } else { // Init vf upgrade data + this.initVfUpgradeData(dependency, vsp); + } + }) + } + }); + } + + public isAllComponentsUpgraded = ():boolean => { + let isAllComponentUpgrade = _.filter(this.uiComponentsToUpgrade, (component:ServiceContainerToUpgradeUiObject) => { + return !component.isAlreadyUpgrade; + }); + return isAllComponentUpgrade.length === 0; + } + + public isAllComponentsLocked = ():boolean => { + let unLockedComponents = _.filter(this.uiComponentsToUpgrade, (component:ServiceContainerToUpgradeUiObject) => { + return !component.isLock; + }); + return unLockedComponents.length === 0; + } + + public isUpgradeNeeded = ():boolean => { + let neededUpgradeList = _.filter(this.uiComponentsToUpgrade, (component:ServiceContainerToUpgradeUiObject) => { + return !component.isLock && !component.isAlreadyUpgrade; + }); + return neededUpgradeList.length > 0; + } + + private getTextByComponentType (textLabel: string, params?:ITranslateArgs) { + return this.translateService.translate(this.componentType + textLabel, params); + } + public getInformationTextToDisplay = ():string => { + + let isAllComponentsUpgraded = this.isAllComponentsUpgraded(); + let isAllComponentsLocked = this.isAllComponentsLocked(); + let params = {vspName: this.vspComponent.name, vspVersion: this.vspComponent.version}; + + if (this.uiComponentsToUpgrade.length === 0) { + return this.getTextByComponentType("_NOTHING_TO_UPGRADE", params); + } + + switch (true) { + + case this.isUpgradeNeeded(): + { + return this.getTextByComponentType("_AUTOMATED_UPGRADE_WITH_COMPONENTS_TO_UPGRADE", params); + } + case !this.isUpgradeNeeded() && isAllComponentsLocked: + { + return this.getTextByComponentType("_AUTOMATED_UPGRADE_ALL_COMPONENTS_LOCKED", params); + } + case !this.isUpgradeNeeded() && !isAllComponentsLocked && isAllComponentsUpgraded: + { + return this.getTextByComponentType("_AUTOMATED_UPGRADE_ALL_COMPONENTS_UPGRADED", params); + } + case !this.isUpgradeNeeded() && !isAllComponentsLocked && !isAllComponentsUpgraded: + { + return this.getTextByComponentType("_AUTOMATED_UPGRADE_ALL_COMPONENTS_LOCKED", params); + } + } + } + + public openAutomatedUpgradeModal = (componentsToUpgrade:Array<IDependenciesServerResponse>, component:Component, isAfterCertification?:boolean):void => { + + this.vspComponent = component; + this.componentType = this.vspComponent.isResource() ? ComponentType.RESOURCE : ComponentType.SERVICE; + + this.convertToComponentsToUpgradeUiObjArray(componentsToUpgrade); + let informationalText = this.getInformationTextToDisplay(); + let modalTitle = this.getTextByComponentType("_UPGRADE_TITLE"); + let certificationText = isAfterCertification ? this.getTextByComponentType("_CERTIFICATION_STATUS_TEXT", {resourceName: this.vspComponent.name}) : undefined; + + let upgradeVspModalConfig:IModalConfig = { + title: modalTitle, + size: "md", + type: "custom", + testId: "upgradeVspModal", + buttons: [ + { + text: this.vspComponent.isResource() ? "UPGRADE" : "UPDATE", + spinner_position: Placement.left, + size: 'sm', + callback: this.automatedUpgrade, + closeModal: false, + disabled: !this.isUpgradeNeeded(), + + }, + {text: 'CLOSE', size: 'sm', closeModal: true, type: 'secondary'} + ] + }; + + this.modalService.openCustomModal(upgradeVspModalConfig, AutomatedUpgradeComponent, { + componentsToUpgrade: this.uiComponentsToUpgrade, + informationText: informationalText, + certificationStatusText: certificationText + }); + } +} + diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.html new file mode 100644 index 0000000000..731d6a3855 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.html @@ -0,0 +1,14 @@ +<div class="component-details-panel-header" data-tests-id="w-sdc-designer-sidebar-head"> + + <div class="icon"> + <div class="large {{iconClassName}}"> + <div [ngClass]="{'non-certified': nonCertified}" tooltip="Not certified"></div> + </div> + </div> + + <div class="title" data-tests-id="selectedCompTitle" tooltip="​{{name}}">{{name}}</div> + + <svg-icon-label *ngIf="!isViewOnly" name="edit-file-o" clickable="true" size="small" class="rename-instance" data-tests-id="renameInstance" (click)="renameInstance()"></svg-icon-label> + <svg-icon-label *ngIf="!isViewOnly" name="trash-o" clickable="true" size="small" class="delete-instance" data-tests-id="deleteInstance" (click)="deleteInstance()"></svg-icon-label> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.less new file mode 100644 index 0000000000..9bbc765761 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.less @@ -0,0 +1,34 @@ +/deep/ +.component-details-panel-header { + display: flex; + flex-direction: row; + height: 120px; + align-items: center; + + .icon { + margin: 0 20px; + } + + .title { + font-size: 16px; + text-overflow: ellipsis; + max-width: 180px; + white-space: nowrap; + overflow: hidden; + } + + .rename-instance { + position: absolute; + top: 14px; + right: 20px; + cursor: pointer; + } + + .delete-instance { + position: absolute; + top: 14px; + right: 40px; + cursor: pointer; + } + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.ts new file mode 100644 index 0000000000..ab659a3b8f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.component.ts @@ -0,0 +1,135 @@ +/*- + * ============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========================================================= + */ + +import { Component, Input, AfterViewInit, SimpleChanges, OnInit, OnChanges } from "@angular/core"; +import { SdcUiComponents } from "sdc-ui/lib/angular"; +import { IModalConfig } from 'sdc-ui/lib/angular/modals/models/modal-config'; +import { ZoneInstanceType } from 'app/models/graph/zones/zone-instance'; +import { ValueEditComponent } from './../../../../components/ui/forms/value-edit/value-edit.component'; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; +import { PoliciesService } from '../../../../services/policies.service'; +import { GroupsService } from '../../../../services/groups.service'; +import {IZoneService} from "../../../../../models/graph/zones/zone"; +import { EventListenerService, LoaderService } from "../../../../../services"; +import { GRAPH_EVENTS, EVENTS } from "../../../../../utils"; +import { UIZoneInstanceObject } from "../../../../../models/ui-models/ui-zone-instance-object"; +import { ModalButtonComponent } from "sdc-ui/lib/angular/components"; + +@Component({ + selector: 'ng2-composition-panel-header', + templateUrl: './panel-header.component.html', + styleUrls: ['./panel-header.component.less'] +}) +export class CompositionPanelHeaderComponent implements OnInit, OnChanges { + + @Input() topologyTemplate: TopologyTemplate; + @Input() selectedZoneInstanceType: ZoneInstanceType; + @Input() selectedZoneInstanceId: string; + @Input() name: string; + @Input() nonCertified: boolean; + @Input() isViewOnly: boolean; + @Input() isLoading: boolean; + + constructor(private groupsService:GroupsService, private policiesService: PoliciesService, + private modalService:SdcUiComponents.ModalService, private eventListenerService:EventListenerService) { } + + private service:IZoneService; + private iconClassName: string; + + ngOnInit(): void { + this.init(); + } + + ngOnChanges (changes:SimpleChanges):void { + if(changes.selectedZoneInstanceId){ + this.init(); + } + } + + ngOnDestroy() { + + + } + private init = (): void => { + if (this.selectedZoneInstanceType === ZoneInstanceType.POLICY) { + this.iconClassName = "sprite-policy-icons policy"; + this.service = this.policiesService; + } else if (this.selectedZoneInstanceType === ZoneInstanceType.GROUP) { + this.iconClassName = "sprite-group-icons group"; + this.service = this.groupsService; + } else { + this.iconClassName = "sprite-resource-icons defaulticon"; + } + } + + private renameInstance = (): void => { + const modalConfig = { + title: "Edit Name", + size: "sm", + type: "custom", + testId: "renameInstanceModal", + buttons: [ + {id: 'saveButton', text: 'OK', size: 'xsm', callback: this.saveInstanceName, closeModal: false}, + {id: 'cancelButton', text: 'Cancel', size: 'sm', closeModal: true} + ] as ModalButtonComponent[] + } as IModalConfig; + this.modalService.openCustomModal(modalConfig, ValueEditComponent, {name: this.name, validityChangedCallback: this.enableOrDisableSaveButton}); + }; + + private enableOrDisableSaveButton = (shouldEnable: boolean): void => { + let saveButton: ModalButtonComponent = this.modalService.getCurrentInstance().getButtonById('saveButton'); + saveButton.disabled = !shouldEnable; + } + + private saveInstanceName = ():void => { + let currentModal = this.modalService.getCurrentInstance(); + let nameFromModal:string = currentModal.innerModalContent.instance.name; + + if(nameFromModal != this.name){ + currentModal.buttons[0].disabled = true; + this.service.updateName(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.selectedZoneInstanceId, nameFromModal).subscribe((success)=>{ + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, nameFromModal); + this.modalService.closeModal(); + }, (error)=> { + currentModal.buttons[0].disabled = false; + }); + } else { + this.modalService.closeModal(); + } + }; + + private deleteInstance = (): void => { + let title:string = "Delete Confirmation"; + let message:string = "Are you sure you would like to delete "+ this.name + "?"; + this.modalService.openAlertModal(title, message, "OK", this.deleteInstanceConfirmed, "deleteInstanceModal"); + }; + + private deleteInstanceConfirmed = () => { + this.eventListenerService.notifyObservers(EVENTS.SHOW_LOADER_EVENT + 'composition-graph'); + this.service.deleteZoneInstance(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.selectedZoneInstanceId).finally(()=> { + this.eventListenerService.notifyObservers(EVENTS.HIDE_LOADER_EVENT + 'composition-graph'); + }).subscribe(()=> { + let deletedItem:UIZoneInstanceObject = new UIZoneInstanceObject(this.selectedZoneInstanceId, this.selectedZoneInstanceType, this.name); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, deletedItem); + }); + }; + +} + diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.module.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.module.ts new file mode 100644 index 0000000000..bde0a14669 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-header/panel-header.module.ts @@ -0,0 +1,51 @@ +/*- + * ============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========================================================= + */ +import { NgModule } from "@angular/core"; +import { HttpModule } from "@angular/http"; +import { FormsModule } from "@angular/forms"; +import { BrowserModule } from "@angular/platform-browser"; +import { CompositionPanelHeaderComponent } from "./panel-header.component"; +import { UiElementsModule } from './../../../../components/ui/ui-elements.module'; +import { ValueEditComponent } from './../../../../components/ui/forms/value-edit/value-edit.component'; +import { SdcUiComponentsModule } from "sdc-ui/lib/angular"; +import { ModalFormsModule } from "app/ng2/components/ui/forms/modal-forms.module"; + +@NgModule({ + declarations: [ + CompositionPanelHeaderComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + UiElementsModule, + SdcUiComponentsModule, + ModalFormsModule + ], + entryComponents: [ + CompositionPanelHeaderComponent, ValueEditComponent + ], + exports: [ + CompositionPanelHeaderComponent + ], +}) +export class CompositionPanelHeaderModule { + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/base/base-tab.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/base/base-tab.component.less new file mode 100644 index 0000000000..aa8e75115f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/base/base-tab.component.less @@ -0,0 +1,66 @@ +@import './../../../../../../../assets/styles/mixins'; +@import "./../../../../../../../assets/styles/variables-old"; +@import './../../../../../../../assets/styles/mixins_old'; + +/deep/ +.expand-collapse-content { + padding: 20px; +} + +.component-details-panel-tab-no-data { + margin: 0 auto; + padding-top: 30px; + text-align: center; + + .component-details-panel-tab-no-data-title { + font-family: @font-opensans-bold; + } + +} + +.component-details-panel-large-item { + font-family: OpenSans-Semibold, sans-serif; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + height: 32px; + line-height: 32px; + vertical-align: middle; + + &:hover { + background-color: #f8f8f8; + margin: 0 -20px; /* to fill expand-collapse-content padding */ + padding: 0 20px; + .component-details-panel-item-delete { + visibility: visible; + } + } +} + +.component-details-panel-item { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + height: 22px; + line-height: 22px; + vertical-align: middle; + + &.description { + margin-top: 28px; + white-space: normal; + word-wrap: break-word; + .value { + max-width: none; + font-weight: normal; + font-family: @font-opensans-regular; + } + } + + .name { font-family: OpenSans-Semibold, sans-serif; } + .value { } +} + +.component-details-panel-item-delete { + cursor: pointer; + visibility: hidden; +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.html new file mode 100644 index 0000000000..3c875fd930 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.html @@ -0,0 +1,30 @@ +<ng2-expand-collapse state="0"> + + <header tooltip="General Information">General Info</header> + + <content> + <!-- CATEGORY --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_CATEGORY' | translate"></span> + <span class="value" data-tests-id="rightTab_category" tooltip="Group">Group</span> + </div> + + <!-- SUB CATEGORY --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_SUB_CATEGORY' | translate"></span> + <span class="value" data-tests-id="rightTab_subCategory" tooltip="Group">Group</span> + </div> + + <!-- VERSION --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_VERSION' | translate"></span> + <span class="value" data-tests-id="rightTab_version" tooltip="{{group.version}}">{{group.version}}</span> + </div> + + <!-- DESCRIPTION --> + <div class="component-details-panel-item description"> + <span class="name" [innerHTML]="'GENERAL_LABEL_DESCRIPTION' | translate"></span> + <span class="value" ellipsis="group.description" max-chars="55" data-tests-id="rightTab_description">{{group.description}}</span> + </div> + </content> +</ng2-expand-collapse> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.ts new file mode 100644 index 0000000000..26602224da --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-information-tab.component.ts @@ -0,0 +1,39 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter } from "@angular/core"; +import { GroupInstance } from 'app/models/graph/zones/group-instance'; + +@Component({ + selector: 'group-information-tab', + templateUrl: './group-information-tab.component.html', + styleUrls: ['./../base/base-tab.component.less'] +}) +export class GroupInformationTabComponent { + + @Input() group: GroupInstance; + @Input() isViewOnly: boolean; + + constructor() { + + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.html new file mode 100644 index 0000000000..f298a39a87 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.html @@ -0,0 +1,31 @@ +<div class="w-sdc-designer-sidebar-section-title" tooltip="Members">Members + <svg-icon-label *ngIf="!isViewOnly" + class="add-members-btn" + name="plus-circle-o" + mode="primary" + size="medium" + label="ADD" + labelPlacement="right" + (click)="openAddMembersModal()"> + </svg-icon-label> +</div> +<div class="expand-collapse-content"> + <ul> + <li *ngFor="let member of members; let i = index" class="component-details-panel-large-item" + tooltip="{{member.name}}"> + <span>{{member.name}}</span> + <svg-icon-label *ngIf="!isViewOnly" + name="trash-o" + clickable="true" + size="small" + class="component-details-panel-item-delete" + data-tests-id="delete_member" + (click)="deleteMember(member)"></svg-icon-label> + </li> + </ul> + + <div *ngIf="members.length===0" class="component-details-panel-tab-no-data"> + <div class="component-details-panel-tab-no-data-title">No data to display yet</div> + <div class="component-details-panel-tab-no-data-content">Add members to group to see members</div> + </div> +</div> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.less new file mode 100644 index 0000000000..1006e864fa --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.less @@ -0,0 +1,13 @@ +/deep/ +.component-details-panel-tab-group-members { + .component-details-panel-large-item { + display: flex; + flex-direction: row; + justify-content: space-between; + } + + .w-sdc-designer-sidebar-section-title { + display: flex; + justify-content: space-between; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.ts new file mode 100644 index 0000000000..148f2133e8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-members-tab.component.ts @@ -0,0 +1,133 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Input, Output, EventEmitter, OnChanges, HostBinding } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { Component as TopologyTemplate } from "app/models"; +import { GroupInstance } from "app/models/graph/zones/group-instance"; +import { GroupsService } from "../../../../../services/groups.service"; +import { SimpleChanges } from "@angular/core/src/metadata/lifecycle_hooks"; +import { MemberUiObject } from "../../../../../../models/ui-models/ui-member-object"; +import { IModalConfig } from "sdc-ui/lib/angular/modals/models/modal-config"; +import { AddElementsComponent } from "../../../../../components/ui/modal/add-elements/add-elements.component"; +import { GRAPH_EVENTS } from 'app/utils'; +import { EventListenerService } from 'app/services/event-listener-service'; +import { ComponentInstance } from "../../../../../../models/componentsInstances/componentInstance"; +import { SdcUiComponents } from "sdc-ui/lib/angular"; + +@Component({ + selector: 'group-members-tab', + templateUrl: './group-members-tab.component.html', + styleUrls: ['./../base/base-tab.component.less', 'group-members-tab.component.less'] +}) + +export class GroupMembersTabComponent implements OnChanges { + + + private members: Array<MemberUiObject>; + + @Input() group: GroupInstance; + @Input() topologyTemplate: TopologyTemplate; + @Input() isViewOnly: boolean; + @Output() isLoading: EventEmitter<boolean> = new EventEmitter<boolean>(); + @HostBinding('class') classes = 'component-details-panel-tab-group-members'; + + constructor(private translateService: TranslateService, + private groupsService: GroupsService, + private modalService: SdcUiComponents.ModalService, + private eventListenerService: EventListenerService + ) { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, this.initMembers) + } + + ngOnChanges(changes:SimpleChanges):void { + this.initMembers(); + } + + deleteMember = (member: MemberUiObject):void => { + this.isLoading.emit(true); + this.groupsService.deleteGroupMember(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.group, member.uniqueId).subscribe( + (updatedMembers:Array<string>) => { + this.group.members = updatedMembers; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, this.group); + }, + error => console.log("Error deleting member!"), + () => this.isLoading.emit(false) + ); + } + + private initMembers = (groupInstance?: GroupInstance) => { + this.group = groupInstance ? groupInstance : this.group; + this.members = this.group.getMembersAsUiObject(this.topologyTemplate.componentInstances); + } + + addMembers = ():void => { + var membersToAdd:Array<MemberUiObject> = this.modalService.getCurrentInstance().innerModalContent.instance.existingElements; //TODO refactor sdc-ui modal in order to return the data + if(membersToAdd.length > 0) { + this.modalService.closeModal(); + this.isLoading.emit(true); + var updatedMembers: Array<MemberUiObject> = _.union(this.members, membersToAdd); + this.groupsService.updateMembers(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.group.uniqueId, updatedMembers).subscribe( + (updatedMembers:Array<string>) => { + this.group.members = updatedMembers; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, this.group); + }, + error => { + console.log("Error updating members!"); + }, () => + this.isLoading.emit(false) + ); + } + } + + getOptionalsMembersToAdd():Array<MemberUiObject> { + + let optionalsMembersToAdd:Array<MemberUiObject> = []; + + // adding all instances as optional members to add if not already exist + _.forEach(this.topologyTemplate.componentInstances, (instance:ComponentInstance) => { + if (!_.some(this.members, (member:MemberUiObject) => { + return member.uniqueId === instance.uniqueId + })) { + optionalsMembersToAdd.push(new MemberUiObject(instance.uniqueId, instance.name)); + } + }); + return optionalsMembersToAdd; + } + + openAddMembersModal():void { + let addMembersModalConfig:IModalConfig = { + title: this.group.name + " ADD MEMBERS", + size: "md", + type: "custom", + testId: "addMembersModal", + buttons: [ + {text: 'ADD MEMBERS', size: 'xsm', callback: this.addMembers, closeModal: false}, + {text: 'CANCEL', size: 'sm', type: "secondary", closeModal: true} + ] + }; + var optionalsMembersToAdd = this.getOptionalsMembersToAdd(); + this.modalService.openCustomModal(addMembersModalConfig, AddElementsComponent, { + elementsToAdd: optionalsMembersToAdd, + elementName: "member" + }); + } +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.html new file mode 100644 index 0000000000..9de489e316 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.html @@ -0,0 +1,23 @@ +<ng2-expand-collapse state="0"> + <header tooltip="Properties">Properties</header> + <content> + <ul> + <li *ngFor="let property of properties; let i = index" + class="i-sdc-designer-sidebar-section-content-item-property-and-attribute" data-tests-id="propertyRow"> + <div class="i-sdc-designer-sidebar-section-content-item-property-and-attribute-label hand" + [attr.data-tests-id]="'propertyName_'+property.name" + tooltip="{{property.name}}" + (click)="!isViewOnly && editProperty(property)">{{property.name}} + </div> + <div class="i-sdc-designer-sidebar-section-content-item-property-value" + [attr.data-tests-id]="'value_'+property.name" + tooltip="{{property.value || property.defaultValue}}">{{property.value || property.defaultValue}} + </div> + </li> + </ul> + + <div *ngIf="properties.length===0" class="component-details-panel-tab-no-data"> + <div class="component-details-panel-tab-no-data-title">No properties to display</div> + </div> + </content> +</ng2-expand-collapse> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.less new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.less diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.ts new file mode 100644 index 0000000000..69079347c4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-properties-tab.component.ts @@ -0,0 +1,64 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter, OnChanges, SimpleChanges } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { GroupInstance } from 'app/models/graph/zones/group-instance'; +import { PropertyBEModel } from 'app/models'; +import { PropertyModel } from './../../../../../../models/properties'; +import { ModalsHandler } from "app/utils"; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; + +@Component({ + selector: 'group-properties-tab', + templateUrl: './group-properties-tab.component.html', + styleUrls: ['./../base/base-tab.component.less', 'group-properties-tab.component.less'], + host: {'class': 'component-details-panel-tab-group-properties'} +}) +export class GroupPropertiesTabComponent implements OnChanges { + + @Input() group:GroupInstance; + @Input() topologyTemplate:TopologyTemplate; + @Input() isViewOnly: boolean; + + private properties:Array<PropertyModel>; + + constructor(private translateService:TranslateService, private ModalsHandler:ModalsHandler) { + } + + ngOnChanges(changes: SimpleChanges): void { + console.log("GroupPropertiesTabComponent: ngAfterViewInit: "); + console.log("group: " + JSON.stringify(this.group)); + this.properties = []; + this.initProperties(); + } + + initProperties = ():void => { + this.properties= this.group.properties; + } + + editProperty = (property?:PropertyModel):void => { + this.ModalsHandler.openEditPropertyModal((property ? property : new PropertyModel()), this.topologyTemplate, this.properties, false, 'group', this.group.uniqueId).then((updatedProperty:PropertyModel) => { + console.log("ok"); + }); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.html new file mode 100644 index 0000000000..94b6619500 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.html @@ -0,0 +1,11 @@ +<sdc-tabs> + <sdc-tab titleIcon="info-circle"> + <group-information-tab [group]="group" [isViewOnly]="isViewOnly" *ngIf="group"></group-information-tab> + </sdc-tab> + <sdc-tab titleIcon="inputs-o"> + <group-members-tab [group]="group" [topologyTemplate]="topologyTemplate" [isViewOnly]="isViewOnly" (isLoading)="setIsLoading($event)" *ngIf="group"></group-members-tab> + </sdc-tab> + <sdc-tab titleIcon="settings-o"> + <group-properties-tab [group]="group" [topologyTemplate]="topologyTemplate" [isViewOnly]="isViewOnly" *ngIf="group"></group-properties-tab> + </sdc-tab> +</sdc-tabs> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.ts new file mode 100644 index 0000000000..975d5c6153 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.component.ts @@ -0,0 +1,67 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter, SimpleChanges, OnChanges } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; +import { GroupsService } from '../../../../../services/groups.service'; +import { GroupInstance } from "app/models/graph/zones/group-instance"; + +@Component({ + selector: 'group-tabs', + templateUrl: './group-tabs.component.html' +}) +export class GroupTabsComponent implements OnChanges { + + @Input() topologyTemplate:TopologyTemplate; + @Input() selectedZoneInstanceType:string; + @Input() selectedZoneInstanceId:string; + @Input() isViewOnly: boolean; + @Output() isLoading: EventEmitter<boolean> = new EventEmitter<boolean>(); + + private group:GroupInstance; + + constructor(private translateService:TranslateService, + private groupsService:GroupsService + ) { + } + + ngOnChanges(changes: SimpleChanges): void { + this.initGroup(); + } + + private initGroup = ():void => { + this.isLoading.emit(true); + this.groupsService.getSpecificGroup(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.selectedZoneInstanceId).subscribe( + group => { + this.group = group; + console.log(JSON.stringify(group)); + }, + error => console.log("Error getting group!"), + () => this.isLoading.emit(false) + ); + } + + private setIsLoading = (value) :void => { + this.isLoading.emit(value); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.module.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.module.ts new file mode 100644 index 0000000000..50797f862c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/groups/group-tabs.module.ts @@ -0,0 +1,71 @@ +/*- + * ============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========================================================= + */ +import { NgModule } from "@angular/core"; +import { HttpModule } from "@angular/http"; +import { FormsModule } from "@angular/forms"; +import { BrowserModule } from "@angular/platform-browser"; +import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; +import { ExpandCollapseComponent } from 'app/ng2/components/ui/expand-collapse/expand-collapse.component'; +import { PoliciesService } from "../../../../../services/policies.service"; +import { GroupInformationTabComponent } from './group-information-tab.component'; +import { TooltipModule } from './../../../../../components/ui/tooltip/tooltip.module'; +import { GroupTabsComponent } from "./group-tabs.component"; +import { SdcUiComponentsModule } from "sdc-ui/lib/angular"; +import { GroupMembersTabComponent } from './group-members-tab.component'; +import { TranslateModule } from './../../../../../shared/translator/translate.module'; +import { GroupPropertiesTabComponent } from "./group-properties-tab.component"; + +@NgModule({ + declarations: [ + GroupInformationTabComponent, + GroupMembersTabComponent, + GroupTabsComponent, + GroupPropertiesTabComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + TooltipModule, + UiElementsModule, + SdcUiComponentsModule, + TranslateModule + ], + entryComponents: [ + GroupInformationTabComponent, + GroupMembersTabComponent, + GroupTabsComponent, + GroupPropertiesTabComponent, + ExpandCollapseComponent + ], + exports: [ + TooltipModule, + GroupInformationTabComponent, + GroupMembersTabComponent, + GroupTabsComponent, + GroupPropertiesTabComponent + ], + providers: [ + PoliciesService + ] +}) +export class GroupTabsModule { + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.html new file mode 100644 index 0000000000..a1b942d56b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.html @@ -0,0 +1,34 @@ +<ng2-expand-collapse state="0"> + <header tooltip="General Information">General Info</header> + <content> + <!-- TYPE --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_TYPE' | translate"></span> + <span class="value" data-tests-id="rightTab_componentType" tooltip="{{policy.policyTypeUid}}">{{policy.policyTypeUid}}</span> + </div> + + <!-- CATEGORY --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_CATEGORY' | translate"></span> + <span class="value" data-tests-id="rightTab_category" tooltip="Policy">Policy</span> + </div> + + <!-- SUB CATEGORY --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_SUB_CATEGORY' | translate"></span> + <span class="value" data-tests-id="rightTab_subCategory" tooltip="Policy">Policy</span> + </div> + + <!-- VERSION --> + <div class="component-details-panel-item"> + <span class="name" [innerHTML]="'GENERAL_LABEL_VERSION' | translate"></span> + <span class="value" data-tests-id="rightTab_version" tooltip="{{policy.version}}">{{policy.version}}</span> + </div> + + <!-- DESCRIPTION --> + <div class="component-details-panel-item description"> + <span class="name" [innerHTML]="'GENERAL_LABEL_DESCRIPTION' | translate"></span> + <span class="value" ellipsis="policy.description" max-chars="55" data-tests-id="rightTab_description">{{policy.description}}</span> + </div> + </content> +</ng2-expand-collapse> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.ts new file mode 100644 index 0000000000..3639639c88 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-information-tab.component.ts @@ -0,0 +1,39 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { PolicyInstance } from 'app/models/graph/zones/policy-instance'; + +@Component({ + selector: 'policy-information-tab', + templateUrl: './policy-information-tab.component.html', + styleUrls: ['./../base/base-tab.component.less'] +}) +export class PolicyInformationTabComponent { + + @Input() policy:PolicyInstance; + @Input() isViewOnly: boolean; + + constructor(private translateService:TranslateService) { + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.html new file mode 100644 index 0000000000..9de489e316 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.html @@ -0,0 +1,23 @@ +<ng2-expand-collapse state="0"> + <header tooltip="Properties">Properties</header> + <content> + <ul> + <li *ngFor="let property of properties; let i = index" + class="i-sdc-designer-sidebar-section-content-item-property-and-attribute" data-tests-id="propertyRow"> + <div class="i-sdc-designer-sidebar-section-content-item-property-and-attribute-label hand" + [attr.data-tests-id]="'propertyName_'+property.name" + tooltip="{{property.name}}" + (click)="!isViewOnly && editProperty(property)">{{property.name}} + </div> + <div class="i-sdc-designer-sidebar-section-content-item-property-value" + [attr.data-tests-id]="'value_'+property.name" + tooltip="{{property.value || property.defaultValue}}">{{property.value || property.defaultValue}} + </div> + </li> + </ul> + + <div *ngIf="properties.length===0" class="component-details-panel-tab-no-data"> + <div class="component-details-panel-tab-no-data-title">No properties to display</div> + </div> + </content> +</ng2-expand-collapse> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.less new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.less diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.ts new file mode 100644 index 0000000000..5862135df2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-properties-tab.component.ts @@ -0,0 +1,64 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter, OnChanges, SimpleChanges } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { PolicyInstance } from 'app/models/graph/zones/policy-instance'; +import { PropertyBEModel } from 'app/models'; +import { PropertyModel } from './../../../../../../models/properties'; +import { ModalsHandler } from "app/utils"; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; + +@Component({ + selector: 'policy-properties-tab', + templateUrl: './policy-properties-tab.component.html', + styleUrls: ['./../base/base-tab.component.less', 'policy-properties-tab.component.less'], + host: {'class': 'component-details-panel-tab-policy-properties'} +}) +export class PolicyPropertiesTabComponent implements OnChanges { + + @Input() policy:PolicyInstance; + @Input() topologyTemplate:TopologyTemplate; + @Input() isViewOnly: boolean; + + private properties:Array<PropertyModel>; + + constructor(private translateService:TranslateService, private ModalsHandler:ModalsHandler) { + } + + ngOnChanges(changes: SimpleChanges): void { + console.log("PolicyPropertiesTabComponent: ngAfterViewInit: "); + console.log("policy: " + this.policy); + this.properties = []; + this.initProperties(); + } + + initProperties = ():void => { + this.properties= this.policy.properties; + } + + editProperty = (property?:PropertyModel):void => { + this.ModalsHandler.openEditPropertyModal((property ? property : new PropertyModel()), this.topologyTemplate, this.properties, false, 'policy', this.policy.uniqueId).then((updatedProperty:PropertyModel) => { + console.log("ok"); + }); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.html new file mode 100644 index 0000000000..b11ad7ebf9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.html @@ -0,0 +1,12 @@ +<sdc-tabs> + <sdc-tab titleIcon="info-circle"> + <policy-information-tab [policy]="policy" [isViewOnly]="isViewOnly" *ngIf="policy"></policy-information-tab> + </sdc-tab> + <sdc-tab titleIcon="inputs-o"> + <policy-targets-tab [policy]="policy" [topologyTemplate]="topologyTemplate" [isViewOnly]="isViewOnly" (isLoading)="setIsLoading($event)" *ngIf="policy"></policy-targets-tab> + </sdc-tab> + <sdc-tab titleIcon="settings-o"> + <policy-properties-tab [policy]="policy" [topologyTemplate]="topologyTemplate" [isViewOnly]="isViewOnly" *ngIf="policy"></policy-properties-tab> + </sdc-tab> +</sdc-tabs> + diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.ts new file mode 100644 index 0000000000..1e2739901d --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.component.ts @@ -0,0 +1,72 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter, AfterViewInit, OnChanges } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { PoliciesService } from "../../../../../services/policies.service"; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; +import { PolicyInstance } from 'app/models/graph/zones/policy-instance'; +import { GRAPH_EVENTS } from './../../../../../../utils/constants'; +import { EventListenerService } from 'app/services/event-listener-service'; +import { ZoneInstance } from 'app/models/graph/zones/zone-instance'; +import { SimpleChanges } from "@angular/core/src/metadata/lifecycle_hooks"; + +@Component({ + selector: 'policy-tabs', + templateUrl: './policy-tabs.component.html' +}) +export class PolicyTabsComponent implements OnChanges { + + @Input() topologyTemplate:TopologyTemplate; + @Input() selectedZoneInstanceType:string; + @Input() selectedZoneInstanceId:string; + @Input() isViewOnly: boolean; + @Output() isLoading: EventEmitter<boolean> = new EventEmitter<boolean>(); + + private policy:PolicyInstance; + + constructor(private translateService:TranslateService, + private policiesService:PoliciesService + ) { + + } + + ngOnChanges(changes: SimpleChanges): void { + this.initPolicy(); + } + + private initPolicy = ():void => { + this.isLoading.emit(true); + this.policiesService.getSpecificPolicy(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.selectedZoneInstanceId).subscribe( + policy => { + this.policy = policy; + console.log(JSON.stringify(policy)); + }, + error => console.log("Error getting policy!"), + () => this.isLoading.emit(false) + ); + } + + private setIsLoading = (value) :void => { + this.isLoading.emit(value); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.module.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.module.ts new file mode 100644 index 0000000000..38dc19e1af --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-tabs.module.ts @@ -0,0 +1,68 @@ +/*- + * ============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========================================================= + */ +import { NgModule } from "@angular/core"; +import { HttpModule } from "@angular/http"; +import { FormsModule } from "@angular/forms"; +import { BrowserModule } from "@angular/platform-browser"; +import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; +import { ExpandCollapseComponent } from 'app/ng2/components/ui/expand-collapse/expand-collapse.component'; +import { PoliciesService } from "../../../../../services/policies.service"; +import { PolicyInformationTabComponent } from "./policy-information-tab.component"; +import { PolicyTargetsTabComponent } from "./policy-targets-tab.component"; +import { PolicyTabsComponent } from "./policy-tabs.component"; +import { PolicyPropertiesTabComponent } from "./policy-properties-tab.component"; +import { SdcUiComponentsModule } from "sdc-ui/lib/angular"; +import { TranslateModule } from './../../../../../shared/translator/translate.module'; + +@NgModule({ + declarations: [ + PolicyInformationTabComponent, + PolicyTargetsTabComponent, + PolicyPropertiesTabComponent, + PolicyTabsComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + SdcUiComponentsModule, + TranslateModule, + UiElementsModule + ], + entryComponents: [ + PolicyInformationTabComponent, + PolicyTargetsTabComponent, + PolicyPropertiesTabComponent, + PolicyTabsComponent, + ExpandCollapseComponent + ], + exports: [ + PolicyInformationTabComponent, + PolicyTargetsTabComponent, + PolicyPropertiesTabComponent, + PolicyTabsComponent + ], + providers: [ + PoliciesService + ] +}) +export class PolicyTabsModule { + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.html new file mode 100644 index 0000000000..097b3a4584 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.html @@ -0,0 +1,32 @@ +<div class="w-sdc-designer-sidebar-section-title" titleTooltip="Targets">Targets + <svg-icon-label *ngIf="!isViewOnly" + class="add-policy-button" + name="plus-circle-o" + mode="primary" + size="medium" + label="ADD" + labelPlacement="right" + (click)="openAddTargetModal()"> + </svg-icon-label> +</div> +<div class="expand-collapse-content"> + <ul> + <li *ngFor="let target of targets; let i = index" class="component-details-panel-large-item" + tooltip="{{target.name}}"> + <span>{{target.name}}</span> + <svg-icon-label *ngIf="!isViewOnly" + name="trash-o" + clickable="true" + size="small" + class="component-details-panel-item-delete" + data-tests-id="delete_target" + (click)="deleteTarget(target)"></svg-icon-label> + </li> + </ul> + + <div *ngIf="targets.length===0" class="component-details-panel-tab-no-data"> + <div class="component-details-panel-tab-no-data-title">No data to display yet</div> + <div class="component-details-panel-tab-no-data-content">Add targets to policy to see targets</div> + </div> +</div> + diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.less new file mode 100644 index 0000000000..cd7ace2b6f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.less @@ -0,0 +1,12 @@ +/deep/ +.component-details-panel-tab-policy-targets { + .component-details-panel-large-item { + display: flex; + flex-direction: row; + justify-content: space-between; + } + .w-sdc-designer-sidebar-section-title { + display: flex; + justify-content: space-between; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.ts new file mode 100644 index 0000000000..b79f4d9e07 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/policies/policy-targets-tab.component.ts @@ -0,0 +1,145 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Input, Output, EventEmitter, OnChanges, HostBinding, OnDestroy } from "@angular/core"; +import { TranslateService } from './../../../../../shared/translator/translate.service'; +import { Component as TopologyTemplate } from "app/models"; +import { PoliciesService } from "../../../../../services/policies.service"; +import { PolicyInstance, PolicyTargetsMap } from './../../../../../../models/graph/zones/policy-instance'; +import { SimpleChanges } from "@angular/core/src/metadata/lifecycle_hooks"; +import { SdcUiComponents } from "sdc-ui/lib/angular"; +import { IModalConfig } from "sdc-ui/lib/angular/modals/models/modal-config"; +import { AddElementsComponent } from "../../../../../components/ui/modal/add-elements/add-elements.component"; +import { TargetUiObject } from "../../../../../../models/ui-models/ui-target-object"; +import { ComponentInstance } from "../../../../../../models/componentsInstances/componentInstance"; +import { TargetOrMemberType } from "../../../../../../utils/constants"; +import { GRAPH_EVENTS } from 'app/utils'; +import { EventListenerService } from 'app/services/event-listener-service'; + +@Component({ + selector: 'policy-targets-tab', + templateUrl: './policy-targets-tab.component.html', + styleUrls: ['./../base/base-tab.component.less', 'policy-targets-tab.component.less'] +}) + +export class PolicyTargetsTabComponent implements OnChanges, OnDestroy { + + private targets: Array<TargetUiObject>; // UI object to hold all targets with names. + + @Input() policy: PolicyInstance; + @Input() topologyTemplate: TopologyTemplate; + @Input() isViewOnly: boolean; + @Output() isLoading: EventEmitter<boolean> = new EventEmitter<boolean>(); + @HostBinding('class') classes = 'component-details-panel-tab-policy-targets'; + + constructor(private translateService: TranslateService, + private policiesService: PoliciesService, + private modalService: SdcUiComponents.ModalService, + private eventListenerService: EventListenerService + ) { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, this.initTargets) + } + + ngOnChanges(changes:SimpleChanges):void { + this.initTargets(); + } + + ngOnDestroy() { + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE); + } + + deleteTarget(target: TargetUiObject): void { + this.isLoading.emit(true); + this.policiesService.deletePolicyTarget(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.policy, target.uniqueId, target.type).subscribe( + (policyInstance:PolicyInstance) => { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, policyInstance); + }, + error => console.log("Error deleting target!"), + () => this.isLoading.emit(false) + ); + } + + private initTargets = (policyInstance?: PolicyInstance) => { + this.policy = policyInstance ? policyInstance : this.policy; + this.targets = this.policy.getTargetsAsUiObject(this.topologyTemplate.componentInstances, this.topologyTemplate.groupInstances); + } + + addTargets = ():void => { + + var targetsToAdd:Array<TargetUiObject> = this.modalService.getCurrentInstance().innerModalContent.instance.existingElements; //TODO refactor sdc-ui modal in order to return the data + if(targetsToAdd.length > 0) { + this.modalService.closeModal(); + this.isLoading.emit(true); + var updatedTarget: Array<TargetUiObject> = _.union(this.targets, targetsToAdd); + this.policiesService.updateTargets(this.topologyTemplate.componentType, this.topologyTemplate.uniqueId, this.policy.uniqueId, updatedTarget).subscribe( + (updatedPolicyInstance:PolicyInstance) => { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, updatedPolicyInstance); + }, + error => { + console.log("Error updating targets!"); + }, + () => this.isLoading.emit(false) + ); + } + } + + getOptionalsTargetsToAdd():Array<TargetUiObject> { + let optionalsTargetsToAdd:Array<TargetUiObject> = []; + // adding all instances as optional targets to add if not already exist + _.forEach(this.topologyTemplate.componentInstances, (instance:ComponentInstance) => { + if (!_.some(this.targets, (target:TargetUiObject) => { + return target.uniqueId === instance.uniqueId + })) { + optionalsTargetsToAdd.push(new TargetUiObject(instance.uniqueId, TargetOrMemberType.COMPONENT_INSTANCES, instance.name)); + } + }); + + // adding all groups as optional targets to add if not already exist + _.forEach(this.topologyTemplate.groupInstances, (groupInstance:ComponentInstance) => { // adding all instances as optional targets to add if not already exist + if (!_.some(this.targets, (target:TargetUiObject) => { + return target.uniqueId === groupInstance.uniqueId + })) { + optionalsTargetsToAdd.push(new TargetUiObject(groupInstance.uniqueId, TargetOrMemberType.GROUPS, groupInstance.name)); + } + }); + + return optionalsTargetsToAdd; + } + + openAddTargetModal(): void { + let addTargetModalConfig: IModalConfig = { + title: this.policy.name + " ADD TARGETS", + size: "md", + type: "custom", + testId: "addTargetsModal", + buttons: [ + {text: "ADD TARGETS", size: 'xsm', callback: this.addTargets, closeModal: false}, + {text: 'CANCEL', size: 'sm', type: "secondary", closeModal: true} + ] + }; + var optionalTargetsToAdd = this.getOptionalsTargetsToAdd(); + this.modalService.openCustomModal(addTargetModalConfig, AddElementsComponent, { + elementsToAdd: optionalTargetsToAdd, + elementName: "target" + }); + + } +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.html new file mode 100644 index 0000000000..430b272ff0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.html @@ -0,0 +1,34 @@ +<ng2-composition-panel-header + [name]="selectedZoneInstanceName" + [topologyTemplate]="topologyTemplate" + [selectedZoneInstanceType]="selectedZoneInstanceType" + [selectedZoneInstanceId]="selectedZoneInstanceId" + [nonCertified]="nonCertified" + [isViewOnly]="isViewOnly" + [isLoading]="isLoading" +></ng2-composition-panel-header> + +<div class="component-details-panel-tabs"> + <loader [display]="isLoading" [size]="'large'" [relative]="true" [loaderDelay]="500"></loader> + + <div *ngIf="selectedZoneInstanceType === zoneInstanceType.POLICY"> + <policy-tabs + [topologyTemplate]="topologyTemplate" + [selectedZoneInstanceType]="selectedZoneInstanceType" + [selectedZoneInstanceId]="selectedZoneInstanceId" + [isViewOnly]="isViewOnly" + (isLoading)="setIsLoading($event)" + ></policy-tabs> + </div> + + <div *ngIf="selectedZoneInstanceType === zoneInstanceType.GROUP"> + <group-tabs + [topologyTemplate]="topologyTemplate" + [selectedZoneInstanceType]="selectedZoneInstanceType" + [selectedZoneInstanceId]="selectedZoneInstanceId" + [isViewOnly]="isViewOnly" + (isLoading)="setIsLoading($event)" + ></group-tabs> + </div> + +</div> diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.less b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.less new file mode 100644 index 0000000000..1777d54486 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.less @@ -0,0 +1,11 @@ +/deep/ +.component-details-panel { + + color: #666666; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; + + .component-details-panel-tabs { + + } +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.ts new file mode 100644 index 0000000000..53599d6366 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel.component.ts @@ -0,0 +1,60 @@ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import { Component, Inject, Input, Output, EventEmitter, AfterViewInit, SimpleChanges, HostBinding } from "@angular/core"; +import { Component as TopologyTemplate, ComponentInstance, IAppMenu } from "app/models"; +import { PolicyInstance } from 'app/models/graph/zones/policy-instance'; +import { TranslateService } from 'app/ng2/shared/translator/translate.service'; +import { ZoneInstanceType } from "app/models/graph/zones/zone-instance"; +import { GroupsService } from "../../../services/groups.service"; +import { PoliciesService } from "../../../services/policies.service"; +import { SdcUiComponents } from "sdc-ui/lib/angular"; +import { IZoneService } from "../../../../models/graph/zones/zone"; + +@Component({ + selector: 'ng2-composition-panel', + templateUrl: './panel.component.html', + styleUrls: ['./panel.component.less'], + providers: [TranslateService] +}) +export class CompositionPanelComponent { + + @Input() topologyTemplate: TopologyTemplate; + @Input() selectedZoneInstanceType: ZoneInstanceType; + @Input() selectedZoneInstanceId: string; + @Input() selectedZoneInstanceName: string; + @Input() nonCertified: boolean; + @Input() isViewOnly: boolean; + @Input() isLoading: boolean; + + + @HostBinding('class') classes = 'component-details-panel'; + + private zoneInstanceType = ZoneInstanceType; // Expose ZoneInstanceType to use in template. + + constructor(){ + } + + private setIsLoading = (value):void => { + this.isLoading = value; + } + +} diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel.module.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel.module.ts new file mode 100644 index 0000000000..57f6be8b8e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel.module.ts @@ -0,0 +1,54 @@ +/*- + * ============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========================================================= + */ +import {NgModule} from "@angular/core"; +import {HttpModule} from "@angular/http"; +import {FormsModule} from "@angular/forms"; +import {BrowserModule} from "@angular/platform-browser"; +import {CompositionPanelComponent} from "./panel.component"; +import {CompositionPanelHeaderModule} from "app/ng2/pages/composition/panel/panel-header/panel-header.module"; +import {GroupTabsModule} from "./panel-tabs/groups/group-tabs.module"; +import {PolicyTabsModule} from "./panel-tabs/policies/policy-tabs.module"; +import {SdcUiComponents} from "sdc-ui/lib/angular"; +import {UiElementsModule} from 'app/ng2/components/ui/ui-elements.module'; +import {AddElementsModule} from "../../../components/ui/modal/add-elements/add-elements.module"; + +@NgModule({ + declarations: [ + CompositionPanelComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + CompositionPanelHeaderModule, + PolicyTabsModule, + GroupTabsModule, + UiElementsModule, + AddElementsModule + ], + entryComponents: [ + CompositionPanelComponent + ], + exports: [], + providers: [SdcUiComponents.ModalService] +}) +export class CompositionPanelModule { + +} diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts index bf5ec4c4f6..af8dcb4956 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts @@ -22,19 +22,6 @@ export class ConnectionWizardService { // this.selectedCapability = rel } - - // getComponentInstanceIdOfSelectedCapability = (): string => { - // if(this.selectedMatch.capability){ - // if(this.selectedMatch.isFromTo) { - // return this.selectedMatch.toNode; - // } else { - // return this.selectedMatch.fromNode; - // } - // } - // return ''; - // - // } - getOptionalRequirementsByInstanceUniqueId = (isFromTo: boolean, matchWith?:Capability): Dictionary<Requirement[]> => { let requirements: Array<Requirement> = []; _.forEach(this.connectRelationModel.possibleRelations, (match: Match) => { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html index 03e9f1fa7e..5142bba383 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html @@ -10,7 +10,7 @@ [searchTerm]="searchQuery" [selectedPropertyId]="selectedFlatProperty.path" [propertyNameSearchText]="searchPropertyName" - [readonly]="isReadonly" + [readonly]="isReadonly || resourceIsReadonly" [isLoading]="loadingProperties || savingChangedData" [hasDeclareOption]="true" (propertyChanged)="dataChanged($event)" @@ -59,7 +59,7 @@ <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onInstanceSelectedUpdate($event)" [displayData]="isInputsTabSelected ? []: instancesNavigationData" - [selectedItem]="selectedInstanceData.uniqueId" + [selectedItem]="selectedInstanceData?.uniqueId" [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation> </div> </tab> @@ -79,9 +79,4 @@ </tabs> </div> </div> - <template #saveChangedDataModalContentTemplate> - <loader [display]="savingChangedData" [size]="'medium'" [relative]="true"></loader> - Your changes{{isValidChangedData ? '' : ' (invalid)'}} have not been saved.<br> - Do you want to {{isValidChangedData ? 'save' : 'discard'}} them? - </template> </div> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts index 40818bce78..5a21e86879 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts @@ -25,7 +25,7 @@ import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, Inst import { ResourceType } from "app/utils"; import {ComponentServiceNg2} from "../../services/component-services/component.service"; import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service" -import { InputBEModel, InputFEModel, ComponentInstance, PropertyBEModel, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models"; +import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models"; import { KeysPipe } from 'app/ng2/pipes/keys.pipe'; import {WorkspaceMode, EVENTS} from "../../../utils/constants"; import {EventListenerService} from "app/services/event-listener-service" @@ -38,6 +38,11 @@ import {ComponentModeService} from "../../services/component-services/component- import {ModalService} from "../../services/modal.service"; import {Tabs, Tab} from "../../components/ui/tabs/tabs.component"; import {InputsUtils} from "./services/inputs.utils"; +import { InstanceFeDetails } from "../../../models/instance-fe-details"; +import { SdcUiComponents } from "sdc-ui/lib/angular"; +//import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service"; +import { IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config"; +import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component"; @Component({ templateUrl: './properties-assignment.page.component.html', @@ -47,30 +52,30 @@ export class PropertiesAssignmentComponent { title = "Properties & Inputs"; component: ComponentData; - componentInstanceNamesMap: Map<string, string> = new Map<string, string>();//instanceUniqueId, name + componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass} propertiesNavigationData = []; instancesNavigationData = []; instanceFePropertiesMap:InstanceFePropertiesMap; inputs: Array<InputFEModel> = []; - instances: Array<ComponentInstance> = []; + instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = []; searchQuery: string; propertyStructureHeader: string; selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty(); - selectedInstanceType: string; - selectedInstanceData: ComponentInstance = new ComponentInstance(); + selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null; checkedPropertiesCount: number = 0; hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens'); - hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name'); + hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass'); displayClearSearch = false; searchPropertyName:string; currentMainTab:Tab; isInputsTabSelected:boolean; isPropertiesTabSelected:boolean; isReadonly:boolean; + resourceIsReadonly:boolean; loadingInstances:boolean = false; loadingInputs:boolean = false; loadingProperties:boolean = false; @@ -83,8 +88,7 @@ export class PropertiesAssignmentComponent { @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs; @ViewChild('propertyInputTabs') propertyInputTabs: Tabs; @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent; - @ViewChild('saveChangedDataModalContentTemplate') saveChangedDataModalContentTemplateRef: TemplateRef<void>; - + constructor(private propertiesService: PropertiesService, private hierarchyNavService: HierarchyNavService, private propertiesUtils:PropertiesUtils, @@ -97,14 +101,15 @@ export class PropertiesAssignmentComponent { @Inject("Notification") private Notification:any, private componentModeService:ComponentModeService, private ModalService:ModalService, - private EventListenerService:EventListenerService) { + private EventListenerService:EventListenerService, + private ModalServiceSdcUI: SdcUiComponents.ModalService) { this.instanceFePropertiesMap = new InstanceFePropertiesMap(); /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/ this.component = _stateParams.component; - this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout); + this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout); this.updateViewMode(); this.changedData = []; @@ -129,13 +134,16 @@ export class PropertiesAssignmentComponent { }, error => {}); //ignore error this.componentServiceNg2 - .getComponentResourceInstances(this.component) + .getComponentResourcePropertiesData(this.component) .subscribe(response => { - this.instances = response.componentInstances; + this.instances = []; + this.instances.push(...response.componentInstances); + this.instances.push(...response.groupInstances); + this.instances.push(...response.policies); _.forEach(this.instances, (instance) => { this.instancesNavigationData.push(instance); - this.componentInstanceNamesMap[instance.uniqueId] = instance.name; + this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived}; }); this.loadingInstances = false; if (this.instancesNavigationData[0] == undefined) { @@ -148,17 +156,15 @@ export class PropertiesAssignmentComponent { // stop if has changed properties if (this.hasChangedData) { event.preventDefault(); - this.openChangedDataModal().then((proceed) => { - if (proceed) { - this.$state.go(toState, toParams); - } - }); + this.showUnsavedChangesAlert().then(() => { + this.$state.go(toState, toParams); + }, () => {}); } }); }; ngOnDestroy() { - this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT); + this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE); this.stateChangeStartUnregister(); } @@ -178,47 +184,64 @@ export class PropertiesAssignmentComponent { } - onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => { - console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate"); - + onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => { // stop if has changed properties if (this.hasChangedData) { - this.openChangedDataModal().then((proceed) => { - if (proceed) { - this.onInstanceSelectedUpdate(resourceInstance); - } + this.showUnsavedChangesAlert().then((resolve)=> { + this.changeSelectedInstance(instance) + }, (reject) => { }); return; } + this.changeSelectedInstance(instance); + }; - let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); - this.selectedInstanceData = resourceInstance; - this.selectedInstanceType = resourceInstance.originType; - + changeSelectedInstance = (instance: ComponentInstance|GroupInstance|PolicyInstance) => { + this.selectedInstanceData = instance; this.loadingProperties = true; - if (this.isInput(resourceInstance.originType)) { + if (instance instanceof ComponentInstance) { + let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + if (this.isInput(instance.originType)) { + this.componentInstanceServiceNg2 + .getComponentInstanceInputs(this.component, instance) + .subscribe(response => { + instanceBePropertiesMap[instance.uniqueId] = response; + this.processInstancePropertiesResponse(instanceBePropertiesMap, true); + this.loadingProperties = false; + }, error => { + }); //ignore error + } else { + this.componentInstanceServiceNg2 + .getComponentInstanceProperties(this.component, instance.uniqueId) + .subscribe(response => { + instanceBePropertiesMap[instance.uniqueId] = response; + this.processInstancePropertiesResponse(instanceBePropertiesMap, false); + this.loadingProperties = false; + }, error => { + }); //ignore error + } + + this.resourceIsReadonly = (instance.componentName === "vnfConfiguration"); + } else if (instance instanceof GroupInstance) { + let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); this.componentInstanceServiceNg2 - .getComponentInstanceInputs(this.component, resourceInstance) - .subscribe(response => { - instanceBePropertiesMap[resourceInstance.uniqueId] = response; - this.processInstancePropertiesResponse(instanceBePropertiesMap, true); + .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId) + .subscribe((response) => { + instanceBePropertiesMap[instance.uniqueId] = response; + this.processInstancePropertiesResponse(instanceBePropertiesMap, false); this.loadingProperties = false; - - }, error => { - }); //ignore error - } else { + }); + } else if (instance instanceof PolicyInstance) { + let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); this.componentInstanceServiceNg2 - .getComponentInstanceProperties(this.component, resourceInstance.uniqueId) - .subscribe(response => { - instanceBePropertiesMap[resourceInstance.uniqueId] = response; + .getComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId) + .subscribe((response) => { + instanceBePropertiesMap[instance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, false); this.loadingProperties = false; - }, error => { - }); //ignore error - } - - if (resourceInstance.componentName === "vnfConfiguration") { - this.isReadonly = true; + }); + } else { + this.loadingProperties = false; } if (this.searchPropertyName) { @@ -289,7 +312,7 @@ export class PropertiesAssignmentComponent { this.propertyStructureHeader = null; // Build hirarchy tree for the navigation and update propertiesNavigationData with it. - if(this.selectedInstanceData.originType !== ResourceType.VF) { + if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) { let simpleFlatProperty:Array<SimpleFlatProperty>; if (property instanceof PropertyFEModel) { simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName); @@ -322,14 +345,11 @@ export class PropertiesAssignmentComponent { tabChanged = (event) => { // stop if has changed properties if (this.hasChangedData) { - this.openChangedDataModal().then((proceed) => { - if (proceed) { - this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title)); - } - }); - - // return to show the current tab this.propertyInputTabs.triggerTabChange(this.currentMainTab.title); + this.showUnsavedChangesAlert().then((proceed) => { + this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title)); + }, ()=> { + }); return; } @@ -347,21 +367,28 @@ export class PropertiesAssignmentComponent { declareProperties = (): void => { console.log("==>" + this.constructor.name + ": declareProperties"); - let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); - let selectedInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap(); let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []); angular.forEach(instancesIds, (instanceId: string): void => { - let selectedInstanceData: ResourceInstance = this.instances.find(instance => instance.uniqueId == instanceId); - let originType: string = (selectedInstanceData) ? selectedInstanceData.originType : this.selectedInstanceType; - if (!this.isInput(originType)) { - selectedProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); - } else { - selectedInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); + let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId); + if (selectedInstanceData instanceof ComponentInstance) { + if (!this.isInput(selectedInstanceData.originType)) { + selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); + } else { + selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); + } + } else if (selectedInstanceData instanceof GroupInstance) { + selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); + } else if (selectedInstanceData instanceof PolicyInstance) { + selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]); } }); - let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedInputs, selectedProperties); + let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties); this.componentServiceNg2 .createInput(this.component, inputsToCreate) @@ -399,20 +426,46 @@ export class PropertiesAssignmentComponent { return propBE; }); - if (this.isInput(this.selectedInstanceData.originType)) { + if (this.selectedInstanceData instanceof ComponentInstance) { + if (this.isInput(this.selectedInstanceData.originType)) { + request = this.componentInstanceServiceNg2 + .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties); + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resInput) => { + const changedProp = <PropertyFEModel>this.changedData.shift(); + this.propertiesUtils.resetPropertyValue(changedProp, resInput.value); + }); + console.log('updated instance inputs:', response); + }; + } else { + request = this.componentInstanceServiceNg2 + .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties) + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resProp) => { + const changedProp = <PropertyFEModel>this.changedData.shift(); + this.propertiesUtils.resetPropertyValue(changedProp, resProp.value); + }); + resolve(response); + console.log("updated instance properties: ", response); + }; + } + } else if (this.selectedInstanceData instanceof GroupInstance) { request = this.componentInstanceServiceNg2 - .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties); + .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties); handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list - response.forEach((resInput) => { + response.forEach((resProp) => { const changedProp = <PropertyFEModel>this.changedData.shift(); - this.propertiesUtils.resetPropertyValue(changedProp, resInput.value); + this.propertiesUtils.resetPropertyValue(changedProp, resProp.value); }); - console.log('updated instance inputs:', response); + resolve(response); + console.log("updated group instance properties: ", response); }; - } else { + } else if (this.selectedInstanceData instanceof PolicyInstance) { request = this.componentInstanceServiceNg2 - .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties) + .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties); handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list response.forEach((resProp) => { @@ -420,7 +473,7 @@ export class PropertiesAssignmentComponent { this.propertiesUtils.resetPropertyValue(changedProp, resProp.value); }); resolve(response); - console.log("updated instance properties: ", response); + console.log("updated policy instance properties: ", response); }; } } else if (this.isInputsTabSelected) { @@ -484,66 +537,59 @@ export class PropertiesAssignmentComponent { const curHasChangedData:boolean = (this.changedData.length > 0); if (curHasChangedData !== this.hasChangedData) { this.hasChangedData = curHasChangedData; - this.$scope.$emit('setWorkspaceTopBarActive', !this.hasChangedData); - } + if(this.hasChangedData) { + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert); + } else { + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false); + } + } return this.hasChangedData; }; - doSaveChangedData = ():void => { + doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => { this.saveChangedData().then( () => { this.Notification.success({ message: 'Successfully saved changes', title: 'Saved' }); + if(onSuccessFunction) onSuccessFunction(); }, () => { this.Notification.error({ message: 'Failed to save changes!', title: 'Failure' }); + if(onError) onError(); } ); }; - openChangedDataModal = ():Promise<boolean> => { - let modalTitle; + showUnsavedChangesAlert = ():Promise<any> => { + let modalTitle:string; if (this.isPropertiesTabSelected) { modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`; } else if (this.isInputsTabSelected) { modalTitle = `Unsaved inputs for ${this.component.name}`; } - return new Promise<boolean>((resolve) => { - const modal = this.ModalService.createCustomModal(new ModalModel( - 'sm', - modalTitle, - null, - [ - new ButtonModel('Cancel', 'outline grey', () => { - modal.instance.close(); - resolve(false); - }), - new ButtonModel('Discard', 'outline blue', () => { - this.reverseChangedData(); - modal.instance.close(); - resolve(true); - }), - new ButtonModel('Save', 'blue', () => { - this.saveChangedData().then(() => { - modal.instance.close(); - resolve(true); - }, () => { - modal.instance.close(); - resolve(false); - }); - }, () => !this.isValidChangedData) - ] - )); - this.ModalService.addDynamicTemplateToModal(modal, this.saveChangedDataModalContentTemplateRef); - modal.instance.open(); + return new Promise<any>((resolve, reject) => { + const modal = this.ModalServiceSdcUI.openCustomModal( + { + title: modalTitle, + size: 'sm', + type: 'custom', + testId: "id", + + buttons: [ + {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()}, + {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}}, + {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)} + ] as IModalButtonComponent[] + }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData}); }); - }; + + } updatePropertyValueAfterDeclare = (input: InputFEModel) => { if (this.instanceFePropertiesMap[input.instanceUniqueId]) { @@ -567,7 +613,16 @@ export class PropertiesAssignmentComponent { this.propertyInputTabs.setTabIndication('Inputs', numInputs); }; + resetUnsavedChangesForInput = (input:InputFEModel) => { + this.inputsUtils.resetInputDefaultValue(input, input.defaultValue); + this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId); + this.updateHasChangedData(); + } + deleteInput = (input: InputFEModel) => { + //reset any unsaved changes to the input before deleting it + this.resetUnsavedChangesForInput(input); + console.log("==>" + this.constructor.name + ": deleteInput"); let inputToDelete = new InputBEModel(input); @@ -577,7 +632,7 @@ export class PropertiesAssignmentComponent { this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId); //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead! - this.onInstanceSelectedUpdate(this.selectedInstanceData); + this.changeSelectedInstance(this.selectedInstanceData); // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)]; // if (instanceFeProperties) { diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html index bbbf6ae694..e029b7f1fa 100644 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html @@ -1,5 +1,45 @@ -<ui-element-dropdown data-tests-id="linkSrc" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="source" [(value)]="link.fromNode" (valueChange)="onSourceSelected($event)"></ui-element-dropdown> -<ui-element-dropdown data-tests-id="linkSrcCP" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="srcCP" [(value)]="link.fromCP" (valueChange)="onSrcCPSelected($event)"></ui-element-dropdown> -<ui-element-dropdown data-tests-id="linkTarget" [readonly]="!link.canEdit" class="cell link-selector" [values]="target" [(value)]="link.toNode" (valueChange)="onTargetSelected($event)"></ui-element-dropdown> -<ui-element-dropdown data-tests-id="linkTargetCP" [readonly]="!link.canEdit" class="cell link-selector" [values]="targetCP" [(value)]="link.toCP" (valueChange)="onTargetCPSelected($event)"></ui-element-dropdown> -<div class="cell remove" data-tests-id="removeLnk"><span *ngIf="link.canRemove" class="sprite-new delete-item-icon" (click)="removeRow()"></span></div>
\ No newline at end of file +<ui-element-dropdown + data-tests-id="linkSrc" + [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" + class="cell link-selector" + [values]="source" + [(value)]="link.fromNode" + (valueChange)="onSourceSelected($event)"> +</ui-element-dropdown> + +<ui-element-dropdown + data-tests-id="linkSrcCP" + [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" + class="cell link-selector" + [values]="srcCP" + [(value)]="link.fromCP" + (valueChange)="onSrcCPSelected($event)"> +</ui-element-dropdown> + +<ui-element-dropdown + data-tests-id="linkTarget" + [readonly]="!link.canEdit" + class="cell link-selector" + [values]="target" + [(value)]="link.toNode" + (valueChange)="onTargetSelected($event)"> +</ui-element-dropdown> + +<ui-element-dropdown + data-tests-id="linkTargetCP" + [readonly]="!link.canEdit" + class="cell link-selector" + [values]="targetCP" + [(value)]="link.toCP" + (valueChange)="onTargetCPSelected($event)"> +</ui-element-dropdown> + +<div + class="cell remove" + data-tests-id="removeLnk"> + <span + *ngIf="link.canRemove" + class="sprite-new delete-item-icon" + (click)="removeRow()"> + </span> +</div> diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts index 16433242d6..e4fc1d4522 100644 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts @@ -14,7 +14,7 @@ export class LinkRowComponent { @Input() data:Array<ServicePathMapItem>; @Input() link:Link; @Input() removeRow:Function; - source:Array<DropdownValue> = []; + source: Array<DropdownValue> = []; target: Array<DropdownValue> = []; srcCP: Array<DropdownValue> = []; targetCP: Array<DropdownValue> = []; @@ -32,11 +32,9 @@ export class LinkRowComponent { if (!srcCPOptions) { return; } this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); if (this.link.fromCP) { - let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); - if (!targetOptions) { return; } - this.target = this.convertValuesToDropDownOptions(targetOptions); + this.target = this.convertValuesToDropDownOptions(data); if (this.link.toNode) { - let targetCPOptions = this.findOptions(targetOptions, this.link.toNode); + let targetCPOptions = this.findOptions(data, this.link.toNode); if (!targetCPOptions) { return; } this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); } @@ -45,7 +43,7 @@ export class LinkRowComponent { } private findOptions(items: Array<ServicePathMapItem>, nodeOrCPId: string) { - let item = items.find((dataItem)=> nodeOrCPId === dataItem.id); + let item = _.find(items, (dataItem) => nodeOrCPId === dataItem.id); if (item && item.data && item.data.options) { return item.data.options; } @@ -53,12 +51,12 @@ export class LinkRowComponent { return null; } - private convertValuesToDropDownOptions(values: Array<ServicePathMapItem>) : Array<DropdownValue> { - let result = []; + private convertValuesToDropDownOptions(values: Array<ServicePathMapItem>): Array<DropdownValue> { + let result:Array<DropdownValue> = []; for (let i = 0; i < values.length ; i++) { result[result.length] = new DropdownValue(values[i].id, values[i].data.name); } - return result; + return result.sort((a, b) => a.label.localeCompare(b.label)); } onSourceSelected(id) { @@ -75,10 +73,9 @@ export class LinkRowComponent { onSrcCPSelected (id) { if (id) { - let srcCPData = this.data.find((dataItem)=> this.link.fromNode === dataItem.id).data; - let srcCPOptions = srcCPData.options; - let targetOptions = this.findOptions(srcCPOptions, id); - this.target = this.convertValuesToDropDownOptions(targetOptions); + let srcCPOptions = this.findOptions(this.data, this.link.fromNode); + let srcCPData = srcCPOptions.find(option => id === option.id).data; + this.target = this.convertValuesToDropDownOptions(this.data); this.link.fromCPOriginId = srcCPData.ownerId; this.link.toNode = ''; this.link.toCP = ''; @@ -89,9 +86,7 @@ export class LinkRowComponent { onTargetSelected(id) { if (id) { - let srcCPOptions = this.findOptions(this.data, this.link.fromNode); - let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); - let targetCPOptions = this.findOptions(targetOptions, id); + let targetCPOptions = this.findOptions(this.data, id); this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); this.link.toCP = ''; } @@ -100,11 +95,9 @@ export class LinkRowComponent { onTargetCPSelected(id) { if (id) { - let srcCPOptions = this.findOptions(this.data, this.link.fromNode); - let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); - let targetCPOptions = this.findOptions(targetOptions, this.link.toNode); - let targetCPDataObj = targetCPOptions.find((dataItem)=> id === dataItem.id).data; + let targetCPOptions = this.findOptions(this.data, this.link.toNode); + let targetCPDataObj = targetCPOptions.find(option => id === option.id).data; this.link.toCPOriginId = targetCPDataObj.ownerId; } } -}
\ No newline at end of file +} diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html index 96cd83eef6..76c5c53290 100644 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html @@ -1,27 +1,24 @@ <div class="service-path-creator"> <form class="w-sdc-form"> <div class="i-sdc-form-item" > - <label class="i-sdc-form-label required">Path Name</label> - <!-- <ui-element-input type="text" name="pathName" [value]="pathName" ></ui-element-input> --> - <input type="text" data-tests-id="pathName" name="pathName" [(ngModel)]="forwardingPath.name" [attr.maxLength]="100" /> <!-- TODO - make unique --> + <label class="i-sdc-form-label required">Flow Name</label> + <input type="text" data-tests-id="pathName" name="pathName" [(ngModel)]="forwardingPath.name" [attr.maxLength]="200" /> </div> <div class="side-by-side"> <div class="i-sdc-form-item" > <label class="i-sdc-form-label">Protocol</label> - <!-- <ui-element-input type="text" name="protocol" [value]="protocol" ></ui-element-input> --> - <input type="text" data-tests-id="pathProtocol" name="protocol" [(ngModel)]="forwardingPath.protocol" [attr.maxLength]="100" /> + <input type="text" data-tests-id="pathProtocol" name="protocol" [(ngModel)]="forwardingPath.protocol" [attr.maxLength]="200" /> </div> <div class="i-sdc-form-item" > <label class="i-sdc-form-label">Destination Port Numbers</label> - <!-- <ui-element-input type="text" name="portNumbers" [value]="portNumbers" ></ui-element-input> --> - <input type="text" data-tests-id="pathPortNumbers" name="portNumbers" [(ngModel)]="forwardingPath.destinationPortNumber" pattern="[0-9,]*" /> <!-- TODO - validate delimiter --> + <input type="text" data-tests-id="pathPortNumbers" name="portNumbers" [(ngModel)]="forwardingPath.destinationPortNumber" pattern="[0-9,]*" /> </div> </div> <div class="separator-buttons"> <span class="based-on-title">Based On</span> - <a (click)="addRow()" [ngClass]="{'disabled':!isExtendAllowed()}" data-tests-id="extendPathlnk">Extend Path</a> + <a (click)="addRow()" [ngClass]="{'disabled':!isExtendAllowed()}" data-tests-id="extendPathlnk">Extend Flow</a> </div> <div class="generic-table"> diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts index dac41a37bc..bffb1c5e7e 100644 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts @@ -73,12 +73,11 @@ export class ServicePathCreatorComponent { this.forwardingPath.uniqueId = forwardingPath.uniqueId; this.links = []; _.forEach(forwardingPath.pathElements.listToscaDataDefinition, (link:ForwardingPathLink) => { - this.links[this.links.length] = new Link( link, false, false, false); + this.links[this.links.length] = new Link(link, false, false, false); }); - this.links[this.links.length -1].canEdit = true; - this.links[this.links.length -1].canRemove = true; + this.links[this.links.length - 1].canEdit = true; + this.links[this.links.length - 1].canRemove = true; this.links[0].isFirst = true; - } } @@ -98,7 +97,18 @@ export class ServicePathCreatorComponent { addRow() { this.disableRows(); - this.links[this.links.length] = new Link( new ForwardingPathLink(this.links[this.links.length-1].toNode,this.links[this.links.length-1].toCP,'','',this.links[this.links.length-1].toCPOriginId,''),true, true, false); + this.links[this.links.length] = new Link( + new ForwardingPathLink(this.links[this.links.length-1].toNode, + this.links[this.links.length-1].toCP, + '', + '', + this.links[this.links.length-1].toCPOriginId, + '' + ), + true, + true, + false + ); } disableRows() { diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html index 8a31c76998..39c41916a2 100644 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html @@ -1,5 +1,5 @@ <div class="service-path-list"> - <div class="add-path-link"><a (click)="onAddServicePath()" data-tests-id="add-service-path-lnk" >+ Add Path</a></div> + <div class="add-path-link" *ngIf="!isViewOnly"><a (click)="onAddServicePath()" data-tests-id="add-service-path-lnk" >+ Add Flow</a></div> <div class="generic-table table-container" > <div class="header-row"> <div class="cell header-cell" *ngFor="let header of headers"> @@ -10,11 +10,11 @@ <div class="cell" data-tests-id="path-name" >{{path.name}}</div> <div class="cell path-action-buttons"> <span class="sprite-new update-component-icon" (click)="onEditServicePath(path.uniqueId)" data-tests-id="update-service-path-btn" ></span> - <span class="sprite-new delete-item-icon" (click)="deletePath(path.uniqueId)" data-tests-id="delete-service-path-btn"></span> + <span class="sprite-new delete-item-icon" *ngIf="!isViewOnly" (click)="deletePath(path.uniqueId)" data-tests-id="delete-service-path-btn"></span> </div> </div> <div *ngIf="paths && paths.length === 0" class="no-row-text" > - No paths have been added yet. + No flows have been added yet. </div> </div> diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less index aff597fd85..291119f58c 100644 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less @@ -18,4 +18,7 @@ .sprite-new { cursor: pointer; } + & > span:only-child { + margin: auto; +} }
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts index 04083e8685..1625ab4b66 100644 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts @@ -38,9 +38,10 @@ export default class ServicePathsListComponent { input:any; onAddServicePath: Function; onEditServicePath: Function; + isViewOnly: boolean; constructor(private serviceService:ServiceServiceNg2) { - this.headers = ['Path Name','Actions']; + this.headers = ['Flow Name','Actions']; } ngOnInit() { @@ -52,6 +53,7 @@ export default class ServicePathsListComponent { }); this.onAddServicePath = this.input.onCreateServicePath; this.onEditServicePath = this.input.onEditServicePath; + this.isViewOnly = this.input.isViewOnly; } deletePath = (id:string):void => { diff --git a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts index 3761aa808f..758a7e886c 100644 --- a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts +++ b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts @@ -2,6 +2,7 @@ import {ContentAfterLastDotPipe} from "./contentAfterLastDot.pipe"; import {SearchFilterPipe} from "./searchFilter.pipe"; import {KeysPipe} from "./keys.pipe"; import {GroupByPipe} from "./groupBy.pipe"; +import {ResourceNamePipe} from "./resource-name.pipe"; import {NgModule} from "@angular/core"; import {SafeUrlSanitizerPipe} from "./safeUrlSanitizer.pipe"; @@ -10,15 +11,17 @@ import {SafeUrlSanitizerPipe} from "./safeUrlSanitizer.pipe"; ContentAfterLastDotPipe, GroupByPipe, KeysPipe, + SafeUrlSanitizerPipe, SearchFilterPipe, - SafeUrlSanitizerPipe + ResourceNamePipe ], exports: [ ContentAfterLastDotPipe, GroupByPipe, KeysPipe, + SafeUrlSanitizerPipe, SearchFilterPipe, - SafeUrlSanitizerPipe + ResourceNamePipe ] }) diff --git a/catalog-ui/src/app/ng2/services/responses/properties.response.ts b/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts index a1c0660c0f..fdf9526375 100644 --- a/catalog-ui/src/app/ng2/services/responses/properties.response.ts +++ b/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts @@ -7,9 +7,9 @@ * 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. @@ -18,10 +18,17 @@ * ============LICENSE_END========================================================= */ -export class PropertiesResponse { - properties: Array<Property>; -} -class Property { - name: string +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({name: 'resourceName'}) +export class ResourceNamePipe implements PipeTransform { + transform(value) : any { + if (value) { + //newName = _.last(newName.split('.')); + const newName:string = + _.last(value.split(/tosca\.nodes\..*network\..*relationships\..*org\.openecomp\..*resource\.nfv\..*nodes\.module\..*cp\..*vl\./)); + return (newName) ? newName : value; + } + } } diff --git a/catalog-ui/src/app/ng2/services/archive.service.ts b/catalog-ui/src/app/ng2/services/archive.service.ts new file mode 100644 index 0000000000..83f1c502c2 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/archive.service.ts @@ -0,0 +1,67 @@ +/*- + * ============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========================================================= + */ + +import { Injectable, Inject } from "@angular/core"; +import { Observable } from "rxjs/Observable"; +import { HttpService } from "./http.service"; +import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config"; +import { Component } from "../../models"; +import { ComponentFactory } from 'app/utils/component-factory'; + + +@Injectable() +export class ArchiveService { + protected baseUrl; + + constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig, private componentFactory:ComponentFactory/*, @Inject(ComponentFactory) componentFactory */) { + this.baseUrl = sdcConfig.api.root ; + } + + public getArchiveCatalog() { + let archiveCatalogItems:Component[] = []; + let archiveCatalogResourceItems:Component[] = []; + let archiveCatalogServiceItems:Component[] = []; + + return this.http.get(this.baseUrl + '/v1/catalog/archive/', {}).map(res => { + let archiveCatalogObject = res.json(); + if (archiveCatalogObject.resources) archiveCatalogResourceItems = this.getResourceItems(archiveCatalogObject.resources); + if (archiveCatalogObject.services) archiveCatalogServiceItems = this.getServiceItems(archiveCatalogObject.services); + archiveCatalogItems = [].concat(archiveCatalogResourceItems, archiveCatalogServiceItems); + + return archiveCatalogItems; + }); + } + + + private getResourceItems(resources){ + let resourceItems = resources.map((resource)=>{ + return this.componentFactory.createResource(resource) + }) + return resourceItems; + } + + private getServiceItems(services){ + let serviceItems = services.map((service)=>{ + return this.componentFactory.createService(service) + }) + return serviceItems; + } + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts index 0947b2aa7f..15750020dc 100644 --- a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts +++ b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts @@ -92,4 +92,32 @@ export class ComponentInstanceServiceNg2 { return res.json().map((resInput) => new PropertyBEModel(resInput)); }); } + + getComponentGroupInstanceProperties(component: Component, groupInstanceId: string): Observable<Array<PropertyBEModel>> { + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/groups/' + groupInstanceId + '/properties') + .map((res: Response) => { + return CommonUtils.initBeProperties(res.json()); + }); + } + + updateComponentGroupInstanceProperties(component: Component, groupInstanceId: string, properties: PropertyBEModel[]): Observable<Array<PropertyBEModel>> { + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/groups/' + groupInstanceId + '/properties', properties) + .map((res: Response) => { + return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + }); + } + + getComponentPolicyInstanceProperties(component: Component, policyInstanceId: string): Observable<Array<PropertyBEModel>> { + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policyInstanceId + '/properties') + .map((res: Response) => { + return CommonUtils.initBeProperties(res.json()); + }); + } + + updateComponentPolicyInstanceProperties(component: Component, policyInstanceId: string, properties: PropertyBEModel[]): Observable<Array<PropertyBEModel>> { + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policyInstanceId + '/properties', properties) + .map((res: Response) => { + return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + }); + } } diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.ts index 381995d91c..3546ebd374 100644 --- a/catalog-ui/src/app/ng2/services/component-services/component.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/component.service.ts @@ -25,6 +25,7 @@ import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; import {Response, URLSearchParams} from '@angular/http'; import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, CreateOperationResponse} from "app/models"; +import {downgradeInjectable} from '@angular/upgrade/static'; import {COMPONENT_FIELDS} from "app/utils"; import {ComponentGenericResponse} from "../responses/component-generic-response"; import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map"; @@ -32,6 +33,9 @@ import {API_QUERY_PARAMS} from "app/utils"; import { ComponentType, ServerTypeUrl } from "../../../utils/constants"; import { HttpService } from '../http.service'; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import {IDependenciesServerResponse} from "../responses/dependencies-server-response"; +import {AutomatedUpgradeGenericResponse} from "../responses/automated-upgrade-response"; +import {IAutomatedUpgradeRequestObj} from "../../pages/automated-upgrade/automated-upgrade.service"; declare var angular:angular.IAngularStatic; @@ -83,7 +87,11 @@ export class ComponentServiceNg2 { } getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> { - return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_GROUPS]); + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]); + } + + getComponentResourcePropertiesData(component:Component):Observable<ComponentGenericResponse> { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]); } getComponentResourceInstances(component:Component):Observable<ComponentGenericResponse> { @@ -171,6 +179,15 @@ export class ComponentServiceNg2 { }) } + restoreComponent(componentType:string, componentId:string){ + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/restore', {}) + } + + archiveComponent(componentType:string, componentId:string){ + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/archive', {}) + } + + deleteInput(component:Component, input:InputBEModel):Observable<InputBEModel> { return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') @@ -198,5 +215,19 @@ export class ComponentServiceNg2 { return res.json(); }); } + + getDependencies(componentType:string, componentId: string):Observable<Array<IDependenciesServerResponse>> { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/dependencies') + .map((res:Response) => { + return res.json(); + }); + } + + automatedUpgrade(componentType:string, componentId: string, componentsIdsToUpgrade:Array<IAutomatedUpgradeRequestObj>):Observable<AutomatedUpgradeGenericResponse> { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/automatedupgrade', componentsIdsToUpgrade) + .map((res:Response) => { + return res.json(); + }); + } } diff --git a/catalog-ui/src/app/ng2/services/component-services/service.service.ts b/catalog-ui/src/app/ng2/services/component-services/service.service.ts index 0439f2047e..15e624b81f 100644 --- a/catalog-ui/src/app/ng2/services/component-services/service.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/service.service.ts @@ -126,7 +126,7 @@ export class ServiceServiceNg2 extends ComponentServiceNg2 { } getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> { - return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, SERVICE_FIELDS.FORWARDING_PATHS]); + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, SERVICE_FIELDS.FORWARDING_PATHS, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]); } protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { diff --git a/catalog-ui/src/app/ng2/services/config.service.ts b/catalog-ui/src/app/ng2/services/config.service.ts index a9a4e86a55..1774a663e2 100644 --- a/catalog-ui/src/app/ng2/services/config.service.ts +++ b/catalog-ui/src/app/ng2/services/config.service.ts @@ -38,7 +38,7 @@ export class ConfigService { constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { this.api = this.sdcConfig.api; - this.baseUrl = this.sdcConfig.api.root + this.sdcConfig.api.component_api_root; + this.baseUrl = this.api.root + this.sdcConfig.api.component_api_root; } loadValidationConfiguration(): Promise<ValidationConfiguration> { diff --git a/catalog-ui/src/app/ng2/services/groups.service.ts b/catalog-ui/src/app/ng2/services/groups.service.ts new file mode 100644 index 0000000000..e3b3d85b50 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/groups.service.ts @@ -0,0 +1,78 @@ +import {IZoneInstanceAssignment} from '../../models/graph/zones/zone-instance'; +import {Injectable, Inject} from "@angular/core"; +import {Observable} from "rxjs/Observable"; +import {HttpService} from "./http.service"; +import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; +import {GroupInstance} from '../../models/graph/zones/group-instance'; +import {UiBaseObject} from "../../models/ui-models/ui-base-object"; +import {IZoneService} from "../../models/graph/zones/zone"; + +@Injectable() +export class GroupsService implements IZoneService { + + protected baseUrl; + + private mapApiDirections = { + 'RESOURCE': 'resources', + 'SERVICE': 'services' + } + + constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + this.baseUrl = sdcConfig.api.root; + } + + public createGroupInstance(componentType:string, componentUniqueId:string, groupType:string) { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[componentType.toUpperCase()] + '/' + componentUniqueId + '/groups/' + groupType, {}).map(resp => { + return resp.json(); + }); + }; + + public addGroupMember(topologyTemplateType:string, topologyTemplateId:string, group:GroupInstance, memberId:string) { + let members:Array<string> = Object.assign({}, group.members); + members.push(memberId); + return this.updateGroupMembers(topologyTemplateType, topologyTemplateId, group.uniqueId, members); + } + + public deleteGroupMember(topologyTemplateType:string, topologyTemplateId:string, group:GroupInstance, memberId:string) { + let _members:Array<string> = angular.copy(group.members); + _members =_.without(_members, memberId); + return this.updateGroupMembers(topologyTemplateType, topologyTemplateId, group.uniqueId, _members); + } + + public updateGroupMembers(topologyTemplateType:string, topologyTemplateId:string, groupId:string, members:Array<string>):Observable<Array<string>> { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId + '/members', members) + .map(response => response.json()); + } + + public updateMembers(topologyTemplateType:string, topologyTemplateId:string, groupId:string, members:Array<UiBaseObject>):Observable<Array<string>> { + let membersIds:Array<string> = members.map(member => member.uniqueId); + return this.updateGroupMembers(topologyTemplateType, topologyTemplateId, groupId, membersIds); + } + + public getSpecificGroup(topologyTemplateType:string, topologyTemplateId:string, groupId:string):Observable<GroupInstance> { + return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId) + .map(res => { + return new GroupInstance(res.json()); + }); + } + + public updateName(topologyTemplateType:string, topologyTemplateId:string, groupId:string, newName:string):Observable<GroupInstance> { + return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId, {name: newName}).map(resp => { + return resp.json(); + }); + }; + + public deleteGroup(topologyTemplateType:string, topologyTemplateId:string, groupId:string) { + return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId).map(resp => { + return resp.json(); + }); + }; + + public updateZoneInstanceAssignments(topologyTemplateType:string, topologyTemplateId:string, policyId:string, members:Array<IZoneInstanceAssignment>):Observable<any> { + return this.updateMembers(topologyTemplateType, topologyTemplateId, policyId, members); + }; + + public deleteZoneInstance(topologyTemplateType:string, topologyTemplateId:string, policyId:string):Observable<any> { + return this.deleteGroup(topologyTemplateType, topologyTemplateId, policyId); + }; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/modal.service.ts b/catalog-ui/src/app/ng2/services/modal.service.ts index 4e86d6accf..a8f1b99ac2 100644 --- a/catalog-ui/src/app/ng2/services/modal.service.ts +++ b/catalog-ui/src/app/ng2/services/modal.service.ts @@ -24,6 +24,10 @@ export class ModalService { this.createCustomModal(modalModel).instance.open(); } + public openErrorModal = (closeButtonText?: string, errorMessage?: string):void => { + let errorModal = this.createErrorModal(closeButtonText, errorMessage); + errorModal.instance.open(); + }; /** * Shortcut method to open a basic modal with title, message, and an action button with callback, as well as close button. @@ -77,7 +81,7 @@ export class ModalService { return wizardInstance; } - + public closeCurrentModal = () => { if (!this.currentModal) return; this.currentModal.instance.close(); @@ -102,3 +106,5 @@ export class ModalService { } + + diff --git a/catalog-ui/src/app/ng2/services/policies.service.ts b/catalog-ui/src/app/ng2/services/policies.service.ts index 2b564b8915..3675a7b9ab 100644 --- a/catalog-ui/src/app/ng2/services/policies.service.ts +++ b/catalog-ui/src/app/ng2/services/policies.service.ts @@ -18,32 +18,107 @@ * ============LICENSE_END========================================================= */ -import { Injectable, Inject } from "@angular/core"; -import { Headers } from "@angular/http"; -import { Observable } from "rxjs/Observable"; -import { HttpService } from "./http.service"; -import { Cookie2Service } from "./cookie.service"; +import {Injectable, Inject} from "@angular/core"; +import {Observable} from "rxjs/Observable"; +import {HttpService} from "./http.service"; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; +import {PolicyInstance, PolicyTargetsRequest} from '../../models/graph/zones/policy-instance'; +import {IZoneInstanceAssignment} from "../../models/graph/zones/zone-instance"; +import {IZoneService} from "../../models/graph/zones/zone"; +import {TargetUiObject} from "../../models/ui-models/ui-target-object"; +import {TargetOrMemberType} from "../../utils/constants"; @Injectable() -export class PoliciesService { +export class PoliciesService implements IZoneService { protected baseUrl; private mapApiDirections = { - 'RESOURCE':'resources', - 'SERVICE':'services' + 'RESOURCE': 'resources', + 'SERVICE': 'services' } - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { - this.baseUrl = sdcConfig.api.root ; + constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + this.baseUrl = sdcConfig.api.root; } - public createPolicyInstance(entityType:string, id:string, policyType:string) { - return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[entityType] + '/' + id +'/policies/' + policyType, {}).map(resp => { - return resp.json(); + public createPolicyInstance(topologyTemplateType:string, topologyTemplateId:string, policyType:string) { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyType, {}).map(resp => { + return resp.json(); }); } + public addPolicyTarget(topologyTemplateType:string, topologyTemplateId:string, policy:PolicyInstance, targetId:string, targetType:TargetOrMemberType) { + let _targets:Array<string>; + let _members:Array<string>; + + if (targetType === TargetOrMemberType.COMPONENT_INSTANCES) { + _targets = angular.copy(policy.targets.COMPONENT_INSTANCES); + _targets.push(targetId); + } else if (targetType === TargetOrMemberType.GROUPS) { + _members = angular.copy(policy.targets.GROUPS); + _members.push(targetId); + } + let policyTargetRequest:PolicyTargetsRequest = new PolicyTargetsRequest(_members, _targets); + return this.updatePolicyTargets(topologyTemplateType, topologyTemplateId, policy.uniqueId, policyTargetRequest); + } + + public deletePolicyTarget(topologyTemplateType:string, topologyTemplateId:string, policy:PolicyInstance, targetId:string, targetType:TargetOrMemberType): Observable<PolicyInstance> { + let _targets:Array<string> = angular.copy(policy.targets.COMPONENT_INSTANCES); + let _members:Array<string> = angular.copy(policy.targets.GROUPS); + if (targetType === TargetOrMemberType.COMPONENT_INSTANCES) { + _targets = _.without(_targets, targetId); + } else if (targetType === TargetOrMemberType.GROUPS) { + _members = _.without(_members, targetId); + } + let policyTargetRequest:PolicyTargetsRequest = new PolicyTargetsRequest(_members, _targets); + return this.updatePolicyTargets(topologyTemplateType, topologyTemplateId, policy.uniqueId, policyTargetRequest); + } + + public updatePolicyTargets(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:PolicyTargetsRequest): Observable<PolicyInstance> { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId + '/targets', targets.requestItems) + .map(response => new PolicyInstance(response.json())); + } + + public updateTargets(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:Array<TargetUiObject>):Observable<PolicyInstance> { + let instances:Array<string> = _.filter(targets, (target:TargetUiObject)=> { + return target.type === TargetOrMemberType.COMPONENT_INSTANCES; + }).map(target => target.uniqueId); + + let groups:Array<string> = _.filter(targets, (target:TargetUiObject)=> { + return target.type === TargetOrMemberType.GROUPS; + }).map(target => target.uniqueId); + + let policyTargetRequest:PolicyTargetsRequest = new PolicyTargetsRequest(groups, instances); + return this.updatePolicyTargets(topologyTemplateType, topologyTemplateId, policyId, policyTargetRequest); + } + + public getSpecificPolicy(topologyTemplateType:string, topologyTemplateId:string, policyId:string):Observable<PolicyInstance> { + return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId) + .map(res => { + return new PolicyInstance(res.json()); + }); + } + + public updateName(topologyTemplateType:string, topologyTemplateId:string, policyId:string, newName:string):Observable<any> { + return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId, {name: newName}).map(res => { + return res.json(); + }); + }; + + public deletePolicy(topologyTemplateType:string, topologyTemplateId:string, policyId:string) { + return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId).map(resp => { + return resp.json(); + }); + }; + + public updateZoneInstanceAssignments(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:Array<IZoneInstanceAssignment>):Observable<PolicyInstance>{ + return this.updateTargets(topologyTemplateType, topologyTemplateId, policyId, targets); + }; + + public deleteZoneInstance(topologyTemplateType:string, topologyTemplateId:string, policyId:string):Observable<any> { + return this.deletePolicy(topologyTemplateType, topologyTemplateId, policyId); + }; + } diff --git a/catalog-ui/src/app/ng2/services/responses/automated-upgrade-response.ts b/catalog-ui/src/app/ng2/services/responses/automated-upgrade-response.ts new file mode 100644 index 0000000000..feac8685a7 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/responses/automated-upgrade-response.ts @@ -0,0 +1,16 @@ +/** + * Created by ob0695 on 4/29/2018. + */ + +export class AutomatedUpgradeStatusResponse { + name:string; + status:string; + uniqueId:string; + version:string; +} + +export class AutomatedUpgradeGenericResponse { + error:string; + status:string; + componentToUpgradeStatus:Array<AutomatedUpgradeStatusResponse>; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts index 5036a10a9d..a77133e09f 100644 --- a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts +++ b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts @@ -28,6 +28,7 @@ import {CommonUtils} from "app/utils"; import {Serializable} from "../utils/serializable"; import {PropertyBEModel} from "../../../models/properties-inputs/property-be-model"; import { PolicyInstance } from "app/models/graph/zones/policy-instance"; +import { GroupInstance } from "../../../models/graph/zones/group-instance"; export class ComponentGenericResponse implements Serializable<ComponentGenericResponse> { @@ -45,7 +46,8 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR public properties:Array<PropertyModel>; public attributes:Array<AttributeModel>; public policies:Array<PolicyInstance>; - public groups:Array<Module>; + public groupInstances: Array<GroupInstance>; + public modules:Array<Module>; public interfaces:any; public interfaceOperations:Array<OperationModel>; public additionalInformation:any; @@ -96,7 +98,8 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR this.metadata = new ComponentMetadata().deserialize(response.metadata); } if(response.groups) { - this.groups = CommonUtils.initModules(response.groups); + this.modules = CommonUtils.initModules(response.groups); + this.groupInstances = CommonUtils.initGroups(response.groups) } if(response.policies) { this.policies = CommonUtils.initPolicies(response.policies); diff --git a/catalog-ui/src/app/ng2/services/responses/dependencies-server-response.ts b/catalog-ui/src/app/ng2/services/responses/dependencies-server-response.ts new file mode 100644 index 0000000000..be8aaea75d --- /dev/null +++ b/catalog-ui/src/app/ng2/services/responses/dependencies-server-response.ts @@ -0,0 +1,14 @@ +import { state } from '@angular/core'; +/** + * Created by ob0695 on 4/23/2018. + */ +export interface IDependenciesServerResponse { + icon: string; + name: string; + type: string; + uniqueId: string; + version: string; + state: string; + dependencies: Array<IDependenciesServerResponse>; + instanceNames: Array<string>; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/window.service.ts b/catalog-ui/src/app/ng2/services/window.service.ts new file mode 100644 index 0000000000..0a11166f7b --- /dev/null +++ b/catalog-ui/src/app/ng2/services/window.service.ts @@ -0,0 +1,8 @@ +import {Injectable} from "@angular/core"; + +@Injectable() +export class WindowRef { + get nativeWindow(): any { + return window; + } +} diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html new file mode 100644 index 0000000000..c9f59f071c --- /dev/null +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html @@ -0,0 +1,6 @@ +<div class="multiline-ellipsis-container" [ngClass]="className" [ngStyle]="stylesContainer" #multilineEllipsisContainer> + <div class="multiline-ellipsis-content" [ngStyle]="stylesContent" #multilineEllipsisContent> + <ng-content></ng-content> + <div class="multiline-ellipsis-dots" [ngStyle]="stylesDots"></div> + </div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.less b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.less new file mode 100644 index 0000000000..c616d37bd8 --- /dev/null +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.less @@ -0,0 +1,25 @@ +.multiline-ellipsis-container { + position: relative; + display: block; + overflow: hidden; + white-space: normal; + //max-height: @num_lines * @line_height; + + .multiline-ellipsis-content { + word-break: break-all; + position: relative; + //max-height: (@num_lines + 1) * @line_height; + + .multiline-ellipsis-dots { + display: block; + position: absolute; + right: 0; + //top: calc(#{@num_lines * 2} - 100%); + + &::before { + display: block; + content: '...'; + } + } + } +} diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts new file mode 100644 index 0000000000..68cfedb32b --- /dev/null +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts @@ -0,0 +1,58 @@ +import {Component, OnChanges, AfterViewChecked, ViewChild, ElementRef, Input, Output, SimpleChanges, EventEmitter} from "@angular/core"; +import {WindowRef} from "../../services/window.service"; + +@Component({ + selector: 'multiline-ellipsis', + templateUrl: 'multiline-ellipsis.component.html', + styleUrls: ['multiline-ellipsis.component.less'] +}) +export class MultilineEllipsisComponent implements OnChanges, AfterViewChecked { + + @Input() public lines: number; + @Input() public lineHeight: string; + @Input() public className: string; + @Output() public hasEllipsisChanged: EventEmitter<boolean>; + + @ViewChild('multilineEllipsisContainer') public elmContainer: ElementRef; + @ViewChild('multilineEllipsisContent') public elmContent: ElementRef; + + public stylesContainer: {[key: string]: string}; + public stylesContent: {[key: string]: string}; + public stylesDots: {[key: string]: string}; + + private hasEllipsis: boolean; + + public constructor(private windowRef: WindowRef) { + this.hasEllipsisChanged = new EventEmitter<boolean>(); + } + + public ngOnChanges(changes: SimpleChanges) { + this.prepareStyles() + } + + public ngAfterViewChecked() { + const hasEllipsis = (this.elmContainer.nativeElement.offsetHeight < this.elmContent.nativeElement.offsetHeight); + if (hasEllipsis !== this.hasEllipsis) { + this.hasEllipsis = hasEllipsis; + this.hasEllipsisChanged.emit(this.hasEllipsis); + } + } + + private prepareStyles() { + const lineHeight = this.lineHeight || this.getLineHeight(); + this.stylesContainer = { + 'max-height': `calc(${this.lines} * ${lineHeight})` + }; + this.stylesContent = { + 'max-height': `calc(${this.lines + 1} * ${lineHeight})` + }; + this.stylesDots = { + 'top': `calc(${2 * this.lines} * ${lineHeight} - 100%)` + }; + } + + private getLineHeight() { + return this.windowRef.nativeWindow.getComputedStyle(this.elmContainer.nativeElement)['line-height']; + } + +} diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.module.ts b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.module.ts new file mode 100644 index 0000000000..24ed7b38af --- /dev/null +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.module.ts @@ -0,0 +1,11 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {MultilineEllipsisComponent} from './multiline-ellipsis.component'; + +@NgModule({ + declarations: [MultilineEllipsisComponent], + imports: [CommonModule], + exports: [MultilineEllipsisComponent], + entryComponents: [MultilineEllipsisComponent] +}) +export class MultilineEllipsisModule {} diff --git a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts index c005efcbe8..fcb21c0c83 100644 --- a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts +++ b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts @@ -21,12 +21,14 @@ /** * Created by rc2122 on 4/6/2017. */ -import {DataTypesService} from "../../services/data-types-service"; +import { DataTypesService } from "../../services/data-types-service"; import ICacheObject = angular.ICacheObject; -import {SharingService} from "../../services/sharing-service"; -import {CookieService} from "../../services/cookie-service"; -import {CacheService} from "../../services/cache-service"; -import {EventListenerService} from "app/services/event-listener-service"; +import { SharingService } from "../../services/sharing-service"; +import { CookieService } from "../../services/cookie-service"; +import { CacheService } from "../../services/cache-service"; +import {ComponentFactory} from "../../utils/component-factory" +import { EventListenerService } from "app/services/event-listener-service"; +import { ModalsHandler } from "app/utils"; import IScope = angular.IScope; /** Services we need to upgrade from angular1 to angular2 - in the future we need to rewrite them all to angular2 **/ @@ -39,6 +41,10 @@ export function sharingServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('Sdc.Services.SharingService'); } +export function componentServiceFactory(cacheObj: ICacheObject) { + return cacheObj.get('Sdc.Services.ComponentFactory'); +} + export function cookieServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('Sdc.Services.CookieService'); } @@ -47,14 +53,14 @@ export function stateServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('$state'); } -export function scopeServiceFactory(cacheObj: ICacheObject) { - return cacheObj.get('$scope'); -} - export function stateParamsServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('$stateParams'); } +export function scopeServiceFactory(cacheObj: ICacheObject) { + return cacheObj.get('$scope'); +} + export function cacheServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('Sdc.Services.CacheService'); } @@ -67,6 +73,18 @@ export function notificationServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('Notification'); } + +export const ComponentFactoryProvider = { + provide: ComponentFactory, + useFactory: componentServiceFactory, + deps: ['$injector'] +}; + + +export function ModalsHandlerFactory(cacheObj: ICacheObject) { + return cacheObj.get('ModalsHandler'); +} + export const DataTypesServiceProvider = { provide: DataTypesService, useFactory: dataTypesServiceFactory, @@ -93,7 +111,7 @@ export const StateServiceFactory = { deps: ['$injector'] }; -export const ScopeServiceFactory= { +export const ScopeServiceFactory = { provide: '$scope', useFactory: scopeServiceFactory, deps: ['$injector'] @@ -104,7 +122,6 @@ export const StateParamsServiceFactory = { useFactory: stateParamsServiceFactory, deps: ['$injector'] }; - export const CacheServiceProvider = { provide: CacheService, useFactory: cacheServiceFactory, @@ -122,3 +139,9 @@ export const NotificationServiceProvider = { useFactory: notificationServiceFactory, deps: ['$injector'] }; + +export const ModalsHandlerProvider = { + provide: ModalsHandler, + useFactory: ModalsHandlerFactory, + deps: ['$injector'] +} |