diff options
author | Michael Lando <ml636r@att.com> | 2018-07-29 16:13:45 +0300 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2018-07-29 16:20:34 +0300 |
commit | 5b593496b8f1b8e8be8d7d2dbcc223332e65a49b (patch) | |
tree | 2f9dfc45191e723da69cf74be7829784e9741b94 /catalog-ui/src/app/view-models/workspace | |
parent | 9200382f2ce7b4bb729aa287d0878004b2d2b4f9 (diff) |
re base code
Change-Id: I12a5ca14a6d8a87e9316b9ff362eb131105f98a5
Issue-ID: SDC-1566
Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/src/app/view-models/workspace')
21 files changed, 1451 insertions, 886 deletions
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts index 46c2d2edf9..a77377bac4 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts @@ -19,22 +19,34 @@ */ 'use strict'; import * as _ from "lodash"; -import {Component, ComponentInstance, IAppMenu} from "app/models"; -import {SharingService, CacheService, EventListenerService, LeftPaletteLoaderService} from "app/services"; -import {ModalsHandler, GRAPH_EVENTS, ComponentFactory, ChangeLifecycleStateHandler, MenuHandler, EVENTS} from "app/utils"; -import {IWorkspaceViewModelScope} from "../../workspace-view-model"; -import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; -import {Resource} from "app/models/components/resource"; -import {ResourceType,ComponentType} from "app/utils/constants"; -import {ComponentServiceFactoryNg2} from "app/ng2/services/component-services/component.service.factory"; -import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; -import {Service} from "app/models/components/service"; +import { Component, ComponentInstance, IAppMenu, Requirement, Capability, ButtonModel } from "app/models"; +import { SharingService, CacheService, EventListenerService, LeftPaletteLoaderService } from "app/services"; +import { ModalsHandler, GRAPH_EVENTS, ComponentFactory, ChangeLifecycleStateHandler, MenuHandler, EVENTS, ComponentInstanceFactory } from "app/utils"; +import { IWorkspaceViewModelScope } from "../../workspace-view-model"; +import { ComponentGenericResponse } from "app/ng2/services/responses/component-generic-response"; +import { Resource } from "app/models/components/resource"; +import { ResourceType, ComponentType } from "app/utils/constants"; +import { ComponentServiceFactoryNg2 } from "app/ng2/services/component-services/component.service.factory"; +import { ServiceGenericResponse } from "app/ng2/services/responses/service-generic-response"; +import { Service } from "app/models/components/service"; +import { ZoneInstance } from "app/models/graph/zones/zone-instance"; +import { ComponentServiceNg2 } from "app/ng2/services/component-services/component.service"; +import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service" +import { IModalConfig, IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config"; +import { ValueEditComponent } from "app/ng2/components/ui/forms/value-edit/value-edit.component"; +import { UnsavedChangesComponent } from "../../../../ng2/components/ui/forms/unsaved-changes/unsaved-changes.component"; +import { ModalButtonComponent } from "sdc-ui/lib/angular/components"; + export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { currentComponent:Component; + + //Added for now, in the future need to remove and use only id and type to pass to tabs. selectedComponent: Component; + selectedZoneInstance: ZoneInstance; + componentInstanceNames: Array<string>; isLoading:boolean; graphApi:any; @@ -42,21 +54,24 @@ export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { sdcMenu:IAppMenu; version:string; isViewOnly:boolean; + isCanvasTagging:boolean; isLoadingRightPanel:boolean; disabledTabs:boolean; openVersionChangeModal(pathsToDelete:string[]):ng.IPromise<any>; onComponentInstanceVersionChange(component:Component); isComponentInstanceSelected():boolean; - updateSelectedComponent():void + updateSelectedComponent():void; openUpdateModal(); deleteSelectedComponentInstance():void; onBackgroundClick():void; setSelectedInstance(componentInstance:ComponentInstance):void; + setSelectedZoneInstance(zoneInstance: ZoneInstance):void; + changeZoneInstanceName(newName:string):void; printScreen():void; isPNF():boolean; isConfiguration():boolean; preventMoveTab(state: boolean):void; - + ComponentServiceNg2:ComponentServiceNg2, cacheComponentsInstancesFullData:Component; } @@ -76,8 +91,11 @@ export class CompositionViewModel { 'ChangeLifecycleStateHandler', 'LeftPaletteLoaderService', 'ModalsHandler', + 'ModalServiceSdcUI', 'EventListenerService', - 'ComponentServiceFactoryNg2' + 'ComponentServiceFactoryNg2', + 'ComponentServiceNg2', + 'Notification' ]; constructor(private $scope:ICompositionViewModelScope, @@ -93,8 +111,12 @@ export class CompositionViewModel { private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, private LeftPaletteLoaderService:LeftPaletteLoaderService, private ModalsHandler:ModalsHandler, + private ModalServiceSdcUI: ModalServiceSdcUI, private eventListenerService:EventListenerService, - private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2) { + private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2, + private ComponentServiceNg2:ComponentServiceNg2, + private Notification:any + ) { this.$scope.setValidState(true); this.initScope(); @@ -104,16 +126,17 @@ export class CompositionViewModel { private initGraphData = ():void => { - if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations ) { + if(!this.hasCompositionGraphData(this.$scope.component)) { this.$scope.isLoading = true; let service = this.ComponentServiceFactoryNg2.getComponentService(this.$scope.component); service.getComponentCompositionData(this.$scope.component).subscribe((response:ComponentGenericResponse) => { if (this.$scope.component.isService()) { (<Service> this.$scope.component).forwardingPaths = (<ServiceGenericResponse>response).forwardingPaths; } - this.$scope.component.componentInstances = response.componentInstances; - this.$scope.component.componentInstancesRelations = response.componentInstancesRelations; - this.$scope.component.policies = response.policies; + this.$scope.component.componentInstances = response.componentInstances || []; + this.$scope.component.componentInstancesRelations = response.componentInstancesRelations || []; + this.$scope.component.policies = response.policies || []; + this.$scope.component.groupInstances = response.groupInstances || []; this.$scope.isLoading = false; this.initComponent(); this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); @@ -124,25 +147,134 @@ export class CompositionViewModel { this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); }; + private hasCompositionGraphData = (component:Component):boolean => { + return !!(component.componentInstances && component.componentInstancesRelations && component.policies && component.groupInstances); + }; private cacheComponentsInstancesFullData:Array<Component>; private initComponent = ():void => { this.$scope.currentComponent = this.$scope.component; this.$scope.selectedComponent = this.$scope.currentComponent; + this.$scope.selectedZoneInstance = null; this.updateUuidMap(); this.$scope.isViewOnly = this.$scope.isViewMode(); }; private registerGraphEvents = (scope:ICompositionViewModelScope):void => { this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, scope.setSelectedZoneInstance); this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_START, () => { + scope.isCanvasTagging = true; + this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, true, this.showUnsavedChangesAlert); + }); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_END, () => { + scope.isCanvasTagging = false; + this.resetUnsavedChanges(); + }); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, scope.changeZoneInstanceName); + this.eventListenerService.registerObserverCallback(EVENTS.UPDATE_PANEL, this.removeSelectedZoneInstance); }; - private openUpdateComponentInstanceNameModal = ():void => { - this.ModalsHandler.openUpdateComponentInstanceNameModal(this.$scope.currentComponent).then(()=> { - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance); + private showUnsavedChangesAlert = (afterSave?:Function):Promise<any> => { + let deferred = new Promise<any>((resolve, reject)=> { + const modal = this.ModalServiceSdcUI.openCustomModal( + { + title: "Unsaved Changes", + size: 'sm', + type: 'custom', + + buttons: [ + {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()}, + {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.resetUnsavedChanges(); resolve()}}, + {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, callback: () => { reject(); this.saveUnsavedChanges(afterSave); }} + ] as IModalButtonComponent[] + }, UnsavedChangesComponent, { isValidChangedData: true}); }); + + return deferred; + } + + private unRegisterGraphEvents = (scope: ICompositionViewModelScope):void => { + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, scope.setSelectedZoneInstance); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CANVAS_TAG_START); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CANVAS_TAG_END); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, scope.changeZoneInstanceName); + this.eventListenerService.unRegisterObserver(EVENTS.UPDATE_PANEL, this.removeSelectedZoneInstance); + + }; + + private resetUnsavedChanges = () => { + this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false); + } + + private saveUnsavedChanges = (afterSaveFunction?:Function):void => { + this.$scope.selectedZoneInstance.forceSave.next(afterSaveFunction); + this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false); + } + + private openUpdateComponentInstanceNameModal = ():void => { + + let modalConfig:IModalConfig = { + title: "Edit Name", + size: "sm", + type: "custom", + testId: "renameInstanceModal", + buttons: [ + {id: 'saveButton', text: 'OK', size: 'xsm', callback: this.saveInstanceName, closeModal: false}, + {id: 'cancelButton', text: 'Cancel', size: 'sm', closeModal: true} + ] + }; + + this.ModalServiceSdcUI.openCustomModal(modalConfig, ValueEditComponent, {name: this.$scope.currentComponent.selectedInstance.name, validityChangedCallback: this.enableOrDisableSaveButton}); + + }; + + + private enableOrDisableSaveButton = (shouldEnable: boolean): void => { + let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton'); + saveButton.disabled = !shouldEnable; + } + + private saveInstanceName = () => { + let currentModal = this.ModalServiceSdcUI.getCurrentInstance(); + let nameFromModal:string = currentModal.innerModalContent.instance.name; + + if(nameFromModal != this.$scope.currentComponent.selectedInstance.name){ + currentModal.buttons[0].disabled = true; + let componentInstanceModel:ComponentInstance = ComponentInstanceFactory.createComponentInstance(this.$scope.currentComponent.selectedInstance); + componentInstanceModel.name = nameFromModal; + + let onFailed = (error) => { + currentModal.buttons[0].disabled = false; + }; + let onSuccess = (componentInstance:ComponentInstance) => { + + this.$scope.currentComponent.selectedInstance.name = componentInstance.name; + //update requirements and capabilities owner name + _.forEach(this.$scope.currentComponent.selectedInstance.requirements, (requirementsArray:Array<Requirement>) => { + _.forEach(requirementsArray, (requirement:Requirement):void => { + requirement.ownerName = componentInstance.name; + }); + }); + + _.forEach(this.$scope.currentComponent.selectedInstance.capabilities, (capabilitiesArray:Array<Capability>) => { + _.forEach(capabilitiesArray, (capability:Capability):void => { + capability.ownerName = componentInstance.name; + }); + }); + this.ModalServiceSdcUI.closeModal(); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance); + }; + + this.$scope.currentComponent.updateComponentInstance(componentInstanceModel).then(onSuccess, onFailed); + } else { + this.ModalServiceSdcUI.closeModal(); + } + }; private removeSelectedComponentInstance = ():void => { @@ -151,6 +283,12 @@ export class CompositionViewModel { this.$scope.selectedComponent = this.$scope.currentComponent; }; + private removeSelectedZoneInstance = ():void => { + this.$scope.currentComponent.selectedInstance = null; + this.$scope.selectedZoneInstance = null; + this.$scope.selectedComponent = this.$scope.currentComponent; + } + private updateUuidMap = ():void => { /** * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined, @@ -165,6 +303,7 @@ export class CompositionViewModel { this.$scope.sdcMenu = this.sdcMenu; this.$scope.isLoading = false; this.$scope.isLoadingRightPanel = false; + this.$scope.isCanvasTagging = false; this.$scope.graphApi = {}; this.$scope.version = this.cacheService.get('version'); this.initComponent(); @@ -175,6 +314,21 @@ export class CompositionViewModel { return this.$scope.currentComponent && this.$scope.currentComponent.selectedInstance != undefined && this.$scope.currentComponent.selectedInstance != null; }; + this.$scope.$on('$destroy', () => { + this.unRegisterGraphEvents(this.$scope); + }) + + this.$scope.restoreComponent = ():void => { + this.ComponentServiceNg2.restoreComponent(this.$scope.selectedComponent.componentType, this.$scope.selectedComponent.uniqueId).subscribe(() => { + this.Notification.success({ + message: '<' + this.$scope.component.name + '> ' + this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TITLE") + }); + this.$scope.selectedComponent.archived = false; + } + ) + }; + this.$scope.updateSelectedComponent = ():void => { if (this.$scope.currentComponent.selectedInstance) { let parentComponentUid = this.$scope.currentComponent.selectedInstance.componentUid @@ -215,15 +369,25 @@ export class CompositionViewModel { this.$log.debug('composition-view-model::onNodeSelected:: with id: ' + selectedComponent.uniqueId); this.$scope.currentComponent.setSelectedInstance(selectedComponent); + this.$scope.selectedZoneInstance = null; this.$scope.updateSelectedComponent(); + + + if (this.$state.current.name === 'workspace.composition.api') { this.$state.go('workspace.composition.details'); } }; + this.$scope.setSelectedZoneInstance = (zoneInstance: ZoneInstance): void => { + this.$scope.currentComponent.selectedInstance = null; + this.$scope.selectedZoneInstance = zoneInstance; + }; + this.$scope.onBackgroundClick = ():void => { this.$scope.currentComponent.selectedInstance = null; + this.$scope.selectedZoneInstance = null; this.$scope.selectedComponent = this.$scope.currentComponent; if (this.$state.current.name === 'workspace.composition.api') { @@ -238,6 +402,10 @@ export class CompositionViewModel { this.$scope.openUpdateModal = ():void => { this.openUpdateComponentInstanceNameModal(); }; + + this.$scope.changeZoneInstanceName = (newName:string):void => { + this.$scope.selectedZoneInstance.instanceData.name = newName; + }; this.$scope.deleteSelectedComponentInstance = ():void => { const {currentComponent} = this.$scope; @@ -258,8 +426,7 @@ export class CompositionViewModel { modalText += `<p>The following service paths will be erased: ${pathNames}</p>`; } } - - this.ModalsHandler.openAlertModal(title, modalText).then(this.removeSelectedComponentInstance); + this.ModalServiceSdcUI.openAlertModal(title, modalText, "OK", this.removeSelectedComponentInstance, "deleteInstanceModal"); }; this.$scope.openVersionChangeModal = (pathsToDelete:string[]):ng.IPromise<any> => { @@ -308,7 +475,7 @@ export class CompositionViewModel { this.$scope.disabledTabs = state; }; - this.eventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.$scope.reload); + this.eventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload); } } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html index fceb73b882..4cd33f3210 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html @@ -2,7 +2,7 @@ <loader data-display="isLoading"></loader> <div class="w-sdc-designer-canvas" data-ng-class="{sidebaractive: displayDesignerRightSidebar}"> <palette current-component="currentComponent" - is-view-only="isViewOnly" + is-view-only="isViewOnly || isCanvasTagging" is-loading="isLoading"></palette> <ng2-palette-popup-panel></ng2-palette-popup-panel> @@ -19,86 +19,107 @@ <div class="w-sdc-designer-sidebar" data-ng-class="{'view-mode':isViewOnly}"> - <div class="w-sdc-designer-sidebar-head" data-tests-id="w-sdc-designer-sidebar-head"> - <div class="w-sdc-designer-sidebar-logo-ph"> - <div class="large {{selectedComponent.iconSprite}} {{selectedComponent.icon}}"> - <div ng-if="isComponentInstanceSelected()" - data-ng-class="{'non-certified':'CERTIFIED' !== selectedComponent.lifecycleState}" - tooltips tooltip-side="top" tooltip-content="Not certified"></div> + <div ng-if="!selectedZoneInstance"> + + <div class="w-sdc-designer-sidebar-head" data-tests-id="w-sdc-designer-sidebar-head"> + <div class="w-sdc-designer-sidebar-logo-ph"> + <div class=" large {{selectedComponent.iconSprite}} {{selectedComponent.icon}}" + ng-class="{'archive-component':selectedComponent.archived}"> + <div ng-if="isComponentInstanceSelected()" + data-ng-class="{'non-certified':'CERTIFIED' !== selectedComponent.lifecycleState}" + tooltips tooltip-side="top" tooltip-content="Not certified"></div> + </div> </div> - </div> - <div class="w-sdc-designer-sidebar-logo"> - <span class="w-sdc-designer-sidebar-logo-title" data-tests-id="selectedCompTitle" tooltips - tooltip-class="tooltip-custom break-word-tooltip" - tooltip-content="​{{isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName}}" - data-ng-bind="isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName"></span> + <div class="w-sdc-designer-sidebar-logo"> + <span class="w-sdc-designer-sidebar-logo-title" data-tests-id="selectedCompTitle" tooltips + tooltip-class="tooltip-custom break-word-tooltip" + tooltip-content="​{{isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName}}" + data-ng-bind="isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName"></span> + </div> + <div class="sprite e-sdc-small-icon-pencil w-sdc-designer-update-resource-icon" + data-tests-id="renameInstance" + data-ng-if="!isViewOnly && isComponentInstanceSelected() && !selectedComponent.archived" + data-ng-click="openUpdateModal()" id="editPencil"></div> + + <div class="sprite e-sdc-small-icon-delete w-sdc-designer-delete-resource-icon" + data-tests-id="deleteInstance" + data-ng-if="!isViewOnly && isComponentInstanceSelected() && !selectedComponent.archived" + data-ng-click="!isLoading && deleteSelectedComponentInstance()" title="Delete Resource Instance"></div> </div> - <div class="sprite e-sdc-small-icon-pencil w-sdc-designer-update-resource-icon" - data-ng-if="!isViewOnly && isComponentInstanceSelected()" - data-ng-click="openUpdateModal()" id="editPencil"></div> - <div class="sprite e-sdc-small-icon-delete w-sdc-designer-delete-resource-icon" - data-tests-id="e-sdc-small-icon-delete" - data-ng-if="!isViewOnly && isComponentInstanceSelected()" - data-ng-click="!isLoading && deleteSelectedComponentInstance()" title="Delete Resource Instance"></div> - </div> + <div class="w-sdc-designer-sidebar-tabs"> + <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" + data-ui-sref="workspace.composition.details" + tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information" + data-tests-id="information-tab" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new info"></div> + </button> + <!--<button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"--> + <!--ui-sref="workspace.composition.structure"--> + <!--tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Composition">--> + <!--<div class="i-sdc-designer-sidebar-tab-icon sprite-new structure"></div>--> + <!--</button>--> + <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" + data-ui-sref="workspace.composition.deployment" + tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Deployment Artifacts" + data-tests-id="deployment-artifact-tab" + data-ng-if="!isPNF() && !isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new deployment-artifacts"></div> + </button> + <button tooltips tooltip-class="tooltip-custom tab-tooltip" + tooltip-content="{{selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'Properties and Attributes': 'Inputs'}}" + class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" + data-ui-sref="workspace.composition.properties" + data-tests-id="properties-and-attributes-tab" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new" + ng-class="selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'properties': 'inputs'"></div> + </button> + <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" + data-ui-sref="workspace.composition.artifacts" + data-ng-if="!isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" + tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information Artifacts" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new information-artifacts"></div> + </button> + <button data-ng-if="!selectedComponent.isService() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" class="i-sdc-designer-sidebar-tab" + data-ui-sref-active="active" ui-sref="workspace.composition.relations" + tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" + data-tests-id="requirements-and-capabilities" + tooltip-content="Requirements and Capabilities" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new relations"></div> + </button> + <button data-ng-if="selectedComponent.isService() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" class="i-sdc-designer-sidebar-tab" + data-ui-sref-active="active" ui-sref="workspace.composition.api" data-tests-id="tab-api" + tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" tooltip-content="API" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new api"></div> + </button> - <div class="w-sdc-designer-sidebar-tabs"> - <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" - data-ui-sref="workspace.composition.details" - tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information" - data-tests-id="information-tab" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new info"></div> - </button> - <!--<button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"--> - <!--ui-sref="workspace.composition.structure"--> - <!--tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Composition">--> - <!--<div class="i-sdc-designer-sidebar-tab-icon sprite-new structure"></div>--> - <!--</button>--> - <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" - data-ui-sref="workspace.composition.deployment" - tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Deployment Artifacts" - data-tests-id="deployment-artifact-tab" - data-ng-if="!isPNF() && !isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new deployment-artifacts"></div> - </button> - <button tooltips tooltip-class="tooltip-custom tab-tooltip" - tooltip-content="{{selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'Properties and Attributes': 'Inputs'}}" - class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" - data-ui-sref="workspace.composition.properties" - data-tests-id="properties-and-attributes-tab" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new" - ng-class="selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'properties': 'inputs'"></div> - </button> - <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" - data-ui-sref="workspace.composition.artifacts" - data-ng-if="!isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" - tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information Artifacts" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new information-artifacts"></div> - </button> - <button data-ng-if="!selectedComponent.isService() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" class="i-sdc-designer-sidebar-tab" - data-ui-sref-active="active" ui-sref="workspace.composition.relations" - tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" - data-tests-id="requirements-and-capabilities" - tooltip-content="Requirements and Capabilities" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new relations"></div> - </button> - <button data-ng-if="selectedComponent.isService() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" class="i-sdc-designer-sidebar-tab" - data-ui-sref-active="active" ui-sref="workspace.composition.api" data-tests-id="tab-api" - tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" tooltip-content="API" - data-ng-class="{'disabled': disabledTabs}"> - <div class="i-sdc-designer-sidebar-tab-icon sprite-new api"></div> - </button> + </div> + <div data-ui-view="" class="w-sdc-designer-sidebar-tab-content-view"></div> </div> - <div data-ui-view="" class="w-sdc-designer-sidebar-tab-content-view"></div> + <!-- Solution for now to support policies and groups working with Angular 2 components --> + <!-- isCertified not relevant for group or policy --> + <!-- (selectedZoneInstanceType === ZoneInstanceType.GROUP || selectedZoneInstanceType === ZoneInstanceType.POLICY) --> + <div ng-if="selectedZoneInstance"> + + <ng2-composition-panel + [is-loading]="isLoading" + [is-view-only]="isViewOnly || isCanvasTagging" + [selected-zone-instance-name]="selectedZoneInstance.instanceData.name" + [selected-zone-instance-id]="selectedZoneInstance.instanceData.uniqueId" + [selected-zone-instance-type]="selectedZoneInstance.type" + [topology-template]="currentComponent" + > + </ng2-composition-panel> + </div> <loader data-display="isLoadingRightPanel" relative="true" size="medium"></loader> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less index f351450e6d..b9bb66cde7 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less @@ -107,7 +107,7 @@ position: fixed; right: -302px; width: 302px; - top: 102px; + top: 103px; transition: right 0.2s; z-index: 9; .box-shadow(-7px -3px 6px -8px @main_color_n); @@ -163,6 +163,13 @@ top: 10px; } + .w-sdc-designer-restore-button { + .hand; + position:absolute; + right: 20px; + top:10px; + width:65px; + } .w-sdc-designer-sidebar-tabs { .bg_c; } @@ -237,7 +244,7 @@ height: 32px; line-height: 32px; margin-top: 1px; - padding: 0 40px 0 20px; + padding: 0 10px 0 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -266,7 +273,7 @@ .sprite-new; .arrow-up; right: 16px; - top: 10px; + top: 13px; transition: .3s all; position: absolute; } @@ -664,8 +671,8 @@ align-items: center; .non-certified { position: relative; - left: 27px; - bottom: 6px; + left: -4px; + top: -4px; .sprite; .s-sdc-state-non-certified; display: block; @@ -682,8 +689,8 @@ .non-certified { position: relative; - left: 43px; - bottom: 3px; + left: 0px; + top: 0px; .sprite; .s-sdc-state-non-certified; display: block; @@ -841,6 +848,11 @@ flex-direction: column; align-items: flex-end; margin-right:10px; + pointer-events: none; + + & > * { + pointer-events: all; + } &.with-sidebar { right:320px; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts index 6e3258f69b..e389395142 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts @@ -143,19 +143,23 @@ export class DetailsViewModel { this.$scope.currentComponent.changeComponentInstanceVersion(componentUid).then(onSuccess, onCancel); }; - this.serviceService.checkComponentInstanceVersionChange(service, componentUid).subscribe((pathsToDelete:string[]) => { - if (pathsToDelete && pathsToDelete.length) { - this.$scope.isLoading = false; - this.$scope.$parent.isLoading = false; - this.$scope.$parent.openVersionChangeModal(pathsToDelete).then(() => { - this.$scope.isLoading = true; - this.$scope.$parent.isLoading = true; + if (this.$scope.currentComponent.isService()) { + this.serviceService.checkComponentInstanceVersionChange(service, componentUid).subscribe((pathsToDelete:string[]) => { + if (pathsToDelete && pathsToDelete.length) { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + this.$scope.$parent.openVersionChangeModal(pathsToDelete).then(() => { + this.$scope.isLoading = true; + this.$scope.$parent.isLoading = true; + onUpdate(); + }, onCancel); + } else { onUpdate(); - }, onCancel); - } else { - onUpdate(); - } - }, onCancel); + } + }, onCancel); + } else { + onUpdate(); + } }; } } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html index 033c4668f3..9468937610 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html @@ -26,9 +26,10 @@ data-ng-if="!isComponentInstanceSelected()" data-tests-id="rightTab_version" data-ng-bind="selectedComponent.version"></span> <ng-form name="editForm" data-ng-if="isComponentInstanceSelected()"> - <select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-tests-id="changeVersion" data-ng-disabled="$parent.isViewOnly || selectedComponent.uniqueId != editResourceVersion.allVersions[editResourceVersion.changeVersion]" + <select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-tests-id="changeVersion" + data-ng-disabled="$parent.isViewOnly || selectedComponent.uniqueId != editResourceVersion.allVersions[editResourceVersion.changeVersion] || selectedComponent.archived" class="i-sdc-designer-sidebar-section-content-item-value i-sdc-form-select" - data-ng-class="{'minor': (editResourceVersion.changeVersion)%1}" + data-ng-class="{'minor': (editResourceVersion.changeVersion)%1, 'disabled':selectedComponent.archived}" data-ng-change="changeResourceVersion()"> <option class="select-instance-version" data-ng-class="{'minor': key%1}" ng-repeat="(key, value) in editResourceVersion.allVersions">{{key}}</option> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts index efd5cfd84d..e3ddecd9a5 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts @@ -139,9 +139,11 @@ export class ResourcePropertiesViewModel { (this.$scope.isPropertyOwner() ? this.$scope.properties[property.parentUniqueId] : this.$scope.properties[property.resourceInstanceUniqueId]) || [], - this.isPropertyValueOwner()).then((updatedProperty:PropertyModel) => { - let oldProp = _.find(this.$scope.properties[updatedProperty.resourceInstanceUniqueId], (prop:PropertyModel) => {return prop.uniqueId == updatedProperty.uniqueId;}); - oldProp.value = updatedProperty.value; + this.isPropertyValueOwner(), "component", property.resourceInstanceUniqueId).then((updatedProperty:PropertyModel) => { + if(updatedProperty){ + let oldProp = _.find(this.$scope.properties[updatedProperty.resourceInstanceUniqueId], (prop:PropertyModel) => {return prop.uniqueId == updatedProperty.uniqueId;}); + oldProp.value = updatedProperty.value; + } }); }; @@ -224,7 +226,9 @@ export class ResourcePropertiesViewModel { return this.$filter("resourceName")(this.$scope.currentComponent.name); default: - return this.$filter("resourceName")((_.find(this.$scope.currentComponent.componentInstances, {uniqueId: key})).name); + let componentInstance = _.find(this.$scope.currentComponent.componentInstances, {uniqueId: key}); + if(componentInstance) + return this.$filter("resourceName")(componentInstance.name); } }; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less index 41a90bff9d..ce5acc83e5 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less @@ -1,5 +1,6 @@ .w-sdc-designer-sidebar-tab-content.properties { .i-sdc-designer-sidebar-section-content-item-property-and-attribute-label{ + display:block; font-weight: bold; } .i-sdc-designer-sidebar-section-content-item-button.update{ diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts index feda7fe17f..9df377c5fc 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts @@ -108,7 +108,7 @@ export class DeploymentViewModel { }; private initRightTabs = ()=> { - if (this.$scope.currentComponent.groups) { + if (this.$scope.currentComponent.modules) { this.$templateCache.put("hierarchy-view.html", require('app/view-models/tabs/hierarchy/hierarchy-view.html')); let hierarchyTab = new Tab("hierarchy-view.html", 'Sdc.ViewModels.HierarchyViewModel', 'hierarchy', this.$scope.isViewMode(), this.$scope.currentComponent, 'hierarchy'); this.$scope.tabs.push(hierarchyTab) @@ -116,11 +116,11 @@ export class DeploymentViewModel { } private initGraphData = ():void => { - if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations || !this.$scope.component.groups) { + if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations || !this.$scope.component.modules) { this.ComponentServiceNg2.getDeploymentGraphData(this.$scope.component).subscribe((response:ComponentGenericResponse) => { this.$scope.component.componentInstances = response.componentInstances; this.$scope.component.componentInstancesRelations = response.componentInstancesRelations; - this.$scope.component.groups = response.groups; + this.$scope.component.modules = response.modules; this.$scope.isLoading = false; this.initComponent(); this.initRightTabs(); diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html index f8b5f23a25..1065552e46 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html @@ -5,6 +5,6 @@ </div> <div class="w-sdc-deployment-right-bar"> - <sdc-tabs tabs="tabs" is-view-only="isViewOnly" selected-tab="selectedTab"></sdc-tabs> + <ng1-tabs tabs="tabs" is-view-only="isViewOnly" selected-tab="selectedTab"></ng1-tabs> </div> </div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less index 4c548c7331..f51ff6220d 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less @@ -10,9 +10,11 @@ .w-sdc-deployment-canvas { .noselect; .bg_c; + position: relative; bottom: 0; width: 100%; height: 100%; + z-index: 0; .view-mode{ background-color: #f8f8f8; @@ -27,7 +29,6 @@ position: absolute; right: 0px; transition: right 0.2s; - z-index: 10000; top: @action_nav_height; } } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html index 3367193fc7..ff4bbd60c5 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html @@ -1,4 +1,4 @@ -<sdc-modal modal="modalDitributionStatus" type="classic" class="w-sdc-classic-top-line-modal" buttons="footerButtons" header="Distribution by Status" show-close-button="true"> +<ng1-modal modal="modalDitributionStatus" type="classic" class="w-sdc-classic-top-line-modal" buttons="footerButtons" header="Distribution by Status" show-close-button="true"> <div class="w-sdc-distribution-view"> <div class="w-sdc-distribution-view-header"> @@ -127,4 +127,4 @@ </div> -</sdc-modal> +</ng1-modal> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts index b03d7c4d7c..68f789808a 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts @@ -21,11 +21,13 @@ 'use strict'; import * as _ from "lodash"; import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON, - ResourceType, ComponentState} from "app/utils"; + ResourceType, ComponentState, instantiationType, ComponentFactory} from "app/utils"; import {CacheService, EventListenerService, ProgressService, OnboardingService} from "app/services"; -import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent} from "app/models"; +import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent, Component} from "app/models"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {Dictionary} from "lodash"; +import { PREVIOUS_CSAR_COMPONENT } from "../../../../utils/constants"; + export class Validation { componentNameValidationPattern:RegExp; @@ -62,8 +64,11 @@ export interface IGeneralScope extends IWorkspaceViewModelScope { importCsarProgressKey:string; browseFileLabel:string; componentCategories:componentCategories; + instantiationTypes:Array<instantiationType>; - onToscaFileChange():void; + save():Promise<any>; + revert():void; + onImportFileChange():void; validateField(field:any):boolean; validateName(isInit:boolean):void; calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category @@ -74,6 +79,8 @@ export interface IGeneralScope extends IWorkspaceViewModelScope { openOnBoardingModal():void; initCategoreis():void; initEnvironmentContext():void; + initInstantiationTypes():void; + onInstantiationTypeChange():void; updateIcon():void; possibleToUpdateIcon():boolean; } @@ -101,7 +108,8 @@ export class GeneralViewModel { '$interval', '$filter', '$timeout', - 'Sdc.Services.OnboardingService' + 'Sdc.Services.OnboardingService', + 'ComponentFactory' ]; constructor(private $scope:IGeneralScope, @@ -124,7 +132,8 @@ export class GeneralViewModel { protected $interval:any, private $filter:ng.IFilterService, private $timeout:ng.ITimeoutService, - private onBoardingService:OnboardingService) { + private onBoardingService:OnboardingService, + private ComponentFactory:ComponentFactory) { this.initScopeValidation(); this.initScopeMethods(); @@ -146,51 +155,64 @@ export class GeneralViewModel { this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern; }; - private initImportedToscaBrowseFile = ():void =>{ - // Init the decision if to show onboarding - this.$scope.isShowOnboardingSelectionBrowse = false; - if (this.$scope.component.isResource() && - this.$scope.isEditMode() && - (<Resource>this.$scope.component).resourceType == ResourceType.VF && - (<Resource>this.$scope.component).csarUUID) { - this.$scope.isShowOnboardingSelectionBrowse = true; - let onboardCsarFilesMap:Dictionary<Dictionary<string>> = this.cacheService.get('onboardCsarFilesMap'); - // The onboardCsarFilesMap in cache contains map of [packageId]:[vsp display name for brows] - // if the map is empty - Do request to BE - if(onboardCsarFilesMap) { - if (onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID]){ - this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID][(<Resource>this.$scope.component).csarVersion]; - } - } - if(!onboardCsarFilesMap || !this.$scope.importedToscaBrowseFileText){ + private loadOnboardingFileCache = ():ng.IPromise<Dictionary<any>> =>{ - let onSuccess = (vsps:Array<ICsarComponent>): void =>{ - onboardCsarFilesMap = {}; - _.each(vsps, (vsp:ICsarComponent)=>{ - onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {}; - onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")"; - }); - this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap); - this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID][(<Resource>this.$scope.component).csarVersion]; - }; + let onboardCsarFilesMap:Dictionary<Dictionary<string>>; + let onSuccess = (vsps:Array<ICsarComponent>) =>{ + onboardCsarFilesMap = {}; + _.each(vsps, (vsp:ICsarComponent)=>{ + onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {}; + onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")"; + }); + this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap); + return onboardCsarFilesMap; + }; + let onError = (): void =>{ + console.log("Error getting onboarding list"); + }; + return this.onBoardingService.getOnboardingVSPs().then(onSuccess, onError); + }; + + private setImportedFileText = ():void => { - let onError = (): void =>{ - console.log("Error getting onboarding list"); - }; + if(!this.$scope.isShowOnboardingSelectionBrowse) return; - this.onBoardingService.getOnboardingVSPs().then(onSuccess, onError); + //these variables makes it easier to read this logic + let csarUUID:string = (<Resource>this.$scope.component).csarUUID; + let csarVersion:string = (<Resource>this.$scope.component).csarVersion; + + let onboardCsarFilesMap:Dictionary<Dictionary<string>> = this.cacheService.get('onboardCsarFilesMap'); + let assignFileName = ():void => { + if(this.$scope.component.vspArchived){ + this.$scope.importedToscaBrowseFileText = 'VSP is archived'; + } else { + this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[csarUUID][csarVersion]; } } - }; - - private initScope = ():void => { - // Work around to change the csar version - if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { - (<Resource>this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + + if(this.$scope.component.vspArchived || (onboardCsarFilesMap && onboardCsarFilesMap[csarUUID] && onboardCsarFilesMap[csarUUID][csarVersion])){ //check that the file name is already in cache + assignFileName(); + } else { + this.loadOnboardingFileCache().then((onboardingFiles) => { + onboardCsarFilesMap = onboardingFiles; + this.cacheService.set('onboardCsarFilesMap', onboardingFiles); + assignFileName(); + }, ()=> {}); } + + } + isCreateModeAvailable(verifyObj:string): boolean { + var isCheckout:boolean = ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState; + return this.$scope.isCreateMode() || (isCheckout && !verifyObj) + } + + private initScope = ():void => { + + this.$scope.importCsarProgressKey = "importCsarProgressKey"; + this.$scope.browseFileLabel = this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType === ResourceType.VF ? "Upload file" : "Upload VFC"; this.$scope.progressService = this.progressService; this.$scope.componentCategories = new componentCategories(); @@ -216,9 +238,33 @@ export class GeneralViewModel { if (this.$scope.isEditMode() && resource.resourceType == ResourceType.VF && !resource.csarUUID) { this.$scope.isShowFileBrowse = true; } + } else if(this.$scope.component.isService()){ + // Init Instantiation types + this.$scope.initInstantiationTypes(); + } + + // Work around to change the csar version + if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { + //(<Resource>this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + this.cacheService.remove(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + this.$scope.updateUnsavedFileFlag(true); + + if (!this.$scope.isViewMode() && this.cacheService.get(PREVIOUS_CSAR_COMPONENT)) { //keep the old component in the cache until checkout, so we dont need to pass it around + this.$scope.setOriginComponent(this.cacheService.get(PREVIOUS_CSAR_COMPONENT)); + this.cacheService.remove(PREVIOUS_CSAR_COMPONENT); + } } - this.initImportedToscaBrowseFile(); + + // Init the decision if to show onboarding + if (this.$scope.component.isResource() && this.$scope.isEditMode() && + (<Resource>this.$scope.component).resourceType == ResourceType.VF && (<Resource>this.$scope.component).csarUUID) { + this.$scope.isShowOnboardingSelectionBrowse = true; + this.setImportedFileText(); + } else { + this.$scope.isShowOnboardingSelectionBrowse = false; + } + //init file extensions based on the file that was imported. if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile) { @@ -231,11 +277,14 @@ export class GeneralViewModel { (<Resource>this.$scope.component).importedFile.filetype = "yaml"; this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension; } + this.$scope.restoreFile = angular.copy((<Resource>this.$scope.originComponent).importedFile); //create backup } else if (this.$scope.isEditMode() && (<Resource>this.$scope.component).resourceType === ResourceType.VF) { this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension; //(<Resource>this.$scope.component).importedFile.filetype="csar"; } + + this.$scope.setValidState(true); this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => { @@ -252,6 +301,12 @@ export class GeneralViewModel { this.$scope.originComponent.contactId = this.$scope.component.contactId; } + + this.$scope.$on('$destroy', () => { + this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE); + this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE); + }); + }; // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except) @@ -299,14 +354,28 @@ export class GeneralViewModel { } }; + this.$scope.initInstantiationTypes = ():void => { + if (this.$scope.componentType === ComponentType.SERVICE) { + this.$scope.instantiationTypes = new Array(); + this.$scope.instantiationTypes.push(instantiationType.A_LA_CARTE); + this.$scope.instantiationTypes.push(instantiationType.MACRO); + var instantiationTypeField:string =(<Service>this.$scope.component).instantiationType; + if (instantiationTypeField === ""){ + this.$scope.instantiationTypes.push(""); + } + else if (this.isCreateModeAvailable(instantiationTypeField)) { + (<Service>this.$scope.component).instantiationType = instantiationType.A_LA_CARTE; + + } + } + }; this.$scope.initEnvironmentContext = ():void => { if (this.$scope.componentType === ComponentType.SERVICE) { this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext; var environmentContext:string =(<Service>this.$scope.component).environmentContext; - var isCheckout:boolean = ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState; // In creation new service OR check outing old service without environmentContext parameter - set default value - if(this.$scope.isCreateMode() || (isCheckout && !environmentContext)){ + if(this.isCreateModeAvailable(environmentContext)){ (<Service>this.$scope.component).environmentContext = this.$scope.environmentContextObj.defaultValue; } } @@ -320,19 +389,33 @@ export class GeneralViewModel { }; this.$scope.openOnBoardingModal = ():void => { + if(this.$scope.component.vspArchived) return; let csarUUID = (<Resource>this.$scope.component).csarUUID; - this.ModalsHandler.openOnboadrdingModal('Update', csarUUID).then(()=> { - // OK - this.$scope.uploadFileChangedInGeneralTab(); - }, ()=> { - // ERROR - }); + let csarVersion = (<Resource>this.$scope.component).csarVersion; + this.ModalsHandler.openOnboadrdingModal('Update', csarUUID, csarVersion).then((result)=> { + + if(result){ + this.ComponentFactory.getComponentWithMetadataFromServer(result.type.toUpperCase(), result.previousComponent.uniqueId).then( + (component:Component)=> { + if (result.componentCsar && component.isResource()){ + this.cacheService.set(PREVIOUS_CSAR_COMPONENT, angular.copy(component)); + component = this.ComponentFactory.updateComponentFromCsar(result.componentCsar, <Resource>component); + } + + this.$scope.setComponent(component); + this.$scope.updateUnsavedFileFlag(true); + this.setImportedFileText(); + }, ()=> { + // ERROR + }); + } + }, () => {}); }; this.$scope.updateIcon = ():void => { this.ModalsHandler.openUpdateIconModal(this.$scope.component).then((isDirty:boolean)=> { - if(!this.$scope.isCreateMode()){ - this.$state.current.data.unsavedChanges = this.$state.current.data.unsavedChanges || isDirty; + if(isDirty && !this.$scope.isCreateMode()){ + this.setUnsavedChanges(true); } }, ()=> { // ERROR @@ -340,7 +423,7 @@ export class GeneralViewModel { }; this.$scope.possibleToUpdateIcon = ():boolean => { - if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName)){ + if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName) && !this.$scope.component.isAlreadyCertified()){ return true; }else{ return false; @@ -364,7 +447,6 @@ export class GeneralViewModel { return; } - //????????????????????????? let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined; let onFailed = (response) => { @@ -407,6 +489,55 @@ export class GeneralViewModel { } }; + + this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE, (nextState) => { + if (this.$state.current.data.unsavedChanges && this.$scope.isValidForm){ + this.$scope.save().then(() => { + this.$scope.handleChangeLifecycleState(nextState); + }, () => { + console.error("Save failed, unable to change lifecycle state to " + nextState); + }); + } else if(!this.$scope.isValidForm){ + console.error("Form is not valid"); + } else { + let newCsarVersion:string; + if(this.$scope.unsavedFile){ + newCsarVersion = (<Resource>this.$scope.component).csarVersion; + } + this.$scope.handleChangeLifecycleState(nextState, newCsarVersion); + } + }); + + + this.$scope.revert = ():void => { + //in state of import file leave the file in place + + this.$scope.setComponent(this.ComponentFactory.createComponent(this.$scope.originComponent)); + + if (this.$scope.component.isResource() && this.$scope.restoreFile) { + (<Resource>this.$scope.component).importedFile = angular.copy(this.$scope.restoreFile); + } + + this.setImportedFileText(); + this.$scope.updateBreadcrumbs(this.$scope.component); //update on workspace + + this.$scope.componentCategories.selectedCategory = this.$scope.originComponent.selectedCategory; + this.setUnsavedChanges(false); + this.$scope.updateUnsavedFileFlag(false); + this.$scope.editForm.$setPristine(); + }; + + this.$scope.onImportFileChange = () => { + + if( !this.$scope.restoreFile && this.$scope.editForm.fileElement.value && this.$scope.editForm.fileElement.value.filename || //if file started empty but we have added a new one + this.$scope.restoreFile && !angular.equals(this.$scope.restoreFile, this.$scope.editForm.fileElement.value)){ //or file was swapped for a new one + this.$scope.updateUnsavedFileFlag(true); + } else { + this.$scope.updateUnsavedFileFlag(false); + this.$scope.editForm.fileElement.$setPristine(); + } + }; + this.$scope.$watchCollection('component.name', (newData:any):void => { this.$scope.validateName(false); }); @@ -417,9 +548,10 @@ export class GeneralViewModel { }); this.$scope.$watch("editForm.$dirty", (newVal, oldVal) => { - if (newVal !== oldVal) { - this.$state.current.data.unsavedChanges = newVal && !this.$scope.isCreateMode(); + if (newVal && !this.$scope.isCreateMode()) { + this.setUnsavedChanges(true); } + }); this.$scope.onCategoryChange = ():void => { @@ -439,9 +571,12 @@ export class GeneralViewModel { this.$scope.component.icon = DEFAULT_ICON; } }; - this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.$scope.reload); - this.EventListenerService.registerObserverCallback(EVENTS.ON_REVERT, ()=>{ - this.$scope.componentCategories.selectedCategory = this.$scope.originComponent.selectedCategory; - }); + this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload); + + }; + + private setUnsavedChanges = (hasChanges:boolean):void => { + this.$state.current.data.unsavedChanges = hasChanges; }; + } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html index 36976c610e..28b033a64b 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html @@ -1,5 +1,14 @@ <div include-padding="true" class="sdc-workspace-general-step"> - + <div class="w-sdc-main-container-body-content-action-buttons"> + <div data-ng-if="unsavedFile && !isCreateMode() && !isViewMode()" class="unsaved-file-warning"> + <span class="sprite-new sdc-warning"></span> Click save to update to the new VSP + </div> + <button class="tlv-btn blue" data-ng-if="isDesigner()" data-ng-show="isGeneralView()" data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode() || isCreateMode()}" + data-ng-click="save()" data-tests-id="create/save" tooltips tooltip-content="Save">Save</button> + <span data-ng-if="isDesigner()" data-ng-class="{'disabled' :isDisableMode() || isViewMode() || isCreateMode()}" ng-click="revert()" class="sprite-new revert-btn" data-tests-id="revert" + data-ng-show="isGeneralView()" tooltips tooltip-content="Revert"></span> + + </div> <form novalidate class="w-sdc-form" name="editForm" validation-on-load form-to-validate="editForm"> <div class="w-sdc-form-section-container"> @@ -9,10 +18,13 @@ <div class="w-sdc-form-column"> <div class="upper-general-fields"> <div class="selected-icon-container" data-ng-class="{'show-only-on-over':'defaulticon'!=component.icon && !isViewMode()}"> - <div class="selected-icon-inner-container"> + <div class="selected-icon-inner-container "> <div class="sprite-new update-component-icon" data-ng-click="updateIcon()" data-ng-if="!isViewMode() && possibleToUpdateIcon()"></div> <div class="i-sdc-form-item-suggested-icon large selected-icon {{component.iconSprite}} {{component.icon}}" - data-ng-class="{ 'disable': isViewMode() || !possibleToUpdateIcon() }" + data-ng-class="{ + 'disable': isViewMode() || !possibleToUpdateIcon(), + 'archive-component active-component-static': component.archived + }" ng-model="component.icon" tooltips tooltip-content='{{component.icon | translate}}' > @@ -27,7 +39,7 @@ data-ng-class="{'view-mode': isViewMode()}" name="componentName" data-ng-init="isCreateMode() && validateName(true)" - data-ng-maxlength="50" + data-ng-maxlength="50" data-ng-model="component.name" type="text" data-required @@ -53,7 +65,7 @@ <!--------------------- CATEGORIES --------------------> <div class="i-sdc-form-item" - data-ng-class="{'error': validateField(editForm.category)}"> + data-ng-class="{'error': validateField(editForm.category)}"> <loader data-display="!categories && !initCategoreis()" relative="true"></loader> <label class="i-sdc-form-label required">Category</label> <select class="i-sdc-form-select" @@ -132,14 +144,17 @@ <div class="w-sdc-form-column"> <!--------------------- IMPORT TOSCA FILE USING BROWSE (ALSO VFC) --------------------> <div class="i-sdc-form-item" ng-if="isShowFileBrowse"> + + <!-- // element-disabled="{{!isCreateMode()&&!(isEditMode()&&component.resourceType=='VF')&&component.vspArchived}} || {{isViewMode()}}" --> + <label class="i-sdc-form-label" data-ng-class="{'required':isCreateMode()}">{{browseFileLabel}}</label> <file-upload id="fileUploadElement" class="i-sdc-form-input" element-name="fileElement" - element-disabled="{{!isCreateMode()&&!(isEditMode()&&component.resourceType=='VF')}} || {{isViewMode()}}" + element-disabled="{{(!isCreateMode()&&!(isEditMode()&&component.resourceType=='VF'))|| isViewMode() || component.vspArchived}}" form-element="editForm" file-model="component.importedFile" - on-file-changed-in-directive="uploadFileChangedInGeneralTab" + on-file-changed-in-directive="onImportFileChange" extensions="{{importedFileExtension}}" default-text="'Browse to select file'" data-ng-class="{'error':!(isEditMode()&&component.resourceType=='VF') && (!editForm.fileElement.$valid || !component.importedFile.filename)}"></file-upload> @@ -147,16 +162,16 @@ <!--------------------- IMPORT TOSCA FILE USING ONBOARDING --------------------> <div class="i-sdc-form-item" ng-if="isShowOnboardingSelectionBrowse"> - <label class="i-sdc-form-label required">Select VSP</label> - <div class="i-sdc-form-file-upload i-sdc-form-input"> - <span class="i-sdc-form-file-name" data-tests-id="filename">{{(fileModel && fileModel.filename) || importedToscaBrowseFileText}}</span> - <div class="i-sdc-form-file-upload-x-btn" ng-click="cancel()" data-ng-show="fileModel.filename && fileModel.filename!=='' && elementDisabled!=='true'"></div> - <input type="button" name="fileElement"/> - <div class="file-upload-browse-btn" data-ng-click="openOnBoardingModal()" data-tests-id="browseButton">Browse</div> - </div> + <label class="i-sdc-form-label required">Select VSP</label> + <div class="i-sdc-form-file-upload i-sdc-form-input"> + <span class="i-sdc-form-file-name" data-ng-disabled="component.vspArchived" data-tests-id="filename">{{(fileModel && fileModel.filename) || importedToscaBrowseFileText }}</span> + <div class="i-sdc-form-file-upload-x-btn" ng-click="cancel()" data-ng-show="fileModel.filename && fileModel.filename!=='' && elementDisabled!=='true'"></div> + <input type="button" data-ng-disabled="component.vspArchived" name="fileElement" /> + <div class="file-upload-browse-btn" data-ng-click="openOnBoardingModal()" data-ng-disabled="!component.vspArchived" data-tests-id="browseButton">Browse</div> + </div> </div> - <div class="input-error-file-upload" data-ng-show="component.importedFile && (!editForm.fileElement.$valid || !component.importedFile.filename)"> + <div class="input-error-file-upload" data-ng-disabled="!component.archived" data-ng-show="component.importedFile && (!editForm.fileElement.$valid || !component.importedFile.filename)"> <!-- editForm.fileElement.$error.required <== Can not use this, because the browse is done from outside for the first time --> <span ng-show="!(isEditMode()&&component.resourceType=='VF')&&!component.importedFile.filename" translate="NEW_SERVICE_RESOURCE_ERROR_TOSCA_FILE_REQUIRED"></span><!-- Required --> <span ng-show="editForm.fileElement.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span> @@ -283,7 +298,7 @@ </div> <!--------------------- Resource Model Number --------------------> - <!--------------------- ECOMPGENERATEDNAMING --------------------> + <!--------------------- ECOMPGENERATEDNAMING --------------------> <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.ecompGeneratedNaming)}" @@ -307,7 +322,7 @@ <!--------------------- NAMING POLICY --------------------> <div ng-if="component.isService()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.namingPolicy)}"> - <label class="i-sdc-form-label">Naming policy</label> + <label class="i-sdc-form-label">Naming Policy</label> <input class="i-sdc-form-input" name="namingPolicy" data-ng-class="{'view-mode': isViewMode() || !component.ecompGeneratedNaming}" @@ -389,6 +404,21 @@ </div> <!--------------------- ENVIRONMENT CONTEXT ------------------> + <!--------------------- Instantiation Type --------------------> + <div class="i-sdc-form-item" data-ng-if="component.isService() && instantiationTypes"> + <label class="i-sdc-form-label">Instantiation Type</label> + <select class="i-sdc-form-select" + name="instantiationType" + data-ng-class="{'view-mode': isViewMode()}" + data-ng-disabled="component.isCsarComponent()" + data-ng-model="component.instantiationType" + data-tests-id="selectInstantiationType"> + <option ng-repeat="type in instantiationTypes">{{type}}</option> + + </select> + </div> + + <!--------------------- Instantiation Type --------------------> <div class="meta-data" data-ng-if="component.creationDate"> <div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general.less b/catalog-ui/src/app/view-models/workspace/tabs/general/general.less index b9b59deb1b..b60e4b8de4 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general.less +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general.less @@ -1,5 +1,12 @@ .sdc-workspace-general-step { display: flex; + flex-direction: column; + .w-sdc-main-container-body-content-action-buttons{ + display: flex; + justify-content: flex-end; + align-items: center; + margin-bottom: 10px; + } .w-sdc-form { padding: 0; flex-grow: 10; @@ -26,10 +33,15 @@ width: 100px; height: 28px; text-align: center; + border-left: solid 1px #cfcfcf; &.disabled { cursor: default; } + + &:hover:not(.disabled) { + background-color: #dbdee2; + } } } @@ -80,8 +92,7 @@ padding: 8px 0 2px 20px; text-align: left; background-color: @tlv_color_t; - position: absolute; - bottom: 0; + position: relative; width: 100%; .meta-data-item-value{ padding-bottom: 6px; @@ -97,19 +108,19 @@ display: flex; align-items: center; .selected-icon-inner-container{ - height: 64px; - width: 64px; + height: 60px; + width: 60px; margin: 0 auto; } .update-component-icon{ position: relative; float: right; cursor: pointer; + z-index: 1; } .selected-icon{ position: relative; top: -20px; - z-index: -1; &.disable{ position: inherit; } @@ -137,7 +148,22 @@ } + .unsaved-file-warning { + border: solid 1px #ffb81c; + padding: 5px 8px; + display: flex; + align-items: center; + color: #ffb81c; + margin-right: 10px; + border-radius: 2px; + .sdc-warning { + margin-right:4px; + } + } + .revert-btn { + margin-left: 10px; + } } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts index 2a7cd3dd65..4b9d314a38 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts @@ -17,121 +17,121 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - -'use strict'; -import * as _ from "lodash"; -import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; -import {ComponentInstance, InstancesInputsOrPropertiesMapData, Resource, PropertyModel, InputModel} from "app/models"; -import {ModalsHandler} from "app/utils"; - -export interface IInputsViewModelScope extends IWorkspaceViewModelScope { - InstanceInputsProperties:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used - vfInstancesList:Array<ComponentInstance>; - component:Resource; - - onArrowPressed():void; - getInputPropertiesForInstance(instanceId:string, instance:ComponentInstance):ng.IPromise<boolean> ; - loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise<boolean> ; - openEditValueModal(input:InputModel):void; - openEditPropertyModal(property:PropertyModel):void; -} - -export class ResourceInputsViewModel { - - static '$inject' = [ - '$scope', - '$q', - 'ModalsHandler' - ]; - - constructor(private $scope:IInputsViewModelScope, private $q:ng.IQService, private ModalsHandler:ModalsHandler) { - this.initScope(); - } - - private initScope = ():void => { - - this.$scope.InstanceInputsProperties = new InstancesInputsOrPropertiesMapData(); - this.$scope.vfInstancesList = this.$scope.component.componentInstances; - - // Need to cast all inputs to InputModel for the search to work - let tmpInputs:Array<InputModel> = new Array<InputModel>(); - _.each(this.$scope.component.inputs, (input):void => { - tmpInputs.push(new InputModel(input)); - }); - this.$scope.component.inputs = tmpInputs; - // This function is not supported for resource - //this.$scope.component.getComponentInputs(); - - /* - * When clicking on instance input in the left or right table, this function will load all properties of the selected input - */ - this.$scope.getInputPropertiesForInstance = (instanceId:string, instance:ComponentInstance):ng.IPromise<boolean> => { - let deferred = this.$q.defer(); - instance.properties = this.$scope.component.componentInstancesProperties[instanceId]; - 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 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.InstanceInputsProperties, (properties:Array<PropertyModel>) => { - _.forEach(properties, (property:PropertyModel) => { - property.isAlreadySelected = true; - }); - }); - - // 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); - }; - - this.$scope.component.createInputsFormInstances(this.$scope.InstanceInputsProperties).then(onSuccess); - }; - - this.$scope.openEditValueModal = (input:InputModel) => { - this.ModalsHandler.openEditInputValueModal(input); - }; - - this.$scope.openEditPropertyModal = (property:PropertyModel):void => { - this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.componentInstancesProperties[property.resourceInstanceUniqueId], false).then(() => { - }); - } - } -} +/*********** DEPRECATED -- replaced by prop assignments */ +// 'use strict'; +// import * as _ from "lodash"; +// import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +// import {ComponentInstance, InstancesInputsOrPropertiesMapData, Resource, PropertyModel, InputModel} from "app/models"; +// import {ModalsHandler} from "app/utils"; + +// export interface IInputsViewModelScope extends IWorkspaceViewModelScope { +// InstanceInputsProperties:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used +// vfInstancesList:Array<ComponentInstance>; +// component:Resource; + +// onArrowPressed():void; +// getInputPropertiesForInstance(instanceId:string, instance:ComponentInstance):ng.IPromise<boolean> ; +// loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise<boolean> ; +// openEditValueModal(input:InputModel):void; +// openEditPropertyModal(property:PropertyModel):void; +// } + +// export class ResourceInputsViewModel { + +// static '$inject' = [ +// '$scope', +// '$q', +// 'ModalsHandler' +// ]; + +// constructor(private $scope:IInputsViewModelScope, private $q:ng.IQService, private ModalsHandler:ModalsHandler) { +// this.initScope(); +// } + +// private initScope = ():void => { + +// this.$scope.InstanceInputsProperties = new InstancesInputsOrPropertiesMapData(); +// this.$scope.vfInstancesList = this.$scope.component.componentInstances; + +// // Need to cast all inputs to InputModel for the search to work +// let tmpInputs:Array<InputModel> = new Array<InputModel>(); +// _.each(this.$scope.component.inputs, (input):void => { +// tmpInputs.push(new InputModel(input)); +// }); +// this.$scope.component.inputs = tmpInputs; +// // This function is not supported for resource +// //this.$scope.component.getComponentInputs(); + +// /* +// * When clicking on instance input in the left or right table, this function will load all properties of the selected input +// */ +// this.$scope.getInputPropertiesForInstance = (instanceId:string, instance:ComponentInstance):ng.IPromise<boolean> => { +// let deferred = this.$q.defer<boolean>(); +// instance.properties = this.$scope.component.componentInstancesProperties[instanceId]; +// 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<boolean>(); + +// 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 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.InstanceInputsProperties, (properties:Array<PropertyModel>) => { +// _.forEach(properties, (property:PropertyModel) => { +// property.isAlreadySelected = true; +// }); +// }); + +// // 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); +// }; + +// this.$scope.component.createInputsFormInstances(this.$scope.InstanceInputsProperties).then(onSuccess); +// }; + +// this.$scope.openEditValueModal = (input:InputModel) => { +// this.ModalsHandler.openEditInputValueModal(input); +// }; + +// this.$scope.openEditPropertyModal = (property:PropertyModel):void => { +// this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.componentInstancesProperties[property.resourceInstanceUniqueId], false).then(() => { +// }); +// } +// } +// }
\ No newline at end of file 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 index bdbc0a4334..5e69f5bed6 100644 --- 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 @@ -17,382 +17,382 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - -'use strict'; -import * as _ from "lodash"; -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.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); - }; -} +/***** DEPRECATED - replaced by prop assignments */ +// 'use strict'; +// import * as _ from "lodash"; +// 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.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<boolean>(); + +// 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<boolean>(); + +// 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<boolean>(); + +// 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<boolean>(); + +// 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/properties/properties-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts index 0360c9c805..b09662d7a2 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts @@ -71,7 +71,7 @@ export class PropertiesViewModel { } private openEditPropertyModal = (property:PropertyModel):void => { - this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties, false).then((updatedProperty:PropertyModel) => { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties, false, 'component', this.$scope.component.uniqueId).then((updatedProperty:PropertyModel) => { //property = updatedProperty; }); }; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts index f63ab1ccbc..6eaae44eb2 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts @@ -93,7 +93,7 @@ export class ReqAndCapabilitiesViewModel { _.forEach(this.$scope.filteredProperties[indexInFilteredProperties], (prop:PropertyModel)=> { prop.readonly = true; }); - this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties[indexInFilteredProperties], false).then(() => { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties[indexInFilteredProperties], false, "component", this.$scope.component.uniqueId).then(() => { }); }; diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts index 9abd7139b7..676a2d38d3 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts @@ -26,7 +26,7 @@ import * as _ from "lodash"; import {IUserProperties, IAppMenu, Resource, Component, Plugin, PluginsConfiguration, PluginDisplayOptions} from "app/models"; import { WorkspaceMode, ComponentFactory, ChangeLifecycleStateHandler, Role, ComponentState, MenuItemGroup, MenuHandler, - MenuItem, ModalsHandler, States, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ResourceType + MenuItem, ModalsHandler, States, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ResourceType, PREVIOUS_CSAR_COMPONENT } from "app/utils"; import { EventListenerService, @@ -36,8 +36,11 @@ import { LeftPaletteLoaderService } from "app/services"; import {FileUploadModel} from "../../directives/file-upload/file-upload"; +import {AutomatedUpgradeService} from "../../ng2/pages/automated-upgrade/automated-upgrade.service"; +import {ComponentServiceNg2} from "../../ng2/services/component-services/component.service"; import {EventBusService} from "../../ng2/services/event-bus.service"; import {PluginsService} from "../../ng2/services/plugins.service"; +import {IDependenciesServerResponse} from "../../ng2/services/responses/dependencies-server-response"; export interface IWorkspaceViewModelScope extends ng.IScope { @@ -70,23 +73,35 @@ export interface IWorkspaceViewModelScope extends ng.IScope { menuComponentTitle:string; progressService:ProgressService; progressMessage:string; + ComponentServiceNg2: ComponentServiceNg2; // leftPanelComponents:Array<Models.Components.Component>; //this is in order to load the left panel once, and not wait long time when moving to composition + unsavedChanges:boolean; + unsavedChangesCallback:Function; + unsavedFile:boolean; + + startProgress(message:string):void; + stopProgress():void; + updateBreadcrumbs(component:Component):void; + updateUnsavedFileFlag(isUnsaved:boolean):void; showChangeStateButton():boolean; getComponent():Component; setComponent(component:Component):void; + setOriginComponent(component:Component):void; onMenuItemPressed(state:string, params:any):ng.IPromise<boolean>; - save():ng.IPromise<boolean>; + create():void; + save():Promise<void>; setValidState(isValid:boolean):void; - revert():void; changeLifecycleState(state:string):void; - enabledTabs():void + handleChangeLifecycleState(state:string, newCsarVersion?:string):void; + disableMenuItems():void; + enableMenuItems():void; isDesigner():boolean; isViewMode():boolean; isEditMode():boolean; isCreateMode():boolean; isDisableMode():boolean; - showFullIcons():boolean; + isGeneralView():boolean; goToBreadcrumbHome():void; onVersionChanged(selectedId:string):void; getLatestVersion():void; @@ -120,6 +135,8 @@ export class WorkspaceViewModel { 'Notification', '$stateParams', 'Sdc.Services.ProgressService', + 'ComponentServiceNg2', + 'AutomatedUpgradeService', 'EventBusService', 'PluginsService' ]; @@ -141,8 +158,12 @@ export class WorkspaceViewModel { private Notification:any, private $stateParams:any, private progressService:ProgressService, + private ComponentServiceNg2:ComponentServiceNg2, + private AutomatedUpgradeService:AutomatedUpgradeService, private eventBusService:EventBusService, private pluginsService:PluginsService) { + + this.initScope(); this.initAfterScope(); @@ -170,22 +191,18 @@ export class WorkspaceViewModel { private initChangeLifecycleStateButtons = ():void => { let state = this.$scope.component.isService() && (Role.OPS == this.role || Role.GOVERNOR == this.role) ? this.$scope.component.distributionStatus : this.$scope.component.lifecycleState; - this.$scope.changeLifecycleStateButtons = this.sdcMenu.roles[this.role].changeLifecycleStateButtons[state]; - }; + this.$scope.changeLifecycleStateButtons = (this.sdcMenu.roles[this.role].changeLifecycleStateButtons[state] || [])[this.$scope.component.componentType.toUpperCase()]; - private isNeedSave = ():boolean => { - return this.$scope.isEditMode() && - this.$state.current.data && this.$state.current.data.unsavedChanges; }; private initLeftPalette = ():void => { - this.LeftPaletteLoaderService.loadLeftPanel(this.$scope.component); + //this.LeftPaletteLoaderService.loadLeftPanel(this.$scope.component); }; private initScope = ():void => { this.$scope.component = this.injectComponent; - this.initLeftPalette(); + //this.initLeftPalette(); this.$scope.menuComponentTitle = this.$scope.component.name; this.$scope.disabledButtons = false; this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); @@ -202,7 +219,10 @@ export class WorkspaceViewModel { this.$scope.isComposition = (this.$state.current.name.indexOf(States.WORKSPACE_COMPOSITION) > -1); this.$scope.isDeployment = this.$state.current.name == States.WORKSPACE_DEPLOYMENT; this.$scope.progressService = this.progressService; - this.$scope.isActiveTopBar = true; + this.$scope.unsavedChanges = false; + + this.EventListenerService.registerObserverCallback(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.setWorkspaceButtonState); + //this.EventListenerService.registerObserverCallback(EVENTS.ON_UPDATE_VSP_FILE, this.updateVspFlag); this.$scope.getComponent = ():Component => { return this.$scope.component; @@ -218,19 +238,82 @@ export class WorkspaceViewModel { this.$scope.component = component; }; + this.$scope.setOriginComponent = (component:Component):void => { + this.$scope.originComponent = component; + } + this.$scope.uploadFileChangedInGeneralTab = ():void => { // In case user select browse file, and in update mode, need to disable submit for testing and checkin buttons. if (this.$scope.isEditMode() && this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType == ResourceType.VF) { - this.$scope.disabledButtons = true; + // NOTE: Commented out the disabling of the workspace buttons on CSAR updating due fix of a bug [417534] + // this.$scope.disabledButtons = true; } }; + this.$scope.archiveComponent = ():void => { + this.$scope.isLoading = true; + const typeComponent = this.$scope.component.componentType; + this.ComponentServiceNg2.archiveComponent(typeComponent, this.$scope.component.uniqueId).subscribe(()=>{ + this.$scope.isLoading = false; + if(this.$state.params.previousState){ + switch(this.$state.params.previousState){ + case 'catalog': + case 'dashboard': + this.$state.go(this.$state.params.previousState); + break; + default: + break; + } + } + this.$scope.component.archived = true; + this.deleteArchiveCache(); + + this.Notification.success({ + message: this.$scope.component.name + ' ' + this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TITLE") + }); + }, (error) => { this.$scope.isLoading = false; }); + } + + this.$scope.restoreComponent = ():void => { + this.$scope.isLoading = true; + const typeComponent = this.$scope.component.componentType; + this.ComponentServiceNg2.restoreComponent(typeComponent, this.$scope.component.uniqueId).subscribe(()=>{ + this.$scope.isLoading = false; + this.Notification.success({ + message: this.$scope.component.name + ' ' + this.$filter('translate')("RESTORE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("RESTORE_SUCCESS_MESSAGE_TITLE") + }); + }); + this.$scope.component.archived = false; + this.deleteArchiveCache(); + } + + this.$scope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams) => { + if(this.$scope.isEditMode()){ + if (fromParams.id == toParams.id && this.$state.current.data && this.$state.current.data.unsavedChanges) { + event.preventDefault(); + if(this.$scope.isValidForm){ + this.$scope.save().then(() => { + this.$scope.onMenuItemPressed(toState.name, toParams); + }, ()=> { + console.error("Save failed, unable to navigate to " + toState.name); + }) + } else { + console.error("Form is invalid, unable to navigate to " + toState.name); + } + } + } + + }); + this.$scope.$on('$stateChangeSuccess', (event, toState) => { this.$scope.updateSelectedMenuItem(this.$state.current.name); }); this.$scope.onMenuItemPressed = (state:string, params:any):ng.IPromise<boolean> => { - let deferred = this.$q.defer(); + + let deferred:ng.IDeferred<boolean> = this.$q.defer(); let goToState = ():void => { this.$state.go(state, Object.assign({ id: this.$scope.component.uniqueId, @@ -239,14 +322,8 @@ export class WorkspaceViewModel { }, params)); deferred.resolve(true); }; - if (this.isNeedSave()) { - if (this.$scope.isValidForm) { - this.$scope.save().then(goToState); - } else { - console.log('form is not valid'); - deferred.reject(false); - } - } else if (this.$scope.isEditMode() && //this is a workaround for amdocs - we need to get the artifact in order to avoid saving the vf when moving from their tabs + + if (this.$scope.isEditMode() && //this is a workaround for amdocs - we need to get the artifact in order to avoid saving the vf when moving from their tabs (this.$state.current.name === States.WORKSPACE_MANAGEMENT_WORKFLOW || this.$state.current.name === States.WORKSPACE_NETWORK_CALL_FLOW)) { let onGetSuccess = (component:Component) => { this.$scope.isLoading = false; @@ -271,7 +348,7 @@ export class WorkspaceViewModel { }; this.$scope.onVersionChanged = (selectedId:string):void => { - if (this.$state.current.data && this.$state.current.data.unsavedChanges) { + if (this.$scope.isGeneralView() && this.$state.current.data.unsavedChanges) { this.$scope.changeVersion.selectedVersion = _.find(this.$scope.versionsList, (versionObj)=> { return versionObj.versionId === this.$scope.component.uniqueId; }); @@ -298,37 +375,35 @@ export class WorkspaceViewModel { this.$scope.onVersionChanged(_.first(this.$scope.versionsList).versionId); }; - this.$scope.save = (state?:string):ng.IPromise<boolean> => { - this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_CLICK); - - this.progressService.initCreateComponentProgress(this.$scope.component.uniqueId); + this.$scope.create = () => { + + this.$scope.startProgress("Creating Asset..."); + _.first(this.$scope.leftBarTabs.menuItems).isDisabled = true;//disabled click on general tab (DE246274) - let deferred = this.$q.defer(); - let modalInstance:ng.ui.bootstrap.IModalServiceInstance; + // In case we import CSAR. Notify user that import VF will take long time (the create is performed in the background). + if (this.$scope.component.isResource() && (<Resource>this.$scope.component).csarUUID) { + this.Notification.info({ + message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION"), + title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE") + }); + } let onFailed = () => { + this.$scope.stopProgress(); + this.$scope.isLoading = false; // stop the progress. _.first(this.$scope.leftBarTabs.menuItems).isDisabled = false;//enabled click on general tab (DE246274) this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_ERROR); - this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + let modalInstance:ng.ui.bootstrap.IModalServiceInstance; modalInstance && modalInstance.close(); // Close the modal in case it is opened. this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);// for fix DE246217 - this.$scope.isCreateProgress = false; - this.$scope.isLoading = false; // stop the progress. this.$scope.setValidState(true); // Set the form valid (if sent form is valid, the error from server). - if (!this.$scope.isCreateMode()) { - this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); // Set the component back to the original. - this.enableMenuItems(); // Enable the menu items (left tabs), so user can press on them. - this.$scope.disabledButtons = false; // Enable "submit for testing" & checking buttons. - } - - deferred.reject(false); }; let onSuccessCreate = (component:Component) => { + this.$scope.stopProgress(); this.showSuccessNotificationMessage(); - this.progressService.deleteProgressValue(this.$scope.component.uniqueId); // Update the components list for breadcrumbs this.components.unshift(component); @@ -337,99 +412,73 @@ export class WorkspaceViewModel { id: component.uniqueId, type: component.componentType.toLowerCase(), components: this.components - }, { inherit: false }); - - deferred.resolve(true); + }, {inherit: false}); }; - let onSuccessUpdate = (component:Component) => { - this.$scope.isCreateProgress = false; - this.$scope.disabledButtons = false; - this.showSuccessNotificationMessage(); - this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccessCreate, onFailed); - // Stop the circle loader. - this.$scope.isLoading = false; - - component.tags = _.reject(component.tags, (item)=> { - return item === component.name - }); - // Update the components list for breadcrumbs - const bcIdx = this.MenuHandler.findBreadcrumbComponentIndex(this.components, component); - if (bcIdx !== -1) { - this.components[bcIdx] = component; - this.initBreadcrumbs(); // re-calculate breadcrumbs - } + }; - // Update the component - this.$scope.component = component; - this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); + this.$scope.save = ():Promise<void> => { + + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_CLICK); - // Enable left tags - this.$scope.enabledTabs(); + this.$scope.startProgress("Updating Asset..."); + this.$scope.disableMenuItems(); - if (this.$state.current.data) { - this.$state.current.data.unsavedChanges = false; + return new Promise<void>((resolve, reject) => { + let stopProgressAndEnableUI = () => { + this.$scope.disabledButtons = false; + this.$scope.isLoading = false; + this.$scope.enableMenuItems(); + this.$scope.stopProgress(); } - deferred.resolve(true); - }; - - if (this.$scope.isCreateMode()) { - this.$scope.progressMessage = "Creating Asset..."; - // CREATE MODE - this.$scope.isCreateProgress = true; + let onFailed = () => { + stopProgressAndEnableUI(); + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_ERROR); - _.first(this.$scope.leftBarTabs.menuItems).isDisabled = true;//disabled click on general tab (DE246274) + reject(); + }; - // Start creating the component - this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccessCreate, onFailed); + let onSuccessUpdate = (component:Component) => { + stopProgressAndEnableUI(); + this.showSuccessNotificationMessage(); - // In case we import CSAR. Notify user that import VF will take long time (the create is performed in the background). - if (this.$scope.component.isResource() && (<Resource>this.$scope.component).csarUUID) { - this.Notification.info({ - message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION"), - title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE") + component.tags = _.reject(component.tags, (item)=> { + return item === component.name }); - } - } else { - // UPDATE MODE - this.$scope.isCreateProgress = true; - this.$scope.progressMessage = "Updating Asset..."; - this.disableMenuItems(); + this.$scope.updateBreadcrumbs(component); + + //update the component + this.$scope.setComponent(component); + this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); - // Work around to change the csar version - if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { - (<Resource>this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); - this.cacheService.remove(CHANGE_COMPONENT_CSAR_VERSION_FLAG); - } + if (this.cacheService.contains(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { + this.cacheService.remove(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + } + if (this.cacheService.contains(PREVIOUS_CSAR_COMPONENT)){ + this.cacheService.remove(PREVIOUS_CSAR_COMPONENT); + } + + //clear edit flags + this.$state.current.data.unsavedChanges = false; + this.$scope.unsavedFile = false; + resolve(); + }; this.$scope.component.updateComponent().then(onSuccessUpdate, onFailed); - } - return deferred.promise; - }; + }); - this.$scope.revert = ():void => { - //in state of import file leave the file in place - if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile) { - let tempFile:FileUploadModel = (<Resource>this.$scope.component).importedFile; - this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); - (<Resource>this.$scope.component).importedFile = tempFile; - } else { - this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); - } - this.EventListenerService.notifyObservers(EVENTS.ON_REVERT); }; this.$scope.changeLifecycleState = (state:string):void => { - if (this.isNeedSave() && state !== 'deleteVersion') { - this.$scope.save().then(() => { - changeLifecycleState(state); - }) + if (this.$scope.isGeneralView() && state !== 'deleteVersion') { + this.EventListenerService.notifyObservers(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE, state); } else { - changeLifecycleState(state); + this.$scope.handleChangeLifecycleState(state); } }; @@ -440,7 +489,7 @@ export class WorkspaceViewModel { this.$state.go('dashboard'); }; - let changeLifecycleState = (state:string) => { + this.$scope.handleChangeLifecycleState = (state:string, newCsarVersion?:string) => { if ('monitor' === state) { this.$state.go('workspace.distribution'); return; @@ -466,9 +515,9 @@ export class WorkspaceViewModel { // only checkOut get the full component from server // this.$scope.component = component; // Work around to change the csar version - if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { - (<Resource>this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); - } + if(newCsarVersion) { + this.cacheService.set(CHANGE_COMPONENT_CSAR_VERSION_FLAG, newCsarVersion); + } //when checking out a minor version uuid remains const bcIdx = _.findIndex(this.components, (item) => { @@ -480,20 +529,17 @@ export class WorkspaceViewModel { //when checking out a major(certified) version this.components.unshift(component); } - // this.$state.go(this.$state.current.name, { - // id: component.uniqueId, - // type: component.componentType.toLowerCase(), - // components: this.components - // }); this.$scope.mode = this.initViewMode(); this.initChangeLifecycleStateButtons(); this.initVersionObject(); this.$scope.isLoading = false; this.EventListenerService.notifyObservers(EVENTS.ON_CHECKOUT, component); + this.Notification.success({ message: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TEXT"), title: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TITLE") }); + }); break; case 'lifecycleState/CHECKIN': @@ -528,11 +574,9 @@ export class WorkspaceViewModel { }); break; case 'lifecycleState/certify': - defaultActionAfterChangeLifecycleState(); - this.Notification.success({ - message: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TEXT"), - title: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TITLE") - }); + + this.$scope.handleCertification(component); + break; //DE203504 Bug Fix Start case 'lifecycleState/startCertification': @@ -588,11 +632,7 @@ export class WorkspaceViewModel { this.ChangeLifecycleStateHandler.changeLifecycleState(this.$scope.component, data, this.$scope, onSuccess); }; - this.$scope.enabledTabs = ():void => { - this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { - item.isDisabled = false; - }); - }; + this.$scope.isViewMode = ():boolean => { return this.$scope.mode === WorkspaceMode.VIEW; @@ -606,7 +646,7 @@ export class WorkspaceViewModel { return this.$scope.mode === WorkspaceMode.VIEW && this.$scope.component.lifecycleState === ComponentState.NOT_CERTIFIED_CHECKIN; }; - this.$scope.showFullIcons = ():boolean => { + this.$scope.isGeneralView = ():boolean => { //we show revert and save icons only in general view return this.$state.current.name === States.WORKSPACE_GENERAL; }; @@ -697,15 +737,96 @@ export class WorkspaceViewModel { }; this.$scope.reload = (component:Component):void => { - this.$state.go(this.$state.current.name,{id:component.uniqueId},{reload:true}); + this.$state.go(this.$state.current.name, {id: component.uniqueId}, {reload: true}); }; - this.$scope.$on('setWorkspaceTopBarActive', (event:ng.IAngularEvent, isActive:boolean) => { - this.$scope.isActiveTopBar = isActive; + this.$scope.$on('$destroy', () => { + this.EventListenerService.unRegisterObserver(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES); }); + + this.$scope.openAutomatedUpgradeModal = ():void => { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getDependencies(this.$scope.component.componentType, this.$scope.component.uniqueId).subscribe((response:Array<IDependenciesServerResponse>)=> { + this.$scope.isLoading = false; + this.AutomatedUpgradeService.openAutomatedUpgradeModal(response, this.$scope.component, false); + }); + } + + this.$scope.handleCertification = (certifyComponent): void => { + if (this.$scope.component.getComponentSubType() === ResourceType.VF) { + this.ComponentServiceNg2.getDependencies(this.$scope.component.componentType, this.$scope.component.uniqueId).subscribe((response:Array<IDependenciesServerResponse>) => { + this.$scope.isLoading = false; + + let isUpgradeNeeded = _.filter(response, (componentToUpgrade:IDependenciesServerResponse) => { + return componentToUpgrade.dependencies && componentToUpgrade.dependencies.length > 0; + }); + if(isUpgradeNeeded.length === 0) { + this.onSuccessWithoutUpgradeNeeded(); + return; + } + this.refreshDataAfterChangeLifecycleState(certifyComponent); + this.AutomatedUpgradeService.openAutomatedUpgradeModal(response, this.$scope.component, true); + }); + } else { + this.onSuccessWithoutUpgradeNeeded(); + } + } + + this.$scope.disableMenuItems = () => { + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.isDisabled = (States.WORKSPACE_GENERAL != item.state); + }); + } + + this.$scope.enableMenuItems = () => { + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.isDisabled = false; + }); + } + + + this.$scope.startProgress = (message:string):void => { + this.progressService.initCreateComponentProgress(this.$scope.component.uniqueId); + this.$scope.isCreateProgress = true; + this.$scope.progressMessage = message; + }; + + this.$scope.stopProgress = ():void => { + this.$scope.isCreateProgress = false; + this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + } + + this.$scope.updateBreadcrumbs = (component:Component):void => { + // Update the components list for breadcrumbs + const bcIdx = this.MenuHandler.findBreadcrumbComponentIndex(this.components, component); + if (bcIdx !== -1) { + this.components[bcIdx] = component; + this.initBreadcrumbs(); // re-calculate breadcrumbs + } + } + + this.$scope.updateUnsavedFileFlag = (isUnsaved:boolean) => { + this.$scope.unsavedFile = isUnsaved; + } }; + private onSuccessWithoutUpgradeNeeded = ():void => { + this.$scope.isLoading = false; + this.Notification.success({ + message: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TITLE") + }); + this.$state.go('dashboard'); + } + private refreshDataAfterChangeLifecycleState = (component:Component):void => { + this.$scope.isLoading = false; + this.$scope.mode = this.initViewMode(); + this.initChangeLifecycleStateButtons(); + this.initVersionObject(); + this.EventListenerService.notifyObservers(EVENTS.ON_LIFECYCLE_CHANGE, component); + } + private initAfterScope = ():void => { // In case user select csar from the onboarding modal, need to disable checkout and submit for testing. if (this.$state.params['disableButtons'] === true) { @@ -743,6 +864,10 @@ export class WorkspaceViewModel { return tempMenuItems; }; + private deleteArchiveCache = () => { + this.cacheService.remove("archiveComponents"); //delete the cache to ensure the archive is reloaded from server + }; + private initBreadcrumbs = () => { this.components = this.cacheService.get('breadcrumbsComponents'); let breadcrumbsComponentsLvl = this.MenuHandler.generateBreadcrumbsModelFromComponents(this.components, this.$scope.component); @@ -787,32 +912,16 @@ export class WorkspaceViewModel { } menuItem.callback = () => this.$scope[menuItem.action](menuItem.state, menuItem.params); menuItem.isDisabled = (inCreateMode && States.WORKSPACE_GENERAL != menuItem.state) || - (States.WORKSPACE_DEPLOYMENT === menuItem.state && this.$scope.component.groups && this.$scope.component.groups.length === 0 && this.$scope.component.isResource()); + (States.WORKSPACE_DEPLOYMENT === menuItem.state && this.$scope.component.modules && this.$scope.component.modules.length === 0 && this.$scope.component.isResource()); return menuItem; }); if (this.cacheService.get('breadcrumbsComponents')) { this.initBreadcrumbs(); - } else { - let onSuccess = (components:Array<Component>) => { - this.cacheService.set('breadcrumbsComponents', components); - this.initBreadcrumbs(); - }; - this.EntityService.getCatalog().then(onSuccess); //getAllComponents() doesnt return components from catalog } } - private disableMenuItems() { - this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { - item.isDisabled = (States.WORKSPACE_GENERAL != item.state); - }); - } - private enableMenuItems() { - this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { - item.isDisabled = false; - }); - } private showSuccessNotificationMessage = ():void => { this.Notification.success({ @@ -821,4 +930,9 @@ export class WorkspaceViewModel { }); }; + private setWorkspaceButtonState = (newState:boolean, callback?:Function) => { + this.$scope.unsavedChanges = newState; + this.$scope.unsavedChangesCallback = callback; + } + } diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view.html b/catalog-ui/src/app/view-models/workspace/workspace-view.html index 1452754024..f6fed6a41d 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace-view.html +++ b/catalog-ui/src/app/view-models/workspace/workspace-view.html @@ -17,9 +17,9 @@ <div class="version-container"> <span data-ng-if="!isCreateMode() && !component.isLatestVersion()" class="not-latest"></span> - <select class="version-selector" data-ng-if="!isCreateMode()" data-tests-id="versionHeader" data-ng-model="changeVersion.selectedVersion" data-ng-class="{'disabled': !isActiveTopBar}" - ng-options="'V'+version.versionNumber for version in versionsList" data-ng-change="onVersionChanged(changeVersion.selectedVersion.versionId)"> - </select> + <select class="version-selector" data-ng-if="!isCreateMode()" data-tests-id="versionHeader" data-ng-model="changeVersion.selectedVersion" data-ng-class="{'disabled': unsavedChanges}" + ng-options="'V'+version.versionNumber for version in versionsList" data-ng-change="onVersionChanged(changeVersion.selectedVersion.versionId)"> + </select> </div> <div class="lifecycle-state"> @@ -27,6 +27,9 @@ <span class="lifecycle-state-text" data-tests-id="formlifecyclestate">{{getStatus()}}</span> </div> + <div class="archive-state-label" ng-if="component.archived"> + <div class="sprite-new archive-label" ></div> + </div> <div class="progress-container" > <top-progress class="general-view-top-progress" progress-value="progressService.getProgressValue(component.uniqueId)" progress-message="progressMessage"></top-progress> @@ -34,47 +37,64 @@ <div class="sdc-workspace-top-bar-buttons"> - <span ng-if="!isCreateMode() && !component.isLatestVersion() && !showChangeStateButton()" [disabled]="!isActiveTopBar">Switch to the <a ng-click="getLatestVersion()">latest version</a></span> + <span ng-if="!isCreateMode() && !component.isLatestVersion() && !showChangeStateButton()" [disabled]="unsavedChanges">Switch to the <a ng-click="getLatestVersion()">latest version</a></span> - <button ng-if="isDesigner() && !isCreateMode()" - data-ng-class="{'disabled' :!isValidForm || isDisableMode() || isViewMode() || !isActiveTopBar}" - ng-click="save()" + <button ng-if="isDesigner() && !isCreateMode() && component.lifecycleState === 'CERTIFIED' && (component.isService() || component.getComponentSubType() === 'VF')" + ng-click="openAutomatedUpgradeModal()" class="tlv-btn blue" - data-tests-id="create/save" - data-ng-show="showFullIcons()" - sdc-smart-tooltip="">Update</button> + data-ng-class="{'disabled' : component.archived}" + data-tests-id="open-upgrade-vsp-popup" + sdc-smart-tooltip="" prevent-double-click>{{component.isResource() ? 'Upgrade Services' : 'Update Services'}}</button> + <button ng-repeat="(key,button) in changeLifecycleStateButtons" ng-click="changeLifecycleState(key)" ng-if="showChangeStateButton() && key != 'deleteVersion'" - data-ng-disabled="isCreateMode() || button.disabled || disabledButtons || !isValidForm || !isActiveTopBar" + data-ng-disabled="isCreateMode() || button.disabled || disabledButtons || !isValidForm || unsavedChanges || component.archived" class="change-lifecycle-state-btn tlv-btn" ng-class="$first ? 'outline green' : 'grey'" - data-tests-id="{{button.text | testsId}}"> + data-tests-id="{{button.text | testsId}}" prevent-double-click> {{button.text}} + </button> - <button ng-if="!isViewMode() && isCreateMode()" data-ng-disabled="!isValidForm || isDisableMode() || isLoading || !isActiveTopBar" ng-click="save()" class="tlv-btn outline green" data-tests-id="create/save">Create</button> - <span data-ng-if="isDesigner() && !isCreateMode() && component.lifecycleState === 'NOT_CERTIFIED_CHECKOUT'" sdc-smart-tooltip="" - data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode() || !isActiveTopBar}" ng-click="changeLifecycleState('deleteVersion')" - class="sprite-new delete-btn" data-tests-id="delete_version" sdc-smart-tooltip="">Delete</span> + <button ng-if="!isCreateMode() && component.archived" + data-ng-class="{'disabled' :!isDesigner() || !component.isLatestVersion()}" + ng-click="restoreComponent()" + class="tlv-btn blue" + data-tests-id="restore-component-button" + prevent-double-click>Restore</button> + + <button ng-if="!isViewMode() && isCreateMode()" data-ng-disabled="!isValidForm || isDisableMode() || isLoading || unsavedChanges" ng-click="create()" class="tlv-btn outline green" data-tests-id="create/save">Create</button> + + <span data-ng-if="isDesigner() && !isCreateMode() && component.lifecycleState === 'NOT_CERTIFIED_CHECKOUT' && !component.archived" sdc-smart-tooltip="" + data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode() || unsavedChanges}" ng-click="changeLifecycleState('deleteVersion')" + class="sprite-new delete-btn" data-tests-id="delete_version" sdc-smart-tooltip="Delete" prevent-double-click>Delete</span> + + <span data-ng-click = "archiveComponent()" + ng-model-options="{ debounce: 300 }" + data-ng-class="{'disabled' : !component.isLatestVersion()}" + data-ng-if = "isDesigner() && component.lifecycleState !== 'NOT_CERTIFIED_CHECKOUT' && !isCreateMode() && !component.archived" + data-tests-id="archive-component-button" + class="sprite-new archive-btn" sdc-smart-tooltip="Archive" prevent-double-click>Archive</span> + - <span data-ng-if="isDesigner()" data-ng-class="{'disabled' :isDisableMode() || isViewMode() || !isActiveTopBar}" ng-click="revert()" class="sprite-new revert-btn" data-tests-id="revert" - data-ng-show="showFullIcons()" sdc-smart-tooltip="">Revert</span> <span class="delimiter"></span> - <span class="sprite-new x-btn" data-ng-click="goToBreadcrumbHome()" sdc-smart-tooltip="">Close</span> + <span class="sprite-new x-btn" data-ng-click="goToBreadcrumbHome()" data-ng-class="{'disabled' : unsavedChanges}" sdc-smart-tooltip="">Close</span> </div> </div> <div class="w-sdc-main-container-body-content-wrapper"> - <div class="tab-title" data-ng-if="!isComposition && !isDeployment && !isPlugins"> - {{getTabTitle()}} + <div class="w-sdc-main-container-body-content-header"> + <div class="tab-title" data-ng-if="!isComposition && !isDeployment && !isPlugins"> + {{getTabTitle()}} + </div> </div> <div class="w-sdc-main-container-body-content" data-ng-class="{'third-party':thirdParty}" data-ui-view></div> </div> </div> </div> - <top-nav [hide-search]="true" [menu-model]="breadcrumbsModel" [version]="version"></top-nav> + <top-nav [hide-search]="true" [menu-model]="breadcrumbsModel" [version]="version" [unsaved-changes]="unsavedChanges" [unsaved-changes-callback]="unsavedChangesCallback"></top-nav> </div> diff --git a/catalog-ui/src/app/view-models/workspace/workspace.less b/catalog-ui/src/app/view-models/workspace/workspace.less index 0cc30ece20..b9956c655b 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace.less +++ b/catalog-ui/src/app/view-models/workspace/workspace.less @@ -128,7 +128,7 @@ .delimiter { height: 32px; width: 1px; - background-color: #959595; + background-color: @main_color_o; display: inline-block; vertical-align: middle; margin-right: 20px; @@ -136,6 +136,16 @@ } + .archive-state-label { + padding: 7px 0 0 10px; + margin: 2px 0 7px 10px; + border-left: 1px solid @main_color_o; + line-height: 15px; + font-family: @font-opensans-bold; + color: @main_color_m; + font-size:12px; + } + .lifecycle-state { padding: 7px 0 0 10px; margin: 2px 0 7px 10px; @@ -191,7 +201,26 @@ } .w-sdc-main-container-body-content-wrapper { overflow: auto; - height: calc(~'100% - @{action_nav_height}') + height: calc(~'100% - @{action_nav_height}'); + .w-sdc-main-container-body-content-header { + display: flex; + .tab-title { + flex-grow: 1; + } + .w-sdc-main-container-body-content-action-buttons { + margin:72px 100px 0 0; + > * { + display: inline-block; + vertical-align: middle; + } + .revert-btn { + text-indent: 100%; + } + .save-btn { + text-indent: 100%; + } + } + } } } } |