summaryrefslogtreecommitdiffstats
path: root/catalog-ui/app/scripts/view-models/wizard
diff options
context:
space:
mode:
authorMichael Lando <ml636r@att.com>2017-02-19 10:28:42 +0200
committerMichael Lando <ml636r@att.com>2017-02-19 10:51:01 +0200
commit451a3400b76511393c62a444f588a4ed15f4a549 (patch)
treee4f5873a863d1d3e55618eab48b83262f874719d /catalog-ui/app/scripts/view-models/wizard
parent5abfe4e1fb5fae4bbd5fbc340519f52075aff3ff (diff)
Initial OpenECOMP SDC commit
Change-Id: I0924d5a6ae9cdc161ae17c68d3689a30d10f407b Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/app/scripts/view-models/wizard')
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/ReadMe.txt54
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html137
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less107
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts228
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts304
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html147
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less45
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html48
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less50
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts168
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html270
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less34
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts381
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html40
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less125
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts149
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html26
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less55
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts150
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html57
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less55
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts123
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts163
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts250
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html133
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less7
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html12
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less60
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts399
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts114
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts164
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts64
32 files changed, 4119 insertions, 0 deletions
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 @@
+<div class="artifact-deployment-step" data-ng-controller="Sdc.ViewModels.Wizard.ArtifactDeploymentStepViewModel">
+
+ <div data-tests-id="add-deployment-artifact-button" data-tests-id="addGrey" class="w-sdc-classic-btn gray" data-ng-click="addOrUpdate({})">Add</div>
+
+ <div class="table-container-flex">
+
+ <div class="table">
+
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+
+ <form class="body" name="editForm">
+
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+
+ <!-- Artifact row -->
+ <div ng-if="noArtifactsToShow()" class="no-row-text" translate="DEPLOYMENT_ARTIFACT_NO_ARTIFACTS_TO_DISPLAY"></div>
+ <div data-ng-repeat-start="artifact in artifacts | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected || undefined==artifact.selected && updateInProgress}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="update(artifact)">
+ <loader data-display="isLoading"></loader>
+ <span class="sprite table-arrow" data-tests-id="{{artifact.artifactDisplayName}}" data-ng-class="{'opened': artifact.selected || undefined==artifact.selected && updateInProgress}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.timeout}}">
+ {{artifact.timeout? artifact.timeout:''}}
+ </div>
+
+ <div class="table-btn-col flex-item" sdc-keyboard-events>
+ <button class="table-edit-btn" data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="table-delete-btn" data-ng-click="delete(artifact)"> </button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected || undefined==artifact.selected && updateInProgress" class="w-sdc-form item-opened">
+ <!-- Artifact panel opened -->
+
+ <!-- Description field -->
+ <div class="w-sdc-form-item" ng-form="descriptionForm" data-ng-class="{error:(descriptionForm.$dirty && descriptionForm.$invalid)}">
+ <label class="i-sdc-env-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea {{$index}}"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-ng-required="true"
+ name="description"
+ data-ng-model="artifact.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="getValidationPattern('string')"
+ data-tests-id="description">
+ </textarea>
+
+ <div class="input-error" data-ng-show="descriptionForm.$dirty && descriptionForm.$invalid">
+ <span ng-show="descriptionForm.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="descriptionForm.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="descriptionForm.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!-- Parameters in 2 columns -->
+ <div class="w-sdc-form-columns-wrapper" data-ng-if="artifact.heatParameters">
+ <!-- Left column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm" data-ng-repeat="parameter in artifact.heatParameters.slice(0, artifact.heatParameters.length%2+artifact.heatParameters.length/2) | orderBy: 'name' track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+ <!-- Right column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm" data-ng-repeat="parameter in artifact.heatParameters.slice(artifact.heatParameters.length%2+artifact.heatParameters.length/2) | orderBy: 'name' track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+ </div><!-- Close: Parameters in 2 columns -->
+ </div><!-- Close: Artifact panel opened -->
+
+ <!-- Add artifacts buttons -->
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ data-ng-show="!artifact.esId"
+ data-tests-id="{{artifact.artifactDisplayName}}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+
+ <!-- Top add button -->
+ <button class="add-button" translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER" data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </form>
+ </div>
+ </div>
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ interface IArtifactDeploymentStepViewModelScope extends IWizardCreationStepScope {
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+ component: Models.Components.Component;
+ artifacts: Array<Models.ArtifactModel>;
+ 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<Models.ArtifactModel>= [];
+ _.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 = <ArtifactModel[]>_.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 = <ArtifactModel[]>_.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 = <ArtifactModel[]>_.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 = <ArtifactModel[]>_.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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+
+ export interface IEditArtifactStepModel {
+ artifactResource: Models.ArtifactModel;
+ artifactTypes: Array<string>;
+ artifactsFormList: any;
+ artifactFile:any;
+ }
+
+ export interface IArtifactResourceFormStepViewModelScope extends ng.IScope {
+ editForm:ng.IFormController;
+ forms:any;
+ footerButtons: Array<any>;
+ 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<string>;
+ 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() && (<Resource>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 = <Models.ArtifactModel>_.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 = <Models.ArtifactModel>{};
+ 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 @@
+<sdc-modal modal="modalInstanceArtifact" type="classic" class="sdc-add-artifact" buttons="footerButtons" header="{{getFormTitle()}}" show-close-button="true">
+
+ <loader data-display="isLoading"></loader>
+
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm">
+
+ <!--------------------- ARTIFACT FILE -------------------->
+ <div class="i-sdc-form-item">
+ <label class="required">Upload File</label>
+ <file-upload id="fileUploadElement"
+ form-element="forms.editForm"
+ element-required="{{fileUploadRequired()}}"
+ element-name="myArtifactFile"
+ file-model="editArtifactResourceModel.artifactFile"
+ extensions="{{fileExtensions()}}"
+ data-ng-class="{'error': forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid">
+ <span ng-show="forms.editForm.myArtifactFile.$error.required && !forms.editForm.myArtifactFile.$error.emptyFile" translate="ADD_ARTIFACT_ERROR_FILE_REQUIRED"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span>
+ <span ng-if="'DEPLOYMENT' === artifactGroupType" ng-show="forms.editForm.myArtifactFile.$error.filetype" translate="ADD_ARTIFACT_ERROR_VALID_EXTENSIONS" translate-values="{'extensions': '{{fileExtensions()}}'}"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ </div>
+ </div>
+ <!--------------------- ARTIFACT FILE -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.placeHolder.$dirty && forms.editForm.placeHolder.$invalid)}" data-ng-show="isPlaceHolderArtifact">
+ <label class="i-sdc-form-label required">Artifact</label>
+ <select class="i-sdc-form-select"
+ name="placeHolder"
+ data-ng-disabled="!isNew"
+ data-ng-model="selectedArtifact"
+ data-ng-change="changeArtifact(selectedArtifact)"
+ data-tests-id="selectArtifact">
+ <option disabled value="?">Select Artifact</option>
+ <option data-ng-repeat="(key,value) in editArtifactResourceModel.artifactsFormList">{{value.artifactDisplayName}}</option>
+ <option value="">Create New Artifact</option>
+
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.placeHolder.$dirty && forms.editForm.placeHolder.$invalid">
+ <span ng-show="forms.editForm.placeHolder.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid)}" data-ng-show="!isPlaceHolderArtifact">
+ <label class="i-sdc-form-label required">Artifact Label</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactLabel"
+ type="text"
+ name="artifactLabel"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="labelValidationPattern"
+ maxlength="25"
+ data-ng-disabled="!isNew || editArtifactResourceModel.artifactResource.mandatory"
+ data-tests-id="artifactLabel"
+ autofocus/>
+ <span class="w-sdc-icon-cancel" data-ng-click="selectedArtifact='?'; removeInputLabel()"></span>
+
+ <div class="input-error" data-ng-show="forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid">
+ <span ng-show="forms.editForm.artifactLabel.$error.required" translate="ADD_ARTIFACT_ERROR_LABEL_REQUIRED"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid)}"
+ data-ng-if="isDeploymentHeat()">
+ <label class="i-sdc-form-label">Deployment Timeout (minutes)</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.timeout"
+ type="number"
+ name="timeout"
+ min="1"
+ max="2147483647"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="integerValidationPattern"
+ data-ng-init="setDefaultTimeout()"
+ data-ng-change="setDefaultTimeout()"
+ maxlength="25"
+ data-tests-id="timeout"/>
+
+ <div class="input-error" data-ng-show="forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid">
+ <span ng-show="forms.editForm.timeout.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.timeout.$error.pattern" translate="ADD_ARTIFACT_ERROR_TIMEOUT_PATTERN"></span>
+ <span ng-show="forms.editForm.timeout.$error.min" translate="ADD_ARTIFACT_ERROR_TIMEOUT_MIN"></span>
+ </div>
+ </div>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="type"
+ data-ng-disabled="!isNew || editArtifactResourceModel.artifactResource.mandatory || '?'==selectedArtifact"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-options="type as type for type in editArtifactResourceModel.artifactTypes track by type | uppercase"
+ data-tests-id="artifacttype">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-required
+ name="description"
+ data-ng-model="editArtifactResourceModel.artifactResource.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="commentValidationPattern"
+ data-tests-id="description"
+ >
+ </textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+
+ </form>
+
+</sdc-modal>
+
+
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 @@
+<div class="artifact-step" data-ng-controller="Sdc.ViewModels.Wizard.ArtifactInformationStepViewModel">
+ <div data-tests-id="add-information-artifact-button" data-tests-id="addGrey" class="w-sdc-classic-btn gray" data-ng-click="addOrUpdate({})" type="button">Add </div>
+ <div class="table-container-flex">
+ <div class="table">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="showNoArtifactMessage()" class="no-row-text">
+ There are no information artifacts to display
+ </div>
+ <div data-ng-repeat-start="artifact in artifacts| orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected">
+ <span class="sprite table-arrow" data-ng-class="{'opened': artifact.selected}" data-tests-id="{{artifact.artifactDisplayName}}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+
+ <div class="table-btn-col flex-item" sdc-keyboard-events>
+ <button class="table-edit-btn" data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="table-delete-btn" data-ng-click="delete(artifact)"> </button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected" class="item-opened" data-ng-bind="artifact.description"></div>
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ data-ng-show="!artifact.esId"
+ data-tests-id="{{artifact.artifactDisplayName}}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="add-button" translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER"
+ data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ export interface IArtifactInformationStepScope extends IWizardCreationScope {
+ artifacts: Array<Models.ArtifactModel>;
+ tableHeadersList: Array<any>;
+ 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 = <ArtifactModel[]>_.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 = <ArtifactModel[]>_.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 = <ArtifactModel[]>_.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 @@
+<div include-padding="true" class="sdc-wizard-general-step">
+ <div ng-controller="Sdc.ViewModels.Wizard.GeneralStepViewModel">
+ <form novalidate class="w-sdc-form" name="editForm">
+ <div class="w-sdc-form-section-container">
+
+ <!--------------------- IMPORT TOSCA FILE -------------------->
+ <file-upload id="fileUploadElement"
+ ng-if="!isCreate"
+ element-name="fileElement"
+ element-disabled="{{!isNew}}"
+ form-element="editForm"
+ file-model="model.tosca"
+ extensions="{{toscaFileExtensions}}"
+ data-ng-class="{'error': !editForm.fileElement.$valid || !model.tosca.filename}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="!isCreate && (!editForm.fileElement.$valid || !model.tosca.filename)">
+ <!-- editForm.fileElement.$error.required <== Can not use this, because the browse is done from outside for the first time -->
+ <span ng-show="!model.tosca.filename && !editForm.fileElement.$error.emptyFile" translate="NEW_SERVICE_RESOURCE_ERROR_TOSCA_FILE_REQUIRED"></span><!-- Required -->
+ <span ng-show="editForm.fileElement.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ <span ng-show="editForm.fileElement.$error.filetype" translate="NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS" translate-values="{'extensions': '{{toscaFileExtensions}}' }"></span>
+ </div>
+ <!--------------------- IMPORT TOSCA FILE -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- NAME -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.componentName)}">
+ <label class="i-sdc-form-label required">Name</label>
+ <input class="i-sdc-form-input"
+ name="componentName"
+ data-ng-init="isNew && validateName(true)"
+ data-ng-change="validateName()"
+ data-ng-maxlength="{{component.isProduct()?'25':'50'}}"
+ maxlength="{{component.isProduct()?'25':'50'}}"
+ data-ng-minlength="{{component.isProduct()?'4':'0'}}"
+ minlength="{{component.isProduct()?'4':'0'}}"
+ data-ng-model="model.name"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.validationPattern"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-tests-id="name"
+ autofocus
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.componentName)">
+ <span ng-show="editForm.componentName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.componentName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.componentName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.componentName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.componentName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- FULL NAME -------------------->
+ <div ng-if="component.isProduct()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.fullName)}">
+ <label class="i-sdc-form-label required">Full Name</label>
+ <input class="i-sdc-form-input"
+ name="fullName"
+ data-ng-change="validateName()"
+ data-ng-maxlength="100"
+ maxlength="100"
+ data-ng-minlength="4"
+ minlength="4"
+ data-ng-model="model.fullName"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.validationPattern"
+ data-tests-id="fullName"
+ autofocus
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.fullName)">
+ <span ng-show="editForm.fullName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.fullName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.fullName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.fullName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.fullName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- DESCRIPTION -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.description)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea description"
+ name="description"
+ data-ng-maxlength="1024"
+ data-required
+ data-ng-model="model.description"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.commentValidationPattern"
+ maxlength="1024"
+ data-tests-id="description"></textarea>
+ <!-- placeholder="Description here..." -->
+
+ <div class="input-error" data-ng-show="validateField(editForm.description)">
+ <span ng-show="editForm.description.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '1024' }"></span>
+ <span ng-show="editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- DESCRIPTION -------------------->
+
+ <!--------------------- CATEGORIES -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.category)}"
+ data-ng-if="categories && categories.length && !component.isProduct()">
+ <label class="i-sdc-form-label required">Category</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="category"
+ data-ng-change="setIconToDefault()"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-ng-model="model.category"
+ data-tests-id="selectGeneralCategory"
+ >
+ <option value="">Select category</option>
+ <optgroup ng-if="component.isResource()" data-ng-repeat="mainCategory in categories | orderBy:['name']" label="{{mainCategory.name}}" data-tests-id="{{mainCategory.name}}">
+ <option data-ng-repeat="subCategory in mainCategory.subcategories track by $index"
+ data-ng-selected="model.category===calculateUnique(mainCategory.name,subCategory.name)"
+ data-tests-id="{{subCategory.name}}"
+ value="{{calculateUnique(mainCategory.name,subCategory.name)}}">{{subCategory.name}}
+
+ </option>
+ </optgroup>
+ <option ng-if="component.isService()" data-ng-repeat="mainCategory in categories | orderBy:['name']"
+ data-ng-selected="model.category===mainCategory.name"
+ value="{{mainCategory.name}}"
+ data-tests-id="{{mainCategory.name}}">{{mainCategory.name}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="validateField(editForm.category)">
+ <span ng-show="editForm.category.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED"></span>
+ </div>
+ </div>
+ <!--------------------- CATEGORIES -------------------->
+
+ <!--------------------- VENDOR NAME -------------------->
+ <div ng-if="component.isResource()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.vendorName)}">
+ <label class="i-sdc-form-label required">Vendor</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.vendorName"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ ng-click="oldValue = model.vendorName"
+ name="vendorName"
+ data-ng-change="onVendorNameChange(oldValue)"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-tests-id="vendorName"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorName)">
+ <span ng-show="editForm.vendorName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_NAME_REQUIRED"></span>
+ <span ng-show="editForm.vendorName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="editForm.vendorName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!--------------------- VENDOR NAME -------------------->
+
+ <!--------------------- VENDOR RELEASE -------------------->
+ <div ng-if="component.isResource()"
+ class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.vendorRelease)}">
+ <label class="i-sdc-form-label required">Vendor Release</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.vendorRelease"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ name="vendorRelease"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-tests-id="vendorRelease"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorRelease)">
+ <span ng-show="editForm.vendorRelease.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_RELEASE_REQUIRED"></span>
+ <span ng-show="editForm.vendorRelease.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="editForm.vendorRelease.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- VENDOR RELEASE -------------------->
+
+
+
+ </div><!-- Close w-sdc-form-column -->
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- RESOURCE TAGS -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.tags)}">
+ <label class="i-sdc-form-label">Tags</label>
+
+ <sdc-tags form-element="editForm" element-name="tags" max-tags="20" class="i-sdc-form-item-tags" tags="model.tags" pattern="validation.tagValidationPattern" special-tag="model.name"></sdc-tags>
+
+ <div class="input-error" data-ng-show="validateField(editForm.tags)">
+ <span ng-show="editForm.tags.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- RESOURCE TAGS -------------------->
+
+ <!--------------------- CONTACT ID -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.contactId)}">
+ <label class="i-sdc-form-label required" translate="GENERAL_LABEL_CONTACT_ID"></label>
+ <input class="i-sdc-form-input disabled" type="text"
+ data-ng-model="model.userId"
+ data-required
+ name="contactId"
+ data-ng-pattern="validation.contactIdValidationPattern"
+ data-ng-model-options="{ debounce: 500 }"
+ data-tests-id="userId"
+ maxlength="50"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.contactId)">
+ <span ng-show="editForm.contactId.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED"></span>
+ <span ng-show="editForm.contactId.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID"></span>
+ </div>
+ </div>
+ <!--------------------- CONTACT ID -------------------->
+
+ <!--------------------- PROJECT CODE -------------------->
+ <div class="i-sdc-form-item" data-ng-if="!component.isResource()"
+ data-ng-class="{'error': validateField(editForm.projectCode)}">
+ <label class="i-sdc-form-label required" translate="GENERAL_LABEL_PROJECT_CODE"></label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.projectCode"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="128"
+ data-required
+ name="projectCode"
+ data-ng-pattern="validation.projectCodeValidationPattern"
+ maxlength="50"
+ data-tests-id="projectCode"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.projectCode)">
+ <span ng-show="editForm.projectCode.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_REQUIRED"></span>
+ <span ng-show="editForm.projectCode.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_NOT_VALID"></span>
+ </div>
+ </div>
+ <!--------------------- VENDOR RELEASE -------------------->
+
+
+ </div><!-- Close w-sdc-form-column -->
+
+ </div><!-- Close w-sdc-form-column -->
+
+ <div class="w-sdc-form-messages-wrapper">
+ <span class="w-sdc-form-messages-msg" data-ng-show="isSaved"><span class="w-sdc-form-messages-msg-v"></span>Your resource has been saved</span>
+ </div>
+
+ </div><!-- Close w-sdc-form-section-container -->
+
+ </form>
+
+ </div>
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+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<string>;
+ 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<IMainCategory>;
+ 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()) {
+ (<Models.Components.Resource>this.$scope.component).resourceType = "VF";
+
+ // Handle the tosca file
+ if (this.$scope.model.tosca && this.$scope.isNew) {
+ (<Models.Components.Resource>this.$scope.component).payloadData = this.$scope.model.tosca.base64;
+ (<Models.Components.Resource>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;
+ (<Models.Components.Product>this.$scope.component).contacts = new Array<string>();
+ (<Models.Components.Product>this.$scope.component).contacts.push(this.$scope.component.contactId);
+ (<Models.Components.Product>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 = (<Models.Components.Product>component).fullName;
+
+ }
+
+ };
+
+ // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except)
+ private convertCategoryStringToOneArray = ():Array<Models.IMainCategory> => {
+ 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 = <Models.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 = <Models.IMainCategory> mainCategoryClone;
+
+ let result:Array<Models.IMainCategory> = [];
+ result.push(tmpSelected);
+
+ return result;
+ };
+
+ private convertCategoryOneArrayToString = (categories:Array<Models.IMainCategory>):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 @@
+<div class="hierarchy-step" data-ng-controller="Sdc.ViewModels.Wizard.HierarchyStepViewModel">
+ <div class="dropdown-container" clicked-outside="{onClickedOutside: 'clickOutside()', clickedOutsideEnable: 'true'}" >
+ <input placeholder="Add Group" data-ng-click="onInputTextClicked()" class="dropdown-input-text" data-ng-model="search.filterTerms" data-ng-model-options="{debounce: 200}"/>
+ <div data-ng-class="{'show': showDropDown}" class="dropdown-content" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="false" class="scrollbar-container">
+ <div ng-repeat="category in categoriesOptions track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="dropdown-option" ng-show="!category.filteredGroup || category.filteredGroup.length > 0">
+ <div class="category-container">
+ <div class="category">{{category.name}}</div>
+ <div class="subcategory">{{subcategory.name}}</div>
+ </div>
+ <div class="groupings-container">
+ <div ng-init="group.filterTerms = group.name + ' ' + category.name + ' ' + subcategory.name"
+ ng-repeat="group in (category.filteredGroup = (subcategory.groupings | filter:search )) track by $index">
+ <div class="group" data-ng-disabled="group.isDisabled" data-ng-class="{'disabled-group': group.isDisabled}" ng-click="onGroupSelected(category, subcategory, group)">
+ <span >{{group.name}}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ <div class="hierarchy-groups-container">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div ng-if="!product.categories.length || product.categories.length === 0" class="no-groups-text" translate="NEW_PRODUCT_NO_CATEGORIES_TO_DISPLAY"></div>
+ <div ng-repeat="category in product.categories track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="group-tag" ng-repeat="group in subcategory.groupings track by $index"
+ data-ng-init="tooltip = '<b>' + category.name + '</b><br />' + subcategory.name">
+ <sdc-tag data-on-delete="deleteGroup(uniqueId)" data-tag-data="{tag: group.name, tooltip: tooltip, id: group.uniqueId }"></sdc-tag>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IHierarchyStepScope extends IWizardCreationScope {
+
+ categoriesOptions: Array<Models.IMainCategory>;
+ 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 = <Models.Components.Product>this.$scope.getComponent();
+ this.initScope();
+ }
+
+ private initCategories = () => {
+ this.$scope.categoriesOptions = angular.copy(this.cacheService.get('productCategories'));
+ let selectedGroup:Array<Models.IGroup> = [];
+ _.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 = <Models.Components.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 @@
+<div class="icons-step" data-ng-controller="Sdc.ViewModels.Wizard.IconsStepViewModel">
+
+ <form novalidate class="w-sdc-form" name="iconForm">
+ <label class="i-sdc-form-label icons-label required">Icons</label>
+ <div class="selected-icon-container">
+ <div class="i-sdc-form-item-suggested-icon medium selected-icon {{iconSprite}} {{component.icon}}"
+ ng-model="component.icon"
+ tooltips tooltip-content='{{component.icon | translate}}'
+ >
+ </div>
+ </div>
+
+ <label class="i-sdc-form-label icons-label required">Select one of the icons below for the asset</label>
+ <div class="i-sdc-form-item suggested-icons-container">
+ <div class ="suggested-icon-wrapper" ng-class="component.icon==='{{iconSrc}}' ? 'selected' : '' " data-ng-repeat="iconSrc in icons track by $index">
+ <div class="i-sdc-form-item-suggested-icon medium {{iconSprite}} {{iconSrc}}" data-ng-class="component.isAlreadyCertified()? 'disable':'hand'"
+ ng-model="component.icon"
+ data-tests-id="{{iconSrc}} iconBox"
+ data-ng-click="!component.isAlreadyCertified() && setComponentIcon(iconSrc)"
+ tooltips tooltip-content='{{iconSrc | translate}}'
+ >
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IIconsStepScope extends IWizardCreationStepScope{
+ icons : Array<string>;
+ 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<string>();
+ 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<string> = 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 @@
+<div class="properties-step" data-ng-controller="Sdc.ViewModels.Wizard.PropertiesStepViewModel">
+
+ <div class="w-sdc-classic-btn gray" data-tests-id="addGrey" data-ng-click="addOrUpdateProperty()">Add</div>
+
+ <div class="table-container-flex">
+
+ <div class="table">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ <!--div class="table-no-text-header head-row flex-item"></div-->
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="component.properties.length === 0 " class="no-row-text">
+ There are no properties to display <br>
+ click <a data-ng-click="addOrUpdateProperty()">here</a> to add one
+ </div>
+ <div data-ng-repeat-start="property in component.properties | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row" data-ng-class="{'selected': property.selected}"
+ data-ng-click="property.selected = !property.selected">
+
+ <div class="table-col-general flex-item text">
+ <span class="sprite table-arrow" data-ng-class="{'opened': property.selected}"></span>
+ <span data-tests-id="{{property.name}}" tooltips tooltip-content="{{property.name}}">{{property.name}}</span>
+
+ </div>
+
+ <div class="table-col-general flex-item text" data-tests-id="{{property.type}}" data-ng-bind="property.type"></div>
+
+ <div class="table-col-general flex-item text">
+ <span tooltips tooltip-content="{{property.defaultValue}}" data-tests-id="{{property.defaultValue}}" data-ng-bind="property.defaultValue"></span>
+ </div>
+
+ <div class="table-btn-col flex-item">
+ <button class="table-edit-btn" data-ng-show="property.parentUniqueId==component.uniqueId"
+ data-ng-click="addOrUpdateProperty(property); $event.stopPropagation();"> </button>
+ <button class="table-delete-btn" data-ng-show="property.parentUniqueId==component.uniqueId"
+ data-ng-click="delete(property); $event.stopPropagation();"> </button>
+ </div>
+
+ <!--div class="table-btn-col flex-item">
+
+ </div-->
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="property.selected && property.description" class="item-opened" data-ng-bind="property.description">
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+
+</div>
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=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ interface IPropertiesStepViewModelScope extends IWizardCreationStepScope {
+ component: Models.Components.Component;
+ tableHeadersList: Array<any>;
+ 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 <Models.Components.Component> 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=========================================================
+ */
+/// <reference path="../../../references"/>
+
+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=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IEditPropertyModel{
+ property: Models.PropertyModel;
+ types: Array<string>;
+ simpleTypes: Array<string>;
+ sources: Array<string>;
+ }
+
+ export interface IPropertyFormViewModelScope extends ng.IScope{
+
+ $$childTail: any;
+ editForm:ng.IFormController;
+ forms:any;
+ footerButtons: Array<any>;
+ 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 @@
+<sdc-modal modal="modalInstanceProperty" type="classic" class="sdc-add-property" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true">
+
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Name</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew || editPropertyModel.property.readonly"
+ maxlength="50"
+ data-ng-model="editPropertyModel.property.name"
+ type="text"
+ name="propertyName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="propertyName"
+ autofocus />
+
+ <div class="input-error" data-ng-show="forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid">
+ <span ng-show="forms.editForm.propertyName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property name' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}"
+ data-ng-if="showSchema()">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="schemaType"
+ data-tests-id="schemaType"
+ data-ng-change="onTypeChange(false)"
+ data-ng-model="editPropertyModel.property.schema.property.type"
+ data-ng-options="type for type in editPropertyModel.simpleTypes">
+ <option value="">Choose Schema Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid">
+ <span ng-show="forms.editForm.schemaType.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <label class="i-sdc-form-label">Default Value</label>
+ <input class="i-sdc-form-input"
+ ng-if="!(editPropertyModel.property.type == 'boolean')"
+ data-ng-maxlength="100"
+ data-ng-disableddddddd="editPropertyModel.property.readonly && !isService && !isPropertyValueOwner()"
+ maxlength="100"
+ data-ng-model="editPropertyModel.property.defaultValue"
+ type="text"
+ name="value"
+ data-custom-validation="" data-validation-func="validateUniqueKeys"
+ data-ng-pattern="getValidation()"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editPropertyModel.property.defaultValue)) || onValueChange())"
+ data-tests-id="defaultValue"
+ autofocus />
+ <select class="i-sdc-form-select"
+ ng-if="editPropertyModel.property.type == 'boolean'"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="editPropertyModel.property.defaultValue">
+ <option value=""></option>
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="{{getValidationTranslate()}}"></span>
+ <span ng-show="forms.editForm.value.$error.customValidation" translate="PROPERTY_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+
+ </div>
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ data-ng-disableddddddd="editPropertyModel.property.readonly"
+ data-tests-id="propertyType"
+ name="type"
+ data-ng-change="onTypeChange(true)"
+ data-ng-model="editPropertyModel.property.type"
+ data-ng-options="type for type in editPropertyModel.types">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disableddddddd="editPropertyModel.property.readonly"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editPropertyModel.property.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"
+ ></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </form>
+
+</sdc-modal>
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 @@
+<loader data-display="isLoading"></loader>
+<div class="sdc-wizard-name-type-label" data-ng-if="assetName!==undefined"><span class="sprite sprite-green-tick animated flash"></span><span class="name">{{assetName}}</span>&nbsp;|&nbsp;<span class="type">{{assetType}}</span></div>
+<sdc-modal modal="modalInstance" type="classic" get-close-modal-response="getComponent" buttons="footerButtons" class="sdc-wizard" header="{{modalTitle}}" show-close-button="true">
+ <div class="sdc-wizard-wrapper">
+ <div class="sdc-wizard-left-content">
+ <sdc-wizard-step steps="directiveSteps" control="assetCreationControl" class="wizard-steps-line"></sdc-wizard-step>
+ </div>
+ <div class="sdc-wizard-right-content">
+ <ng-include src="templateUrl" class="sdc-wizard-right-include"></ng-include>
+ </div>
+ </div>
+</sdc-modal>
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=========================================================
+ */
+/// <reference path="../../references"/>
+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<Sdc.Directives.IWizardStep>; // 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 <ng-include> via HTML
+ footerButtons: Array<Sdc.Directives.ISdcModalButton>; // 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.
+ */
+/// <reference path="../../../references"/>
+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=========================================================
+ */
+/// <reference path="../../../references"/>
+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=========================================================
+ */
+/// <reference path="../../../references"/>
+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";
+ };
+ }
+}