diff options
Diffstat (limited to 'catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input')
3 files changed, 559 insertions, 0 deletions
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts new file mode 100644 index 0000000000..f3b2de0943 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts @@ -0,0 +1,378 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentInstance, InstancesInputsOrPropertiesMapData, Service, IAppMenu, InputModel, PropertyModel, InputPropertyBase} from "app/models"; +import {DataTypesService} from "app/services"; +import {ModalsHandler, ResourceType} from "app/utils"; + + +interface IServiceInputsViewModelScope extends IWorkspaceViewModelScope { + + vfInstancesList:Array<ComponentInstance>; + instanceInputsMap:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used + instancePropertiesMap:InstancesInputsOrPropertiesMapData; + component:Service; + sdcMenu:IAppMenu; + isViewOnly:boolean; + isArrowDisabled:boolean; + onArrowPressed():void; + checkArrowState():void; + loadComponentInputs():void; + loadInstanceInputs(instance:ComponentInstance):ng.IPromise<boolean> ; + loadInstanceProperties(instance:ComponentInstance):ng.IPromise<boolean> ; + loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise<boolean> ; + loadInputInputs(input:InputModel):ng.IPromise<boolean>; + deleteInput(input:InputModel):void + openEditValueModal(input:InputModel):void; + openSelectPropertyDataTypeViewModel(instanceId:string, property:PropertyModel):void; + openEditPropertyDataTypeViewModel(property:PropertyModel):void; + dataTypesService:DataTypesService; +} + +export class ServiceInputsViewModel { + + static '$inject' = [ + '$scope', + '$q', + 'ModalsHandler', + 'Sdc.Services.DataTypesService' + ]; + + constructor(private $scope:IServiceInputsViewModelScope, + private $q:ng.IQService, + private ModalsHandler:ModalsHandler, + private DataTypesService:DataTypesService) { + this.initScope(); + this.$scope.updateSelectedMenuItem(); + this.$scope.isViewOnly = this.$scope.isViewMode(); + } + + + private getInputsOrPropertiesAlreadySelected = (instanceNormalizeName:string, arrayToFilter:Array<InputPropertyBase>):Array<any> => { + let alreadySelectedInput = []; + _.forEach(arrayToFilter, (inputOrProperty:InputPropertyBase) => { + let expectedServiceInputName = instanceNormalizeName + '_' + inputOrProperty.name; + let inputAlreadyInService = _.find(this.$scope.component.inputs, (serviceInput:InputModel) => { + //Checking if the input prefix is the instance name + '_' + property/input name (prefix because we don't need to check full name for complex dataType) + return serviceInput.name.substring(0, expectedServiceInputName.length) === expectedServiceInputName; + }); + if (inputAlreadyInService) { + inputOrProperty.isAlreadySelected = true; + alreadySelectedInput.push(inputOrProperty); + } else { + inputOrProperty.isAlreadySelected = false; + } + }); + return alreadySelectedInput; + }; + + + /* + * When loading the screen again, we need to disabled the inputs that already created on the service, + * we do that by comparing the service input name, to the instance name + '_' + the resource instance input name. + */ + private disableEnableSelectedInputsOrPropertiesOnInit = (instance:ComponentInstance):void => { + + if (instance.originType === ResourceType.VF) { + this.$scope.instanceInputsMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.inputs); + } else { + this.$scope.instancePropertiesMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.properties); + } + }; + + /* + * Enable Input/Property after delete + */ + private enableInputsAfterDelete = (propertiesOrInputsDeletes:Array<InputPropertyBase>):void => { + + _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted + + let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => { + return inputOrProperty.uniqueId === deletedInputInput.uniqueId; + }); + inputOrPropertyDeleted.isAlreadySelected = false; + delete _.remove(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0]; + }); + }; + + /* + * Enable Input/Property after delete + */ + private enablePropertiesAfterDelete = (propertiesOrInputsDeletes:Array<InputPropertyBase>):void => { + + _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted + let componentInstance = _.find(this.$scope.vfInstancesList, (instance:ComponentInstance) => { + return instance.uniqueId === deletedInputInput.componentInstanceId; + }); + let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => { + return inputOrProperty.uniqueId === deletedInputInput.uniqueId; + }); + + let expectedName = componentInstance.normalizedName + '_' + inputOrPropertyDeleted.name; + let isAnotherInputExist = _.find(this.$scope.component.inputs, (input:InputModel) => { + return input.name.substring(0, expectedName.length) === expectedName; + }); + if (!isAnotherInputExist) { + inputOrPropertyDeleted.isAlreadySelected = false; + delete _.remove(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0]; + } + }); + }; + + private initScope = ():void => { + + this.$scope.instanceInputsMap = new InstancesInputsOrPropertiesMapData(); + this.$scope.instancePropertiesMap = new InstancesInputsOrPropertiesMapData(); + this.$scope.isLoading = true; + this.$scope.isArrowDisabled = true; + // Why do we need this? we call this later. + //this.$scope.component.getComponentInputs(); + + let onSuccess = (componentInstances:Array<ComponentInstance>) => { + console.log("component instances loaded: ", componentInstances); + this.$scope.vfInstancesList = componentInstances; + this.$scope.isLoading = false; + }; + + //This function will get al component instance for the left table - in + // future the instances will be filter according to search text + this.$scope.component.getComponentInstancesFilteredByInputsAndProperties().then(onSuccess); + + // This function will get the service inputs for the right table + this.$scope.component.getComponentInputs(); + + /* + * When clicking on instance in the left table, this function will load all instance inputs + */ + this.$scope.loadInstanceInputs = (instance:ComponentInstance):ng.IPromise<boolean> => { + let deferred = this.$q.defer(); + + let onSuccess = (inputs:Array<InputModel>) => { + instance.inputs = inputs; + this.disableEnableSelectedInputsOrPropertiesOnInit(instance); + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false); + }; + + if (!instance.inputs) { + this.$scope.component.getComponentInstanceInputs(instance.uniqueId, instance.componentUid).then(onSuccess, onError); + //this.disableEnableSelectedInputs(instance); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + + this.$scope.loadInstanceProperties = (instance:ComponentInstance):ng.IPromise<boolean> => { + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array<PropertyModel>) => { + instance.properties = properties; + this.disableEnableSelectedInputsOrPropertiesOnInit(instance); + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false); + }; + + if (!instance.properties) { + this.$scope.component.getComponentInstanceProperties(instance.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When clicking on instance input in the left or right table, this function will load all properties of the selected input + */ + this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:InputModel):ng.IPromise<boolean> => { + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array<PropertyModel>) => { + input.properties = properties; + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false) + }; + + if (!input.properties) { + this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When clicking on input in the right table, this function will load all inputs of the selected input + */ + this.$scope.loadInputInputs = (input:InputModel):ng.IPromise<boolean> => { + let deferred = this.$q.defer(); + + let onSuccess = () => { + deferred.resolve(true); + }; + let onError = () => { + deferred.resolve(false); + }; + + if (!input.inputs) { // Caching, if exists do not get it. + this.$scope.component.getServiceInputInputsAndProperties(input.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When pressing the arrow, we create service inputs from the inputs selected + */ + this.$scope.onArrowPressed = ():void => { + let onSuccess = (inputsCreated:Array<InputModel>) => { + + //disabled all the inputs in the left table + _.forEach(this.$scope.instanceInputsMap, (inputs:Array<InputModel>, instanceId:string) => { + _.forEach(inputs, (input:InputModel) => { + input.isAlreadySelected = true; + }); + }); + _.forEach(this.$scope.instancePropertiesMap, (properties:Array<PropertyModel>, instanceId:string) => { + _.forEach(properties, (property:PropertyModel) => { + property.isAlreadySelected = true; + }); + }); + this.addColorToItems(inputsCreated); + }; + + let onFailed = (error:any) => { + this.$scope.isArrowDisabled = false; + console.log("Error declaring input/property"); + }; + + this.$scope.isArrowDisabled = true; + this.$scope.component.createInputsFormInstances(this.$scope.instanceInputsMap, this.$scope.instancePropertiesMap).then(onSuccess, onFailed); + }; + + + /* Iterates through array of selected inputs and properties and returns true if there is at least one new selection on left */ + this.$scope.checkArrowState = ()=> { + + let newInputSelected:boolean = _.some(this.$scope.instanceInputsMap, (inputs:Array<InputModel>) => { + return _.some(inputs, (input:InputModel)=> { + return input.isAlreadySelected === false; + }); + }); + + let newPropSelected:boolean = _.some(this.$scope.instancePropertiesMap, (properties:Array<PropertyModel>) => { + return _.some(properties, (property:PropertyModel) => { + return property.isAlreadySelected === false; + }); + }); + + this.$scope.isArrowDisabled = !(newInputSelected || newPropSelected); + + }; + + this.$scope.deleteInput = (inputToDelete:InputModel):void => { + + let onDelete = ():void => { + + let onSuccess = (deletedInput:InputModel):void => { + if (deletedInput.inputs && deletedInput.inputs.length > 0) { // Enable input declared from input + this.enableInputsAfterDelete(deletedInput.inputs); + } + + if (deletedInput.properties && deletedInput.properties.length > 0) { // Enable properties + this.enablePropertiesAfterDelete(deletedInput.properties); + } + deletedInput.isDeleteDisabled = false; + this.$scope.checkArrowState(); + + }; + + let onFailed = (error:any):void => { + console.log("Error deleting input"); + inputToDelete.isDeleteDisabled = false; + }; + + inputToDelete.isDeleteDisabled = true; + this.addColorToItems([inputToDelete]); + this.$scope.component.deleteServiceInput(inputToDelete.uniqueId).then((deletedInput:InputModel):void => { + onSuccess(deletedInput); + }, onFailed); + }; + + // Get confirmation modal text from menu.json + let state = "deleteInput"; + let title:string = this.$scope.sdcMenu.alertMessages[state].title; + let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([inputToDelete.name]); + + // Open confirmation modal + this.ModalsHandler.openAlertModal(title, message).then(onDelete); + }; + + this.$scope.openEditValueModal = (input:InputModel) => { + this.ModalsHandler.openEditInputValueModal(input); + }; + + this.$scope.openSelectPropertyDataTypeViewModel = (instanceId:string, property:PropertyModel) => { + //to open the select data type modal + let selectedInstance = _.find(this.$scope.vfInstancesList, {uniqueId: instanceId}); + this.DataTypesService.selectedInstance = selectedInstance; //set the selected instance on the service for compering the input name on the service & the complex property + this.DataTypesService.selectedComponentInputs = this.$scope.component.inputs; // set all the service inputs on the data type service + let filteredPropertiesMap = _.filter(this.$scope.instancePropertiesMap[instanceId], (instanceProperty)=> { + return instanceProperty.name == property.name; + });//get all properties under the specific property + this.DataTypesService.selectedPropertiesName = property.propertiesName; + + this.ModalsHandler.openSelectDataTypeModal(property, this.$scope.component, this.$scope.component.properties, filteredPropertiesMap).then((selectedProperty:PropertyModel)=> { + if (selectedProperty && selectedProperty.propertiesName) { + let propertyToUpdate:PropertyModel = _.find(selectedInstance.properties, {uniqueId: selectedProperty.uniqueId}); + let existingProperty:PropertyModel = (<PropertyModel>_.find(this.$scope.instancePropertiesMap[instanceId], {uniqueId: propertyToUpdate.uniqueId})); + + if (existingProperty) { + existingProperty.propertiesName = selectedProperty.propertiesName; + existingProperty.input = selectedProperty.input; + existingProperty.isAlreadySelected = false; + } else { + propertyToUpdate.propertiesName = selectedProperty.propertiesName; + propertyToUpdate.input = selectedProperty.input; + this.$scope.instancePropertiesMap[instanceId].push(propertyToUpdate); + + } + this.$scope.checkArrowState(); + + } + }); + }; + + + this.$scope.openEditPropertyDataTypeViewModel = (property:PropertyModel)=> { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.properties, false).then(() => { + }); + } + }; + + private addColorToItems = (inputsCreated:Array<InputModel>):void => { + + // Adding color to the new inputs (right table) + _.forEach(inputsCreated, (input) => { + input.isNew = true; + }); + + // Removing color to the new inputs (right table) + setTimeout(() => { + _.forEach(inputsCreated, (input) => { + input.isNew = false; + }); + this.$scope.$apply(); + }, 3000); + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html new file mode 100644 index 0000000000..cb4d853f58 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html @@ -0,0 +1,134 @@ +<div class="workspace-inputs"> + <div class="table-container-flex"> + <div class="w-sdc-inputs-search pull-left hideme"> + <input type="text" class="w-sdc-inputs-search-input" placeholder="Search"/> + <div class="search-icon-container"> + <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span> + </div> + </div> + <div class="table"> + <div class="table-header">Resource instance inputs</div> + <div class="body"> + <div class="table-loader" ng-class="{'tlv-loader large loader': isLoading}"></div> + <perfect-scrollbar scroll-y-margin-offset="0" class="scrollbar-container"> + + <expand-collapse expanded-selector=".vf-instance-list.{{$index}}" + class="expand-collapse-table-row" + load-data-function="instance.originType=='VF' ? loadInstanceInputs(instance):loadInstanceProperties(instance)" + is-close-on-init="true" + data-ng-repeat-start="instance in vfInstancesList track by $index"> + <div class="flex-container data-row"> + <div class="expand-collapse-inputs-table-icon"></div> + <div class="table-col-general flex-item text" data-tests-id="inputs-vf-instance-{{$index}}"> + <span class="title-text">{{instance.name}}</span> + </div> + </div> + </expand-collapse> + + <div data-ng-repeat-end="" class="vf-instance-list {{$index}}"> + <div data-ng-if="instance.originType=='VF'"> + + <expand-collapse expanded-selector=".input-list.{{$parent.$index}}-{{$index}}" + class="expand-collapse-table-row" + load-data-function="loadInputPropertiesForInstance(instance.uniqueId, input)" + is-close-on-init="true" + data-ng-repeat-start="input in instance.inputs track by $index"> + <input-row input="input" + is-view-only='isViewOnly' + instance-id='instance.uniqueId' + instance-name='instance.name' + instance-inputs-map="instanceInputsMap" + on-checkbox-clicked="checkArrowState()"></input-row> + </expand-collapse> + + <div data-ng-repeat-end="" class="input-list {{$parent.$index}}-{{$index}}"> + <div class="empty-row" ng-if="input.properties.length===0">No properties to display + </div> + <div ng-repeat="property in input.properties track by $index"> + <property-row property="property" instance-name="instance.name"></property-row> + </div> + </div> + </div> + <div data-ng-if="instance.originType!='VF'"> + <div class="empty-row" data-tests-id="empty-row" ng-if="instance.properties.length===0"> No + properties to display + </div> + <div ng-repeat="property in instance.properties track by $index"> + <property-row instance-properties-map="instancePropertiesMap" property="property" + on-name-clicked="openSelectPropertyDataTypeViewModel(instance.uniqueId,property)" + on-checkbox-clicked="checkArrowState()" + instance-name="instance.name" + instance-id='instance.uniqueId'></property-row> + </div> + </div> + </div> + + </perfect-scrollbar> + </div> + </div> + </div> + + <div class="inputs-button-container pull-left"> + <div ng-click="onArrowPressed()" ng-class="{disabled: isArrowDisabled || isViewOnly}" data-ng-disabled="isArrowDisabled || isViewOnly" class="right-arrow-btn" data-tests-id="add-inputs-to-service-button"></div> + </div> + + <div class="table-container-flex"> + <div class="w-sdc-inputs-search pull-left"> + <input type="text" class="w-sdc-inputs-search-input" data-ng-model="search.filterTerm" placeholder="Search" + data-ng-model-options="{debounce: 200}"/> + <div class="search-icon-container"> + <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span> + </div> + </div> + <div class="table"> + <div class="body"> + <div class="table-header">Service inputs</div> + <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container"> + <expand-collapse expanded-selector=".service-inputs.{{$index}}" + class="expand-collapse-table-row" + load-data-function="loadInputInputs(serviceInput)" + is-close-on-init="true" + data-ng-repeat-start="serviceInput in component.inputs | filter:search track by $index "> + <input-row input="serviceInput" is-view-only='isViewOnly' instance-name='serviceInput.name' + delete-input='deleteInput(serviceInput)' + data-tests-id="service-input-{{$index}}" + class="service-input-row" + on-name-clicked="openEditValueModal(serviceInput)" + ng-class="serviceInput.isNew ? 'new-input': ''" + ></input-row> + </expand-collapse> + + <div data-ng-repeat-end="" class="service-inputs {{$index}}"> + <div ng-if="serviceInput.inputs.length > 0"> + <expand-collapse expanded-selector=".input-inputs-list.{{$parent.$index}}-{{$index}}" + class="expand-collapse-table-row" + load-data-function="loadInputPropertiesForInstance(input.componentInstanceId, input)" + is-close-on-init="true" + data-ng-repeat-start="input in serviceInput.inputs track by $index"> + <input-row input="input" + is-view-only='isViewOnly' + instance-name='input.componentInstanceName'></input-row> + </expand-collapse> + + <div data-ng-repeat-end="" class="input-inputs-list {{$parent.$index}}-{{$index}}"> + <div class="empty-row" ng-if="input.properties.length===0">No properties to display + </div> + <div ng-repeat="property in input.properties track by $index"> + <property-row property="property" instance-name="property.name" is-clickable="false"></property-row> + </div> + </div> + </div> + <div ng-if="serviceInput.properties.length > 0"> + <div class="empty-row" ng-if="serviceInput.properties.length===0">No properties to display</div> + <div ng-repeat="property in serviceInput.properties track by $index"> + <property-row property="property" instance-name="property.name" is-clickable="false"></property-row> + </div> + </div> + </div> + + + </perfect-scrollbar> + </div> + </div> + </div> +</div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less new file mode 100644 index 0000000000..f783d0b6d6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less @@ -0,0 +1,47 @@ +.workspace-inputs { + + .service-inputs-view { + + .table-container-flex { + width:100% !important; + } + + .table-loader { + position: relative; + top:215px; + } + + } + + .infinite-scroll { + + overflow-y: scroll; + overflow-x: hidden; + max-height: 400px; + } + + .class_with_css_props_leading_to_a_scroll { + height: 100%; + overflow-y: auto; + } + + .table-container-flex { + .expand-collapse-table-row { + .service-input-row { + padding-left: 15px; + border: none; + border-bottom: rgba(120, 136, 148, 0.26) solid 1px; + + &.service-input-row { + background: @tlv_color_u; + &.new-input { + background: @tlv_color_v; + } + } + } + + + } + } + +} |