diff options
author | Michael Lando <ml636r@att.com> | 2018-03-04 14:53:33 +0200 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2018-03-07 13:19:05 +0000 |
commit | a5445100050e49e83f73424198d73cd72d672a4d (patch) | |
tree | cacf4df817df31be23e4e790d1dda857bdae061e /catalog-ui/src/app/ng2 | |
parent | 51157f92c21976cba4914c378aaa3cba49826931 (diff) |
Sync Integ to Master
Change-Id: I71e3acc26fa612127756ac04073a522b9cc6cd74
Issue-ID: SDC-977
Signed-off-by: Gitelman, Tal (tg851x) <tg851x@intl.att.com>
Diffstat (limited to 'catalog-ui/src/app/ng2')
94 files changed, 2465 insertions, 388 deletions
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index ffeb1fd89f..727f0fe8f5 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -28,7 +28,8 @@ import {UpgradeModule} from '@angular/upgrade/static'; import {PropertiesAssignmentModule} from './pages/properties-assignment/properties-assignment.module'; import { DataTypesServiceProvider, SharingServiceProvider, CookieServiceProvider, StateServiceFactory, - StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory + StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory, + NotificationServiceProvider } from "./utils/ng1-upgraded-provider"; import {ConfigService} from "./services/config.service"; import {HttpModule} from '@angular/http'; @@ -36,6 +37,7 @@ 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"; @@ -43,12 +45,18 @@ import {UiElementsModule} from "./components/ui/ui-elements.module"; import {ConnectionWizardModule} from "./pages/connection-wizard/connection-wizard.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 { 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 {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'; export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule)); @@ -76,7 +84,11 @@ export function configServiceFactory(config:ConfigService) { //We need to import them here since we use them in angular1 ConnectionWizardModule, PropertiesAssignmentModule, - PluginFrameModule + PluginFrameModule, + ServicePathCreatorModule, + ServicePathsListModule, + ServicePathModule, + ServicePathSelectorModule ], exports: [], entryComponents: [], @@ -85,18 +97,22 @@ export function configServiceFactory(config:ConfigService) { SharingServiceProvider, CookieServiceProvider, StateServiceFactory, - ScopeServiceFactory, StateParamsServiceFactory, + ScopeServiceFactory, CacheServiceProvider, EventListenerServiceProvider, + NotificationServiceProvider, AuthenticationService, Cookie2Service, ConfigService, ComponentServiceNg2, + ComponentServiceFactoryNg2, ModalService, ServiceServiceNg2, HttpService, UserService, + PoliciesService, + DynamicComponentService, SdcConfig, ComponentInstanceServiceNg2, TranslationServiceConfig, 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 b8b0e80861..12f8df8296 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 @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Component, Inject, Input, Output, EventEmitter} from "@angular/core"; import {IHostedApplication, IUserProperties} from "app/models"; import {MenuItemGroup, MenuItem} from "app/utils"; @@ -142,9 +143,9 @@ export class TopNavComponent { } } - goToState(state:string, params:any):Promise<boolean> { + goToState(state:string, params:Array<any>):Promise<boolean> { return new Promise((resolve, reject) => { - this.$state.go(state, params || undefined); + this.$state.go(state, params && params.length > 0 ? [0] : undefined); resolve(true); }); } diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html index ddda82ab58..cc8aed70d4 100644 --- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html +++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html @@ -7,7 +7,7 @@ <checkbox [label]="'All'" [(checked)]="allSelected" (checkedChange)="selectAll()" data-tests-id="filter-checkbox-all"></checkbox> </div> <div *ngFor="let type of typesOptions"> - <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)"></checkbox> + <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)" [attr.data-tests-id]="'filter-checkbox-' + type.toLowerCase()"></checkbox> </div> </div> <div class="field"> diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts index 5a9bfbcecb..fe2c7bb09a 100644 --- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts @@ -21,6 +21,7 @@ /** * Created by rc2122 on 5/16/2017. */ +import * as _ from "lodash"; import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core'; import {ButtonModel, ButtonsModelMap, FilterPropertiesAssignmentData} from "app/models"; import * as sdcConfig from "../../../../../../configurations/dev" diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts index dc0a02c277..1698157e90 100644 --- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts @@ -37,12 +37,10 @@ export class HierarchyNavigationComponent { onClick = ($event, item) => { $event.stopPropagation(); - this.selectedItem = item; this.updateSelected.emit(item); }; onSelectedUpdate = ($event) => { - this.selectedItem = $event; this.updateSelected.emit($event); } } 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 57e0474c66..b7cde7eb23 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 @@ -30,11 +30,11 @@ <dynamic-element class="value-input" *ngIf="input.isSimpleType" pattern="validationUtils.getValidationPattern(input.type)" - [(value)]="input.defaultValue" + [value]="input.defaultValueObj" [type]="input.type" [name]="input.name" - (change)="onInputValueChanged(input);" - [readonly]="readonly"> + (elementChanged)="onInputChanged(input, $event)" + [readonly]="readonly"> </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 0545874f53..d709f3f0c5 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 @@ -11,7 +11,7 @@ text-align:left; .inner-cell-div{ - width: 100%; + max-width: 100%; text-overflow: ellipsis; overflow: hidden; height: 20px; @@ -85,8 +85,8 @@ border-right:#d2d2d2 solid 1px; } &.col1 { - flex: 1 0 200px; - max-width:300px; + flex: 1 0 210px; + max-width:210px; display: flex; justify-content: space-between; @@ -121,7 +121,6 @@ .value-input { flex: 1; - max-height: 24px; border: none; background-color: inherit; 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 0add1cd707..ebecbc9390 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 @@ -36,7 +36,7 @@ export class InputsTableComponent { @Input() instanceNamesMap: Map<string, string>; @Input() readonly:boolean; @Input() isLoading:boolean; - @Output() inputValueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>(); @Output() deleteInput: EventEmitter<any> = new EventEmitter<any>(); selectedInputToDelete:InputFEModel; @@ -44,8 +44,9 @@ export class InputsTableComponent { constructor(private modalService: ModalService){ } - onInputValueChanged = (input) => { - this.inputValueChanged.emit(input); + onInputChanged = (input, event) => { + input.updateDefaultValueObj(event.value, event.isValid); + this.inputChanged.emit(input); }; onDeleteInput = () => { 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 14b6c7d4c0..4805875d83 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 @@ -10,7 +10,16 @@ </div> <div class="table-cell" *ngIf="!canBeDeclared && !property.isChildOfListOrMap">{{property.name}}</div> <!-- simple children of complex type within map or list --> <div class="table-cell map-entry" *ngIf="property.isChildOfListOrMap && propType == derivedPropertyTypes.MAP"><!-- map left cell --> - <input [value]="property.mapKey" #mapKey (change)="mapKeyChanged.emit(mapKey)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':!mapKey.validity.valid}" required/> + <!--<input [value]="property.mapKey" [placeholder]="property.name" (input)="mapKeyChanged.emit($event.target.value)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':property.mapKeyError}" required/>--> + <dynamic-element #mapKeyInput + class="value-input" + pattern="validationUtils.getValidationPattern(string)" + [value]="property.mapKey" + type="string" + [name]="property.name" + (elementChanged)="mapKeyChanged.emit($event.value)" + [readonly]="readonly" + ></dynamic-element> </div> </ng-container> <!-- RIGHT CELL OR FULL WIDTH CELL--> @@ -18,11 +27,11 @@ <div class="table-cell"> <dynamic-element class="value-input" pattern="validationUtils.getValidationPattern(property.type)" - [(value)]="property.valueObj" + [value]="property.valueObj" [type]="property.isDeclared ? 'string' : property.type" [name]="property.name" [path]="property.propertiesName" - (valueChange)="valueChanged.emit();" + (elementChanged)="onElementChanged($event)" [readonly]="readonly || property.isDeclared || property.isDisabled" ></dynamic-element> </div> @@ -54,8 +63,8 @@ [propertyNameSearchText]="propertyNameSearchText" [readonly]="readonly" [hasChildren]="getHasChildren(prop)" - (valueChanged)="childValueChanged(prop)" - (mapKeyChanged)="removeValueFromParent(prop, $event)" + (propertyChanged)="childValueChanged(prop)" + (mapKeyChanged)="updateChildKeyInParent(prop, $event)" (expandChild)="expandChildById($event)" (deleteItem)="deleteListOrMapItem($event)" (clickOnPropertyRow)="onClickPropertyRow($event)" 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 04cb26d030..6f7e57b643 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 @@ -18,13 +18,15 @@ * ============LICENSE_END========================================================= */ -import {Component, Input, Output, EventEmitter} from "@angular/core"; +import * as _ from "lodash"; +import {Component, Input, Output, EventEmitter, ViewChild, ComponentRef} from "@angular/core"; import { PropertyFEModel, DerivedFEProperty, DerivedPropertyType } from "app/models"; import { PROPERTY_TYPES } from 'app/utils'; import { DataTypeService } from "../../../../services/data-type.service"; import { trigger, state, style, transition, animate } from '@angular/core'; import {PropertiesUtils} from "../../../../pages/properties-assignment/services/properties.utils"; - +import {IUiElementChangeEvent} from "../../../ui/form-components/ui-element-base.component"; +import {DynamicElementComponent} from "../../../ui/dynamic-element/dynamic-element.component"; @Component({ selector: 'dynamic-property', @@ -49,7 +51,7 @@ export class DynamicPropertyComponent { @Input() hasChildren: boolean; @Input() hasDeclareOption:boolean; - @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output('propertyChanged') emitter: EventEmitter<void> = new EventEmitter<void>(); @Output() expandChild: EventEmitter<string> = new EventEmitter<string>(); @Output() checkProperty: EventEmitter<string> = new EventEmitter<string>(); @Output() deleteItem: EventEmitter<string> = new EventEmitter<string>(); @@ -57,6 +59,7 @@ export class DynamicPropertyComponent { @Output() mapKeyChanged: EventEmitter<string> = new EventEmitter<string>(); @Output() addChildPropsToParent: EventEmitter<Array<DerivedFEProperty>> = new EventEmitter<Array<DerivedFEProperty>>(); + @ViewChild('mapKeyInput') public mapKeyInput: DynamicElementComponent; constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) { } @@ -68,6 +71,17 @@ export class DynamicPropertyComponent { this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length; } + ngDoCheck() { + // set custom error for mapKeyInput + if (this.mapKeyInput) { + const mapKeyInputControl = this.mapKeyInput.cmpRef.instance.control; + const mapKeyError = (<DerivedFEProperty>this.property).mapKeyError; + if (mapKeyInputControl.getError('mapKeyError') !== mapKeyError) { + mapKeyInputControl.setErrors({mapKeyError}); + } + } + } + onClickPropertyRow = (property, event) => { // Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined @@ -91,9 +105,15 @@ export class DynamicPropertyComponent { }).length > 1; } + onElementChanged = (event: IUiElementChangeEvent) => { + this.property.updateValueObj(event.value, event.isValid); + this.emitter.emit(); + }; + createNewChildProperty = (): void => { - let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined); + let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", null); + this.propertiesUtils.assignFlattenedChildrenValues(this.property.valueObj, [newProps[0]], this.property.propertiesName); if (this.property instanceof PropertyFEModel) { this.addChildProps(newProps, this.property.name); } else { @@ -108,28 +128,25 @@ export class DynamicPropertyComponent { this.property.flattenedChildren.splice(insertIndex, 0, ...newProps); //using ES6 spread operator this.expandChildById(newProps[0].propertiesName); - - if(!newProps[0].schema.property.isSimpleType){ - if ( newProps[0].mapKey ) {//prevent update the new item value on parent property valueObj and saving on BE if it is map item, it will be updated and saved only after user enter key (when it is list item- the map key is the es type) - this.updateMapKeyValueOnMainParent(newProps); - if (this.property.getParentNamesArray(newProps[0].propertiesName, []).indexOf('') === -1) { - this.valueChanged.emit(this.property.name); - } - } - } + this.updateMapKeyValueOnMainParent(newProps); + this.emitter.emit(); } } updateMapKeyValueOnMainParent(childrenProps: Array<DerivedFEProperty>){ if (this.property instanceof PropertyFEModel) { + const property: PropertyFEModel = <PropertyFEModel>this.property; //Update only if all this property parents has key name - if (this.property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){ + if (property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){ angular.forEach(childrenProps, (prop:DerivedFEProperty):void => { //Update parent PropertyFEModel with value for each child, including nested props - (<PropertyFEModel>this.property).childPropUpdated(prop); + property.childPropUpdated(prop); + if (prop.isChildOfListOrMap && prop.mapKey !== undefined) { + property.childPropMapKeyUpdated(prop, prop.mapKey, true); + } },this); //grab the cumulative value for the new item from parent PropertyFEModel and assign that value to DerivedFEProp[0] (which is the list or map parent with UUID of the set we just added) - let parentNames = (<PropertyFEModel>this.property).getParentNamesArray(childrenProps[0].propertiesName, []); - childrenProps[0].valueObj = _.get(this.property.valueObj, parentNames.join('.')); + let parentNames = (<PropertyFEModel>property).getParentNamesArray(childrenProps[0].propertiesName, []); + childrenProps[0].valueObj = _.get(property.valueObj, parentNames.join('.'), null); } } } @@ -140,7 +157,7 @@ export class DynamicPropertyComponent { if (this.property.getParentNamesArray(property.propertiesName, []).indexOf('') === -1) {//If one of the parents is empty key -don't save this.property.childPropUpdated(property); this.dataTypeService.checkForCustomBehavior(this.property); - this.valueChanged.emit(this.property.name); + this.emitter.emit(); } } } @@ -153,46 +170,42 @@ export class DynamicPropertyComponent { } } - removeValueFromParent = (item: DerivedFEProperty, target?: any) => { + removeValueFromParent = (item: DerivedFEProperty) => { if (this.property instanceof PropertyFEModel) { - let itemParent = (item.parentName == this.property.name) ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName); + let itemParent = (item.parentName == this.property.name) + ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName); + if (!itemParent) { + return; + } if (item.derivedDataType == DerivedPropertyType.MAP) { - let oldKey = item.mapKey; - if (target && typeof target.value == 'string') { //allow saving empty string - let replaceKey:string = target.value; - if (!replaceKey) {//prevent delete map key - return; - } - if(Object.keys(itemParent.valueObj).indexOf(replaceKey) > -1){//the key is exists - target.setCustomValidity('This key is already exists.'); - return; - }else { - target.setCustomValidity(''); - _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]); - item.mapKey = replaceKey; - //If the map key was empty its valueObj was not updated on its prent property valueObj, and now we should update it. - if(!oldKey && !item.schema.property.isSimpleType){ - //Search this map item children and update these value on parent property valueOBj - let mapKeyFlattenChildren:Array<DerivedFEProperty> = _.filter(this.property.flattenedChildren, (prop:DerivedFEProperty) => { - return _.startsWith(prop.propertiesName, item.propertiesName); - }); - this.updateMapKeyValueOnMainParent(mapKeyFlattenChildren); - } - } - } + const oldKey = item.getActualMapKey(); delete itemParent.valueObj[oldKey]; + if (itemParent instanceof PropertyFEModel) { + delete itemParent.valueObjValidation[oldKey]; + itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid(); + } + this.property.childPropMapKeyUpdated(item, null); // remove map key } else { - let itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName); + const itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName); itemParent.valueObj.splice(itemIndex, 1); - } - if (item.mapKey) {//prevent going to BE if user tries to delete map item without key (it was not saved in BE) - if (itemParent instanceof PropertyFEModel) { //direct child - this.valueChanged.emit(this.property.name); - } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc) - this.childValueChanged(itemParent); + if (itemParent instanceof PropertyFEModel) { + itemParent.valueObjValidation.splice(itemIndex, 1); + itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid(); } } + if (itemParent instanceof PropertyFEModel) { //direct child + this.emitter.emit(); + } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc) + this.childValueChanged(itemParent); + } + } + } + + updateChildKeyInParent(childProp: DerivedFEProperty, newMapKey: string) { + if (this.property instanceof PropertyFEModel) { + this.property.childPropMapKeyUpdated(childProp, newMapKey); + 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 933b80f577..ecfa7e7c5e 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 @@ -48,7 +48,7 @@ [expandedChildId]="property.expandedChildPropertyId" [propertyNameSearchText]="propertyNameSearchText" [readonly]="readonly" - (valueChanged)="propValueChanged(property);" + (propertyChanged)="onPropertyChanged(property)" (expandChild)="property.updateExpandedChildPropertyId($event)" (clickOnPropertyRow)="onClickPropertyInnerRow($event, instanceId)" (checkProperty)="propertyChecked(property, $event)" 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 32bbb1b3a0..093fae1684 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 @@ -39,7 +39,7 @@ export class PropertiesTableComponent { @Input() hasDeclareOption:boolean; @Input() hidePropertyType:boolean; - @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>(); @Output() selectPropertyRow: EventEmitter<PropertyRowSelectedEvent> = new EventEmitter<PropertyRowSelectedEvent>(); @Output() updateCheckedPropertyCount: EventEmitter<boolean> = new EventEmitter<boolean>();//only for hasDeclareOption @@ -49,8 +49,8 @@ export class PropertiesTableComponent { ngOnInit() { } - propValueChanged = (property) => { - this.valueChanged.emit(property); + onPropertyChanged = (property) => { + this.emitter.emit(property); }; // Click on main row (row of propertyFEModel) 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 bcd33efc30..6e0f93f750 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 @@ -22,7 +22,8 @@ </div> </div> <div class="inner-container properties-table-container" *ngIf="selectedReqOrCapOption == 'Capability'" [ngClass]="{'cap-selected':selectedReqOrCapModel}"> - <properties-table *ngIf="selectedReqOrCapModel" + <loader [display]="loadingCapabilityProperties" size="medium" [relative]="true"></loader> + <properties-table *ngIf="selectedReqOrCapModel && !loadingCapabilityProperties" class="properties-table" [readonly]="true" [fePropertiesMap]="capabilityProperties" diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts index ab67dc1850..2e3c21c210 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts @@ -1,10 +1,10 @@ /** * Created by rc2122 on 9/4/2017. */ +import * as _ from "lodash"; import {Component, EventEmitter, Input, OnInit, Output, SimpleChanges} from '@angular/core'; -import {RadioButtonModel, Match, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models"; +import {RadioButtonModel, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models"; import {Dictionary} from "lodash"; -import {DropdownValue} from "../../ui/form-components/dropdown/ui-element-dropdown.component"; import {ComponentInstanceServiceNg2} from "../../../services/component-instance-services/component-instance.service"; import {PropertiesUtils} from "app/ng2/pages/properties-assignment/services/properties.utils"; import {Requirement} from "../../../../models/requirement"; @@ -33,7 +33,6 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { @Input() selectedReqOrCapModel:RequirementCapabilityModel; @Output() updateSelectedReqOrCap:EventEmitter<RequirementCapabilityModel> = new EventEmitter<RequirementCapabilityModel>(); - @Output() updateCapabilityProperties:EventEmitter<Array<PropertyModel>> = new EventEmitter<Array<PropertyModel>>(); types:Array<string> = []; selectedType:string; @@ -47,10 +46,14 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { displayCapReqListFilterByType:RequirementCapabilityModel[]; capabilityProperties:InstanceFePropertiesMap; + loadingCapabilityProperties:boolean; + + private _loadingCapabilityProperties: Array<Capability>; constructor(private componentInstanceServiceNg2:ComponentInstanceServiceNg2, private propertiesUtils:PropertiesUtils) { this.selectOptions = [new RadioButtonModel(REQUIREMENT, REQUIREMENT), new RadioButtonModel(CAPABILITY, CAPABILITY)]; + this._loadingCapabilityProperties = []; } private initDefaultReqOrCapSelection = (): void => { @@ -87,14 +90,17 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { initCapabilityPropertiesTable = ():void => { if(this.selectedReqOrCapModel instanceof Capability ) { let selectedCapability = <Capability>this.selectedReqOrCapModel; - if(selectedCapability.properties){ + if (selectedCapability.properties && selectedCapability.properties.length) { this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({ CAPABILITY : selectedCapability.properties}, false); + } else { + this.capabilityProperties = null; } } } ngOnChanges(changes:SimpleChanges) { if (changes.selectedReqOrCapModel) { + this.capabilityProperties = null; if (this.selectedReqOrCapModel && this.selectedReqOrCapOption === CAPABILITY) { this.setCapabilityProperties(); } @@ -168,15 +174,27 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { private setCapabilityProperties = ():void => { let selectedCapability = <Capability>this.selectedReqOrCapModel; - if (selectedCapability.properties === undefined) { - this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability.type, selectedCapability.name) - .subscribe((response:Array<PropertyModel>) => { - this.capabilityProperties = (response && response.length) ? this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : response}, false) : null; - this.updateCapabilityProperties.emit(response); - }, error => {}); - }else{ - this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : selectedCapability.properties}, false); - this.updateCapabilityProperties.emit(selectedCapability.properties); + if (!selectedCapability.properties) { + this.loadingCapabilityProperties = true; + if (this._loadingCapabilityProperties.indexOf(selectedCapability) == -1) { + this._loadingCapabilityProperties.push(selectedCapability); + this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability) + .subscribe((response: Array<PropertyModel>) => { + if (this.selectedReqOrCapModel === selectedCapability) { + delete this.loadingCapabilityProperties; + } + this.initCapabilityPropertiesTable(); + }, (error) => { + if (this.selectedReqOrCapModel === selectedCapability) { + delete this.loadingCapabilityProperties; + } + }, () => { + this._loadingCapabilityProperties.splice(this._loadingCapabilityProperties.indexOf(selectedCapability), 1); + }); + } + } else { + delete this.loadingCapabilityProperties; + this.initCapabilityPropertiesTable(); } } } diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts index cb12dea02a..259ae12380 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts @@ -5,6 +5,7 @@ import {FormElementsModule} from "../../ui/form-components/form-elements.module" import {CommonModule} from "@angular/common"; import {GlobalPipesModule} from "app/ng2/pipes/global-pipes.module"; import {PropertyTableModule} from "../properties-table/property-table.module"; +import {UiElementsModule} from "../../ui/ui-elements.module"; @NgModule({ declarations: [ @@ -15,7 +16,9 @@ import {PropertyTableModule} from "../properties-table/property-table.module"; FormsModule, FormElementsModule, GlobalPipesModule, - PropertyTableModule], + UiElementsModule, + PropertyTableModule + ], exports: [SelectRequirementOrCapabilityComponent], providers: [] 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 new file mode 100644 index 0000000000..72e083534c --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html @@ -0,0 +1,11 @@ +<div class="service-path-selector"> + <label>Service Paths:</label> + <ui-element-dropdown + class="path-dropdown" + data-tests-id="service-path-selector" + [readonly]="dropdownOptions.length < 3" + [(value)]="selectedPathId" + [values]="dropdownOptions" + (valueChange)="onSelectPath()"> + </ui-element-dropdown> +</div> diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less new file mode 100644 index 0000000000..f3cb4a3c34 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less @@ -0,0 +1,24 @@ +@import './../../../../../assets/styles/variables.less'; +.service-path-selector { + margin: 10px 35px 10px 0; + display: flex; + font-size: 12px; + + /deep/ .path-dropdown { + width: 150px; + select { + font-size: 14px; + font-family: @font-opensans-regular; + padding: 4px 10px; + } + } + + label { + margin-right: 10px; + align-self: center; + font-size: 14px; + font-family: @font-opensans-regular; + font-weight: normal; + margin-bottom: initial; + } +} 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 new file mode 100644 index 0000000000..be9966acef --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts @@ -0,0 +1,134 @@ +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"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; + +@Component({ + selector: 'service-path-selector', + templateUrl: './service-path-selector.component.html', + styleUrls:['service-path-selector.component.less'], + providers: [TranslateService] +}) + +export class ServicePathSelectorComponent implements DoCheck { + + defaultSelectedId: string; + hideAllValue: string; + hideAllId: string = '0'; + showAllValue: string; + showAllId: string = '1'; + + paths: Array<ForwardingPath> = []; + dropdownOptions: Array<DropdownValue>; + differ: KeyValueDiffer; + + @Input() service: Service; + @Input() drawPath: Function; + @Input() deletePaths: Function; + @Input() selectedPathId: string; + + constructor(private differs: KeyValueDiffers, private translateService: TranslateService) { + + this.defaultSelectedId = this.hideAllId; + this.convertPathsToDropdownOptions(); + + this.translateService.languageChangedObservable.subscribe(lang => { + this.hideAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_HIDE_ALL_VALUE"); + this.showAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE"); + this.convertPathsToDropdownOptions(); + }); + + } + + ngOnInit(): void { + + this.selectedPathId = this.defaultSelectedId; + this.differ = this.differs.find(this.service.forwardingPaths).create(null); + + } + + ngDoCheck(): void { + + const pathsChanged = this.differ.diff(this.service.forwardingPaths); + + if (pathsChanged) { + let oldPaths = _.cloneDeep(this.paths); + this.populatePathsFromService(); + + if (!(_.isEqual(oldPaths, this.paths))) { + this.convertPathsToDropdownOptions(); + + let temp = this.selectedPathId; + this.selectedPathId = '-1'; + + setTimeout(() => { + this.selectedPathId = temp; + this.onSelectPath(); + }, 0); + } + } + + } + + populatePathsFromService(): void { + + this.paths = []; + let {forwardingPaths} = this.service; + + _.forEach(forwardingPaths, path => { + this.paths.push(path); + }); + this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> { + return a.name.localeCompare(b.name); + }); + + } + + convertPathsToDropdownOptions(): void { + + let result = [ + new DropdownValue(this.hideAllId, this.hideAllValue), + new DropdownValue(this.showAllId, this.showAllValue) + ]; + + _.forEach(this.paths, (value: ForwardingPath) => { + result[result.length] = new DropdownValue(value.uniqueId, value.name); + }); + + this.dropdownOptions = result; + + } + + onSelectPath = (): void => { + + if (this.selectedPathId !== '-1') { + this.deletePaths(); + + switch (this.selectedPathId) { + case this.hideAllId: + break; + + case this.showAllId: + _.forEach(this.paths, path => + this.drawPath(path) + ); + break; + + default: + let path = this.paths.find(path => + path.uniqueId === this.selectedPathId + ); + if (!path) { + this.selectedPathId = this.defaultSelectedId; + this.onSelectPath(); // currently does nothing in default case, but if one day it does, we want the selection to behave accordingly. + break; + } + this.drawPath(path); + break; + } + } + + } +} diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts new file mode 100644 index 0000000000..c07061ce9a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathSelectorComponent} from "./service-path-selector.component"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; + +@NgModule({ + declarations: [ + ServicePathSelectorComponent + ], + imports: [ + CommonModule, + UiElementsModule + ], + exports: [], + entryComponents: [ + ServicePathSelectorComponent + ], + providers: [] +}) +export class ServicePathSelectorModule { +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..ff7902e2b9 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html @@ -0,0 +1,22 @@ +<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 > + <ul> + <li><div class="hand" (click)="onCreateServicePath()" data-tests-id="createPathMenuItem"> + Create Service Path + </div></li> + <li><div class="hand" (click)="onListServicePath()" data-tests-id="pathsListMenuItem"> + Service Paths 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.less b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less new file mode 100644 index 0000000000..777b206714 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less @@ -0,0 +1,51 @@ +//@import 'src/assets/styles/variables.less'; +@import './../../../../../assets/styles/variables.less'; +.service-path { + position: relative; + .create-path-button{ + &:extend(.search-bar-button); + width: 30px; + height: 30px; + &:hover { + color: @main_color_a; + } + &:active { + background: @main_color_a; + color: @main_color_p; + } + &:focus { + outline: none; + } + } + .service-path-menu { + border: 1px solid @main_color_o; + border-radius: 0 0 2px 2px; + border-top-color: @main_color_a; + border-top-width: 3px; + + box-sizing: border-box; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.30); + + background-color: @main_color_p; + + padding: 5px 0; + right: 34px; + position: absolute; + top: 10px; + width: 150px; + font-size: 13px; + font-family: @font-opensans-regular; + + li { + color: @main_color_m; + padding: 0 10px; + line-height: 20px; + &:hover { + cursor: pointer; + color: @main_color_a; + background-color: fade(@main_color_a, 5%); + } + } + + } +} 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 new file mode 100644 index 0000000000..4a6209fb6f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts @@ -0,0 +1,83 @@ +/*- + * ============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, ComponentRef} from '@angular/core'; +import {ModalService} from 'app/ng2/services/modal.service'; +import {ModalModel, ButtonModel} from 'app/models'; +import {ServicePathCreatorComponent} from 'app/ng2/pages/service-path-creator/service-path-creator.component'; +import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component'; +import ServicePathsListComponent from "app/ng2/pages/service-paths-list/service-paths-list.component"; +import {Service} from "app/models/components/service"; + +@Component({ + selector: 'service-path', + templateUrl: './service-path.component.html', + styleUrls: ['service-path.component.less'], + providers: [ModalService] +}) + +export class ServicePathComponent { + showServicePathMenu: boolean = false; + modalInstance: ComponentRef<ModalComponent>; + @Input() service: Service; + @Input() onCreate: Function; + @Input() onSave: Function; + + constructor(private ModalServiceNg2: ModalService) {} + + onCreateServicePath = ():void => { + 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); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service}); + this.modalInstance.instance.open(); + }; + + 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); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathsListComponent, {service: this.service, + onCreateServicePath: this.onCreateServicePath, onEditServicePath: this.onEditServicePath}); + this.modalInstance.instance.open(); + }; + + createPath = ():void => { + this.onCreate(this.modalInstance.instance.dynamicContent.instance.createServicePathData()); + this.ModalServiceNg2.closeCurrentModal(); + }; + + 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]); + 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(); + }; +} + diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts new file mode 100644 index 0000000000..96af247086 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.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 { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathComponent} from "./service-path.component"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; + +@NgModule({ + declarations: [ + ServicePathComponent + ], + imports: [CommonModule, + UiElementsModule], + exports: [], + entryComponents: [ + ServicePathComponent + ], + providers: [] +}) +export class ServicePathModule { +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..e279e3f704 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html @@ -0,0 +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__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"> + <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 new file mode 100644 index 0000000000..3e77c5ca3b --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less @@ -0,0 +1,62 @@ +.sdc-canvas-zone { + width: 280px; + max-height:186px; + display:flex; + flex-direction:column; + align-self: flex-end; + color:white; + font-family:OpenSans-Regular, sans-serif; + transition: width .2s ease-in-out, max-height .2s ease-in-out .1s; + position:relative; + bottom:0px; + + .sdc-canvas-zone__header { + background: #5A5A5A; + border-radius: 2px 2px 0 0; + padding: 5px 10px; + display:flex; + justify-content: space-between; + font-size: 14px; + text-transform:uppercase; + .sdc-canvas-zone__state-button { + font-weight:bold; + cursor:pointer; + } + } + + .sdc-canvas-zone__container { + padding:5px; + background-color: #5A5A5A; + opacity:0.9; + flex: 1; + display:flex; + flex-direction: row; + align-items: flex-start; + flex-wrap:wrap; + overflow-y:auto; + min-height: 80px; + max-height: 170px; + } + + + &.minimized { + max-height:30px; + width:120px; + cursor:pointer; + + .sdc-canvas-zone__state-button { + display:none; + } + .sdc-canvas-zone__container { + flex: 0 0 0; + min-height: 0; + padding: 0; + overflow-y:hidden; + transition: min-height .2s ease-in-out .2s; + transition: padding .1s ease-in-out 0s; + } + } + &.hidden { + display:none; + } +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..7e60cb37a6 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts @@ -0,0 +1,25 @@ +import { Component, Input, Output, ViewEncapsulation, EventEmitter } from '@angular/core'; +import { EventListenerService } from 'app/services'; +import { GRAPH_EVENTS } from 'app/utils'; + +@Component({ + selector: 'zone-container', + templateUrl: './zone-container.component.html', + styleUrls: ['./zone-container.component.less'], + encapsulation: ViewEncapsulation.None +}) + +export class ZoneContainerComponent { + @Input() title:string; + @Input() class:string; + @Input() count:number; + @Input() showZone:boolean; + @Input() minifyZone:boolean; + constructor(private eventListenerService:EventListenerService) {} + + private unminifyZone = () => { + this.minifyZone = !this.minifyZone; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE); + } + +}
\ 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 new file mode 100644 index 0000000000..d36b7aec6f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html @@ -0,0 +1,8 @@ +<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> + <div class="zone-child__name">{{config.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 new file mode 100644 index 0000000000..a1d56df96e --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less @@ -0,0 +1,110 @@ +.zone-child { + position:relative; + width:76px; + margin:5px; + + .zone-child__handle { + display:none; + position:absolute; + right:4px; + top:10px; + width:22px; + height:22px; + cursor:pointer; + border: solid white 1px; + border-radius: 2px; + text-align: center; + font-weight:bold; + } + + .zone-child__body { + margin:0 auto; + width:43px; + height:43px; + display:flex; + padding:3px; + + } + + .zone-child__body-content { + border-radius: 2px; + flex:1; + color:white; + font-size:18px; + text-align:center; + display:flex; + align-items: center; + justify-content: center; + } + + .zone-child__name { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + text-align:center; + } + /* Dynamic classes below */ + + &.mode-1, &.mode-2, &.mode-3 { //hover, selected, tag + .zone-child__body { + border:solid 2px; + border-radius: 2px; + padding:2px; + cursor:pointer; + } + .zone-child__handle{ + display:block; + cursor:pointer; + } + } + + &.locked { + cursor: default; + } + + // &:not(.locked):hover .zone-child__handle{ + // display:block; + // } + .zone-child__body { + cursor: default; + } + &.mode-3 .zone-child__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-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-child__body-content { + background: #009FDB; + } +} + +.sdc-canvas-zone.policy-zone { + .zone-child__handle { + background-color:#0568AE; + } + .zone-child__body { + border-color:#1287D9; + .zone-child__body-content { + background: #1287D9; + } + } + .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 new file mode 100644 index 0000000000..8057ae908a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts @@ -0,0 +1,28 @@ +import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; +import { ZoneInstanceConfig, ZoneInstanceMode } from 'app/models/graph/zones/zone-child'; + +@Component({ + selector: 'zone-instance', + templateUrl: './zone-instance.component.html', + styleUrls: ['./zone-instance.component.less'], + encapsulation: ViewEncapsulation.None +}) +export class ZoneInstanceComponent { + + @Input() config:ZoneInstanceConfig; + @Input() defaultIconText:string; + @Input() isActive:boolean; + @Input() activeInstanceMode: ZoneInstanceMode; + @Output() modeChange: EventEmitter<any> = new EventEmitter<any>(); + private MODE = ZoneInstanceMode; + + 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}); + } + if(event){ + 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 53d1590b1c..d1e68f3088 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 @@ -18,14 +18,25 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactoryResolver } from '@angular/core' import {ValidationConfiguration} from "app/models"; +import {IUiElementChangeEvent} from "../form-components/ui-element-base.component"; import {UiElementInputComponent} from "../form-components/input/ui-element-input.component"; import {UiElementPopoverInputComponent} from "../form-components/popover-input/ui-element-popover-input.component"; import {UiElementIntegerInputComponent} from "../form-components/integer-input/ui-element-integer-input.component"; import {UiElementDropDownComponent, DropdownValue} from "../form-components/dropdown/ui-element-dropdown.component"; import {PROPERTY_DATA} from "../../../../utils/constants"; +enum DynamicElementComponentCreatorIdentifier { + STRING, + INTEGER, + FLOAT, + BOOLEAN, + SUBNETPOOLID, + DEFAULT +} + @Component({ selector: 'dynamic-element', template: `<div #target></div>`, @@ -44,16 +55,14 @@ export class DynamicElementComponent { @Input() name: string; @Input() readonly:boolean; @Input() path:string;//optional param. used only for for subnetpoolid type - value:any; - // Two way binding for value (need to write the "Change" word like this) - @Output('valueChange') emitter: EventEmitter<string> = new EventEmitter<any>(); - @Input('value') set setValueValue(value) { - this.value = value; - } + @Input() value: any; + @Output() valueChange: EventEmitter<any> = new EventEmitter<any>(); + @Output('elementChanged') emitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>(); cmpRef: ComponentRef<any>; private isViewInitialized: boolean = false; + private elementCreatorIdentifier: DynamicElementComponentCreatorIdentifier; validation = ValidationConfiguration.validation; constructor( @@ -66,29 +75,72 @@ export class DynamicElementComponent { if (!this.isViewInitialized) { return; } - if (this.cmpRef) { - this.cmpRef.destroy(); - } - // Factory to create component based on type or peroperty name. + // Factory to create component based on type or other property attributes. + const prevElementCreatorIdentifier: DynamicElementComponentCreatorIdentifier = this.elementCreatorIdentifier; switch(true) { case this.path && this.path.toUpperCase().indexOf("SUBNETPOOLID") !== -1: + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.SUBNETPOOLID; + break; + case this.type === 'integer': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER; + break; + case this.type === 'float': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT; + break; + case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1: + case this.type === 'string': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING; + break; + case this.type === 'boolean': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN; + break; + default: + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT; + } + + // In case the dynamic element creator is changed, then destroy old and build new. + if (this.elementCreatorIdentifier !== prevElementCreatorIdentifier) { + if (this.cmpRef) { + this.cmpRef.destroy(); + } + this.createComponentByIdentifier(); + } + + // Update attributes in base element class + if (this.cmpRef) { + this.cmpRef.instance.name = this.name; + this.cmpRef.instance.type = this.type; + this.cmpRef.instance.value = this.value; + this.cmpRef.instance.readonly = this.readonly; + } + } + + createComponentByIdentifier() { + switch(this.elementCreatorIdentifier) { + case DynamicElementComponentCreatorIdentifier.SUBNETPOOLID: if(this.name.toUpperCase().indexOf("SUBNETPOOLID") == -1){//if it's an item of subnetpoolid list get the parent name let pathArray = this.path.split("#"); this.name = pathArray[pathArray.length - 2]; } this.createComponent(UiElementPopoverInputComponent); break; - case this.type == 'integer': + + case DynamicElementComponentCreatorIdentifier.INTEGER: this.createComponent(UiElementIntegerInputComponent); this.cmpRef.instance.pattern = this.validation.validationPatterns.integer; break; - case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1: - case this.type == 'string': + + case DynamicElementComponentCreatorIdentifier.FLOAT: + this.createComponent(UiElementIntegerInputComponent); + this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/; + break; + + case DynamicElementComponentCreatorIdentifier.STRING: this.createComponent(UiElementInputComponent); break; - case this.type == 'boolean': + case DynamicElementComponentCreatorIdentifier.BOOLEAN: this.createComponent(UiElementDropDownComponent); // Build drop down values @@ -96,25 +148,20 @@ export class DynamicElementComponent { tmp.push(new DropdownValue(true,'TRUE')); tmp.push(new DropdownValue(false,'FALSE')); this.cmpRef.instance.values = tmp; + if(!_.isUndefined(this.value)){//contains the real value (and not a string) + this.value = JSON.parse(this.value); + } break; + + case DynamicElementComponentCreatorIdentifier.DEFAULT: default: this.createComponent(UiElementInputComponent); console.log("ERROR: No ui component to handle type: " + this.type); } - // Additional attributes in base element class - if (this.cmpRef) { - this.cmpRef.instance.name = this.name; - this.cmpRef.instance.type = this.type; - this.cmpRef.instance.value = this.value; - this.cmpRef.instance.readonly = this.readonly; - } - // Subscribe to change event of of ui-element-basic and fire event to change the value - this.cmpRef.instance.baseEmitter.subscribe((value):void => { - this.emitter.emit(value) - }); - + this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); }); + this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); }); } createComponent(ComponentToCreate:any):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 c6b8384183..805e5ac295 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)="onSave()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType"> +<select name='{{name}}' [(ngModel)]="value" (change)="onChange()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType"> <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/dropdown/ui-element-dropdown.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts index 5abf32c61b..03a1fc6040 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts @@ -43,9 +43,4 @@ export class UiElementDropDownComponent extends UiElementBase implements UiEleme constructor() { super(); } - - onSave() { - this.baseEmitter.emit(this.value); - } - } 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 b7d7c859c7..057e731ada 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 @@ -4,7 +4,7 @@ type="text" [name]="name" [(ngModel)]="value" - (change)="onSave()" + (input)="onChange()" [attr.maxlength]="validation.propertyValue.max" [attr.minlength]="validation.propertyValue.min" [pattern]="pattern" diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts index fb3b3db859..ce231e76e8 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts @@ -32,10 +32,4 @@ export class UiElementInputComponent extends UiElementBase implements UiElementB super(); this.pattern = this.validation.validationPatterns.comment; } - - onSave() { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value); - } - } } 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 9fbc9e1094..e1555e87fd 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 @@ -4,7 +4,7 @@ type="text" [name]="name" [(ngModel)]="value" - (change)="onSave()" + (input)="onChange()" [attr.maxlength]="validation.propertyValue.max" [attr.minlength]="validation.propertyValue.min" [pattern]="pattern" diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts index 1667f4393d..9aa4b5872c 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts @@ -33,9 +33,8 @@ export class UiElementIntegerInputComponent extends UiElementBase implements UiE //this.pattern = this.validation.validationPatterns.comment; } - onSave() { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value ? JSON.parse(this.value) : this.value); - } + onChange() { + this.value = this.control.valid && this.value ? JSON.parse(this.value) : this.value; + super.onChange(); } } 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 61688df3f0..525cd1742c 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 @@ -35,17 +35,10 @@ export class UiElementPopoverInputComponent extends UiElementBase implements UiE saveButton: ButtonModel; buttonsArray: ButtonsModelMap; - onSave = ():void => { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value); - this.popoverContentComponent.hide(); - } - } - constructor() { super(); // Create Save button and insert to buttons map - this.saveButton = new ButtonModel('save', 'blue', this.onSave); + this.saveButton = new ButtonModel('save', 'blue', this.onChange); this.buttonsArray = { 'test': this.saveButton }; // Define the regex pattern for this controller diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts index 0f80e2ad44..3e8c3b5813 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts @@ -12,10 +12,6 @@ import {UiElementBaseInterface, UiElementBase} from "../ui-element-base.componen }) export class RadioButtonComponent extends UiElementBase implements UiElementBaseInterface { - onSave() { - this.baseEmitter.emit(this.value); - } - @Input() options:Array<RadioButtonModel>; @Input() readonly:boolean; @Input() direction:string = 'vertical'; //get 'horizontal' | 'vertical' @@ -23,7 +19,7 @@ export class RadioButtonComponent extends UiElementBase implements UiElementBas select(value:any) { this.value = value; - this.baseEmitter.emit(this.value); + this.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 ae2013ff70..b4e9e7d36a 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 @@ -23,7 +23,12 @@ import { ValidationConfiguration } from "app/models"; import { FormControl, Validators } from '@angular/forms'; export interface UiElementBaseInterface { - onSave(); + onChange(); +} + +export interface IUiElementChangeEvent { + value: any; + isValid: boolean; } @Component({ @@ -35,21 +40,29 @@ export class UiElementBase { protected validation = ValidationConfiguration.validation; protected control: FormControl; - // Two way binding for value (need to write the "Change" word like this) - @Output('valueChange') baseEmitter: EventEmitter<string> = new EventEmitter<any>(); - @Input('value') set setValueValue(value) { - this.value = value; - } + @Input() value: any; + @Output() valueChange: EventEmitter<any> = new EventEmitter<any>(); + @Output('elementChanged') baseEmitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>(); @Input() name: string; @Input() type: string; - @Input() value: any; @Input() pattern: any; @Input() readonly:boolean; constructor() { //this.control = new FormControl('', [Validators.required]); this.control = new FormControl('', []); + + this.baseEmitter.subscribe((changeEvent: IUiElementChangeEvent) => { + this.valueChange.emit(changeEvent.value); + }) + } + + onChange() { + this.baseEmitter.emit({ + value: this.value, + isValid: this.control.valid + }); } } diff --git a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts index f66aa551e2..585c36660e 100644 --- a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts @@ -74,10 +74,10 @@ export class LoaderComponent { if (this.relative === true) { let parentElement = this.viewContainerRef.element.nativeElement.parentElement; this.offset = { - left: (parentElement.offsetLeft) ? parentElement.offsetLeft + "px" : undefined, - top: (parentElement.offsetTop) ? parentElement.offsetTop + "px" : undefined, - width: (parentElement.offsetWidth) ? parentElement.offsetWidth + "px" : undefined, - height: (parentElement.offsetHeight) ? parentElement.offsetHeight + "px" : undefined + left: (parentElement.offsetLeft !== undefined) ? parentElement.offsetLeft + "px" : undefined, + top: (parentElement.offsetTop !== undefined) ? parentElement.offsetTop + "px" : undefined, + width: (parentElement.offsetWidth !== undefined) ? parentElement.offsetWidth + "px" : undefined, + height: (parentElement.offsetHeight !== undefined) ? parentElement.offsetHeight + "px" : undefined }; } this.isVisible = true; 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 9a0fdf6bf4..6fc55d19e7 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,14 @@ <div class="custom-modal {{input.size}}"> - <div class="ng2-modal-content"> - <div class="ng2-modal-header modal-type-{{input.type}}"> + <div class="ng2-modal-content" + ngDraggable + [ngDraggable]="input.isMovable" + [handle]="ModalHandle" + [bounds]="ModalBounds" + [inBounds]="true" + [preventDefaultEvent]="false"> + <div #ModalHandle + class="ng2-modal-header modal-type-{{input.type}}" + [ngClass]="{'movable': input.isMovable}"> <span class="title">{{ input.title }}</span> <span class="close-button" (click)="close()"></span> </div> @@ -18,4 +26,4 @@ </div> </div> </div> -<div class="modal-background"></div> +<div #ModalBounds class="modal-background" [ngClass]="{'transparent': input.isMovable}"></div> diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less index fac1ae74a8..03b2a70667 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less @@ -12,7 +12,7 @@ bottom: 0; left: 0; z-index: 15007; - overflow: auto; + overflow: visible; margin: auto; display: flex; align-items: center; @@ -22,6 +22,8 @@ width: 100%; box-shadow: 0 5px 15px rgba(0,0,0,.5); border-radius: 4px; + + .ng2-modal-body{ padding: 20px; } @@ -29,19 +31,28 @@ .ng2-modal-header{ .m_18_r; font-weight: bold; + -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; + height: 50px; line-height: 50px; + margin: 0px 20px; + display: -webkit-box; display: -ms-flexbox; display: flex; + text-align: left; -webkit-box-align: center; -ms-flex-align: center; align-items: center; - margin: 0px 20px; + + &.ng-draggable { + cursor: move; + user-select: none; + } &.modal-type-standard { border-bottom: solid 3px @main_color_a; @@ -101,9 +112,14 @@ background-color: #000; opacity: 0.5; z-index: 900; + + &.transparent { + background-color: transparent; + } } + .xl { width: 1200px; } diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts index 89db8d1140..777e9bdc06 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts @@ -33,6 +33,7 @@ import { ButtonsModelMap, ModalModel } from 'app/models'; }) export class ModalComponent implements OnInit, OnDestroy { + @Input() isMovable: boolean; @Input() input: ModalModel; @Input() dynamicContent: any; @ViewChild('dynamicContentContainer', { read: ViewContainerRef }) dynamicContentContainer: ViewContainerRef; //Allows for custom component as body instead of simple message. See ModalService.createActionModal for implementation details, and HttpService's catchError() for example. diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts index c38e60194b..2999528d13 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts @@ -1,5 +1,6 @@ import { NgModule } from "@angular/core"; import { CommonModule } from '@angular/common'; +import { AngularDraggableModule } from 'angular2-draggable'; import { ModalService } from 'app/ng2/services/modal.service'; import { ErrorMessageComponent } from "./error-message/error-message.component"; import {ModalComponent} from "./modal.component"; @@ -9,7 +10,7 @@ import {ModalComponent} from "./modal.component"; ModalComponent, ErrorMessageComponent ], - imports: [CommonModule], + imports: [CommonModule, AngularDraggableModule], exports: [ModalComponent, ErrorMessageComponent], entryComponents: [ //need to add anything that will be dynamically created ModalComponent, diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html new file mode 100644 index 0000000000..2a776d444f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html @@ -0,0 +1,4 @@ +<div class="palette-animation-wrapper" [style.top]="from.y + 50 + 'px'" [style.left]="from.x + 'px'" [style.transform]="transformStyle" [class.hidden]="!visible" + (transitionend)="animationComplete()"> +<div class="medium small sprite-resource-icons sprite-{{iconName}}-icons {{iconName}}" ></div> +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less new file mode 100644 index 0000000000..54f04189c0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less @@ -0,0 +1,5 @@ +.palette-animation-wrapper{ + position: absolute; + z-index: 100; + transition: all 2s ease-in-out; +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..609a1fc5e1 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.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 {Component, Input } from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import { setTimeout } from 'core-js/library/web/timers'; +import { EventListenerService } from 'app/services'; +import { GRAPH_EVENTS } from 'app/utils'; +import { Point } from 'app/models'; + + + +@Component({ + selector: 'palette-animation', + templateUrl: './palette-animation.component.html', + styleUrls:['./palette-animation.component.less'], +}) + +export class PaletteAnimationComponent { + + @Input() from : Point; + @Input() to : Point; + @Input() iconName : string; + @Input() data : any; + + public animation; + private visible:boolean = false; + private transformStyle:string = ""; + + + constructor(private eventListenerService:EventListenerService) {} + + public runAnimation() { + this.visible = true; + let positionDiff:Point = new Point(this.to.x - this.from.x, this.to.y - this.from.y); + setTimeout(()=>{ + this.transformStyle = 'translate('+ positionDiff.x + 'px,' + positionDiff.y +'px)'; + }, 0); + }; + + public animationComplete = (e) => { + this.visible = false; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE); + }; + + +} diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts new file mode 100644 index 0000000000..8674571138 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { PaletteAnimationComponent } from "./palette-animation.component"; + + +@NgModule({ + declarations: [ + PaletteAnimationComponent + ], + imports: [ CommonModule ], + exports: [ PaletteAnimationComponent ] +}) + +export class PaletteAnimationModule { + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html new file mode 100644 index 0000000000..ed172bf2b0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html @@ -0,0 +1,9 @@ +<div class="popup-panel" [ngClass]="{'hide':!isShowPanel}" [style.left]="popupPanelPosition.x + 'px'" [style.top]="popupPanelPosition.y + 'px'" + (mousedown)="addZoneInstance()" + (mouseenter)="onMouseEnter()" + (mouseleave)="onMouseLeave()"> + <div class="popup-panel-group"> + <div class="popup-panel-plus">+</div> + <div class="popup-panel-title">{{panelTitle}}</div> + </div> +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less new file mode 100644 index 0000000000..24f0485e76 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less @@ -0,0 +1,37 @@ +.popup-panel { + position: absolute; + display: inline-block; + background-color: white; + border: solid 1px #d2d2d2; + border-top: solid 3px #13a7df; + left: 208px; top: 0px; + width: 140px; + height: 40px; + z-index: 10000; + + &:hover { + background-color: whitesmoke; + } + + .popup-panel-group { + padding-left: 8px; + padding-top: 8px; + cursor: pointer; + + .popup-panel-plus { + border-radius: 50%; + color: white; + background-color: #13a7df; + width: 20px; + text-align: center; + display: inline-block; + } + + .popup-panel-title { + padding-left: 10px; + display: inline-block; + } + + } + +} 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 new file mode 100644 index 0000000000..d30d5f6178 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts @@ -0,0 +1,87 @@ +import {Component, OnInit} from '@angular/core'; +import {GRAPH_EVENTS} from "app/utils"; +import {LeftPaletteComponent, Point} from "app/models"; +import {EventListenerService} from "app/services"; +import {LeftPaletteMetadataTypes} from "../../../../models/components/displayComponent"; + +@Component({ + selector: 'app-palette-popup-panel', + templateUrl: './palette-popup-panel.component.html', + styleUrls: [ './palette-popup-panel.component.less' ], +}) +export class PalettePopupPanelComponent implements OnInit { + + public panelTitle: string; + public isShowPanel: boolean; + private component: Component; + private displayComponent: LeftPaletteComponent; + private popupPanelPosition:Point = new Point(0,0); + + constructor(private eventListenerService: EventListenerService) { + this.isShowPanel = false; + } + + ngOnInit() { + this.registerObserverCallbacks(); + } + + public onMouseEnter() { + this.isShowPanel = true; + } + + public onMouseLeave() { + this.isShowPanel = false; + } + + public addZoneInstance(): void { + if(this.displayComponent) { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, this.component, this.displayComponent, this.popupPanelPosition); + } + } + + private registerObserverCallbacks() { + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL, + (component: Component, displayComponent: LeftPaletteComponent, sectionElem: HTMLElement) => { + + this.component = component; + this.showPopupPanel(displayComponent, sectionElem); + }); + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL, () => this.hidePopupPanel()); + } + + private getPopupPanelPosition (sectionElem: HTMLElement):Point { + let pos: ClientRect = sectionElem.getBoundingClientRect(); + let offsetX: number = -30; + const offsetY: number = pos.height / 2; + return new Point((pos.right + offsetX), (pos.top - offsetY + window.pageYOffset)); + }; + + private setPopupPanelTitle(component: LeftPaletteComponent): void { + if (component.categoryType === LeftPaletteMetadataTypes.Group) { + this.panelTitle = "Add Group"; + return; + } + + if (component.categoryType === LeftPaletteMetadataTypes.Policy) { + this.panelTitle = "Add Policy"; + return; + } + } + + private showPopupPanel(displayComponent:LeftPaletteComponent, sectionElem: HTMLElement) { + if(!this.isShowPanel){ + this.displayComponent = displayComponent; + this.setPopupPanelTitle(displayComponent); + this.popupPanelPosition = this.getPopupPanelPosition(sectionElem); + this.isShowPanel = true; + } + }; + + private hidePopupPanel() { + if(this.isShowPanel){ + this.isShowPanel = false; + } + }; +} diff --git a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html index 6d76f0ad06..fc6e821f34 100644 --- a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html +++ b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html @@ -15,7 +15,8 @@ <ng-content></ng-content> <div class="popover-footer"> <button *ngFor="let buttonName of buttonsNames" - class="tlv-btn {{buttons[buttonName].cssClass}}" + class="tlv-btn {{buttons[buttonName].cssClass}}" + [attr.data-tests-id]="'filter-' + buttons[buttonName].text.toLowerCase() + '-button'" [disabled] = "buttons[buttonName].getDisabled && buttons[buttonName].getDisabled()" (click) = "buttons[buttonName].callback()">{{buttons[buttonName].text}}</button> </div> diff --git a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts index 21d2bbad43..f4b410347b 100644 --- a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts @@ -23,6 +23,8 @@ import { Tab } from './tab/tab.component'; import { ViewEncapsulation } from '@angular/core'; import { trigger, state, style, transition, animate, keyframes } from '@angular/core'; +export {Tab}; + @Component({ selector: 'tabs', templateUrl: './tabs.component.html', 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 526096524c..44314734c3 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 @@ -27,6 +27,10 @@ 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"; @@ -35,15 +39,21 @@ 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"; @NgModule({ declarations: [ LoaderComponent, SearchBarComponent, - SearchWithAutoCompleteComponent - ], + SearchWithAutoCompleteComponent, + PalettePopupPanelComponent, + ZoneContainerComponent, + ZoneInstanceComponent, + PaletteAnimationComponent +], imports: [ + //SdcUiComponentsModule, BrowserModule, FormsModule, CommonModule, @@ -63,6 +73,9 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n MultiStepsWizardModule, SearchBarComponent, SearchWithAutoCompleteComponent, + PalettePopupPanelComponent, + ZoneContainerComponent, + ZoneInstanceComponent, DynamicElementModule, NavbarModule, FormElementsModule, @@ -71,10 +84,10 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n TabModule, TooltipModule, MenuListModule, - MenuListNg2Module + MenuListNg2Module, + PaletteAnimationComponent ], - - entryComponents: [SearchWithAutoCompleteComponent] + entryComponents: [SearchWithAutoCompleteComponent, PalettePopupPanelComponent, ZoneContainerComponent, ZoneInstanceComponent, PaletteAnimationComponent] }) export class UiElementsModule {} 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 a097fb04ea..bf5ec4c4f6 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 @@ -1,3 +1,4 @@ +import * as _ from "lodash"; import {ConnectRelationModel} from "../../../models/graph/connectRelationModel"; import {Injectable} from "@angular/core"; import { Requirement, Capability} from "app/models"; diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html index 0c9d9e6e26..a45f07b3e2 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html @@ -3,6 +3,5 @@ [selectedReqOrCapModel]="connectWizardService.selectedMatch && (connectWizardService.selectedMatch.isFromTo ? connectWizardService.selectedMatch.requirement : connectWizardService.selectedMatch.capability)" [currentComponent]="connectWizardService.currentComponent" [componentInstanceId]="connectWizardService.connectRelationModel.fromNode.componentInstance.uniqueId" - (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)" - (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)"> + (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"> </select-requirement-or-capability>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts index edbbf8a0a3..054d38b063 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts @@ -32,10 +32,6 @@ export class FromNodeStepComponent implements IStepComponent, OnInit{ return true; } - onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) { - this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties; - } - private updateSelectedReqOrCap = (selected:Requirement|Capability):void => { if(!selected){ this.connectWizardService.selectedMatch = null; diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html index 9e34893272..9b1df69d77 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html @@ -5,7 +5,7 @@ </div> <div class="properties-table-container"> <properties-table class="properties-table" - (valueChanged)="propertyValueChanged($event)" + (propertyChanged)="propertyValueChanged($event)" [fePropertiesMap]="capabilityPropertiesMap" [selectedPropertyId]="''" [hidePropertyType]="true"> diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts index 3e48785a3c..946d1858dc 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts @@ -25,7 +25,7 @@ export class PropertiesStepComponent implements IStepComponent{ constructor(@Inject(forwardRef(() => ConnectionWizardService)) public connectWizardService: ConnectionWizardService, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, private propertiesUtils:PropertiesUtils) { - this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capabilityProperties}, false); + this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capability.properties}, false); } ngOnInit() { @@ -42,7 +42,8 @@ export class PropertiesStepComponent implements IStepComponent{ propertyValueChanged = (property: PropertyFEModel) => { if (!property.isDeclared) { const propChangedIdx = this.connectWizardService.changedCapabilityProperties.indexOf(property); - if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) { + if (property.hasValueObjChanged()) { + // if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) { console.log("==>" + this.constructor.name + ": propertyValueChanged " + property); if (propChangedIdx === -1) { this.connectWizardService.changedCapabilityProperties.push(property); diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html index 67bb12e6fc..a8343ce7e2 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html @@ -4,6 +4,5 @@ [selectedReqOrCapOption]="displayRequirementsOrCapabilities" [currentComponent]="connectWizardService.currentComponent" [componentInstanceId]="connectWizardService.connectRelationModel.toNode.componentInstance.uniqueId" - (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)" - (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)"> + (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"> </select-requirement-or-capability>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts index 9c7bf4dfe6..ea3b129c7b 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts @@ -43,10 +43,6 @@ export class ToNodeStepComponent implements IStepComponent{ return false; } - onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) { - this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties; - } - private updateSelectedReqOrCap = (selected:Requirement|Capability):void => { if (!selected) { if (this.connectWizardService.selectedMatch.isFromTo) { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts index 203c75dd11..907f7638bb 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts @@ -32,6 +32,7 @@ import {DataTypeService} from "../../services/data-type.service"; import {PropertiesAssignmentComponent} from "./properties-assignment.page.component"; import {HierarchyNavService} from "./services/hierarchy-nav.service"; import {PropertiesUtils} from "./services/properties.utils"; +import {InputsUtils} from "./services/inputs.utils"; import {ComponentModeService} from "../../services/component-services/component-mode.service"; @NgModule({ @@ -53,7 +54,7 @@ import {ComponentModeService} from "../../services/component-services/component- exports: [ PropertiesAssignmentComponent ], - providers: [PropertiesService, HierarchyNavService, PropertiesUtils, DataTypeService, ComponentModeService] + providers: [PropertiesService, HierarchyNavService, PropertiesUtils, InputsUtils, DataTypeService, ComponentModeService] }) export class PropertiesAssignmentModule { 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 beea3fe73f..03e9f1fa7e 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 @@ -1,43 +1,50 @@ <div class="properties-assignment-page"> <div class="main-content"> <div class="left-column"> - <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true"> - <tab tabTitle="Properties"> - <properties-table class="properties-table" - [fePropertiesMap]="instanceFePropertiesMap" - [feInstanceNamesMap]="componentInstanceNamesMap" - [searchTerm]="searchQuery" - [selectedPropertyId]="selectedFlatProperty.path" - [propertyNameSearchText]="searchPropertyName" - [readonly]="isReadonly" - [isLoading]="loadingProperties" - [hasDeclareOption]="true" - (valueChanged)="propertyValueChanged($event)" - (propertySelected)="propertySelected($event)" - (selectPropertyRow)="selectPropertyRow($event)" - (selectChildProperty)="selectChildProperty($event)" - (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)" - (selectInstanceRow)="selectInstanceRow($event)"> + <div class="main-tabs-section"> + <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true"> + <tab tabTitle="Properties"> + <properties-table class="properties-table" + [fePropertiesMap]="instanceFePropertiesMap" + [feInstanceNamesMap]="componentInstanceNamesMap" + [searchTerm]="searchQuery" + [selectedPropertyId]="selectedFlatProperty.path" + [propertyNameSearchText]="searchPropertyName" + [readonly]="isReadonly" + [isLoading]="loadingProperties || savingChangedData" + [hasDeclareOption]="true" + (propertyChanged)="dataChanged($event)" + (propertySelected)="propertySelected($event)" + (selectPropertyRow)="selectPropertyRow($event)" + (selectChildProperty)="selectChildProperty($event)" + (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)" + (selectInstanceRow)="selectInstanceRow($event)"> </properties-table> - </tab> - <tab tabTitle="Inputs"> - <inputs-table class="properties-table" - [readonly]="isReadonly" - [inputs]="inputs | searchFilter:'name':searchQuery" - [instanceNamesMap]="componentInstanceNamesMap" - [isLoading]="loadingInputs" - (deleteInput)="deleteInput($event)" - (inputValueChanged)="inputValueChanged($event)"></inputs-table> - </tab> - </tabs> + </tab> + <tab tabTitle="Inputs"> + <inputs-table class="properties-table" + [readonly]="isReadonly" + [inputs]="inputs | searchFilter:'name':searchQuery" + [instanceNamesMap]="componentInstanceNamesMap" + [isLoading]="loadingInputs" + (deleteInput)="deleteInput($event)" + (inputChanged)="dataChanged($event)"> + </inputs-table> + </tab> + </tabs> + <div class="main-tabs-section-buttons"> + <button class="tlv-btn outline blue" [disabled]="!hasChangedData || savingChangedData" (click)="reverseChangedData()" data-tests-id="properties-reverse-button">Discard</button> + <button class="tlv-btn blue" [disabled]="!hasChangedData || !isValidChangedData || savingChangedData" (click)="doSaveChangedData()" data-tests-id="properties-save-button">{{savingChangedData ? 'Saving' : 'Save'}}</button> + </div> + </div> <div class="header"> - <div class="search-filter-container" [class.without-filter]="isInpusTabSelected"> - <span *ngIf="displayClearSearch && !isInpusTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span> + <div class="search-filter-container" [class.without-filter]="isInputsTabSelected"> + <span *ngIf="displayClearSearch && isPropertiesTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span> <input type="text" class="search-box" placeholder="Search" [(ngModel)]="searchQuery" data-tests-id="search-box"/> <span class="sprite search-icon" data-tests-id="search-button"></span> - <filter-properties-assignment *ngIf="!isInpusTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> + <filter-properties-assignment *ngIf="isPropertiesTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> </div> - <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly" (click)="declareProperties()" data-tests-id="declare-button">Declare</button> + <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly || hasChangedData" (click)="declareProperties()" data-tests-id="declare-button">Declare</button> </div> </div> <div class="right-column gray-border"> @@ -48,10 +55,10 @@ <div class="hierarchy-header white-sub-header"> <span tooltip="{{component.name}}">{{component.name}}</span> </div> - <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInputsTabSelected">No data to display</div> <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onInstanceSelectedUpdate($event)" - [displayData]="isInpusTabSelected ? []: instancesNavigationData" + [displayData]="isInputsTabSelected ? []: instancesNavigationData" [selectedItem]="selectedInstanceData.uniqueId" [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation> </div> @@ -59,12 +66,12 @@ <tab tabTitle="Property Structure"> <div class="hierarchy-nav-container"> <div class="hierarchy-header white-sub-header" [class.selected]="selectedFlatProperty.path == propertyStructureHeader"> - <span tooltip="{{!isInpusTabSelected ? propertyStructureHeader : ''}}">{{!isInpusTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span> + <span tooltip="{{isPropertiesTabSelected ? propertyStructureHeader : ''}}">{{isPropertiesTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span> </div> - <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInputsTabSelected">No data to display</div> <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onPropertySelectedUpdate($event)" - [displayData]="isInpusTabSelected ? [] : propertiesNavigationData" + [displayData]="isInputsTabSelected ? [] : propertiesNavigationData" [selectedItem]="selectedFlatProperty.path" [displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation> </div> @@ -72,4 +79,9 @@ </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.less b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less index 03974bf723..6de6dda7bb 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less @@ -76,6 +76,7 @@ outline: none; font-style: italic; color:@ng2-med-dark-gray; + width: auto; &::-moz-placeholder { color:@ng2-med-dark-gray;} &::-webkit-input-placeholder{ color:@ng2-med-dark-gray;} @@ -113,6 +114,17 @@ top: 0; right: 0; } + + .main-tabs-section { + position: relative; + + .main-tabs-section-buttons { + position: absolute; + top: 45px; + right: 0; + padding: 4px; + } + } } .right-column { 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 9603648bd8..40818bce78 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 @@ -18,11 +18,11 @@ * ============LICENSE_END========================================================= */ -import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core"; +import * as _ from "lodash"; +import {Component, ViewChild, Inject, TemplateRef} from "@angular/core"; import { PropertiesService } from "../../services/properties.service"; -import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models"; +import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models"; import { ResourceType } from "app/utils"; -import property = require("lodash/property"); 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"; @@ -35,6 +35,9 @@ import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/ import {HierarchyNavService} from "./services/hierarchy-nav.service"; import {PropertiesUtils} from "./services/properties.utils"; import {ComponentModeService} from "../../services/component-services/component-mode.service"; +import {ModalService} from "../../services/modal.service"; +import {Tabs, Tab} from "../../components/ui/tabs/tabs.component"; +import {InputsUtils} from "./services/inputs.utils"; @Component({ templateUrl: './properties-assignment.page.component.html', @@ -64,24 +67,36 @@ export class PropertiesAssignmentComponent { hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name'); displayClearSearch = false; searchPropertyName:string; - isInpusTabSelected:boolean; + currentMainTab:Tab; + isInputsTabSelected:boolean; + isPropertiesTabSelected:boolean; isReadonly:boolean; loadingInstances:boolean = false; loadingInputs:boolean = false; loadingProperties:boolean = false; - - @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef; - @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef; + changedData:Array<PropertyFEModel|InputFEModel>; + hasChangedData:boolean; + isValidChangedData:boolean; + savingChangedData:boolean; + stateChangeStartUnregister:Function; + + @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, + private inputsUtils:InputsUtils, private componentServiceNg2:ComponentServiceNg2, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, @Inject("$stateParams") _stateParams, - private renderer: Renderer, + @Inject("$scope") private $scope:ng.IScope, + @Inject("$state") private $state:ng.ui.IStateService, + @Inject("Notification") private Notification:any, private componentModeService:ComponentModeService, + private ModalService:ModalService, private EventListenerService:EventListenerService) { this.instanceFePropertiesMap = new InstanceFePropertiesMap(); @@ -91,6 +106,10 @@ export class PropertiesAssignmentComponent { this.component = _stateParams.component; this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout); this.updateViewMode(); + + this.changedData = []; + this.updateHasChangedData(); + this.isValidChangedData = true; } ngOnInit() { @@ -102,7 +121,9 @@ export class PropertiesAssignmentComponent { .getComponentInputs(this.component) .subscribe(response => { _.forEach(response.inputs, (input: InputBEModel) => { - this.inputs.push(new InputFEModel(input)); //only push items that were declared via SDC + const newInput: InputFEModel = new InputFEModel(input); + this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue); + this.inputs.push(newInput); //only push items that were declared via SDC }); this.loadingInputs = false; @@ -123,10 +144,22 @@ export class PropertiesAssignmentComponent { this.selectFirstInstanceByDefault(); }, error => {}); //ignore error + this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => { + // stop if has changed properties + if (this.hasChangedData) { + event.preventDefault(); + this.openChangedDataModal().then((proceed) => { + if (proceed) { + this.$state.go(toState, toParams); + } + }); + } + }); }; ngOnDestroy() { this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT); + this.stateChangeStartUnregister(); } selectFirstInstanceByDefault = () => { @@ -147,12 +180,23 @@ export class PropertiesAssignmentComponent { onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => { console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate"); + + // stop if has changed properties + if (this.hasChangedData) { + this.openChangedDataModal().then((proceed) => { + if (proceed) { + this.onInstanceSelectedUpdate(resourceInstance); + } + }); + return; + } + let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); this.selectedInstanceData = resourceInstance; this.selectedInstanceType = resourceInstance.originType; this.loadingProperties = true; - if(this.isInput(resourceInstance.originType)) { + if (this.isInput(resourceInstance.originType)) { this.componentInstanceServiceNg2 .getComponentInstanceInputs(this.component, resourceInstance) .subscribe(response => { @@ -160,7 +204,8 @@ export class PropertiesAssignmentComponent { this.processInstancePropertiesResponse(instanceBePropertiesMap, true); this.loadingProperties = false; - }, error => {}); //ignore error + }, error => { + }); //ignore error } else { this.componentInstanceServiceNg2 .getComponentInstanceProperties(this.component, resourceInstance.uniqueId) @@ -168,14 +213,15 @@ export class PropertiesAssignmentComponent { instanceBePropertiesMap[resourceInstance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, false); this.loadingProperties = false; - }, error => {}); //ignore error + }, error => { + }); //ignore error } - if(resourceInstance.componentName === "vnfConfiguration") { + if (resourceInstance.componentName === "vnfConfiguration") { this.isReadonly = true; } - if( this.searchPropertyName ){ + if (this.searchPropertyName) { this.clearSearch(); } //clear selected property from the navigation @@ -193,41 +239,31 @@ export class PropertiesAssignmentComponent { /*** VALUE CHANGE EVENTS ***/ - propertyValueChanged = (event: PropertyFEModel) => { - console.log("==>" + this.constructor.name + ": propertyValueChanged " + event); - // Copying the actual value from the object ref into the value if it's from a complex type - event.value = event.getJSONValue(); - - if (this.isInput(this.selectedInstanceData.originType)) { - console.log("I want to update input value on the resource instance"); - let inputToUpdate = new PropertyBEModel(event); - this.componentInstanceServiceNg2 - .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate) - .subscribe(response => { - console.log("Update resource instance input response: ", response); - }, error => {}); //ignore error - } - else { - let propertyBe = new PropertyBEModel(event); - this.componentInstanceServiceNg2 - .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe) - .subscribe(response => { - console.log("Update resource instance property response: ", response); - }, error => {}); //ignore error - console.log(event); + dataChanged = (item:PropertyFEModel|InputFEModel) => { + let itemHasChanged; + if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) { + itemHasChanged = item.hasValueObjChanged(); + } else if (this.isInputsTabSelected && item instanceof InputFEModel) { + itemHasChanged = item.hasDefaultValueChanged(); } - }; - - inputValueChanged = (event) => { - console.log("==>" + this.constructor.name + ": inputValueChanged"); - let inputToUpdate = new PropertyBEModel(event); + const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item); + if (itemHasChanged) { + if (dataChangedIdx === -1) { + this.changedData.push(item); + } + } else { + if (dataChangedIdx !== -1) { + this.changedData.splice(dataChangedIdx, 1); + } + } - this.componentServiceNg2 - .updateComponentInput(this.component, inputToUpdate) - .subscribe(response => { - console.log("updated the component input and got this response: ", response); - }, error => {}); //ignore error + if (this.isPropertiesTabSelected) { + this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid); + } else if (this.isInputsTabSelected) { + this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid); + } + this.updateHasChangedData(); }; @@ -272,7 +308,7 @@ export class PropertiesAssignmentComponent { // Set selected property in table this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName); - this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']); + this.hierarchyNavTabs.triggerTabChange('Property Structure'); }; @@ -280,13 +316,27 @@ export class PropertiesAssignmentComponent { this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => { return instance.name == $event; }); - this.renderer.invokeElementMethod( - this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); }; 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); + return; + } + console.log("==>" + this.constructor.name + ": tabChanged " + event); - this.isInpusTabSelected = event.title === "Inputs"; + this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title); + this.isPropertiesTabSelected = this.currentMainTab.title === "Properties"; + this.isInputsTabSelected = this.currentMainTab.title === "Inputs"; this.propertyStructureHeader = null; this.searchQuery = ''; }; @@ -320,12 +370,180 @@ export class PropertiesAssignmentComponent { this.checkedPropertiesCount = 0; _.forEach(response, (input: InputBEModel) => { let newInput: InputFEModel = new InputFEModel(input); + this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue); this.inputs.push(newInput); this.updatePropertyValueAfterDeclare(newInput); }); }, error => {}); //ignore error }; + saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => { + return new Promise((resolve, reject) => { + if (!this.isValidChangedData) { + reject('Changed data is invalid - cannot save!'); + return; + } + if (!this.changedData.length) { + resolve([]); + return; + } + + // make request and its handlers + let request; + let handleSuccess, handleError; + if (this.isPropertiesTabSelected) { + const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => { + changedProp = <PropertyFEModel>changedProp; + const propBE = new PropertyBEModel(changedProp); + propBE.value = changedProp.getJSONValue(); + return propBE; + }); + + 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.isInputsTabSelected) { + const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => { + changedInput = <InputFEModel>changedInput; + const inputBE = new InputBEModel(changedInput); + inputBE.defaultValue = changedInput.getJSONDefaultValue(); + return inputBE; + }); + request = this.componentServiceNg2 + .updateComponentInputs(this.component, changedInputs); + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resInput) => { + const changedInput = <InputFEModel>this.changedData.shift(); + this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue); + }); + console.log("updated the component inputs and got this response: ", response); + } + } + + this.savingChangedData = true; + request.subscribe( + (response) => { + this.savingChangedData = false; + handleSuccess && handleSuccess(response); + this.updateHasChangedData(); + resolve(response); + }, + (error) => { + this.savingChangedData = false; + handleError && handleError(error); + this.updateHasChangedData(); + reject(error); + } + ); + }); + }; + + reverseChangedData = ():void => { + // make reverse item handler + let handleReverseItem; + if (this.isPropertiesTabSelected) { + handleReverseItem = (changedItem) => { + changedItem = <PropertyFEModel>changedItem; + this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value); + }; + } else if (this.isInputsTabSelected) { + handleReverseItem = (changedItem) => { + changedItem = <InputFEModel>changedItem; + this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue); + }; + } + + this.changedData.forEach(handleReverseItem); + this.changedData = []; + this.updateHasChangedData(); + }; + + updateHasChangedData = ():boolean => { + const curHasChangedData:boolean = (this.changedData.length > 0); + if (curHasChangedData !== this.hasChangedData) { + this.hasChangedData = curHasChangedData; + this.$scope.$emit('setWorkspaceTopBarActive', !this.hasChangedData); + } + return this.hasChangedData; + }; + + doSaveChangedData = ():void => { + this.saveChangedData().then( + () => { + this.Notification.success({ + message: 'Successfully saved changes', + title: 'Saved' + }); + }, + () => { + this.Notification.error({ + message: 'Failed to save changes!', + title: 'Failure' + }); + } + ); + }; + + openChangedDataModal = ():Promise<boolean> => { + let modalTitle; + 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(); + }); + }; updatePropertyValueAfterDeclare = (input: InputFEModel) => { if (this.instanceFePropertiesMap[input.instanceUniqueId]) { @@ -346,12 +564,12 @@ export class PropertiesAssignmentComponent { }; setInputTabIndication = (numInputs: number): void => { - this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]); + this.propertyInputTabs.setTabIndication('Inputs', numInputs); }; deleteInput = (input: InputFEModel) => { console.log("==>" + this.constructor.name + ": deleteInput"); - let inputToDelete = new PropertyBEModel(input); + let inputToDelete = new InputBEModel(input); this.componentServiceNg2 .deleteInput(this.component, inputToDelete) @@ -389,7 +607,7 @@ export class PropertiesAssignmentComponent { this.processInstancePropertiesResponse(response, false); this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree this.searchPropertyName = filterData.propertyName;//mark in table - this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); this.propertiesNavigationData = []; this.displayClearSearch = true; }, error => {}); //ignore error @@ -408,12 +626,11 @@ export class PropertiesAssignmentComponent { clickOnClearSearch = () => { this.clearSearch(); this.selectFirstInstanceByDefault(); - this.renderer.invokeElementMethod( - this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); }; private isInput = (instanceType:string):boolean =>{ - return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC; + return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR; } } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts index 016b04788e..1a800baac7 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { SimpleFlatProperty, PropertyFEModel, DerivedFEProperty } from 'app/models'; diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts new file mode 100644 index 0000000000..408a00e7b4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts @@ -0,0 +1,40 @@ +/*- + * ============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 } from '@angular/core'; +import { InputFEModel} from "app/models"; + +@Injectable() +export class InputsUtils { + + constructor() {} + + public initDefaultValueObject = (input: InputFEModel): void => { + input.resetDefaultValueObjValidation(); + input.defaultValueObj = input.getDefaultValueObj(); + input.updateDefaultValueObjOrig(); + }; + + public resetInputDefaultValue = (input: InputFEModel, newDefaultValue: string): void => { + input.defaultValue = newDefaultValue; + this.initDefaultValueObject(input); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts index 8f46c6f603..e7b59b96ba 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, DerivedFEProperty, DerivedPropertyType, InputFEModel} from "app/models"; import { DataTypeService } from "app/ng2/services/data-type.service"; @@ -47,10 +48,13 @@ export class PropertiesUtils { let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE - if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map. - newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name); - } - if (newFEProp.getInputValues && newFEProp.getInputValues.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children + this.initValueObjectRef(newFEProp); //initialize valueObj. + propertyFeArray.push(newFEProp); + newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children + this.dataTypeService.checkForCustomBehavior(newFEProp); + + //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children + if (newFEProp.getInputValues && newFEProp.getInputValues.length) { newFEProp.getInputValues.forEach(propInputDetail => { let inputPath = propInputDetail.inputPath; if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath @@ -63,10 +67,6 @@ export class PropertiesUtils { this.propertiesService.disableRelatedProperties(newFEProp, inputPath); }); } - this.initValueObjectRef(newFEProp); //initialize valueObj. - propertyFeArray.push(newFEProp); - newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children - this.dataTypeService.checkForCustomBehavior(newFEProp); } }); instanceFePropertiesMap[instanceId] = propertyFeArray; @@ -103,33 +103,29 @@ export class PropertiesUtils { * Note: This logic is different than assignflattenedchildrenvalues - here we merge values, there we pick either the parents value, props value, or default value - without merging. */ public initValueObjectRef = (property: PropertyFEModel): void => { - if (property.derivedDataType == DerivedPropertyType.SIMPLE || property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component - property.valueObj = property.value || property.defaultValue; - if (property.isDeclared) { - if(typeof property.valueObj == 'object'){ - property.valueObj = JSON.stringify(property.valueObj); - } - }else if(property.valueObj && - property.type !== PROPERTY_TYPES.STRING && - property.type !== PROPERTY_TYPES.JSON && - PROPERTY_DATA.SCALAR_TYPES.indexOf(property.type) == -1){ - property.valueObj = JSON.parse(property.valueObj);//The valueObj contains the real value ans not the value as string + property.resetValueObjValidation(); + if (property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component + property.valueObj = property.value || property.defaultValue || null; // use null for empty value object + if (property.valueObj && typeof property.valueObj == 'object') { + property.valueObj = JSON.stringify(property.valueObj); } } else { - if (property.derivedDataType == DerivedPropertyType.LIST) { - property.valueObj = _.merge([], JSON.parse(property.defaultValue || '[]'), JSON.parse(property.value || '[]')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined. - } else { - property.valueObj = _.merge({}, JSON.parse(property.defaultValue || '{}'), JSON.parse(property.value || '{}')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined. - } - if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) && Object.keys(property.valueObj).length) { + property.valueObj = property.getValueObj(); + if (property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) { + property.flattenedChildren = []; Object.keys(property.valueObj).forEach((key) => { property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key])) }); - } else { + } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) { + property.flattenedChildren = this.createFlattenedChildren(property.type, property.name); this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name); + property.flattenedChildren.forEach((childProp) => { + property.childPropUpdated(childProp); + }); } } - } + property.updateValueObjOrig(); + }; /* * Loops through flattened properties array and to assign values @@ -142,22 +138,24 @@ export class PropertiesUtils { derivedPropArray.forEach((prop, index) => { let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name - prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue + prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue || null); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue + prop.value = (prop.valueObj !== null && (typeof prop.valueObj) != 'string') ? JSON.stringify(prop.valueObj) : prop.valueObj; - if ( prop.isDeclared && typeof prop.valueObj == 'object') { //Stringify objects of items that are declared - prop.valueObj = JSON.stringify(prop.valueObj); - } else if(typeof prop.valueObj == PROPERTY_TYPES.STRING - && (prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN)){ //parse ints and non-string simple types - prop.valueObj = JSON.parse(prop.valueObj); + if ((prop.isDeclared || prop.type == PROPERTY_TYPES.STRING || prop.type == PROPERTY_TYPES.JSON)) { //Stringify objects of items that are declared or from type string/json + prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == 'object') ? JSON.stringify(prop.valueObj) : prop.valueObj; + } else if(prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN){ //parse ints and non-string simple types + prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == PROPERTY_TYPES.STRING) ? JSON.parse(prop.valueObj) : prop.valueObj; } else { //parse strings that should be objects - if (prop.derivedDataType == DerivedPropertyType.COMPLEX && typeof prop.valueObj != 'object') { - prop.valueObj = JSON.parse(prop.valueObj || '{}'); - } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') { - prop.valueObj = JSON.parse(prop.valueObj || '[]'); - } else if (prop.derivedDataType == DerivedPropertyType.MAP && typeof prop.valueObj != 'object' && (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType)) { //dont parse values for children of map of simple - prop.valueObj = JSON.parse(prop.valueObj || '{}'); + if (prop.derivedDataType == DerivedPropertyType.COMPLEX) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj; + } else if (prop.derivedDataType == DerivedPropertyType.LIST) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '[]') : prop.valueObj; + } else if (prop.derivedDataType == DerivedPropertyType.MAP) { + if (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj; + } } - if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && Object.keys(prop.valueObj).length) { + if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && prop.valueObj !== null && Object.keys(prop.valueObj).length) { let newProps: Array<DerivedFEProperty> = []; Object.keys(prop.valueObj).forEach((key) => { newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array @@ -165,6 +163,8 @@ export class PropertiesUtils { propsToPushMap[index + 1] = newProps; } } + + prop.valueObj = PropertyFEModel.cleanValueObj(prop.valueObj); }); //add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes. @@ -178,11 +178,10 @@ export class PropertiesUtils { if (nestedPath) { let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath); newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name); + property.updateValueObjOrig(); } else { this.initValueObjectRef(property); } } - - } 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 new file mode 100644 index 0000000000..bbbf6ae694 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html @@ -0,0 +1,5 @@ +<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 diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less new file mode 100644 index 0000000000..beec9bd567 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less @@ -0,0 +1,21 @@ +@import './../../../../../assets/styles/variables.less'; +.remove { + display: flex; + align-items: center; + justify-content: center; +} + +.cell { + padding: 0; +} + +/deep/ .link-selector { + select { + height: 30px; + border: none; + stroke: none; + } + +} + + 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 new file mode 100644 index 0000000000..16433242d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts @@ -0,0 +1,110 @@ +import {Component, Input} from '@angular/core'; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; +import {Link} from './link.model'; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; + +@Component({ + selector: 'link-row', + templateUrl: './link-row.component.html', + styleUrls: ['./link-row.component.less'] +}) + + +export class LinkRowComponent { + @Input() data:Array<ServicePathMapItem>; + @Input() link:Link; + @Input() removeRow:Function; + source:Array<DropdownValue> = []; + target: Array<DropdownValue> = []; + srcCP: Array<DropdownValue> = []; + targetCP: Array<DropdownValue> = []; + + ngOnChanges() { + if (this.data) { + this.parseInitialData(this.data); + } + } + + parseInitialData(data: Array<ServicePathMapItem>) { + this.source = this.convertValuesToDropDownOptions(data); + if (this.link.fromNode) { + let srcCPOptions = this.findOptions(data, this.link.fromNode); + 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); + if (this.link.toNode) { + let targetCPOptions = this.findOptions(targetOptions, this.link.toNode); + if (!targetCPOptions) { return; } + this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); + } + } + } + } + + private findOptions(items: Array<ServicePathMapItem>, nodeOrCPId: string) { + let item = items.find((dataItem)=> nodeOrCPId === dataItem.id); + if (item && item.data && item.data.options) { + return item.data.options; + } + console.warn('no option was found to match selection of Node/CP with id:' + nodeOrCPId); + return null; + } + + private convertValuesToDropDownOptions(values: Array<ServicePathMapItem>) : Array<DropdownValue> { + let result = []; + for (let i = 0; i < values.length ; i++) { + result[result.length] = new DropdownValue(values[i].id, values[i].data.name); + } + return result; + } + + onSourceSelected(id) { + if (id) { + let srcCPOptions = this.findOptions(this.data, id); + this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); + this.link.fromCP = ''; + this.link.toNode = ''; + this.link.toCP = ''; + this.target = []; + this.targetCP = []; + } + } + + 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); + this.link.fromCPOriginId = srcCPData.ownerId; + this.link.toNode = ''; + this.link.toCP = ''; + this.targetCP = []; + } + + } + + 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); + this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); + this.link.toCP = ''; + } + + } + + 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; + this.link.toCPOriginId = targetCPDataObj.ownerId; + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts new file mode 100644 index 0000000000..80128eb42e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +'use strict'; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; + +export class Link extends ForwardingPathLink { + public canEdit:boolean = false; + public canRemove:boolean = false; + public isFirst:boolean = false; + + constructor(link: ForwardingPathLink, canEdit: boolean, canRemove: boolean, isFirst: boolean) { + super(link.fromNode,link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); + this.canEdit = canEdit; + this.canRemove = canRemove; + this.isFirst = isFirst; + } +} + + 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 new file mode 100644 index 0000000000..96cd83eef6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html @@ -0,0 +1,43 @@ +<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 --> + </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" /> + </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 --> + </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> + </div> + + <div class="generic-table"> + <div class="header-row"> + <div class="cell header-cell" *ngFor="let header of headers"> + {{header}} + </div> + </div> + <div *ngIf="links && links.length === 0" class="no-row-text" > + There is no data to display + </div> + <div> + <link-row *ngFor="let link of links" [data]="linksMap" [link]="link" [removeRow]="removeRow" class="data-row" ></link-row> + </div> + </div> + + + </form> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less new file mode 100644 index 0000000000..5c9e53e229 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less @@ -0,0 +1,45 @@ +@import './../../../../assets/styles/variables.less'; +.service-path-creator { + font-family: @font-opensans-regular; + .separator-buttons { + margin: 10px 0; + display: flex; + justify-content: space-between; + } + .i-sdc-form-label { + font-size: 12px; + } + .w-sdc-form .i-sdc-form-item { + margin-bottom: 15px; + } + + .side-by-side { + display: flex; + .i-sdc-form-item { + flex-basis: 100%; + &:first-child { + margin-right: 10px; + } + } + } + + .generic-table { + max-height: 233px; + .header-row .header-cell { + &:last-child { + padding: 0; + } + } + /deep/ .cell { + &:last-child { + min-width: 30px; + } + } + } + + .based-on-title { + text-transform: uppercase; + font-size: 18px; + font-family: @font-opensans-regular; + } +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..dac41a37bc --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts @@ -0,0 +1,137 @@ +/*- + * ============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, ElementRef, forwardRef, Inject } from '@angular/core'; +import {Link} from './link-row/link.model'; +import {ForwardingPath} from 'app/models/forwarding-path'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; + +@Component({ + selector: 'service-path-creator', + templateUrl: './service-path-creator.component.html', + styleUrls:['./service-path-creator.component.less'], + providers: [ServiceServiceNg2] +}) + +export class ServicePathCreatorComponent { + + linksMap:Array<ServicePathMapItem>; + links:Array<Link> = []; + input:any; + headers: Array<string> = []; + removeRow: Function; + forwardingPath:ForwardingPath; + //isExtendAllowed:boolean = false; + + constructor(private serviceService: ServiceServiceNg2) { + this.forwardingPath = new ForwardingPath(); + this.links = [new Link(new ForwardingPathLink('', '', '', '', '', ''), true, false, true)]; + this.headers = ['Source', 'Source Connection Point', 'Target', 'Target Connection Point', ' ']; + this.removeRow = () => { + if (this.links.length === 1) { + return; + } + this.links.splice(this.links.length-1, 1); + this.enableCurrentRow(); + }; + } + + ngOnInit() { + this.serviceService.getNodesAndLinksMap(this.input.service).subscribe((res:any) => { + this.linksMap = res; + }); + this.processExistingPath(); + + } + + private processExistingPath() { + if (this.input.pathId) { + let forwardingPath = <ForwardingPath>{...this.input.service.forwardingPaths[this.input.pathId]}; + this.forwardingPath.name = forwardingPath.name; + this.forwardingPath.destinationPortNumber = forwardingPath.destinationPortNumber; + this.forwardingPath.protocol = forwardingPath.protocol; + 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 -1].canEdit = true; + this.links[this.links.length -1].canRemove = true; + this.links[0].isFirst = true; + + } + } + + isExtendAllowed():boolean { + if (this.links[this.links.length-1].toCP) { + return true; + } + return false; + } + + enableCurrentRow() { + this.links[this.links.length-1].canEdit = true; + if (this.links.length !== 1) { + this.links[this.links.length-1].canRemove = true; + } + } + + 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); + } + + disableRows() { + for (let i = 0 ; i < this.links.length ; i++) { + this.links[i].canEdit = false; + this.links[i].canRemove = false; + } + } + + createPathLinksObject() { + for (let i = 0 ; i < this.links.length ; i++) { + let link = this.links[i]; + this.forwardingPath.addPathLink(link.fromNode, link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); + } + } + + createServicePathData() { + this.createPathLinksObject(); + return this.forwardingPath; + } + + checkFormValidForSubmit():boolean { + if (this.forwardingPath.name && this.isPathValid() ) { + return true; + } + return false; + } + + isPathValid():boolean { + let lastLink = this.links[this.links.length -1] ; + if (lastLink.toNode && lastLink.toCP && lastLink.fromNode && lastLink.fromCP) { + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts new file mode 100644 index 0000000000..78005317a2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathCreatorComponent} from "./service-path-creator.component"; +import {FormsModule} from "@angular/forms"; +import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; +import {LinkRowComponent} from './link-row/link-row.component' +@NgModule({ + declarations: [ + ServicePathCreatorComponent, + LinkRowComponent + ], + imports: [CommonModule, + FormsModule, + FormElementsModule, + UiElementsModule + ], + exports: [], + entryComponents: [ + ServicePathCreatorComponent + ], + providers: [] +}) +export class ServicePathCreatorModule { +}
\ No newline at end of file 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 new file mode 100644 index 0000000000..8a31c76998 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html @@ -0,0 +1,21 @@ +<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="generic-table table-container" > + <div class="header-row"> + <div class="cell header-cell" *ngFor="let header of headers"> + {{header}} + </div> + </div> + <div *ngFor="let path of paths" class="data-row" > + <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> + </div> + </div> + <div *ngIf="paths && paths.length === 0" class="no-row-text" > + No paths have been added yet. + </div> + </div> + +</div>
\ No newline at end of file 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 new file mode 100644 index 0000000000..aff597fd85 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less @@ -0,0 +1,21 @@ +@import './../../../../assets/styles/variables.less'; + +.add-path-link { + display: flex; + align-items: flex-end; + flex-direction: column; + padding-bottom: 10px; +} + +.generic-table { + max-height: 233px; +} + +.path-action-buttons { + display: flex; + align-items: center; + justify-content: space-between; + .sprite-new { + cursor: pointer; + } +}
\ 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 new file mode 100644 index 0000000000..04083e8685 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts @@ -0,0 +1,66 @@ +/*- + * ============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, ComponentRef} from '@angular/core'; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {ModalService} from "app/ng2/services/modal.service"; +import {ModalComponent} from "app/ng2/components/ui/modal/modal.component"; + +@Component({ + selector: 'service-paths-list', + templateUrl: './service-paths-list.component.html', + styleUrls:['service-paths-list.component.less'], + providers: [ServiceServiceNg2, ModalService] +}) +export default class ServicePathsListComponent { + modalInstance: ComponentRef<ModalComponent>; + headers: Array<string> = []; + paths: Array<ForwardingPath> = []; + input:any; + onAddServicePath: Function; + onEditServicePath: Function; + + constructor(private serviceService:ServiceServiceNg2) { + this.headers = ['Path Name','Actions']; + } + + ngOnInit() { + _.forEach(this.input.service.forwardingPaths, (path: ForwardingPath)=> { + this.paths[this.paths.length] = path; + }); + this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> { + return a.name.localeCompare(b.name); + }); + this.onAddServicePath = this.input.onCreateServicePath; + this.onEditServicePath = this.input.onEditServicePath; + } + + deletePath = (id:string):void => { + this.serviceService.deleteServicePath(this.input.service, id).subscribe((res:any) => { + delete this.input.service.forwardingPaths[id]; + this.paths = this.paths.filter(function(path){ + return path.uniqueId !== id; + }); + }); + }; + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts new file mode 100644 index 0000000000..c236934002 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import ServicePathsListComponent from "./service-paths-list.component"; + +@NgModule({ + declarations: [ + ServicePathsListComponent + ], + imports: [CommonModule], + exports: [], + entryComponents: [ + ServicePathsListComponent + ], + providers: [] +}) +export class ServicePathsListModule { +}
\ No newline at end of file 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 e89a816e95..3761aa808f 100644 --- a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts +++ b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts @@ -13,7 +13,6 @@ import {SafeUrlSanitizerPipe} from "./safeUrlSanitizer.pipe"; SearchFilterPipe, SafeUrlSanitizerPipe ], - exports: [ ContentAfterLastDotPipe, GroupByPipe, 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 b852539edd..0947b2aa7f 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 @@ -23,10 +23,9 @@ import {Response, RequestOptions, Headers} from '@angular/http'; import { Observable } from 'rxjs/Observable'; import {PropertyFEModel, PropertyBEModel} from "app/models"; import {CommonUtils} from "app/utils"; -import {Component, ComponentInstance, InputModel} from "app/models"; +import {Component, ComponentInstance, Capability, PropertyModel} from "app/models"; import { HttpService } from '../http.service'; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; -import {isEqual} from "lodash"; @Injectable() export class ComponentInstanceServiceNg2 { @@ -52,43 +51,45 @@ export class ComponentInstanceServiceNg2 { }) } - updateInstanceProperty(component: Component, componentInstanceId: string, property: PropertyBEModel): Observable<PropertyBEModel> { + updateInstanceProperties(component: Component, componentInstanceId: string, properties: PropertyBEModel[]) { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/property', property) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/properties', properties) .map((res: Response) => { - return new PropertyBEModel(res.json()); - }) + return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + }); } - getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string): Observable<Array<PropertyBEModel>> { + getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability): Observable<Array<PropertyModel>> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType + - '/capabilityName/' + capabilityName + '/properties') + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties') .map((res: Response) => { - return CommonUtils.initBeProperties(res.json()); + capability.properties = res.json().map((capProp) => new PropertyModel(capProp)); // update capability properties + return capability.properties; }) } - updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string, properties: PropertyBEModel[]): Observable<PropertyBEModel[]> { + updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability, properties: PropertyBEModel[]): Observable<Array<PropertyModel>> { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType + - '/capabilityName/' + capabilityName +'/properties', properties) + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties', properties) .map((res: Response) => { - return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + const savedProperties: PropertyModel[] = res.json().map((resProperty) => new PropertyModel(resProperty)); + savedProperties.forEach((savedProperty) => { + const propIdx = capability.properties.findIndex((p) => p.uniqueId === savedProperty.uniqueId); + if (propIdx !== -1) { + capability.properties.splice(propIdx, 1, savedProperty); + } + }); + return savedProperties; }) } - updateInstanceInput(component: Component, componentInstanceId: string, input: PropertyBEModel): Observable<PropertyBEModel> { + updateInstanceInputs(component: Component, componentInstanceId: string, inputs: PropertyBEModel[]): Observable<PropertyBEModel[]> { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/input', input) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/inputs', inputs) .map((res: Response) => { - return new PropertyBEModel(res.json()); - }) - } - - hasPropertyChanged(property: PropertyFEModel) { - let oldValue: any = property.value; - const newValue = property.getJSONValue(); - return ((oldValue || newValue) && !isEqual(oldValue, newValue)); + return res.json().map((resInput) => new PropertyBEModel(resInput)); + }); } } diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts new file mode 100644 index 0000000000..6e9d0e8031 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts @@ -0,0 +1,41 @@ +/*- + * ============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} from "@angular/core"; +import {Component} from "../../../models/components/component"; +import {ComponentServiceNg2} from "./component.service"; +import {ServiceServiceNg2} from "./service.service"; + +@Injectable() +export class ComponentServiceFactoryNg2 { + componentService: ComponentServiceNg2; + serviceService: ServiceServiceNg2; + constructor(componentService: ComponentServiceNg2, serviceService: ServiceServiceNg2) { + this.serviceService = serviceService; + this.componentService = componentService; + } + getComponentService(component: Component):ComponentServiceNg2 { + if (component.isService()) { + return this.serviceService; + } + return this.componentService; + } +}
\ No newline at end of file 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 ba1cb15561..9c3f78a444 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 @@ -18,13 +18,13 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Injectable, Inject} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; import {Response, URLSearchParams} from '@angular/http'; -import { Component, PropertyBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} from "app/models"; -import {downgradeInjectable} from '@angular/upgrade/static'; +import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} from "app/models"; import {COMPONENT_FIELDS} from "app/utils"; import {ComponentGenericResponse} from "../responses/component-generic-response"; import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map"; @@ -40,11 +40,11 @@ export class ComponentServiceNg2 { protected baseUrl; - constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } - private getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> { + protected getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> { let params:URLSearchParams = new URLSearchParams(); _.forEach(fields, (field:string):void => { @@ -53,10 +53,14 @@ export class ComponentServiceNg2 { return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {search: params}) .map((res:Response) => { - return new ComponentGenericResponse().deserialize(res.json()); + return this.analyzeComponentDataResponse(res); }); } + protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { + return new ComponentGenericResponse().deserialize(res.json()); + } + private getServerTypeUrl = (componentType:string):string => { switch (componentType) { case ComponentType.SERVICE: @@ -78,8 +82,8 @@ export class ComponentServiceNg2 { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_ATTRIBUTES]); } - getComponentInstancesAndRelation(component:Component):Observable<ComponentGenericResponse> { - return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES]); + 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]); } getComponentResourceInstances(component:Component):Observable<ComponentGenericResponse> { @@ -125,19 +129,19 @@ export class ComponentServiceNg2 { }) } - deleteInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> { + deleteInput(component:Component, input:InputBEModel):Observable<InputBEModel> { return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') .map((res:Response) => { - return new PropertyBEModel(res.json()); + return new InputBEModel(res.json()); }) } - updateComponentInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> { + updateComponentInputs(component:Component, inputs:InputBEModel[]):Observable<InputBEModel[]> { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', input) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs) .map((res:Response) => { - return new PropertyBEModel(res.json()) + return res.json().map((input) => new InputBEModel(input)); }) } 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 f38dbef595..0439f2047e 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 @@ -22,19 +22,30 @@ import { Injectable, Inject } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; -import { Response } from '@angular/http'; +import { Response, URLSearchParams } from '@angular/http'; import {Service} from "app/models"; import { downgradeInjectable } from '@angular/upgrade/static'; import { HttpService } from '../http.service'; + import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ComponentMetadata} from "app/models/component-metadata"; +import {ComponentType} from "app/utils"; +import {Component} from "app/models/components/component"; +import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; +import {COMPONENT_FIELDS, SERVICE_FIELDS} from "app/utils/constants"; +import {ComponentServiceNg2} from "./component.service"; +import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; @Injectable() -export class ServiceServiceNg2 { +export class ServiceServiceNg2 extends ComponentServiceNg2 { protected baseUrl = ""; - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + super(http, sdcConfig); this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } @@ -46,4 +57,106 @@ export class ServiceServiceNg2 { }); } + getNodesAndLinksMap(service: Service):Observable<Array<ServicePathMapItem>> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/linksMap').map(res => { + return <Array<ServicePathMapItem>>res.json(); + }); + } + + getServicePath(service: Service, id: string):Observable<any> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id) + .map(res => { + return res.json(); + }) + } + + getServicePaths(service: Service):Observable<any> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths') + .map(res => { + return res.json(); + }) + } + + createOrUpdateServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> { + if (inputsToCreate.uniqueId) { + return this.updateServicePath(service, inputsToCreate); + } else { + return this.createServicePath(service, inputsToCreate); + } + } + + createServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> { + let input = new ServicePathRequestData(inputsToCreate); + + return this.http.post(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) + .map(res => { + return this.parseServicePathResponse(res); + }); + } + + deleteServicePath(service: Service, id: string):Observable<any> { + return this.http.delete(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id ) + .map((res) => { + return res.json(); + }); + } + + updateServicePath(service: Service, inputsToUpdate:ForwardingPath):Observable<ForwardingPath> { + let input = new ServicePathRequestData(inputsToUpdate); + + return this.http.put(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) + .map((res) => { + return this.parseServicePathResponse(res); + }); + } + + checkComponentInstanceVersionChange(service: Service, newVersionId: string):Observable<Array<string>> { + let instanceId = service.selectedInstance.uniqueId; + let queries = {componentInstanceId: instanceId, newComponentInstanceId: newVersionId}; + + let params:URLSearchParams = new URLSearchParams(); + _.map(_.keys(queries), (key:string):void => { + params.append(key, queries[key]); + }); + + let url = this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths-to-delete'; + return this.http.get(url, {search: params}).map((res: Response) => { + return res.json().forwardingPathToDelete; + }); + } + + 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]); + } + + protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { + return new ServiceGenericResponse().deserialize(res.json()); + } + + private parseServicePathResponse(res: Response):ForwardingPath { + let resJSON = res.json(); + let pathId = Object.keys(resJSON.forwardingPaths)[0]; + let forwardingPath = resJSON.forwardingPaths[pathId]; + let path:ForwardingPath = new ForwardingPath(); + path.deserialize(forwardingPath); + path.uniqueId = pathId; + return path; + } } + +class ServicePathRequestData { + forwardingPaths: { [key:string]:ForwardingPath } = {}; + componentMetadataDefinition: ComponentMetadata; + toscaType: string = "topology_template"; + + constructor(fp? : ForwardingPath) { + this.componentMetadataDefinition = new ComponentMetadata(); + this.componentMetadataDefinition.ecompGeneratedNaming = true; + this.componentMetadataDefinition.componentType = ComponentType.SERVICE; + if (fp) { + let id = fp.uniqueId ? fp.uniqueId : "NEW"; + this.forwardingPaths[fp.uniqueId] = fp; + } + } +} + diff --git a/catalog-ui/src/app/ng2/services/config.service.ts b/catalog-ui/src/app/ng2/services/config.service.ts index 053f2c7659..3e6e667285 100644 --- a/catalog-ui/src/app/ng2/services/config.service.ts +++ b/catalog-ui/src/app/ng2/services/config.service.ts @@ -37,8 +37,8 @@ export class ConfigService { public api:IApi; constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { - this.api = this.sdcConfig.api; - this.baseUrl = this.api.root + this.api.component_api_root; + this.api = this.sdcConfig.api; + this.baseUrl = this.sdcConfig.api.root + this.sdcConfig.api.component_api_root; } loadValidationConfiguration(): Promise<ValidationConfiguration> { diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index 30c02a4141..6b5908903e 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { DataTypeModel, DataTypesMap, PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedFEPropertyMap } from "app/models"; import { DataTypesService } from "app/services/data-types-service"; diff --git a/catalog-ui/src/app/ng2/services/dynamic-component.service.ts b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts new file mode 100644 index 0000000000..29dd1e9e09 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts @@ -0,0 +1,28 @@ +import { + Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef +} from '@angular/core'; + + + +@Injectable() +export class DynamicComponentService { + + constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { } + + //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root. + //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name)); + public createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> { + + viewContainerRef = viewContainerRef || this.getRootViewContainerRef(); + viewContainerRef.clear(); + + let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader + let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory); + return componentRef; + } + + + private getRootViewContainerRef(): ViewContainerRef { + return this.applicationRef.components[0].instance.viewContainerRef; + } +};
\ 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 22b56c7004..4e86d6accf 100644 --- a/catalog-ui/src/app/ng2/services/modal.service.ts +++ b/catalog-ui/src/app/ng2/services/modal.service.ts @@ -1,11 +1,12 @@ import { Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, - + TemplateRef } from '@angular/core'; import { ModalModel, ButtonModel, StepModel } from 'app/models'; import {MultiStepsWizardComponent} from "../components/ui/multi-steps-wizard/multi-steps-wizard.component"; import {ModalComponent} from "../components/ui/modal/modal.component"; import {WizardHeaderBaseComponent} from "app/ng2/components/ui/multi-steps-wizard/multi-steps-wizard-header-base.component"; +import { DynamicComponentService } from 'app/ng2/services/dynamic-component.service'; @Injectable() @@ -13,7 +14,7 @@ export class ModalService { currentModal: ComponentRef<any>; - constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { } + constructor(private dynamicComponentService: DynamicComponentService) { } /* Shortcut method to open an alert modal with title, message, and close button that simply closes the modal. */ @@ -52,7 +53,7 @@ export class ModalService { /* Use this method to create a modal with title, message, and completely custom buttons. Use response.instance.open() to open */ public createCustomModal = (customModalData: ModalModel): ComponentRef<ModalComponent> => { - let customModal: ComponentRef<ModalComponent> = this.createDynamicComponent(ModalComponent); + let customModal: ComponentRef<ModalComponent> = this.dynamicComponentService.createDynamicComponent(ModalComponent); customModal.instance.input = customModalData; this.currentModal = customModal; @@ -62,12 +63,12 @@ export class ModalService { public createMultiStepsWizard = (title: string, steps:Array<StepModel>, callback: Function, dynamicHeaderType?: Type<WizardHeaderBaseComponent>): ComponentRef<MultiStepsWizardComponent> => { let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline blue', this.closeCurrentModal); let modalModel: ModalModel = new ModalModel('xl', title, '', [cancelButton]); - let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.createDynamicComponent(MultiStepsWizardComponent); + let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.dynamicComponentService.createDynamicComponent(MultiStepsWizardComponent); wizardInstance.instance.input = modalModel; wizardInstance.instance.steps = steps; wizardInstance.instance.callback = callback; if(dynamicHeaderType){ - let dynamicHeader = this.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer); + let dynamicHeader = this.dynamicComponentService.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer); wizardInstance.instance.dynamicHeader = dynamicHeader; wizardInstance.instance.dynamicHeader.instance.currentStepIndex = 0; } @@ -76,38 +77,28 @@ export class ModalService { return wizardInstance; } - + public closeCurrentModal = () => { if (!this.currentModal) return; this.currentModal.instance.close(); this.currentModal.destroy(); + delete this.currentModal; } public addDynamicContentToModal = (modalInstance: ComponentRef<ModalComponent>, dynamicComponentType: Type<any>, dynamicComponentInput?: any) => { - let dynamicContent = this.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer); + let dynamicContent = this.dynamicComponentService.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer); dynamicContent.instance.input = dynamicComponentInput; modalInstance.instance.dynamicContent = dynamicContent; return modalInstance; } - //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root. - //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name)); - private createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> { - - viewContainerRef = viewContainerRef || this.getRootViewContainerRef(); - viewContainerRef.clear(); + public addDynamicTemplateToModal = (modalInstance: ComponentRef<ModalComponent>, templateRef: TemplateRef<void>) => { + modalInstance.instance.dynamicContentContainer.clear(); + modalInstance.instance.dynamicContentContainer.createEmbeddedView(templateRef); + return modalInstance; + }; - let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader - let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory); - return componentRef; - } - - private getRootViewContainerRef(): ViewContainerRef { - return this.applicationRef.components[0].instance.viewContainerRef; - } } - - diff --git a/catalog-ui/src/app/ng2/services/policies.service.ts b/catalog-ui/src/app/ng2/services/policies.service.ts new file mode 100644 index 0000000000..2b564b8915 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/policies.service.ts @@ -0,0 +1,49 @@ +/*- + * ============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 { Headers } from "@angular/http"; +import { Observable } from "rxjs/Observable"; +import { HttpService } from "./http.service"; +import { Cookie2Service } from "./cookie.service"; +import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; + + +@Injectable() +export class PoliciesService { + protected baseUrl; + + private mapApiDirections = { + 'RESOURCE':'resources', + 'SERVICE':'services' + } + + 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(); + }); + } + +} + diff --git a/catalog-ui/src/app/ng2/services/properties.service.ts b/catalog-ui/src/app/ng2/services/properties.service.ts index 86cd2f5c72..c86d207915 100644 --- a/catalog-ui/src/app/ng2/services/properties.service.ts +++ b/catalog-ui/src/app/ng2/services/properties.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { PropertyFEModel, PropertyBEModel, PropertyDeclareAPIModel, DerivedFEProperty} from "app/models"; 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 9450e4bc04..e7c88a0ab8 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 @@ -27,6 +27,7 @@ import { ArtifactGroupModel, PropertyModel, PropertiesGroup, AttributeModel, Att 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"; export class ComponentGenericResponse implements Serializable<ComponentGenericResponse> { @@ -43,6 +44,7 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR public requirements:RequirementsGroup; public properties:Array<PropertyModel>; public attributes:Array<AttributeModel>; + public policies:Array<PolicyInstance>; public groups:Array<Module>; public interfaces:any; public additionalInformation:any; @@ -92,6 +94,9 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR if(response.groups) { this.groups = CommonUtils.initModules(response.groups); } + if(response.policies) { + this.policies = CommonUtils.initPolicies(response.policies); + } return this; } } diff --git a/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts new file mode 100644 index 0000000000..d32ed26bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts @@ -0,0 +1,22 @@ +import * as _ from "lodash"; +import {Serializable} from "../utils/serializable"; +import {ComponentGenericResponse} from "./component-generic-response"; +import {ForwardingPath} from "../../../models/forwarding-path"; + +export class ServiceGenericResponse extends ComponentGenericResponse implements Serializable<ServiceGenericResponse> { + public forwardingPaths: { [key:string]:ForwardingPath } = {}; + + deserialize (response): ServiceGenericResponse { + super.deserialize(response); + if(response.forwardingPaths) { + _.forEach(response.forwardingPaths, (pathResponse, id) => { + let pathId = id; + let path:ForwardingPath = new ForwardingPath(); + path.deserialize(pathResponse); + path.uniqueId = pathId; + this.forwardingPaths[pathId] = path; + }); + } + return this; + } +}
\ No newline at end of file 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 51314c04bd..c005efcbe8 100644 --- a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts +++ b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts @@ -27,6 +27,7 @@ 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 IScope = angular.IScope; /** Services we need to upgrade from angular1 to angular2 - in the future we need to rewrite them all to angular2 **/ @@ -62,6 +63,10 @@ export function eventListenerServiceServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('EventListenerService'); } +export function notificationServiceFactory(cacheObj: ICacheObject) { + return cacheObj.get('Notification'); +} + export const DataTypesServiceProvider = { provide: DataTypesService, useFactory: dataTypesServiceFactory, @@ -111,3 +116,9 @@ export const EventListenerServiceProvider = { useFactory: eventListenerServiceServiceFactory, deps: ['$injector'] }; + +export const NotificationServiceProvider = { + provide: 'Notification', + useFactory: notificationServiceFactory, + deps: ['$injector'] +}; |