From 451a3400b76511393c62a444f588a4ed15f4a549 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Sun, 19 Feb 2017 10:28:42 +0200 Subject: Initial OpenECOMP SDC commit Change-Id: I0924d5a6ae9cdc161ae17c68d3689a30d10f407b Signed-off-by: Michael Lando --- .../app/scripts/view-models/wizard/ReadMe.txt | 54 +++ .../artifact-deployment-step.html | 137 +++++++ .../artifact-deployment-step.less | 107 ++++++ .../artifact-deployment-step.ts | 228 ++++++++++++ .../artifact-form-step-view-model.ts | 304 ++++++++++++++++ .../artifact-form-step-view.html | 147 ++++++++ .../artifact-form-step/artifact-form-step.less | 45 +++ .../artifact-information-step.html | 48 +++ .../artifact-information-step.less | 50 +++ .../artifact-information-step.ts | 168 +++++++++ .../wizard/general-step/general-step.html | 270 ++++++++++++++ .../wizard/general-step/general-step.less | 34 ++ .../wizard/general-step/general-step.ts | 381 ++++++++++++++++++++ .../wizard/hierarchy-step/hierarchy-step.html | 40 +++ .../wizard/hierarchy-step/hierarchy-step.less | 125 +++++++ .../wizard/hierarchy-step/hierarchy-step.ts | 149 ++++++++ .../view-models/wizard/icons-step/icons-step.html | 26 ++ .../view-models/wizard/icons-step/icons-step.less | 55 +++ .../view-models/wizard/icons-step/icons-step.ts | 150 ++++++++ .../wizard/properties-step/properties-step.html | 57 +++ .../wizard/properties-step/properties-step.less | 55 +++ .../wizard/properties-step/properties-step.ts | 123 +++++++ .../property-form-view-model-tests.ts | 163 +++++++++ .../property-form/property-form-view-model.ts | 250 +++++++++++++ .../wizard/property-form/property-form.html | 133 +++++++ .../wizard/property-form/property-form.less | 7 + .../view-models/wizard/wizard-creation-base.html | 12 + .../view-models/wizard/wizard-creation-base.less | 60 ++++ .../view-models/wizard/wizard-creation-base.ts | 399 +++++++++++++++++++++ .../wizard/wizard-state/create-wizard.ts | 114 ++++++ .../view-models/wizard/wizard-state/edit-wizard.ts | 164 +++++++++ .../wizard/wizard-state/import-wizard.ts | 64 ++++ 32 files changed, 4119 insertions(+) create mode 100644 catalog-ui/app/scripts/view-models/wizard/ReadMe.txt create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts create mode 100644 catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts (limited to 'catalog-ui/app/scripts/view-models/wizard') diff --git a/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt b/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt new file mode 100644 index 0000000000..ca5cfb988a --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt @@ -0,0 +1,54 @@ +import-asset wizard +======================================================== + +What should be done in each wizard step: +---------------------------------------- + +1. Each step scope should extend IAssetCreationStepScope (this way you can call methods on the wizard scope). + Example: + export interface IGeneralStepScope extends IAssetCreationStepScope { + + } + +2. Each step class should implements IAssetCreationStep + Example: export class GeneralStepViewModel implements IAssetCreationStep { + + } + +3. Add the method: public save = (callback:Function):void => {} + The method should perform the save and call: callback(true); in case of success, or callback(false); in case of error. + Example: + var onSuccess:Function = (resourceProperties:Services.IResourceResource) => { + this.$scope.setAngularResourceOfResource(resourceProperties); + var resourceObj = new Sdc.Models.Resource(resourceProperties); + this.$scope.setEntity(resourceObj); + this.$scope.latestEntityName = (resourceProperties.resourceName); + callback(true); + }; + +4. Add the first line after the constructor: this.$scope.registerChild(this); + This will register the current step reference in the wizard. + +5. Each step can get and set angular $resource of resource from the wizard. + // Will be called from each step to get current entity. + this.$scope.getAngularResourceOfResource = ():Services.IResourceResource => { + return this.resourceProperties; + }; + + // Will be called from each step after save to update the resource. + this.$scope.setAngularResourceOfResource = (resourceProperties:Services.IResourceResource):void => { + this.resourceProperties = resourceProperties; + this.fillAssetNameAndType(); + }; + + Note: after success save, set setAngularResourceOfResource in the wizard (see example in step 3). + +6. The wizard needs to know if the step is valid (to know if to show next button), I used the following to update the wizard: + this.$scope.$watch("editForm.$valid", function(newVal, oldVal){ + this.$scope.setValidState(newVal); + }); + + Note: in case there is no save for the step, and the step is always valid, call: this.$scope.setValidState(true); + + + diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html new file mode 100644 index 0000000000..97817d59f2 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html @@ -0,0 +1,137 @@ +
+ +
Add
+ +
+ +
+ +
+
{{header.title}} + +
+
+
+ +
+ + + + +
+
+ +
+ + + {{artifact.artifactDisplayName}} +
+ +
+ {{artifact.artifactType}} +
+
+ {{artifact.timeout? artifact.timeout:''}} +
+ +
+ + +
+
+
+ + + +
+ + + +
+ + + +
+ +
+ + +
+ +
+
+ + ? + + +
+ + + +
+
+
+ + +
+
+ + ? + + +
+ + + +
+
+
+ +
+
+ + + + + + +
+
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less new file mode 100644 index 0000000000..043fba3277 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less @@ -0,0 +1,107 @@ +.artifact-deployment-step { + + .table-container-flex .table .body .data-row + div.item-opened { + padding: 10px 40px 10px 30px; + } + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table { + height:412px; + margin-bottom: 0; + } + + .parameter-description { + .circle(18px, @color_p); + content: '?'; + line-height: 18px; + vertical-align: middle; + margin-left: 5px; + cursor: default; + display: inline-block; + position: absolute; + top: 16px; + } + + .table-container-flex { + + margin-top: 0px; + + .flex-item:nth-child(1) { + flex-grow: 15; + padding: 5px 4px; + .hand; + span.table-arrow { + margin-right: 7px; + + } + } + + .flex-item:nth-child(2) { + padding: 5px 4px; + flex-grow: 6; + } + + .flex-item:nth-child(3) { + padding: 5px 4px; + flex-grow: 9; + } + + .flex-item:nth-child(4) { + padding: 5px 4px; + flex-grow: 3; + padding-top: 10px; + } + } + .w-sdc-form{ + text-align: left; + + .w-sdc-env-params{ + border-top: 1px solid #cdcdcd; + margin: 25px 0 10px 0; + } + + .i-sdc-form-textarea { + border: 1px solid @color_e; + min-height: 60px; + padding: 10px 13px; + width: 100%; + resize: none; + + &:disabled { + .disabled; + } + } + + .w-sdc-form-item { + &.error { + .i-sdc-form-input, + .i-sdc-form-select, + .i-sdc-form-textarea { + border-color: @color_h; + outline: none; + box-sizing: border-box; + } + } + } + + .i-sdc-env-form-label{ + .p_9; + overflow: hidden; + max-width: 245px; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + margin-top: 14px; + + &.required::before { + color: #f33; + content: '*'; + margin-right: 4px; + } + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts new file mode 100644 index 0000000000..80f145b9b1 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts @@ -0,0 +1,228 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + import ArtifactModel = Sdc.Models.ArtifactModel; + + interface IArtifactDeploymentStepViewModelScope extends IWizardCreationStepScope { + tableHeadersList: Array; + reverse: boolean; + sortBy:string; + component: Models.Components.Component; + artifacts: Array; + editForm:ng.IFormController; + isLoading:boolean; + artifactDescriptions:any; + updateInProgress:boolean; + + addOrUpdate(artifact:Models.ArtifactModel): void; + update(artifact:Models.ArtifactModel): void; + delete(artifact:Models.ArtifactModel): void; + sort(sortBy:string): void; + noArtifactsToShow():boolean; + getValidationPattern(validationType:string, parameterType?:string):RegExp; + validateJson(json:string):boolean; + resetValue(parameter:any):void; + } + + export class ArtifactDeploymentStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + '$filter', + '$modal', + '$templateCache', + 'ValidationUtils', + 'ModalsHandler' + ]; + + constructor( + private $scope:IArtifactDeploymentStepViewModelScope, + private $filter:ng.IFilterService, + private $modal:ng.ui.bootstrap.IModalService, + private $templateCache:ng.ITemplateCacheService, + private validationUtils: Sdc.Utils.ValidationUtils, + private ModalsHandler: Utils.ModalsHandler + ){ + this.$scope.registerChild(this); + this.$scope.setValidState(true); + this.initScope(); + } + + private initDescriptions = ():void =>{ + this.$scope.artifactDescriptions = {}; + _.forEach(this.$scope.component.deploymentArtifacts,(artifact:Models.ArtifactModel):void => { + this.$scope.artifactDescriptions[artifact.artifactLabel] = artifact.description; + }); + }; + + + private setArtifact = (artifact:Models.ArtifactModel):void =>{ + if(artifact.heatParameters) { + artifact.heatParameters.forEach((parameter:any):void => { + if (!parameter.currentValue && parameter.defaultValue) { + parameter.currentValue = parameter.defaultValue; + } else if ("" === parameter.currentValue) { + parameter.currentValue = null; + } + }); + } + if(!artifact.description || !this.$scope.getValidationPattern('string').test(artifact.description)){ + artifact.description = this.$scope.artifactDescriptions[artifact.artifactLabel]; + } + }; + + private updateAll = ():void =>{ + let artifacts:Array= []; + _.forEach(this.$scope.component.deploymentArtifacts,(artifact:Models.ArtifactModel): void => { + if(artifact.selected) { + this.setArtifact(artifact); + artifacts.push(artifact); + } + }); + this.$scope.component.updateMultipleArtifacts(artifacts); + }; + + + + private initScope = (): void => { + let self = this; + this.$scope.isLoading = false; + this.$scope.updateInProgress = false; + this.$scope.component = this.$scope.getComponent(); + this.initDescriptions(); + this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + + + this.$scope.tableHeadersList = [ + {title:'Name', property: 'artifactDisplayName'}, + {title:'Type', property: 'artifactType'}, + {title:'Deployment timeout', property: 'timeout'} + ]; + + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => { + return this.validationUtils.getValidationPattern(validationType, parameterType); + }; + + this.$scope.validateJson = (json:string):boolean => { + if(!json){ + return true; + } + return this.validationUtils.validateJson(json); + }; + + + this.$scope.addOrUpdate = (artifact:Models.ArtifactModel): void => { + artifact.artifactGroupType = 'DEPLOYMENT'; + let artifactCopy = new Models.ArtifactModel(artifact); + this.ModalsHandler.openWizardArtifactModal(artifactCopy, this.$scope.component).then(() => { + this.$scope.artifactDescriptions[artifactCopy.artifactLabel]= artifactCopy.description; + this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + }) + }; + + this.$scope.noArtifactsToShow = ():boolean =>{ + return !_.some(this.$scope.artifacts, 'esId'); + }; + + this.$scope.resetValue = (parameter:any):void => { + if(!parameter.currentValue && parameter.defaultValue){ + parameter.currentValue = parameter.defaultValue; + } + else if('boolean'==parameter.type){ + parameter.currentValue = parameter.currentValue.toUpperCase(); + } + }; + + + this.$scope.$watch('editForm.$valid', ():void => { + if(this.$scope.editForm) { + this.$scope.setValidState(this.$scope.editForm.$valid); + } + }); + + this.$scope.update = (artifact:Models.ArtifactModel):void =>{ + if(false == this.$scope.isLoading) { + if(artifact.selected) { + this.$scope.isLoading = true; + this.$scope.updateInProgress = true; + let onSuccess = (responseArtifact:Models.ArtifactModel):void => { + this.$scope.artifactDescriptions[responseArtifact.artifactLabel] = responseArtifact.description; + this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + this.$scope.isLoading = false; + this.$scope.updateInProgress = false; + artifact.selected = !artifact.selected; + }; + + let onFailed = (error:any):void => { + console.log('Delete artifact returned error:', error); + this.$scope.isLoading = false; + this.$scope.updateInProgress = false; + artifact.selected = !artifact.selected; + }; + + this.setArtifact(artifact); + this.$scope.component.addOrUpdateArtifact(artifact).then(onSuccess, onFailed); + } else { + artifact.selected = !artifact.selected; + } + } + }; + + this.$scope.delete = (artifact:Models.ArtifactModel):void => { + let onOk = ():void => { + this.$scope.isLoading = true; + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + }; + + let onFailed = (error:any):void => { + this.$scope.isLoading = false; + console.log('Delete artifact returned error:', error); + }; + + this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed); + }; + + let title:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE"); + let message:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + }; + + public save = (callback:Function):void => { + this.updateAll(); + this.$scope.setComponent(this.$scope.component); + callback(true); + }; + + public back = (callback:Function):void => { + this.$scope.setComponent(this.$scope.component); + callback(true); + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts new file mode 100644 index 0000000000..459729c179 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts @@ -0,0 +1,304 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + import Resource = Sdc.Models.Components.Resource; + + export interface IEditArtifactStepModel { + artifactResource: Models.ArtifactModel; + artifactTypes: Array; + artifactsFormList: any; + artifactFile:any; + } + + export interface IArtifactResourceFormStepViewModelScope extends ng.IScope { + editForm:ng.IFormController; + forms:any; + footerButtons: Array; + isNew: boolean; + isPlaceHolderArtifact:boolean; + isLoading: boolean; + validationPattern: RegExp; + urlValidationPattern: RegExp; + labelValidationPattern: RegExp; + integerValidationPattern: RegExp; + commentValidationPattern: RegExp; + artifactType: string; + artifactGroupType:string; + editArtifactResourceModel: IEditArtifactStepModel; + defaultHeatTimeout: number; + validExtensions: any; + originalArtifactName: string; + modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance; + selectedArtifact:string; + + fileExtensions():string; + save(): void; + close(): void; + changeArtifact(selectedArtifact:string):void; + getOptions(): Array; + removeInputLabel(): void; + fileUploadRequired():string; + isDeploymentHeat():boolean; + setDefaultTimeout():void; + getFormTitle():string; + } + + export class ArtifactResourceFormStepViewModel { + + static '$inject' = [ + '$scope', + '$modalInstance', + 'artifact', + 'component', + 'Sdc.Services.CacheService', + 'ValidationPattern', + 'UrlValidationPattern', + 'LabelValidationPattern', + 'IntegerValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'ArtifactsUtils', + '$state', + '$modal', + '$templateCache' + ]; + + private formState:Utils.Constants.FormState; + private entityId:string; + + constructor(private $scope:IArtifactResourceFormStepViewModelScope, + private $modalInstance:ng.ui.bootstrap.IModalServiceInstance, + private artifact:Models.ArtifactModel, + private component:Models.Components.Component, + private cacheService:Services.CacheService, + private ValidationPattern:RegExp, + private UrlValidationPattern:RegExp, + private LabelValidationPattern:RegExp, + private IntegerValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:Sdc.Utils.ValidationUtils, + private ArtifactsUtils:Sdc.Utils.ArtifactsUtils, + private $state:any, + private $modal:ng.ui.bootstrap.IModalService, + private $templateCache:ng.ITemplateCacheService) { + + + this.entityId = this.component.uniqueId; + this.formState = angular.isDefined(artifact.artifactLabel) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE; + + this.initScope(); + this.initEditArtifactResourceModel(); + this.initComponent(); + this.initArtifactTypes(); + } + + private initEditArtifactResourceModel = ():void => { + this.$scope.editArtifactResourceModel = { + artifactResource: null, + artifactTypes: null, + artifactsFormList: {}, + artifactFile: {} + } + }; + + private initComponent = ():void => { + this.$scope.editArtifactResourceModel.artifactResource = this.artifact; + this.$scope.originalArtifactName = this.artifact.artifactName; + let artifacts:any = Utils.Constants.ArtifactGroupType.INFORMATION === this.artifact.artifactGroupType? + this.component.artifacts : this.component.deploymentArtifacts; + this.$scope.editArtifactResourceModel.artifactsFormList = _.pick(artifacts, (artifact:Models.ArtifactModel)=> { + return artifact.artifactLabel && !artifact.esId; + }); + this.$scope.editArtifactResourceModel.artifactFile.filename= this.artifact.artifactName?this.artifact.artifactName:''; + + if(this.artifact.artifactLabel){//this is edit mode + this.$scope.editArtifactResourceModel.artifactsFormList[this.artifact.artifactLabel]= this.artifact; + this.$scope.selectedArtifact = this.artifact.artifactDisplayName; + } + }; + + private initArtifactTypes = ():void => { + let artifactTypes:any = this.cacheService.get('UIConfiguration'); + + if (Utils.Constants.ArtifactGroupType.INFORMATION === this.artifact.artifactGroupType) { + this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> { + return element.name; + }); + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) || + _.has(Utils.Constants.ArtifactType.TOSCA, item); + }) + }else if(Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType) { + + this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts; + if(this.$scope.validExtensions) { + this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions); + } + this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout; + + if(!this.$scope.isPlaceHolderArtifact) { + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return Utils.Constants.ArtifactType.HEAT == item.substring(0,4) || + _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item); + }) + } + + if(this.component.isResource() && (this.component).isCsarComponent()) { + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => { + return this.ArtifactsUtils.isLicenseType(item); + }) + } + } + }; + + private initScope = ():void => { + + this.$scope.modalInstanceArtifact = this.$modalInstance; + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.urlValidationPattern = this.UrlValidationPattern; + this.$scope.labelValidationPattern = this.LabelValidationPattern; + this.$scope.integerValidationPattern = this.IntegerValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + this.$scope.isLoading = false; + this.$scope.isPlaceHolderArtifact = true; + this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE); + this.$scope.artifactGroupType = this.artifact.artifactGroupType; + this.$scope.selectedArtifact = '?'; + + this.$scope.fileExtensions = ():string => { + let type:string = this.$scope.editArtifactResourceModel.artifactResource.artifactType; + return type && this.$scope.validExtensions && this.$scope.validExtensions[type].acceptedTypes ? + this.$scope.validExtensions[type].acceptedTypes.join(',') : ""; + }; + + this.$scope.removeInputLabel = ():void => { + this.$scope.isPlaceHolderArtifact = true; + }; + + this.$scope.fileUploadRequired = ():string => { + if (this.$scope.isNew===false){ + return 'false'; // This is edit mode + } else { + return 'true'; + } + }; + + this.$scope.isDeploymentHeat = ():boolean =>{ + return Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType + && this.$scope.editArtifactResourceModel.artifactResource + && this.$scope.editArtifactResourceModel.artifactResource.artifactType + && Utils.Constants.ArtifactType.HEAT === this.$scope.editArtifactResourceModel.artifactResource.artifactType.substring(0,4); + }; + + this.$scope.getFormTitle =(): string =>{ + let title:string = this.artifact.esId? 'Update':'Add'; + if (Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType) { + title += ' Deployment'; + } + title += ' Artifact'; + return title; + }; + + this.$scope.setDefaultTimeout = ():void => { + if(!this.$scope.editArtifactResourceModel.artifactResource.timeout) { + this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout; + } + }; + + this.$scope.changeArtifact = (selectedArtifact:string):void => { + let tempArtifact:Models.ArtifactModel = this.$scope.editArtifactResourceModel.artifactResource; + this.$scope.editArtifactResourceModel.artifactResource = null; + + if (selectedArtifact && selectedArtifact != '' && selectedArtifact != '?') { + let artifactResource = _.find(this.$scope.editArtifactResourceModel.artifactsFormList,{'artifactDisplayName':selectedArtifact}); + this.$scope.editArtifactResourceModel.artifactResource = new Sdc.Models.ArtifactModel(artifactResource); + this.$scope.originalArtifactName = this.$scope.editArtifactResourceModel.artifactResource.artifactName; + this.$scope.isPlaceHolderArtifact = true; + if(this.$scope.isDeploymentHeat()){ + this.$scope.setDefaultTimeout(); + } + } else if (selectedArtifact === "") { + //this.$scope.editArtifactResourceModel.artifactFile = {}; + this.$scope.editArtifactResourceModel.artifactResource = {}; + this.$scope.editArtifactResourceModel.artifactResource.artifactGroupType = this.$scope.artifactGroupType; + this.$scope.isPlaceHolderArtifact = false; + } + + if (_.size(this.$scope.editArtifactResourceModel.artifactFile) && this.$scope.editArtifactResourceModel.artifactResource) { + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename; + } + if(tempArtifact && tempArtifact.description != ''){ + this.$scope.editArtifactResourceModel.artifactResource.description = tempArtifact.description; + } + + this.initArtifactTypes(); + this.$scope.isNew = true; + + }; + + + this.$scope.save = ():void => { + this.$scope.isLoading = true; + this.$scope.editArtifactResourceModel.artifactResource.description = + this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description); + + if (this.$scope.editArtifactResourceModel.artifactFile) { + this.$scope.editArtifactResourceModel.artifactResource.payloadData = this.$scope.editArtifactResourceModel.artifactFile.base64; + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename; + } + + let onFailed = (response) => { + this.$scope.isLoading = false; + console.info('onFaild', response); + this.$scope.editArtifactResourceModel.artifactResource.esId = undefined; + }; + + let onSuccess = () => { + this.$scope.isLoading = false; + this.$scope.originalArtifactName = ""; + this.$modalInstance.close(); + + }; + + this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFailed); + }; + + + this.$scope.close = ():void => { + this.$modalInstance.dismiss(); + }; + + //new form layout for import asset + this.$scope.forms = {}; + this.$scope.footerButtons = [ + {'name': this.artifact.esId ? 'Update' : 'Add', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + } + + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html new file mode 100644 index 0000000000..2643b99c20 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html @@ -0,0 +1,147 @@ + + + + +
+ + +
+ + + +
+ + + + +
+
+ + +
+
+ +
+ + + +
+ +
+
+ +
+ + + + +
+ + + +
+
+ +
+ + + +
+ + + +
+
+
+ + + +
+ +
+ +
+
+ +
+ +
+ + + +
+ + + +
+ +
+
+
+ + + +
+ +
+ + diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less new file mode 100644 index 0000000000..a189c12d62 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less @@ -0,0 +1,45 @@ +.sdc-add-artifact { + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .w-sdc-form { + + .i-sdc-form-textarea{ + min-height: 95px; + } + + .i-sdc-form-item.upload input[type="file"] { + display: none + } + + .w-sdc-icon-cancel { + position: absolute; + right: 7px; + top: 33px; + .sprite; + .sprite.small-x-btn-black; + .hand; + } + } + + .artifact-info { + text-align: left; + color: rgb(140, 140, 140); + font-size: 13px; + margin-top: -40px; + margin-bottom: 5px; + width: 600px; + min-height: initial; + + span { + color: #666666; + padding-left: 4px; + } + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html new file mode 100644 index 0000000000..ea4566561c --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html @@ -0,0 +1,48 @@ +
+
Add
+
+
+
+
{{header.title}} + +
+
+
+
+ +
+ There are no information artifacts to display +
+
+ +
+ + {{artifact.artifactDisplayName}} +
+ +
+ {{artifact.artifactType}} +
+ +
+ + +
+
+
+ + +
+
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less new file mode 100644 index 0000000000..b0600ca483 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less @@ -0,0 +1,50 @@ +.artifact-step { + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height: 412px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 0px; + + .item-opened{ + word-wrap: break-word; + } + + + .flex-item:nth-child(1) { + padding: 5px 4px; + flex-grow: 15; + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) { + padding: 5px 4px; + flex-grow: 6; + } + + .flex-item:nth-child(3) { + padding: 5px 4px; + flex-grow: 3; + padding-top: 10px; + } + + .flex-item:nth-child(4) { + padding: 5px 4px; + flex-grow: 1; + } + + } + +} + + diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts new file mode 100644 index 0000000000..f6e25a53a3 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts @@ -0,0 +1,168 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + import ArtifactModel = Sdc.Models.ArtifactModel; + + export interface IArtifactInformationStepScope extends IWizardCreationScope { + artifacts: Array; + tableHeadersList: Array; + artifactType: string; + isResourceInstance:boolean; + downloadFile:Models.IFileDownload; + isLoading:boolean; + sortBy:string; + reverse:boolean; + component:Models.Components.Component; + + getTitle(): string; + addOrUpdate(artifact:Models.ArtifactModel): void; + delete(artifact:Models.ArtifactModel): void; + download(artifact:Models.ArtifactModel): void; + clickArtifactName(artifact:any):void; + openEditEnvParametersModal(artifactResource: Models.ArtifactModel):void; + sort(sortBy:string): void; + showNoArtifactMessage():boolean; + } + + export class ArtifactInformationStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + '$filter', + '$modal', + '$templateCache', + 'Sdc.Services.SharingService', + '$state', + 'sdcConfig', + 'ModalsHandler' + ]; + + constructor(private $scope:IArtifactInformationStepScope, + private $filter:ng.IFilterService, + private $modal:ng.ui.bootstrap.IModalService, + private $templateCache:ng.ITemplateCacheService, + private sharingService:Sdc.Services.SharingService, + private $state:any, + private sdcConfig:Models.IAppConfigurtaion, + private ModalsHandler: Utils.ModalsHandler) { + this.$scope.registerChild(this); + this.$scope.setValidState(true); + this.initScope(); + } + + + private getMappedObjects():any { + return { + normal: this.$scope.component.artifacts + }; + } + + private initScope = ():void => { + let self = this; + this.$scope.isLoading = false; + this.$scope.sortBy = 'artifactDisplayName'; + this.$scope.reverse = false; + + this.$scope.artifactType = 'normal'; + this.$scope.getTitle = ():string => { + return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts'; + + }; + + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'artifactDisplayName'}, + {title: 'Type', property: 'artifactType'} + ]; + + + this.$scope.component = this.$scope.getComponent(); + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + + + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + + this.$scope.addOrUpdate = (artifact:Models.ArtifactModel):void => { + artifact.artifactGroupType = 'INFORMATIONAL'; + this.ModalsHandler.openWizardArtifactModal(artifact, this.$scope.getComponent()).then(() => { + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + }); + }; + + this.$scope.showNoArtifactMessage = ():boolean => { + let artifacts:any = []; + artifacts = _.filter(this.$scope.artifacts, (artifact:Models.ArtifactModel)=> { + return artifact.esId; + }); + + if (artifacts.length === 0) { + return true; + } + return false; + } + + this.$scope.delete = (artifact:Models.ArtifactModel):void => { + + let onOk = ():void => { + this.$scope.isLoading = true; + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + }; + + let onFailed = (error:any):void => { + console.log('Delete artifact returned error:', error); + this.$scope.isLoading = false; + }; + + this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed); + }; + + let title:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + this.$scope.clickArtifactName = (artifact:any) => { + if ('deployment' !== this.$scope.artifactType || 'HEAT' !== artifact.artifactType || !artifact.esId) { + this.$scope.addOrUpdate(artifact); + } + + }; + + } + + public save = (callback:Function):void => { + this.$scope.setComponent(this.$scope.component); + callback(true); + } + + public back = (callback:Function):void => { + callback(true); + } + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html new file mode 100644 index 0000000000..db975caf47 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html @@ -0,0 +1,270 @@ +
+
+
+
+ + + + +
+ + + + +
+ + +
+ +
+ + +
+ + + +
+ + + + + +
+
+ + + +
+ + + +
+ + + + + +
+
+ + + +
+ + + + +
+ + + +
+
+ + + +
+ + + +
+ +
+
+ + + +
+ + + +
+ + + +
+ +
+ + + + +
+ + + +
+ + + +
+
+ + + + +
+ +
+ + +
+ + + + +
+ +
+
+ + + +
+ + + +
+ + +
+
+ + + +
+ + + +
+ + +
+
+ + + +
+ +
+ +
+ Your resource has been saved +
+ +
+ +
+ +
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less new file mode 100644 index 0000000000..700997a423 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less @@ -0,0 +1,34 @@ +.sdc-wizard-general-step { + + .w-sdc-form { + padding: 0; + + .w-sdc-form-section-container { + text-align: center; + } + + .i-sdc-form-item { + &.upload { + margin-top: 0; + width: auto; + padding: 10px; + } + } + + .template-desc { + border: 1px dashed @border_color_f; + height: 130px; + overflow: hidden; + padding: 10px 6px 6px 6px; + margin-top: 10px; + } + + .sdc-tag .tag { + max-width: 225px; + } + + } + +} + + diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts new file mode 100644 index 0000000000..74c681e433 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts @@ -0,0 +1,381 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + import ISubCategory = Sdc.Models.ISubCategory; + import IMainCategory = Sdc.Models.IMainCategory; + 'use strict'; + + /* + * TODO: The template (derived from is not necessary here). + * Need to delete it from all remarks. + * */ + + export class UIModel { + tosca:Sdc.Directives.FileUploadModel; + name:string; + description:string; + vendorName:string; + vendorRelease:string; + category:string; + tags:Array; + userId:string; + icon:string; + projectCode:string; + fullName:string; + isAlreadyCertified:boolean; + } + + export class Validation { + validationPattern: RegExp; + contactIdValidationPattern: RegExp; + tagValidationPattern: RegExp; + vendorValidationPattern: RegExp; + commentValidationPattern: RegExp; + projectCodeValidationPattern: RegExp; + } + + export interface IGeneralStepScope extends IWizardCreationStepScope { + model:UIModel; + validation:Validation; + editForm:ng.IFormController; + component: Models.Components.Component; + categories: Array; + latestComponentName:string; + latestCategoryId: string; + latestVendorName: string; + isNew:boolean; + toscaFileExtensions:any; + isCreate:boolean; + + onToscaFileChange():void + validateField(field:any):boolean; + validateName(isInit:boolean): void; + calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category + calculatedTagsMaxLength():number; + setIconToDefault():void; + onVendorNameChange(oldVendorName: string): void; + } + + export class GeneralStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + 'Sdc.Services.CacheService', + 'ValidationPattern', + 'ContactIdValidationPattern', + 'TagValidationPattern', + 'VendorValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'sdcConfig', + 'ComponentFactory', + 'ProjectCodeValidationPattern' + ]; + + constructor(private $scope:IGeneralStepScope, + private cacheService:Services.CacheService, + private ValidationPattern:RegExp, + private ContactIdValidationPattern:RegExp, + private TagValidationPattern:RegExp, + private VendorValidationPattern:RegExp, + private CommentValidationPattern: RegExp, + private ValidationUtils: Sdc.Utils.ValidationUtils, + private sdcConfig:Models.IAppConfigurtaion, + private ComponentFactory: Sdc.Utils.ComponentFactory, + private ProjectCodeValidationPattern:RegExp + ) { + + this.$scope.registerChild(this); + this.initScopeValidation(); + this.initScopeMethods(); + this.initScope(); + this.$scope.isCreate = this.$scope.data.importFile === undefined; + } + + private initScopeValidation = (): void => { + this.$scope.validation = new Validation(); + this.$scope.validation.validationPattern = this.ValidationPattern; + this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern; + this.$scope.validation.tagValidationPattern = this.TagValidationPattern; + this.$scope.validation.vendorValidationPattern = this.VendorValidationPattern; + this.$scope.validation.commentValidationPattern = this.CommentValidationPattern; + this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern; + }; + + private initScope = ():void => { + + // Init UIModel + this.$scope.model = new UIModel(); + + // Init categories + if(this.$scope.data.componentType === Utils.Constants.ComponentType.RESOURCE){ + this.$scope.categories = this.cacheService.get('resourceCategories'); + } + if (this.$scope.data.componentType === Utils.Constants.ComponentType.SERVICE) { + this.$scope.categories = this.cacheService.get('serviceCategories'); + } + + this.$scope.model.category=''; + + //init file extenstions + this.$scope.toscaFileExtensions = this.sdcConfig.toscaFileExtension; + + // Init Tosca import file + if (this.$scope.data.importFile) { + this.$scope.model.tosca = this.$scope.data.importFile; + } + + // Case insert or update + this.$scope.component = this.$scope.getComponent(); + if ( this.$scope.component!==undefined){ + // Update mode + + //this.$scope.latestCategoryId = this.$scope.component[0].uniqueId; + //this.$scope.latestVendorName = this.$scope.component.vendorName; + this.$scope.latestComponentName = this.$scope.component.name; + this.$scope.isNew=false; + this.resource2ModelUi(this.$scope.component); + } else { + // Create mode + this.$scope.isNew=true; + this.$scope.model.tags=[]; // Init tags + this.$scope.model.userId = this.cacheService.get("user").userId; // Fill user ID from logged in user + this.$scope.model.icon = Utils.Constants.DEFAULT_ICON; // Set the default icon + this.$scope.component = this.ComponentFactory.createEmptyComponent(this.$scope.data.componentType); + } + }; + + private initScopeMethods = ():void => { + + this.$scope.validateField = (field:any):boolean => { + if (field && field.$dirty && field.$invalid){ + return true; + } + return false; + }; + + this.$scope.validateName = (isInit:boolean):void => { + if (isInit===undefined){isInit=false;} + + let name = this.$scope.model.name; + if (!name || name===""){ + if (this.$scope.editForm + && this.$scope.editForm["componentName"] + && this.$scope.editForm["componentName"].$error){ + + // Clear the error name already exists + this.$scope.editForm["componentName"].$setValidity('nameExist', true); + } + + return; + } + let subtype:string = Utils.Constants.ComponentType.RESOURCE == this.$scope.data.componentType? + this.$scope.data.importFile? 'VFC':'VF' : undefined; + + let onFailed = (response) => { + //console.info('onFaild', response); + //this.$scope.isLoading = false; + }; + + let onSuccess = (validation:Models.IValidate) => { + this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid); + }; + + if (isInit){ + // When page is init after update + if (this.$scope.model.name !== this.$scope.latestComponentName){ + if(!this.$scope.component.isProduct()) {//TODO remove when backend is ready + this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed); + } + } + } else { + // Validating on change (has debounce) + if (this.$scope.editForm + && this.$scope.editForm["componentName"] + && this.$scope.editForm["componentName"].$error + && !this.$scope.editForm["componentName"].$error.pattern + && this.$scope.model.name !== this.$scope.latestComponentName + ) { + if(!this.$scope.component.isProduct()) { //TODO remove when backend is ready + this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed); + } + } else if (this.$scope.model.name === this.$scope.latestComponentName) { + // Clear the error + this.$scope.editForm["componentName"].$setValidity('nameExist', true); + } + } + }; + + this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => { + let uniqueId: string = mainCategory; + if(subCategory) { + uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category. + } + return uniqueId; + }; + + // Notify the parent if this step valid or not. + this.$scope.$watch("editForm.$valid", (newVal, oldVal) => { + //console.log("editForm validation: " + newVal); + this.$scope.setValidState(newVal); + }); + + this.$scope.setIconToDefault = ():void => { + this.$scope.model.icon = Utils.Constants.DEFAULT_ICON; + }; + + this.$scope.onVendorNameChange = (oldVendorName: string):void => { + if(this.$scope.component.icon === oldVendorName) { + this.$scope.setIconToDefault(); + } + }; + }; + + public save = (callback:Function):void => { + this.modelUi2Resource(); + + let onFailed = (response) => { + callback(false); + }; + + let onSuccess = (component:Models.Components.Component) => { + this.$scope.component = component; + this.$scope.setComponent(this.$scope.component); + this.$scope.latestComponentName = (component.name); + callback(true); + }; + + try { + //Send the form with attached tosca file. + if (this.$scope.isNew===true) { + this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccess, onFailed); + } else { + this.$scope.component.updateComponent().then(onSuccess, onFailed); + } + }catch(e){ + //console.log("ERROR: Error in updating/creating component: " + e); + callback(false); + } + + }; + + public back = (callback:Function):void => { + callback(true); + } + + // Fill the resource properties object with data from UIModel + private modelUi2Resource = ():void => { + + this.$scope.component.name = this.$scope.model.name; + this.$scope.component.description = this.ValidationUtils.stripAndSanitize(this.$scope.model.description); + this.$scope.component.vendorName = this.$scope.model.vendorName; + this.$scope.component.vendorRelease = this.$scope.model.vendorRelease; + this.$scope.component.tags = angular.copy(this.$scope.model.tags); + this.$scope.component.tags.push(this.$scope.model.name); + this.$scope.component.contactId = this.$scope.model.userId; + this.$scope.component.icon = this.$scope.model.icon; + + if(this.$scope.component.isResource()) { + (this.$scope.component).resourceType = "VF"; + + // Handle the tosca file + if (this.$scope.model.tosca && this.$scope.isNew) { + (this.$scope.component).payloadData = this.$scope.model.tosca.base64; + (this.$scope.component).payloadName = this.$scope.model.tosca.filename; + } + + this.$scope.component.categories = this.convertCategoryStringToOneArray(); + } + + if(this.$scope.component.isProduct()) { + this.$scope.component.projectCode = this.$scope.model.projectCode; + // Handle the tosca file + this.$scope.component.categories = undefined; + (this.$scope.component).contacts = new Array(); + (this.$scope.component).contacts.push(this.$scope.component.contactId); + (this.$scope.component).fullName = this.$scope.model.fullName; + } + + if(this.$scope.component.isService()) { + this.$scope.component.projectCode = this.$scope.model.projectCode; + this.$scope.component.categories = this.convertCategoryStringToOneArray(); + } + }; + + // Fill the UIModel from data from resource properties + private resource2ModelUi = (component: Models.Components.Component):void => { + this.$scope.model.name = component.name; + this.$scope.model.description = component.description; + this.$scope.model.vendorName = component.vendorName; + this.$scope.model.vendorRelease = component.vendorRelease; + this.$scope.model.tags = _.reject(component.tags, (item)=>{return item===component.name}); + this.$scope.model.userId = component.contactId; + this.$scope.model.icon = component.icon; + this.$scope.model.projectCode = component.projectCode; + this.$scope.model.isAlreadyCertified = component.isAlreadyCertified(); + + if(!this.$scope.component.isProduct()) { + this.$scope.model.category = this.convertCategoryOneArrayToString(component.categories); + } + + if(component.isProduct()) { + this.$scope.model.fullName = (component).fullName; + + } + + }; + + // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except) + private convertCategoryStringToOneArray = ():Array => { + let tmp = this.$scope.model.category.split("_#_"); + let mainCategory = tmp[0]; + let subCategory = tmp[1]; + + // Find the selected category and add the relevant sub category. + let selectedMainCategory:IMainCategory = _.find(this.$scope.categories, function (item) { + return item["name"] === mainCategory + }); + let mainCategoryClone = jQuery.extend(true, {}, selectedMainCategory); + if(subCategory) { + mainCategoryClone['subcategories'] = [{ + "name": subCategory + }]; + } + let tmpSelected = mainCategoryClone; + + let result:Array = []; + result.push(tmpSelected); + + return result; + }; + + private convertCategoryOneArrayToString = (categories:Array):string => { + let mainCategory:string = categories[0].name; + let subCategory:string = ''; + if(categories[0].subcategories) { + subCategory = categories[0].subcategories[0].name; + } + return this.$scope.calculateUnique(mainCategory, subCategory); + }; + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html new file mode 100644 index 0000000000..7fc3e9224f --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html @@ -0,0 +1,40 @@ +
+ +
+ +
+
+
+
+ +
+
+
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less new file mode 100644 index 0000000000..74786c127a --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less @@ -0,0 +1,125 @@ +.hierarchy-step { + margin-top: 35px; + + .scrollbar-container{ + max-height:400px; + .perfect-scrollbar; + } + + .dropdown-container { + position: relative; + display: inline-block; + width: 100%; + + &:after{ + top: 47%; + right: 1%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(0, 0, 0, 0); + border-top-color: black; + border-width: 4px; + margin-left: -4px; + } + + .dropdown-input-text { + width: 100%; + padding: 4px 10px; + } + + .dropdown-content { + .perfect-scrollbar; + border: 1px solid #d8d8d8; + display: none; + position: absolute; + overflow: hidden; + width: 100%; + .bg_c; + max-height: 400px; + z-index: 999999; + + .dropdown-option { + border-bottom: 1px solid #d8d8d8; + display: inline-block; + width: 100%; + } + + .category-container{ + width: 250px; + float: left; + padding-left: 5px; + + .category { + .bold; + padding: 3px 3px 2px 3px; + &:after{ + .sprite; + .arrow-left; + content: ''; + margin-left: 5px; + transform: rotate(180deg); + } + } + .subcategory { + padding-left: 3px; + } + } + + .groupings-container{ + display: inline-block; + width: 424px; + border-left: 1px solid #d8d8d8; + min-height: 55px; + .group{ + padding: 3px 3px 3px 10px; + &:hover{ + .hand; + .bg_n; + } + &.disabled-group { + opacity: 0.5; + &:hover{ + cursor: auto; + .bg_c; + } + } + } + } + + .seperator { + height: 1px; + width: 100%; + .bg_j; + margin: 5px 0px; + } + } + .show { + display: block; + } + } + + .hierarchy-groups-container{ + .b_9; + width: 100%; + border: 1px solid #d8d8d8; + height: 425px; + padding: 15px; + text-align: center; + + .no-group-text{ + text-align: center; + margin-top:25px; + a { + cursor: pointer; + } + } + .group-tag{ + display: inline-block; + float: left; + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts new file mode 100644 index 0000000000..a974c0af81 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts @@ -0,0 +1,149 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export interface IHierarchyStepScope extends IWizardCreationScope { + + categoriesOptions: Array; + product:Models.Components.Product; + isLoading:boolean; + showDropDown:boolean; + + onInputTextClicked():void; + onGroupSelected(category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void; + clickOutside():void; + deleteGroup(uniqueId:string):void; + } + + export class HierarchyStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + 'Sdc.Services.CacheService', + 'ComponentFactory' + ]; + + constructor(private $scope:IHierarchyStepScope, + private cacheService:Sdc.Services.CacheService, + private ComponentFactory: Sdc.Utils.ComponentFactory) { + + this.$scope.registerChild(this); + this.$scope.setValidState(true); + this.$scope.product = this.$scope.getComponent(); + this.initScope(); + } + + private initCategories = () => { + this.$scope.categoriesOptions = angular.copy(this.cacheService.get('productCategories')); + let selectedGroup:Array = []; + _.forEach(this.$scope.product.categories, (category: Models.IMainCategory) => { + _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => { + selectedGroup = selectedGroup.concat(subcategory.groupings); + }); + }); + _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => { + _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => { + _.forEach(subcategory.groupings, (group:Models.ISubCategory) => { + let componentGroup:Models.IGroup = _.find(selectedGroup, (componentGroupObj) => { + return componentGroupObj.uniqueId == group.uniqueId; + }); + if(componentGroup){ + group.isDisabled = true; + } + }); + }); + }); + }; + + private setFormValidation = ():void => { + if(!this.$scope.product.categories || this.$scope.product.categories.length === 0){ + this.$scope.setValidState(false); + } + else{ + this.$scope.setValidState(true); + } + + }; + + private initScope = ():void => { + this.$scope.isLoading= false; + this.$scope.showDropDown =false; + this.initCategories(); + this.setFormValidation(); + + this.$scope.onGroupSelected = (category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void => { + this.$scope.showDropDown = false; + this.$scope.product.addGroup(category, subcategory, group); + group.isDisabled = true; + this.setFormValidation(); + }; + + this.$scope.onInputTextClicked = ():void => {//just edit the component in place, no pop up nor server update ? + this.$scope.showDropDown = !this.$scope.showDropDown; + }; + + this.$scope.clickOutside = (): any => { + this.$scope.showDropDown = false; + }; + + this.$scope.deleteGroup = (uniqueId:string) : void => { + //delete group from component + this.$scope.product.deleteGroup(uniqueId); + this.setFormValidation(); + //enabled group + _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => { + _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => { + let groupObj:Models.IGroup = _.find (subcategory.groupings, (group) => { + return group.uniqueId === uniqueId; + }); + if(groupObj){ + groupObj.isDisabled = false; + } + }); + }); + } + }; + + public save = (callback:Function):void => { + let onFailed = (response) => { + callback(false); + }; + + let onSuccess = (component: Models.Components.Component) => { + this.$scope.product = this.ComponentFactory.createComponent(component); + this.$scope.setComponent(this.$scope.product); + callback(true); + }; + + try { + this.$scope.product.updateComponent().then(onSuccess, onFailed); + }catch(e){ + //console.log("ERROR: Error in updating/creating component: " + e); + callback(false); + } + }; + + public back = (callback:Function):void => { + this.save(callback); + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html new file mode 100644 index 0000000000..2ae386283c --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html @@ -0,0 +1,26 @@ +
+ +
+ +
+
+
+
+ + +
+
+
+
+
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less new file mode 100644 index 0000000000..c03c949962 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less @@ -0,0 +1,55 @@ +.icons-step { + + .w-sdc-form { + padding-top: 0px; + padding-bottom: 0px; + .selected-icon-container { + text-align: left; + border: 1px solid #cfcfcf; + clear: both; + margin-bottom: 15px; + padding-left: 3px; + padding-bottom: 3px; + .selected-icon { + margin: 8px 5px 0px 6px; + } + } + + .suggested-icons-container { + text-align: left; + border: 1px solid #cfcfcf; + clear: both; + padding-left: 3px; + height: 340px; + margin-bottom: 0px; + + .suggested-icon-wrapper { + margin: 8px 5px 0px 6px; + display: inline-block; + + &.selected { + border: 1px solid @color_p; + border-radius: 25px; + box-shadow: 0 0 2px #888; + display: inline-block; + line-height: 0px; + padding: 2px; + } + + } + .suggested-icon { + // margin: 8px 5px 0px 6px; + display: inline-block; + &.disable{ + opacity: 0.4; + } + } + + + } + + .icons-label { + float: left; + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts new file mode 100644 index 0000000000..4dc5e377fa --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts @@ -0,0 +1,150 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export interface IIconsStepScope extends IWizardCreationStepScope{ + icons : Array; + component: Models.Components.Component; + iconSprite: string; + setComponentIcon(iconSrc:string): void; + } + + export class IconsStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + 'Sdc.Services.AvailableIconsService', + 'ComponentFactory' + ]; + + constructor(private $scope:IIconsStepScope, + private availableIconsService:Services.AvailableIconsService, + private ComponentFactory: Sdc.Utils.ComponentFactory) { + + this.$scope.registerChild(this); + this.$scope.component = this.$scope.getComponent(); + this.$scope.iconSprite = this.$scope.component.iconSprite; + this.initScope(); + this.initIcons(); + + if(this.$scope.component.isResource()) { + this.initVendor(); + } + // In case there is one icons select it. + if( this.$scope.icons.length == 1 && !this.$scope.component.isAlreadyCertified()){ + this.$scope.setComponentIcon(this.$scope.icons[0]); + } + } + + private initIcons = ():void => { + + // For subcategories that where created by admin, there is no icons + this.$scope.icons = new Array(); + if (this.$scope.component.categories && this.$scope.component.categories.length > 0) { + + _.forEach(this.$scope.component.categories, (category:Models.IMainCategory):void => { + if (category.icons) { + this.$scope.icons = this.$scope.icons.concat(category.icons); + } + if (category.subcategories) { + _.forEach(category.subcategories, (subcategory:Models.ISubCategory):void => { + if (subcategory.icons) { + this.$scope.icons = this.$scope.icons.concat(subcategory.icons); + } + }); + } + }); + } + + if (this.$scope.component.isResource()) { + let resourceType:string = this.$scope.component.getComponentSubType(); + if (resourceType === 'VL') { + this.$scope.icons = ['vl']; + } + if (resourceType === 'CP') { + this.$scope.icons = ['cp']; + } + } + + if (this.$scope.icons.length === 0) { + this.$scope.icons = this.availableIconsService.getIcons(this.$scope.component.componentType); + } + + }; + + private initVendor = ():void => { + let vendors:Array = this.availableIconsService.getIcons(this.$scope.component.componentType).slice(5, 19); + let vendorName = this.$scope.component.vendorName.toLowerCase(); + if ('at&t' === vendorName){ + vendorName = 'att'; + } + if ('nokia' === vendorName){ + vendorName = 'nokiasiemens'; + } + + let vendor:string = _.find(vendors, (vendor:string)=>{ + return vendor.replace(/[_]/g, '').toLowerCase() === vendorName; + }); + + if(vendor && this.$scope.icons.indexOf(vendor)===-1) { + this.$scope.icons.push(vendor); + } + }; + + private initScope():void { + this.$scope.icons = []; + + if(this.$scope.component.icon === Utils.Constants.DEFAULT_ICON){ + this.$scope.setValidState(false); + } + + this.$scope.setComponentIcon = (iconSrc:string):void => { + this.$scope.component.icon = iconSrc; + this.$scope.setValidState(true); + } + } + + save(callback:Function):void { + let onFailed = () => { + callback(false); + }; + + let onSuccess = (component:Models.Components.Component) => { + this.$scope.component = component; + this.$scope.setComponent(this.$scope.component); + callback(true); + }; + + try { + this.$scope.component.updateComponent().then(onSuccess, onFailed); + }catch(e){ + callback(false); + } + } + + public back = (callback:Function):void => { + this.save(callback); + } + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html new file mode 100644 index 0000000000..4429451871 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html @@ -0,0 +1,57 @@ +
+ +
Add
+ +
+ +
+
+
{{header.title}} + +
+
+ +
+ +
+ +
+ There are no properties to display
+ click here to add one +
+
+ +
+ + {{property.name}} + +
+ +
+ +
+ +
+ +
+ + +
+ + +
+
+
+
+
+ +
+
+ +
diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less new file mode 100644 index 0000000000..0d7dad8dc2 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less @@ -0,0 +1,55 @@ +.properties-step { + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height: 412px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 0px; + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + padding: 5px 4px; + + } + + .flex-item:nth-child(1) { + flex-grow: 15; + + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 9; + } + + .flex-item:nth-child(4) { + flex-grow: 3; + //padding-top: 10px; + padding: 10px 4px; + + } + + .flex-item:nth-child(5) { + flex-grow: 1; + } + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts new file mode 100644 index 0000000000..08dfb5e153 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts @@ -0,0 +1,123 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + interface IPropertiesStepViewModelScope extends IWizardCreationStepScope { + component: Models.Components.Component; + tableHeadersList: Array; + reverse: boolean; + sortBy:string; + + addOrUpdateProperty(): void; + delete(property: Models.PropertyModel): void; + sort(sortBy:string): void; + } + + export class PropertiesStepViewModel implements IWizardCreationStep { + + static '$inject' = [ + '$scope', + '$filter', + '$modal', + '$templateCache', + 'ModalsHandler' + ]; + + + constructor( + private $scope:IPropertiesStepViewModelScope, + private $filter:ng.IFilterService, + private $modal:ng.ui.bootstrap.IModalService, + private $templateCache:ng.ITemplateCacheService, + private ModalsHandler: Utils.ModalsHandler + ){ + + this.$scope.registerChild(this); + this.$scope.setValidState(true); + this.initScope(); + } + + public save = (callback:Function):void => { + this.$scope.setComponent(this.$scope.component); + callback(true); + }; + + public back = (callback:Function):void => { + this.$scope.setComponent(this.$scope.component); + callback(true); + } + + + private openEditPropertyModal = (property: Models.PropertyModel): void => { + let viewModelsHtmlBasePath: string = '/app/scripts/view-models/'; + + let modalOptions: ng.ui.bootstrap.IModalSettings = { + template: this.$templateCache.get(viewModelsHtmlBasePath+'wizard/property-form/property-form.html'), + controller: 'Sdc.ViewModels.Wizard.PropertyFormViewModel', + size: 'sdc-md', + backdrop: 'static', + keyboard: false, + resolve: { + property: (): Models.PropertyModel => { + return property; + }, + component: (): Models.Components.Component => { + return this.$scope.getComponent(); + } + } + }; + this.$modal.open(modalOptions); + }; + + private initScope = (): void => { + + let self = this; + this.$scope.component = this.$scope.getComponent(); + this.$scope.sortBy = 'name'; + this.$scope.reverse = false; + + this.$scope.tableHeadersList = [ + {title:'Name', property: 'name'}, + {title:'Type', property: 'type'}, + {title:'Default Value', property: 'defaultValue'} + ]; + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + this.$scope.addOrUpdateProperty = (property?: Models.PropertyModel): void => { + this.openEditPropertyModal(property ? property : new Models.PropertyModel()); + }; + + this.$scope.delete = (property: Models.PropertyModel): void => { + + let onOk = (): void => { + this.$scope.component.deleteProperty(property.uniqueId); + }; + let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts new file mode 100644 index 0000000000..3f390841ca --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts @@ -0,0 +1,163 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// + +describe("property form View Model ", () => { + + let $controllerMock:ng.IControllerService; + let $qMock:ng.IQService; + let $httpBackendMock:ng.IHttpBackendService; + let $scopeMock:Sdc.ViewModels.Wizard.IPropertyFormViewModelScope; + let $stateMock:ng.ui.IStateService; + let $stateParams:any; + let component = { + "uniqueId": "ece818e0-fd59-477a-baf6-e27461a7ce23", + "uuid": "8db823c2-6a9c-4636-8676-f5e713270dd7", + "contactId": "uf2345", + "category": "Network Layer 2-3/Router", + "creationDate": 1447235352429, + "description": "u", + "highestVersion": true, + "icon": "network", + "lastUpdateDate": 1447235370064, + "lastUpdaterUserId": "cs0008", + "lastUpdaterFullName": "Carlos Santana", + "lifecycleState": "NOT_CERTIFIED_CHECKOUT", + "name": "u", + "version": "0.1", + "type": 1, + "tags": [ + "u" + ], + "vendorName": "u", + "vendorRelease": "u", + "systemName": "U", + "$$hashKey": "object:34" + }; + + + beforeEach(angular.mock.module('sdcApp')); + + beforeEach(angular.mock.inject((_$controller_:ng.IControllerService, + _$httpBackend_:ng.IHttpBackendService, + _$rootScope_, + _$q_:ng.IQService, + _$state_:ng.ui.IStateService, + _$stateParams_:any) => { + + $controllerMock = _$controller_; + $httpBackendMock = _$httpBackend_ + $scopeMock = _$rootScope_.$new(); + $qMock = _$q_; + $stateMock = _$state_; + $stateParams = _$stateParams_; + + + //handle all http request thet not relevant to the tests + $httpBackendMock.expectGET(/.*languages\/en_US.json.*/).respond(200, JSON.stringify({})); + // $httpBackendMock.expectGET(/.*resources\/certified\/abstract.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*rest\/version.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*configuration\/ui.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*user\/authorize.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*categories\/services.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*categories\/resources.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET(/.*categories\/products.*/).respond(200, JSON.stringify({})); + $httpBackendMock.expectGET('http://feHost:8181/sdc1/feProxy/rest/version').respond(200, JSON.stringify({})); + + /** + * Mock the service + * @type {any} + */ + //getAllEntitiesDefered = $qMock.defer(); + //getAllEntitiesDefered.resolve(getAllEntitiesResponseMock); + //entityServiceMock = jasmine.createSpyObj('entityServiceMock', ['getAllComponents']); + //entityServiceMock.getAllComponents.and.returnValue(getAllEntitiesDefered.promise); + + // $stateParams['show'] = ''; + + /** + * Need to inject into the controller only the objects that we want to MOCK + * those that we need to change theirs behaviors + */ + $controllerMock(Sdc.ViewModels.Wizard.PropertyFormViewModel, { + '$scope': $scopeMock, + 'property': new Sdc.Models.PropertyModel(), + 'component': component, + }); + + })); + + describe("when Controller 'PropertyFormViewModel' created", () => { + + it('should have a regexp per each type', () => { + $scopeMock.$apply(); + expect(Object.keys($scopeMock.listRegex).length).toBe($scopeMock.editPropertyModel["simpleTypes"].length); + }); + + it('should have equal regexps for map and list', () => { + $scopeMock.$apply(); + expect(Object.keys($scopeMock.listRegex).length).toBe(Object.keys($scopeMock.mapRegex).length); + }); + + }); + + /*describe("when Controller 'DashboardViewModel' created", () => { + + it('should generate all entities', () => { + $scopeMock.$apply(); + expect($scopeMock.components.length).toBe(getAllEntitiesResponseMock.length); + }); + + + it('should show tutorial page ', () => { + $stateParams.show = 'tutorial'; + + $controllerMock(Sdc.ViewModels.DashboardViewModel, { + '$scope': $scopeMock, + '$stateParams': $stateParams, + 'Sdc.Services.EntityService': entityServiceMock, + //to complete injects + }); + + $scopeMock.$apply(); + expect($scopeMock.isFirstTime).toBeTruthy(); + expect($scopeMock.showTutorial).toBeTruthy(); + }); + + }); + + + describe("when function 'entitiesCount' invoked", () => { + + beforeEach(() => { + $controllerMock(Sdc.ViewModels.DashboardViewModel, { + '$scope': $scopeMock, + 'Sdc.Services.EntityService': entityServiceMock, + }); + $scopeMock.$apply(); + }); + + it('should return entities count per folder', () => { + + }); + + + });*/ +}); diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts new file mode 100644 index 0000000000..5cb0ef1ddd --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts @@ -0,0 +1,250 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// + +module Sdc.ViewModels.Wizard { + 'use strict'; + + export interface IEditPropertyModel{ + property: Models.PropertyModel; + types: Array; + simpleTypes: Array; + sources: Array; + } + + export interface IPropertyFormViewModelScope extends ng.IScope{ + + $$childTail: any; + editForm:ng.IFormController; + forms:any; + footerButtons: Array; + isNew: boolean; + isLoading: boolean; + validationPattern: RegExp; + propertyNameValidationPattern: RegExp; + integerValidationPattern: RegExp; + floatValidationPattern: RegExp; + commentValidationPattern: RegExp; + listRegex: Sdc.Utils.IMapRegex; + mapRegex: Sdc.Utils.IMapRegex; + editPropertyModel: IEditPropertyModel; + modalInstanceProperty: ng.ui.bootstrap.IModalServiceInstance; + save(doNotCloseModal?: boolean): void; + saveAndAnother(): void; + getValidation(): RegExp; + validateIntRange(value:string):boolean; + close(): void; + onValueChange(): void; + onTypeChange(resetSchema:boolean): void; + showSchema(): boolean; + getValidationTranslate():string; + validateUniqueKeys(viewValue:string):boolean; + } + + export class PropertyFormViewModel{ + + private originalValue: string; + + static '$inject' = [ + '$scope', + '$modalInstance', + 'property', + 'ValidationPattern', + 'PropertyNameValidationPattern', + 'IntegerNoLeadingZeroValidationPattern', + 'FloatValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'component' + ]; + + private formState: Utils.Constants.FormState; + private entityId: string; + private resourceInstanceUniqueId: string; + private readonly: boolean; + + constructor( + private $scope:IPropertyFormViewModelScope, + private $modalInstance: ng.ui.bootstrap.IModalServiceInstance, + private property : Models.PropertyModel, + private ValidationPattern : RegExp, + private PropertyNameValidationPattern: RegExp, + private IntegerNoLeadingZeroValidationPattern : RegExp, + private FloatValidationPattern : RegExp, + private CommentValidationPattern: RegExp, + private ValidationUtils: Sdc.Utils.ValidationUtils, + private component:Models.Components.Component + ){ + this.entityId = this.component.uniqueId; + this.formState = angular.isDefined(property.name) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE; + this.initScope(); + } + + + private initResource = (): void => { + this.$scope.editPropertyModel.property = new Sdc.Models.PropertyModel(this.property); + this.originalValue = this.property.defaultValue; + if(this.$scope.editPropertyModel.types.indexOf(this.property.type) === -1 && !this.$scope.isNew){ + this.property.type = "string"; + } + }; + + private initEditPropertyModel = (): void => { + this.$scope.editPropertyModel = { + property: null, + types: ['integer', 'string', 'float', 'boolean', 'list', 'map'], + simpleTypes: ['integer', 'string', 'float', 'boolean'], + sources: ['A&AI', 'Order', 'Runtime'] + }; + + this.initResource(); + }; + + private initScope = (): void => { + + this.$scope.modalInstanceProperty = this.$modalInstance; + //scope properties + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern; + this.$scope.integerValidationPattern = this.IntegerNoLeadingZeroValidationPattern; + this.$scope.floatValidationPattern = this.FloatValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + + //map & list validation patterns + this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns(); + this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns(); + + this.$scope.isLoading = false; + this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE); + this.initEditPropertyModel(); + + //scope methods + this.$scope.save = (): void => { + this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description); + this.$scope.isLoading = true; + + let onFailed = (response) => { + console.info('onFaild',response); + this.$scope.isLoading = false; + this.$scope.editPropertyModel.property.readonly = this.readonly; + this.$scope.editPropertyModel.property.resourceInstanceUniqueId = this.resourceInstanceUniqueId; + }; + + let onSuccess = (property: Models.PropertyModel): void => { + console.info('property added : ',property); + this.$scope.isLoading = false; + property.resourceInstanceUniqueId = this.resourceInstanceUniqueId; + property.readonly = (property.parentUniqueId !== this.component.uniqueId) /*|| this.component.isService()*/; + + this.$modalInstance.close(); + }; + + this.resourceInstanceUniqueId = this.$scope.editPropertyModel.property.resourceInstanceUniqueId; + this.readonly = this.$scope.editPropertyModel.property.readonly; + this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.defaultValue ? this.$scope.editPropertyModel.property.defaultValue:null; + + this.component.addOrUpdateProperty(this.$scope.editPropertyModel.property).then(onSuccess, onFailed); + }; + + this.$scope.saveAndAnother = (): void => { + this.$scope.save(); + }; + + this.$scope.showSchema = () :boolean => { + return ['list', 'map'].indexOf(this.$scope.editPropertyModel.property.type) > -1; + }; + + this.$scope.getValidationTranslate = () : string => { + let result = "PROPERTY_EDIT_PATTERN"; + if (this.$scope.showSchema()) { + + result = "PROPERTY_EDIT_" + this.$scope.editPropertyModel.property.type.toUpperCase(); + + if(this.$scope.editPropertyModel.property.schema.property.type === 'string') { + result += "_STRING"; + } else { + result += "_GENERIC"; + } + } + + return result; + }; + + this.$scope.getValidation = () : RegExp => { + let type = this.$scope.editPropertyModel.property.type; + switch (type){ + case 'integer': + return this.$scope.integerValidationPattern; + case 'float': + return this.$scope.floatValidationPattern; + case 'list': + return this.$scope.listRegex[this.$scope.editPropertyModel.property.schema.property.type]; + case 'map': + return this.$scope.mapRegex[this.$scope.editPropertyModel.property.schema.property.type]; + default : + return null; + } + }; + + this.$scope.validateUniqueKeys = (viewValue:string) : boolean => { + if(this.$scope.editPropertyModel.property.type === 'map') { + return this.ValidationUtils.validateUniqueKeys(viewValue); + } + else { + return true; //always valid if not a map + } + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.close = (): void => { + this.$modalInstance.close(); + }; + + this.$scope.onValueChange = (): void => { + if(!this.$scope.editPropertyModel.property.defaultValue && this.$scope.editPropertyModel.property.required) { + this.$scope.editPropertyModel.property.defaultValue = this.originalValue; + } + }; + + this.$scope.onTypeChange = (resetSchema:boolean): void => { + this.$scope.editPropertyModel.property.defaultValue = ''; + if (resetSchema) { + this.$scope.editPropertyModel.property.schema.property.type = ''; + } + }; + + //new form layout for import asset + this.$scope.forms = {}; + this.$scope.footerButtons = [ + {'name': this.$scope.isNew ? 'Add' : 'Update', 'css':'blue', 'callback': this.$scope.save}, + {'name':'Cancel', 'css':'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch('forms.editForm.$invalid', () => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + } + + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html new file mode 100644 index 0000000000..be237112a4 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html @@ -0,0 +1,133 @@ + + +
+ +
+ +
+ +
+ + + +
+ + + +
+
+ + +
+ + + +
+ +
+
+ + +
+ + + + +
+ + + + +
+
+ +
+ +
+ +
+ + + +
+ +
+
+ +
+ + + +
+ + + +
+
+ +
+ +
+
+ +
diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less new file mode 100644 index 0000000000..52b8564fdb --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less @@ -0,0 +1,7 @@ +.sdc-add-property{ + + .w-sdc-form { + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html new file mode 100644 index 0000000000..afa9307265 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html @@ -0,0 +1,12 @@ + +
{{assetName}} | {{assetType}}
+ +
+
+ +
+
+ +
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less new file mode 100644 index 0000000000..591186789b --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less @@ -0,0 +1,60 @@ +.sdc-wizard-wrapper { + display: flex; + padding: 80px 0; + height: 620px; + // So input validation error will be shown outside the modal (long messages). + // overflow: hidden; + + .sdc-wizard-left-content { + width: 400px; + white-space: nowrap; + + .wizard-steps-line { + padding: 0 80px 0 0; + display: block; + } + + } + + .sdc-wizard-right-content { + margin-right: 200px; + /* background-color: #fafafa; */ + height: 100%; + width: 100%; + .perfect-scrollbar; + overflow: visible; + //.animation-duration(2s); + + .sdc-wizard-right-include { + + } + } + +} + +.sprite-green-tick.animated { + .animation-duration(3s); +} + +.sdc-wizard-name-type-label { + position: absolute; + top: 64px; + right: 40px; + + span { + .b_7; + &.name {.bold;} + &.sprite-green-tick { + position: absolute; + left: -22px; + top: 5px; + } + } + +} + +.w-wizard-footer { + button.cancel { + margin-right: 120px; + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts new file mode 100644 index 0000000000..365d3aedf6 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts @@ -0,0 +1,399 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export class StepNames { + static general = "General"; + static icon = "Icon"; + static deploymentArtifact = "Deployment Artifact"; + static informationArtifact = "Information Artifact"; + static properties = "Properties"; + static hierarchy = "Hierarchy"; + } + + class FooterButtons { + static cancel = "Cancel"; + static back = "Back"; + static next = "Next"; + static finish = "Finish"; + } + + export class WizardCreationTypes { + static importAsset = "importAsset"; + static create = "create"; + static edit = "edit"; + } + + export class _CreationStep { + name:string; + url:string; + } + + export class CurrentStep { + assetCreationStep:_CreationStep; + reference:IWizardCreationStep; + index:number; + valid: boolean; + } + + export interface IWizardCreationStep { + save(callback:Function):void; + back(callback:Function):void; + } + + export interface IWizardCreationStepScope extends ng.IScope { + data:any; + getComponent():Sdc.Models.Components.Component; + setComponent(component: Sdc.Models.Components.Component):void; + registerChild(child:IWizardCreationStep):void; // Called from the step + setValidState(valid:boolean):void; // Called from the step + } + + export interface IWizardCreationScope extends ng.IScope { + isLoading: boolean; + data:any; // data passed from dashboard (opener), need it on the scope, because general step will use this (extends the scope) + directiveSteps: Array; // Steps for the directive, on the scope (on the scope because need to pass to directive via HTML) + templateUrl: string; // On the scope because need to pass to via HTML + footerButtons: Array; // Wizard footer buttons (on the scope because need to pass to directive via HTML) + assetCreationControl:any; // Link to wizard directive functions. + modalInstance:ng.ui.bootstrap.IModalServiceInstance; // Reference to the modal, so we can close it (on the scope because need to pass to directive via HTML) + assetName:string; + assetType:string; + modalTitle:string; + getComponent():Sdc.Models.Components.Component; + setComponent(component: Sdc.Models.Components.Component):void; + registerChild(child:IWizardCreationStep):void; // Called from the step + setValidState(valid:boolean):void; // Called from the step + } + + export class WizardCreationBaseViewModel { + + component: Sdc.Models.Components.Component; + protected assetCreationSteps: Array<_CreationStep>; // Contains URL and name so we can replace them + currentStep:CurrentStep; + protected type:string; + + constructor(protected $scope:IWizardCreationScope, + protected data:any, + protected ComponentFactory: Utils.ComponentFactory, + protected $modalInstance: ng.ui.bootstrap.IModalServiceInstance + ) { + + this.$scope.data = data; + this.currentStep = new CurrentStep(); + this.currentStep.valid=false; + this.$scope.modalInstance = this.$modalInstance; + this.initScope(); + this.noBackspaceNav(); + + // In case the modal was opened with filled resource (edit mode). + if (data.component){ + this.$scope.setComponent(data.component); + data.componentType = this.$scope.getComponent().componentType; + window.setTimeout(()=>{ + this.safeApply(this.setCurrentStepByIndex(0, false)); + },100); + } else { + // Default step to start with + window.setTimeout(()=>{ + this.safeApply(this.setCurrentStepByIndex(0, false)); + },100); + } + + } + + private safeApply = (fn:any) => { + let phase = this.$scope.$root.$$phase; + if (phase == '$apply' || phase == '$digest') { + if (fn && (typeof(fn) === 'function')) { + fn(); + } + } else { + this.$scope.$apply(fn); + } + }; + + private initScope = ():void => { + + // Control to call functions on wizard step directive + this.$scope.assetCreationControl = {}; + + // Footer buttons definitions for the modal directive. + this.$scope.footerButtons = [ + {"name":FooterButtons.cancel, "css":'white cancel',"callback": ()=>{this.btnCancelClicked();}}, + {"name":FooterButtons.back, "disabled":true, "css":'white back',"callback": ()=>{this.btnBackClicked();}}, + {"name":FooterButtons.next, "disabled":true, "css":'blue next',"callback": ()=>{this.btnNextClicked();}}, + {"name":FooterButtons.finish, "disabled":true, "css":'white finish',"callback": ()=>{this.btnFinishedClicked();}} + ]; + + // Will be called from step constructor to register him. + // So the current step will be the reference. + this.$scope.registerChild=(child:IWizardCreationStep):void => { + this.currentStep.reference=child; + }; + + // Will be called from each step to notify if the step is valid + // The wizard will set the "Next", "Finish" buttons accordingly. + this.$scope.setValidState = (valid:boolean):void => { + this.currentStep.valid=valid; + let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index]; + this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, valid); + this.footerButtonsStateMachine(); + this.wizardButtonsIconsStateMachine(); + }; + + /** + * Will be called from each step to get current entity. + * This will return copy of the entity (not reference), because I do not want that the step will change entity parameters. + * If the step need to update the entity it can call setComponent function. + * @returns {Sdc.Models.IEntity} + */ + this.$scope.getComponent = ():Sdc.Models.Components.Component => { + return this.component; + }; + + // Will be called from each step after save to update the resource. + this.$scope.setComponent = (component:Sdc.Models.Components.Component):void => { + this.component = component; + }; + + }; + + protected setCurrentStepByName = (stepName:string):boolean => { + let stepIndex:number = this.getStepIndex(stepName); + return this.setCurrentStepByIndex(stepIndex); + }; + + // Set the current step, change the URL in ng-include. + protected setCurrentStepByIndex = (index:number, doSave:boolean=true):boolean => { + let result:boolean = false; + if (this.currentStep.index!==index) { // Check that not pressing on same step, also the first time currentStepIndex=undefined + if (doSave===true) { + this.callStepSave(() => { + // This section will be executed only if success save. + + // Set current step in the left wizard directive = valid + let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index]; + this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, true); + + // Move to next step + let step:_CreationStep = this.assetCreationSteps[index]; + this.currentStep.index = index; + this.currentStep.assetCreationStep = step; + this.$scope.templateUrl = step.url; + + // Update the next/back buttons and steps buttons. + this.footerButtonsStateMachine(); + this.wizardButtonsIconsStateMachine(); + + // Can not perform step click without enabling the step + this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it. + this.$scope.assetCreationControl.stepClicked(step.name); + + // After saving the asset name and type will be shown in the top right of the screen. + this.fillAssetNameAndType(); + + result=true; + }); + } else { + // For the first time + let step:_CreationStep = this.assetCreationSteps[index]; + this.currentStep.index = index; + this.currentStep.assetCreationStep=step; + this.$scope.templateUrl = step.url; + this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it. + this.$scope.assetCreationControl.stepClicked(step.name); + result=true; + } + + //this.updateFooterButtonsStates(); + + } else { + result=true; + } + return result; + }; + + // Save the current step + private callStepSave = (successCallback:Function):void => { + this.$scope.isLoading = true; + this.currentStep.reference.save((result:boolean)=>{ + this.$scope.isLoading = false; + if (result===true){ + successCallback(); + } else { + // Set the next and finish button enabled. + //this.updateFooterButtonsStates(true); + } + }); + }; + + // Save the current step + private callStepBack = (successCallback:Function):void => { + this.$scope.isLoading = true; + this.currentStep.reference.back((result:boolean)=>{ + this.$scope.isLoading = false; + if (result===true){ + successCallback(); + } else { + // Set the next and finish button enabled. + //this.updateFooterButtonsStates(true); + } + }); + }; + + private getStepIndex = (stepName:string):number => { + let index:number=-1; + let tmp = _.find(this.assetCreationSteps, function (item, indx) { + index = indx; + return item.name === stepName; + }); + return index; + }; + + private btnNextClicked = ():void => { + if (this.hasNext()===true) { + let tmp = this.currentStep.index+1; + this.setCurrentStepByIndex(tmp); + } + }; + + private btnBackClicked = ():void => { + if (this.hasBack()===true) { + this.callStepBack(() => { + let tmp = this.currentStep.index-1; + this.setCurrentStepByIndex(tmp, false); + }); + } + }; + + private btnCancelClicked = ():void => { + this.$modalInstance.dismiss(this.$scope.getComponent()); + }; + + private btnFinishedClicked = ():void => { + this.callStepSave(() => { + this.$modalInstance.close(this.$scope.getComponent()); + }); + }; + + // Check if we can move next + private hasNext = ():boolean => { + if (this.assetCreationSteps.length-1>this.currentStep.index){ + return true; + } else { + return false; + } + }; + + // Check if we can move back + private hasBack = ():boolean => { + if (this.currentStep.index===0){ + return false; + } else { + return true; + } + }; + + private fillAssetNameAndType=():void => { + this.$scope.assetName = this.$scope.getComponent().name; + this.$scope.assetType = this.$scope.getComponent().getComponentSubType(); + + }; + + protected enableAllWizardSteps=():void => { + this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => { + step.enabled=true; + }); + }; + + protected disableAllWizardSteps=():void => { + this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => { + step.enabled=false; + }); + }; + + private footerButtonsStateMachine = ():void => { + //console.log("footerButtonsStateMachine, current step validity: " + this.currentStep.valid); + let stepIndex:number = this.currentStep.index; + let cancelButton = this.$scope.footerButtons[0]; + let backButton = this.$scope.footerButtons[1]; + let nextButton = this.$scope.footerButtons[2]; + let finishButton = this.$scope.footerButtons[3]; + + // NEXT button + // Disable next button if it is the last step, and if not check the validity of the step. + if (this.hasNext()){ + nextButton.disabled = !this.currentStep.valid; + } else { + nextButton.disabled = true; + } + + // BACK button + backButton.disabled = !this.hasBack(); + + // FINISH button + // If step 2 is valid show the finish button. + if (stepIndex>=1 && this.currentStep.valid===true) { + finishButton.disabled = false; + } + if (this.currentStep.valid===false){ + finishButton.disabled = true; + } + + // EDIT + if (this.type===WizardCreationTypes.edit && this.currentStep.valid===true){ + finishButton.disabled = false; + } + + }; + + + + private wizardButtonsIconsStateMachine = ():void => { + + // Enable or disable wizard directive next step, in case the current step form is valid or not. + let stepIndex:number = this.currentStep.index; + if (this.$scope.directiveSteps[stepIndex + 1]) { + this.$scope.directiveSteps[stepIndex + 1].enabled = this.currentStep.valid; + } + + // In case step 1 and 2 are valid, we can open all other steps. + if (this.$scope.directiveSteps[0].valid===true && this.$scope.directiveSteps[1].valid===true){ + // Enable all wizard directive steps + this.enableAllWizardSteps(); + } else if (this.currentStep.valid===false) { + // Disable all steps + this.disableAllWizardSteps(); + } + }; + + private noBackspaceNav:Function = ():void => { + this.$scope.$on('$locationChangeStart', (event, newUrl, oldUrl):void =>{ + event.preventDefault(); + }) + }; + + + } + +} diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts new file mode 100644 index 0000000000..9490cddfdb --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts @@ -0,0 +1,114 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/** + * Created by obarda on 3/15/2016. + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export class CreateWizardViewModel extends WizardCreationBaseViewModel { + + static '$inject' = [ + '$scope', + 'data', + 'ComponentFactory', + '$modalInstance' + ]; + + constructor(public $scope:IWizardCreationScope, + public data:any, + public ComponentFactory: Sdc.Utils.ComponentFactory, + public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) { + + super($scope, data, ComponentFactory, $modalInstance); + this.type = WizardCreationTypes.create; + this.init(); + this.initCreateAssetScope(); + } + + private init = ():void => { + + switch (this.data.componentType){ + case Utils.Constants.ComponentType.RESOURCE: { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}, + {"name": StepNames.deploymentArtifact, "url": '/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html'}, + {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'}, + {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'} + ]; + } + break; + + case Utils.Constants.ComponentType.SERVICE: { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'} + ]; + } + break; + + case Utils.Constants.ComponentType.PRODUCT: { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.hierarchy, "url": '/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'} + ]; + } + break; + } + }; + + private initCreateAssetScope = ():void => { + switch (this.data.componentType){ + case Utils.Constants.ComponentType.RESOURCE: { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}, + {"name": StepNames.deploymentArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.deploymentArtifact);}}, + {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}}, + {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}} + ]; + this.$scope.modalTitle = "Create VF"; + break; + } + case Utils.Constants.ComponentType.SERVICE: { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}, + + ]; + this.$scope.modalTitle = "Create Service"; + break; + } + case Utils.Constants.ComponentType.PRODUCT: { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.hierarchy, "enabled":false, "callback": ()=> {return this.setCurrentStepByName(StepNames.hierarchy);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}} + ]; + this.$scope.modalTitle = "Create Product"; + break; + } + } + } + } +} diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts new file mode 100644 index 0000000000..353c487e0a --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts @@ -0,0 +1,164 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export class EditWizardViewModel extends WizardCreationBaseViewModel { + + static '$inject' = [ + '$scope', + 'data', + 'ComponentFactory', + '$modalInstance' + ]; + + constructor(public $scope:IWizardCreationScope, + public data:any, + public ComponentFactory: Sdc.Utils.ComponentFactory, + public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) { + + super($scope, data, ComponentFactory, $modalInstance); + this.type = WizardCreationTypes.edit; + this.init(); + this.initCreateAssetScope(); + + // Enable all wizard directive steps + this.enableAllWizardSteps(); + } + + private init = ():void => { + switch (this.data.component.componentType){ + case Utils.Constants.ComponentType.RESOURCE: { + if(this.data.component.isComplex()) { + this.assetCreationSteps = [ + { + "name": StepNames.general, + "url": '/app/scripts/view-models/wizard/general-step/general-step.html' + }, + { + "name": StepNames.icon, + "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html' + }, + { + "name": StepNames.deploymentArtifact, + "url": '/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html' + }, + { + "name": StepNames.informationArtifact, + "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html' + }, + { + "name": StepNames.properties, + "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html' + } + ]; + }else{ + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}, + {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'}, + {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'} + ]; + } + break; + } + case Utils.Constants.ComponentType.SERVICE: { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'} + ]; + break; + } + case Utils.Constants.ComponentType.PRODUCT: { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.hierarchy, "url": '/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'} + ]; + break; + } + } + }; + + private initCreateAssetScope = ():void => { + switch (this.data.component.componentType){ + case Utils.Constants.ComponentType.RESOURCE: { + if(this.data.component.isComplex()) { + this.$scope.directiveSteps = [ + { + "name": StepNames.general, "enabled": true, "callback": ()=> { + return this.setCurrentStepByName(StepNames.general); + } + }, + { + "name": StepNames.icon, "enabled": false, "callback": ()=> { + return this.setCurrentStepByName(StepNames.icon); + } + }, + { + "name": StepNames.deploymentArtifact, "enabled": false, "callback": ()=> { + return this.setCurrentStepByName(StepNames.deploymentArtifact); + } + }, + { + "name": StepNames.informationArtifact, "enabled": false, "callback": ()=> { + return this.setCurrentStepByName(StepNames.informationArtifact); + } + }, + { + "name": StepNames.properties, "enabled": false, "callback": ()=> { + return this.setCurrentStepByName(StepNames.properties); + } + } + ]; + }else{ + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}, + {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}}, + {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}} + ]; + } + this.$scope.modalTitle = "Edit " + this.data.component.resourceType; + break; + } + case Utils.Constants.ComponentType.SERVICE: { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}} + ]; + this.$scope.modalTitle = "Edit Service"; + break; + } + case Utils.Constants.ComponentType.PRODUCT: { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.hierarchy, "enabled":false, "callback": ()=> {return this.setCurrentStepByName(StepNames.hierarchy);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}} + ]; + this.$scope.modalTitle = "Edit Product"; + break; + } + } + } + } +} + diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts new file mode 100644 index 0000000000..5fe1bf7e59 --- /dev/null +++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/// +module Sdc.ViewModels.Wizard { + 'use strict'; + + export class ImportWizardViewModel extends WizardCreationBaseViewModel { + + static '$inject' = [ + '$scope', + 'data', + 'ComponentFactory', + '$modalInstance' + ]; + + constructor(public $scope:IWizardCreationScope, + public data:any, + public ComponentFactory: Sdc.Utils.ComponentFactory, + public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) { + + super($scope, data, ComponentFactory, $modalInstance ); + this.type = WizardCreationTypes.importAsset; + this.init(); + this.initImportAssetScope(); + } + + private init = ():void => { + this.assetCreationSteps = [ + {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'}, + {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}, + {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'}, + {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'} + ]; + }; + + private initImportAssetScope = ():void => { + this.$scope.directiveSteps = [ + {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}}, + {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}, + {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}}, + {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}} + ]; + + this.$scope.modalTitle = "Import Asset"; + }; + } +} -- cgit 1.2.3-korg