diff options
Diffstat (limited to 'catalog-ui/src/app/view-models/forms')
24 files changed, 3241 insertions, 0 deletions
diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts new file mode 100644 index 0000000000..3e912706e0 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts @@ -0,0 +1,358 @@ +'use strict'; +import {ArtifactModel, Resource, Component} from "app/models"; +import {ArtifactsUtils, FormState, ValidationUtils, ArtifactType} from "app/utils"; +import {CacheService} from "app/services"; + +export interface IEditArtifactModel { + artifactResource:ArtifactModel; + artifactTypes:Array<string>; + artifactFile:any; +} + +export interface IArtifactResourceFormViewModelScope extends ng.IScope { + forms:any; + $$childTail:any; + isNew:boolean; + isLoading:boolean; + validationPattern:RegExp; + urlValidationPattern:RegExp; + labelValidationPattern:RegExp; + integerValidationPattern:RegExp; + commentValidationPattern:RegExp; + artifactType:string; + editArtifactResourceModel:IEditArtifactModel; + defaultHeatTimeout:number; + validExtensions:any; + originalArtifactName:string; + editForm:ng.IFormController; + footerButtons:Array<any>; + modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance; + + fileExtensions():string; + save(doNotCloseModal?:boolean):void; + saveAndAnother():void; + close():void; + getOptions():Array<string>; + isDeploymentHeat():boolean; + onFileChange():void; + setDefaultTimeout():void; + openEditEnvParametersModal(artifact:ArtifactModel):void; + getFormTitle():string; + fileUploadRequired():string; + isArtifactOwner():boolean; +} + +export class ArtifactResourceFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'artifact', + 'Sdc.Services.CacheService', + 'ValidationPattern', + 'UrlValidationPattern', + 'LabelValidationPattern', + 'IntegerValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + '$base64', + '$state', + 'ArtifactsUtils', + '$uibModal', + 'component' + ]; + + private formState:FormState; + private entityId:string; + + constructor(private $scope:IArtifactResourceFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private artifact:ArtifactModel, + private cacheService:CacheService, + private ValidationPattern:RegExp, + private UrlValidationPattern:RegExp, + private LabelValidationPattern:RegExp, + private IntegerValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils, + private $base64:any, + private $state:any, + private artifactsUtils:ArtifactsUtils, + private $uibModal:ng.ui.bootstrap.IModalService, + private component:Component) { + + + this.entityId = this.component.uniqueId; + this.formState = angular.isDefined(artifact.artifactLabel) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initEntity = ():void => { + this.$scope.editArtifactResourceModel.artifactResource = this.artifact; + this.$scope.originalArtifactName = this.artifact.artifactName; + }; + + + private initFooterButtons = ():void => { + + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save} + ]; + if (this.$scope.isNew) { + this.$scope.footerButtons.push({ + 'name': 'Add Another', + 'css': 'grey', + 'disabled': !this.$scope.isNew && 'deployment' === this.$scope.artifactType, + 'callback': this.$scope.saveAndAnother + }); + } + }; + + private filterDeploymentArtifactTypeByResourceType = (resourceType:string):any => { + let result = {}; + _.each(this.$scope.validExtensions, function (typeSettings:any, typeName:string) { + if (!typeSettings.validForResourceTypes || typeSettings.validForResourceTypes.indexOf(resourceType) > -1) { + result[typeName] = typeSettings; + } + }); + + return result; + }; + + private initArtifactTypes = ():void => { + + let artifactTypes:any = this.cacheService.get('UIConfiguration'); + + if ('deployment' === this.$scope.artifactType) { + + + if ('HEAT_ENV' == this.artifact.artifactType || this.component.selectedInstance) { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceInstanceDeploymentArtifacts; + } else if (this.component.isResource()) { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts; + this.$scope.validExtensions = this.filterDeploymentArtifactTypeByResourceType((<Resource>this.component).resourceType); + } else { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.serviceDeploymentArtifacts; + } + + if (this.$scope.validExtensions) { + this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions); + } + this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout; + if (this.$scope.isNew) { + let isHeat = 'HEAT_ENV' == this.artifact.artifactType; + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return 'HEAT' == item.substring(0, 4) || (!isHeat && item == "VF_MODULES_METADATA") || + _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item); + }); + } + + } + if (this.$scope.artifactType === 'informational') { + this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> { + return element.name; + }); + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) || + _.has(ArtifactType.TOSCA, item); + }) + } + + if (this.component.isResource() && (<Resource>this.component).isCsarComponent()) { + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => { + return this.artifactsUtils.isLicenseType(item); + }) + } + + }; + + private initEditArtifactResourceModel = ():void => { + this.$scope.editArtifactResourceModel = { + artifactResource: null, + artifactTypes: null, + artifactFile: {} + }; + + this.initEntity(); + }; + + private initScope = ():void => { + + 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.isNew = (this.formState === FormState.CREATE); + this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name); + this.$scope.modalInstanceArtifact = this.$uibModalInstance; + + this.initEditArtifactResourceModel(); + this.initArtifactTypes(); + + // In case of edit, show the file name in browse. + if (this.artifact.artifactName !== "" && 'HEAT_ENV' !== this.artifact.artifactType) { + this.$scope.editArtifactResourceModel.artifactFile = {}; + this.$scope.editArtifactResourceModel.artifactFile.filename = this.artifact.artifactName; + } + + //scope methods + this.$scope.isDeploymentHeat = ():boolean => { + return !this.$scope.isNew && this.$scope.artifactType === 'deployment' + && this.$scope.editArtifactResourceModel.artifactResource.isHEAT(); + + }; + this.$scope.onFileChange = ():void => { + if (this.$scope.editArtifactResourceModel.artifactFile && this.$scope.editArtifactResourceModel.artifactFile.filename) { + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename; + } else { + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.originalArtifactName; + } + }; + this.$scope.setDefaultTimeout = ():void => { + if (this.$scope.isDeploymentHeat() && !this.$scope.editArtifactResourceModel.artifactResource.timeout) { + this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout; + } + + if (this.$scope.editArtifactResourceModel.artifactFile.filename) { + this.$scope.editArtifactResourceModel.artifactFile = {}; + this.$scope.forms.editForm.myArtifactFile.$setValidity('required', false); + } + }; + + 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.save = (doNotCloseModal?:boolean):void => { + this.$scope.isLoading = true; + this.$scope.editArtifactResourceModel.artifactResource.description = this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description); + + if (!this.$scope.isDeploymentHeat()) { + this.$scope.editArtifactResourceModel.artifactResource.timeout = null; + } + + 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 onFaild = (response):void => { + this.$scope.isLoading = false; + console.info('onFaild', response); + }; + + let onSuccess = (artifactResource:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$scope.originalArtifactName = ""; + + if (this.$scope.isDeploymentHeat()) { + if (artifactResource.heatParameters) { + this.$scope.openEditEnvParametersModal(artifactResource); + } + } + + if (!doNotCloseModal) { + this.$uibModalInstance.close(); + } else { + this.$scope.editArtifactResourceModel.artifactFile = null; + angular.element("input[type='file']").val(null); // for support chrome when upload the same file + this.artifactsUtils.addAnotherAfterSave(this.$scope); + } + + }; + + if ('HEAT_ENV' == this.artifact.artifactType) { + if (this.component.selectedInstance) { + this.component.uploadInstanceEnvFile(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } else { + this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + + } + } else if (this.$scope.isArtifactOwner()) { + this.component.addOrUpdateInstanceArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } else { + this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } + }; + + this.$scope.isArtifactOwner = ():boolean=> { + return this.component.isService() && !!this.component.selectedInstance; + }; + + this.$scope.saveAndAnother = ():void => { + this.$scope.save(true); + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.fileUploadRequired = ():string => { + if (this.$scope.editArtifactResourceModel.artifactFile.filename) { + // This is edit mode + return 'false'; + } else { + return 'true'; + } + }; + + this.$scope.getFormTitle = ():string => { + if ('HEAT_ENV' == this.artifact.artifactType) { + return 'Update HEAT ENV'; + } + if (this.$scope.isDeploymentHeat()) { + if (!this.$scope.editArtifactResourceModel.artifactResource.artifactChecksum) { + return 'Add HEAT Template'; + } + return 'Update HEAT Template'; + } + if (this.$scope.isNew) { + return 'Add Artifact'; + } + return 'Update Artifact'; + }; + + this.$scope.openEditEnvParametersModal = (artifactResource:ArtifactModel):void => { + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../env-parameters-form/env-parameters-form.html', + controller: 'Sdc.ViewModels.EnvParametersFormViewModel', + size: 'sdc-md', + backdrop: 'static', + resolve: { + artifact: ():ArtifactModel => { + return artifactResource; + }, + component: ():Component => { + return this.component; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + modalInstance + .result + .then(():void => { + }); + }; + + this.$scope.forms = {}; + + this.initFooterButtons(); + + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + if(this.$scope.forms.editForm) { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + if (this.$scope.isNew) { + this.$scope.footerButtons[1].disabled = this.$scope.forms.editForm.$invalid; + } + } + }); + + } +} diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html new file mode 100644 index 0000000000..0984c6872d --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html @@ -0,0 +1,169 @@ +<sdc-modal modal="modalInstanceArtifact" type="classic" class="sdc-add-artifact" buttons="footerButtons" header="{{getFormTitle()}}" show-close-button="true" get-close-modal-response="close" data-tests-id="sdc-add-artifact"> + + <loader data-display="isLoading"></loader> + + <div class="sdc-edit-artifact-form-container" + data-ng-class="{'mandatory-artifact': (editArtifactResourceModel.artifactResource.mandatory && artifactType !=='deployment') || artifactType === 'api'}"> + <form novalidate class="w-sdc-form" name="forms.editForm"> + + <!--------------------- ARTIFACT FILE START--------------------> + <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()}}" + element-disabled="{{!editArtifactResourceModel.artifactResource.artifactType}}" + 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="artifactType === 'deployment'" 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 END --------------------> + + <div class="w-sdc-form-columns-wrapper"> + + <div class="w-sdc-form-column" data-ng-if="artifactType === 'deployment' || (!editArtifactResourceModel.artifactResource.mandatory && artifactType !== 'api')"> + + <div class="i-sdc-form-item" + data-ng-class="{error:(forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid)}" + data-ng-if="!isDeploymentHeat()"> + <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" + data-tests-id="artifactLabel" + autofocus/> + + <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.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" + data-ng-change="setDefaultTimeout()" + 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 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-disabled="'HEAT_ENV'==editArtifactResourceModel.artifactResource.artifactType" + 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><!-- w-sdc-form-column --> + + <div class="w-sdc-form-column i-sdc-form-url" data-ng-if="artifactType==='api'"> + + <div class="i-sdc-form-item" + data-ng-class="{error:(forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid)}"> + <label class="i-sdc-form-label required">URL</label> + <input class="i-sdc-form-input" + data-ng-maxlength="100" + data-ng-model="editArtifactResourceModel.artifactResource.apiUrl" + data-ng-model-options="{ debounce: 200 }" + type="url" + name="apiUrl" + data-required + ng-pattern="urlValidationPattern" + maxlength="100" + autofocus + invalid-characters=',#?&@$<>~^`\[]{}|")(*!+=;%' /> + + <div class="input-error" data-ng-show="forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid"> + <span ng-show="forms.editForm.apiUrl.$error.required" translate="ADD_ARTIFACT_ERROR_APIURL_REQUIRED"></span> + <span ng-show="forms.editForm.apiUrl.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span> + <span ng-show="forms.editForm.apiUrl.$error.url || forms.editForm.apiUrl.$error.pattern || forms.editForm.apiUrl.$error.invalidCharacters" translate="ADD_ARTIFACT_ERROR_APIURL_URL"></span> + </div> + + </div> + + </div><!-- w-sdc-form-column --> + + <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="ADD_ARTIFACT_ERROR_DESCRIPTION_PATTERN"></span> + </div> + + <div class="w-sdc-form-column artifact-info" data-ng-show="!isNew && editArtifactResourceModel.artifactResource.esId"> + UUID <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactUUID"></span> + <br /> + Version <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactVersion"></span> + </div> + </div> + + </div><!-- w-sdc-form-column --> + + </div><!-- w-sdc-form-columns-wrapper --> + + <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span> + + </form> + </div> +</sdc-modal> + diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less new file mode 100644 index 0000000000..1f77958c88 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less @@ -0,0 +1,44 @@ +.sdc-edit-artifact-form-container { + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .i-sdc-form-textarea{ + min-height: 95px; + } + + .i-sdc-form-url { + padding-bottom: 0px; + } + + &.mandatory-artifact { + .w-sdc-form-column { + width: 100%; + padding: 0; + min-height: initial; + } + } + .w-sdc-form .i-sdc-form-item.upload input[type="file"] { + display: none + } + + .artifact-info { + text-align: left; + color: rgb(140, 140, 140); + font-size: 13px; + margin-top: -10px; + margin-bottom: 5px; + width: 100%; + min-height: initial; + + span { + color: #666666; + padding-left: 4px; + } + } + +} diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html new file mode 100644 index 0000000000..90b8f67df4 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html @@ -0,0 +1,152 @@ +<sdc-modal modal="modalInstanceAttribute" type="classic" class="sdc-edit-attribute-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Attribute" show-close-button="true"> + + <div class="sdc-edit-attribute-form-container" > + <form novalidate class="w-sdc-form two-columns" name="forms.editForm" > + + <div class="w-sdc-form-columns-wrapper"> + + <div class="w-sdc-form-column"> + + <!-- Name --> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid)}"> + <label class="i-sdc-form-label required">Name</label> + <input class="i-sdc-form-input" + data-tests-id="attributeName" + data-ng-maxlength="50" + data-ng-disabled="!isNew" + maxlength="50" + data-ng-model="editAttributeModel.attribute.name" + type="text" + name="attributeName" + data-ng-pattern="propertyNameValidationPattern" + data-required + data-ng-model-options="{ debounce: 200 }" + data-ng-change="validateName()" + autofocus /> + <div class="input-error" data-ng-show="forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid"> + <span ng-show="forms.editForm.attributeName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Attribute name' }"></span> + <span ng-show="forms.editForm.attributeName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span> + <span ng-show="forms.editForm.attributeName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + <span ng-show="forms.editForm.attributeName.$error.nameExist" translate="NEW_ATTRIBUTE_ERROR_NAME_EXISTS"></span> + </div> + </div> + + <!-- Description --> + <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-disabled="editAttributeModel.attribute.readonly" + maxlength="256" + data-ng-pattern="commentValidationPattern" + name="description" + data-ng-model="editAttributeModel.attribute.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 class="w-sdc-form-column"> + <!-- Type --> + <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-tests-id="type-field" + data-required + data-ng-disabled="editAttributeModel.attribute.readonly" + name="type" + data-ng-change="onTypeChange()" + data-ng-model="editAttributeModel.attribute.type" + data-ng-options="type for type in editAttributeModel.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> + + <!-- schema --> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schema.$dirty && forms.editForm.schema.$invalid)}" + data-ng-if="showSchema()"> + <label class="i-sdc-form-label required">Entry Schema</label> + <select class="i-sdc-form-select" ng-if="isSchemaEditable()" + data-required + name="schema" + data-ng-disabled="editAttributeModel.attribute.readonly" + data-ng-change="onTypeChange(false)" + data-ng-model="editAttributeModel.attribute.schema.property.type" + data-ng-options="type for type in editAttributeModel.simpleTypes"> + <option value="">Choose Schema Type</option> + </select> + <input class="i-sdc-form-input" + ng-if="!isSchemaEditable()" + data-tests-id="schema" + data-ng-disabled="true" + data-ng-model="editAttributeModel.attribute.schema.property.type" + type="text" + name="schema"/> + <div class="input-error" data-ng-show="forms.editForm.schema.$dirty && forms.editForm.schema.$invalid"> + <span ng-show="forms.editForm.schema.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span> + </div> + </div> + + <!-- Default value --> + <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" + data-tests-id="defaultvalue" + ng-if="!(editAttributeModel.attribute.type == 'boolean')" + data-ng-maxlength="100" + data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()" + maxlength="100" + data-ng-model="attributeValue.value" + type="text" + name="value" + data-custom-validation="" data-validation-func="validateUniqueKeys" + data-ng-pattern="validationPattern" + data-ng-model-options="{ debounce: 200 }" + data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editAttributeModel.attribute.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editAttributeModel.attribute.value)) || onValueChange())" + autofocus /> + <select class="i-sdc-form-select" + data-tests-id="booleantype" + ng-if="editAttributeModel.attribute.type == 'boolean'" + data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()" + name="value" + data-ng-change="onValueChange()" + data-ng-model="attributeValue.value"> + <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': 'Default value' }"></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="ATTRIBUTE_EDIT_MAP_UNIQUE_KEYS"></span> + </div> + </div> + + <!-- hidden --> + <div class="i-sdc-form-item" data-ng-if="isAttributeValueOwner()"> + <label class="i-sdc-form-label">Hidden</label> + <input class="i-sdc-form-input" + data-tests-id="hidden" + data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()" + data-ng-model="editAttributeModel.attribute.hidden" + type="checkbox" + name="hidden"/> + </div> + </div> + + </div> + + </form> + </div> + +</sdc-modal> diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts new file mode 100644 index 0000000000..122cf10ed2 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts @@ -0,0 +1,241 @@ +'use strict'; +import {AttributeModel, Component} from "app/models"; +import {IMapRegex, ValidationUtils, FormState, PROPERTY_TYPES} from "app/utils"; + +export interface IEditAttributeModel { + attribute:AttributeModel; + types:Array<string>; + simpleTypes:Array<string>; +} + +export class attributeValue {//in order to solve DE226783, we update the value on another obj + value:string; +} + +interface IAttributeFormViewModelScope extends ng.IScope { + $$childTail:any; + forms:any; + editForm:ng.IFormController; + footerButtons:Array<any>; + isService:boolean; + editAttributeModel:IEditAttributeModel; + modalInstanceAttribute:ng.ui.bootstrap.IModalServiceInstance; + isNew:boolean; + listRegex:IMapRegex; + mapRegex:IMapRegex; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + isLoading:boolean; + validationPattern:RegExp; + attributeValue:attributeValue; + + save():void; + close():void; + onTypeChange():void; + onValueChange():void; + isAttributeValueOwner():boolean; + validateIntRange(value:string):boolean; + validateUniqueKeys(viewValue:string):boolean; + getValidationTranslate():string; + showSchema():boolean; + isSchemaEditable():boolean; + validateName():void; +} + +export class AttributeFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'attribute', + 'ValidationUtils', + 'CommentValidationPattern', + 'PropertyNameValidationPattern', + 'component' + ]; + + private formState:FormState; + + + constructor(private $scope:IAttributeFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private attribute:AttributeModel, + private ValidationUtils:ValidationUtils, + private CommentValidationPattern:RegExp, + private PropertyNameValidationPattern:RegExp, + private component:Component) { + this.formState = angular.isDefined(attribute.name) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initResource = ():void => { + this.$scope.editAttributeModel.attribute = new AttributeModel(this.attribute); + if (this.$scope.editAttributeModel.types.indexOf(this.attribute.type) === -1) {//attribute defaulte type is string too? + this.attribute.type = "string"; + } + }; + + private initEditAttributeModel = ():void => { + this.$scope.editAttributeModel = { + attribute: null, + types: ['integer', 'string', 'float', 'boolean', 'list', 'map'], + simpleTypes: ['integer', 'string', 'float', 'boolean'] + }; + + this.initResource(); + }; + + private initScope = ():void => { + + //scope attributes + this.$scope.forms = {}; + this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + + this.$scope.modalInstanceAttribute = this.$uibModalInstance; + this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns(); + this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns(); + + this.$scope.isNew = (this.formState === FormState.CREATE); + this.$scope.isLoading = false; + this.$scope.attributeValue = new attributeValue(); + + this.initEditAttributeModel(); + this.setValidationPattern(); + + //scope methods + this.$scope.save = ():void => { + if (!this.$scope.forms.editForm.$invalid) { + let attribute:AttributeModel = this.$scope.editAttributeModel.attribute; + this.$scope.editAttributeModel.attribute.description = this.ValidationUtils.stripAndSanitize(this.$scope.editAttributeModel.attribute.description); + ////if read only - just closes the modal + if (this.$scope.editAttributeModel.attribute.readonly && !this.$scope.isAttributeValueOwner()) { + this.$uibModalInstance.close(); + return; + } + this.$scope.isLoading = true; + let onAttributeFaild = (response):void => { + console.info('onFaild', response); + this.$scope.isLoading = false; + }; + + let onAttributeSuccess = (attributeFromBE:AttributeModel):void => { + console.info('onAttributeResourceSuccess : ', attributeFromBE); + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + }; + + //in case we have uniqueId we call update method + if (this.$scope.isAttributeValueOwner()) { + attribute.value = this.$scope.attributeValue.value; + this.component.updateInstanceAttribute(attribute).then(onAttributeSuccess, onAttributeFaild); + } else { + attribute.defaultValue = this.$scope.attributeValue.value; + this.component.addOrUpdateAttribute(attribute).then(onAttributeSuccess, onAttributeFaild); + } + } + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.validateName = ():void => { + let existsAttr:AttributeModel = _.find(this.component.attributes, (attribute:AttributeModel) => { + return attribute.name === this.$scope.editAttributeModel.attribute.name; + }); + if (existsAttr) { + this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', false); + } else { + this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', true); + } + + }; + + this.$scope.onTypeChange = ():void => { + this.$scope.editAttributeModel.attribute.value = ''; + this.$scope.editAttributeModel.attribute.defaultValue = ''; + this.setValidationPattern(); + }; + + this.$scope.isAttributeValueOwner = ():boolean=> { + return this.component.isService() || !!this.component.selectedInstance; + }; + + this.$scope.onValueChange = ():void => { + if (!this.$scope.editAttributeModel.attribute.value) { + if (this.$scope.isAttributeValueOwner()) { + this.$scope.editAttributeModel.attribute.value = this.$scope.editAttributeModel.attribute.defaultValue; + } + } + }; + + + this.$scope.validateUniqueKeys = (viewValue:string):boolean => { + if (this.$scope.editAttributeModel.attribute.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.isSchemaEditable = ():boolean => { + let schemaType = this.$scope.editAttributeModel.attribute.schema.property.type; + return this.$scope.editAttributeModel.simpleTypes.indexOf(schemaType) > -1 || !schemaType; + }; + + this.$scope.showSchema = ():boolean => { + return ['list', 'map'].indexOf(this.$scope.editAttributeModel.attribute.type) > -1; + }; + + this.$scope.getValidationTranslate = ():string => { + let result = "ATTRIBUTE_EDIT_PATTERN"; + if (this.$scope.showSchema()) { + + result = "ATTRIBUTE_EDIT_" + this.$scope.editAttributeModel.attribute.type.toUpperCase(); + + if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.STRING) { + result += "_STRING"; + } else if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.BOOLEAN) { + result += "_BOOLEAN"; + } else { + result += "_GENERIC"; + } + } + + return result; + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + this.$scope.attributeValue.value = this.$scope.isAttributeValueOwner() ? this.$scope.editAttributeModel.attribute.value : this.$scope.editAttributeModel.attribute.defaultValue; + }; + + + private setValidationPattern = ():void => { + + if (this.$scope.editAttributeModel.attribute.type === 'list') { + this.$scope.validationPattern = this.$scope.listRegex[this.$scope.editAttributeModel.attribute.schema.property.type]; + } + else if (this.$scope.editAttributeModel.attribute.type === 'map') { + this.$scope.validationPattern = this.$scope.mapRegex[this.$scope.editAttributeModel.attribute.schema.property.type]; + } + else { + this.$scope.validationPattern = this.ValidationUtils.getValidationPattern(this.$scope.editAttributeModel.attribute.type); + } + + }; +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html new file mode 100644 index 0000000000..ae13844532 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html @@ -0,0 +1,92 @@ +<sdc-modal modal="envParametersModal" type="classic" class="sdc-env-form-container" buttons="buttons" header="{{artifactResource.artifactDisplayName}}" show-close-button="true"> + <div class="w-sdc-env-form-container"> + <div class="w-sdc-env-search pull-left"> + <input type="text" class="w-sdc-env-search-input" placeholder="Search" data-ng-model="searchText" data-tests-id="search-env-param-name"/> + <div class="search-icon-container"> + <span class="w-sdc-search-icon env-search-icon magnification-white"></span> + </div> + </div> + <div class="table-container-flex"> + <div class="table"> + <div class="head flex-container"> + <div class="table-header head-row flex-item" ng-repeat="header in tableHeadersList track by $index"> + <info-tooltip class="header-info" data-ng-if="header.info" class="info-button" info-message-translate="{{header.info}}" direction="left"></info-tooltip> + {{header.title}} + </div> + </div> + <div class="body"> + <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container"> + <ng-form name="forms.editForm" class="w-sdc-form"> + <div data-ng-repeat="parameter in heatParameters| filter:{filterTerm:searchText} track by $index " + class="flex-container data-row" + data-ng-init="parameter.filterTerm=parameter.name + ' ' + parameter.currentValue + ' ' + parameter.defaultValue + ' ' +parameter.description"> + <div class="table-col-general flex-item" data-tests-id="heatParameterName_{{parameter.name}}"> + {{parameter.name}} + <span class="sprite-new show-desc hand" + uib-popover-template="templatePopover" + popover-class="parameter-description-popover top" + popover-title="Parameter Description" + popover-placement="top-left" + popover-is-open="selectedParameter.name == parameter.name" + popover-trigger="'none'" + popover-append-to-body="true" + data-ng-click="openDescPopover(parameter)"></span> + </div> + + <div class="table-col-general flex-item text"> + <span data-tests-id="default-value-of-{{parameter.name}}" tooltips tooltip-content="{{parameter.defaultValue}}">{{parameter.defaultValue}}</span> + </div> + + <!--<div class="table-col-general flex-item">--> + <!--<input type="text" value="{{parameter.currentValue}}"/>--> + <!--</div>--> + + <div class="table-col-general flex-item left-column-container"> + + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm[parameter.name].$dirty && forms.editForm[parameter.name].$invalid), required: (parameter.defaultValue)}"> + <span class="required-symbol">*</span> + <div class="input-parameter"> + <input class="i-sdc-form-input" data-ng-class="{error: (forms.editForm[parameter.name].$invalid)}" + data-ng-model-options="{ debounce: 200 }" + data-ng-model="parameter.currentValue" + value="{{parameter.currentValue}}" + type="text" + name="{{parameter.name}}" + data-ng-pattern="getValidationPattern(parameter.type, 'heat')" + data-ng-required="parameter.defaultValue" + data-ng-change="'json'==parameter.type && forms.editForm[parameter.name].$setValidity('pattern', validateJson(parameter.currentValue))" + data-ng-blur="(forms.editForm[parameter.name].$error.required && (parameter.currentValue=parameter.defaultValue))" + data-tests-id="value-field-of-{{parameter.name}}"/> + + <div class="action-button"> + <div class="sprite-new revert-param" data-ng-if="parameter.defaultValue" data-ng-click="parameter.currentValue = parameter.defaultValue" + data-tests-id="revert-{{parameter.name}}"> + </div> + <div class="sprite-new delete-param" + data-ng-if="!parameter.defaultValue" + data-ng-disabled="!parameter.currentValue" + data-ng-class="{disabled:!parameter.currentValue}" + data-ng-click="parameter.currentValue = ''" + data-tests-id="delete-{{parameter.name}}"> + </div> + </div> + </div> + <div class="input-error" data-ng-show="forms.editForm[parameter.name].$invalid"> + <span ng-show="forms.editForm[parameter.name].$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span> + <span ng-show="forms.editForm[parameter.name].$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + <span ng-show="forms.editForm[parameter.name].$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span> + </div> + </div> + + </div> + + </div> + </ng-form> + + </perfect-scrollbar> + </div> + </div> + </div> + </div> + </div> +</sdc-modal> diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less new file mode 100644 index 0000000000..a25a2c5f62 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less @@ -0,0 +1,178 @@ + +.sdc-env-form-container{ + .w-sdc-modal-body{ + padding: 20px 10px 2px 10px; + } + .w-sdc-modal-body-content{ + .b_6; + display: block; + } + + .env-file-generation-label{ + .p_9; + .bold; + margin-bottom: 20px; + } +} + +.w-sdc-env-form-container { + height: 650px; + + .w-sdc-env-search { + padding: 10px 20px 20px 0; + white-space: nowrap; + position: relative; + width: 60%; + height: 64px; + + .env-search-icon { + top: 9px; + right: 11px; + } + + .magnification-white { + .sprite-new; + .search-white-icon; + .hand; + } + + .search-icon-container { + width: 35px; + height: 30px; + background-color: @main_color_a; + white-space: nowrap; + float: right; + position: relative; + bottom: 31px; + right: 1px; + border-radius: 0px 4px 4px 0px; + .hand + } + + .w-sdc-env-search-input { + border: 1px solid @color_e; + .border-radius(4px); + height: 32px; + margin: 0; + padding: 0px 28px 3px 10px; + vertical-align: 4px; + width: 100%; + outline: none; + font-style: italic; + } + } + + .table-container-flex { + height: 570px; + + .table { + height: 100%; + .flex-item:nth-child(1) { + flex-grow: 20; + .show-desc{ + float: right; + top: 10px; + position: relative; + } + } + + .flex-item:nth-child(2) { + flex-grow: 10; + } + + .flex-item:nth-child(3) { + flex-grow: 10; + } + .scrollbar-container{ + max-height: 527px; + } + .left-column-container{ + .required-symbol { + .m_14_m; + color: #f33; + display: none; + position: relative; + left: -4px; + top: 3px; + } + + .i-sdc-form-item{ + border-right: none; + margin: 0px; + + .input-parameter{ + border: none; + height: 30px; + width: 254px; + float: right; + input{ + .m_13_m; + width: 100%; + display: inline-flex; + padding-right: 33px; + } + .action-button{ + border-left: solid 1px @main_color_o; + position: relative; + height: 20px; + width: 25px; + top: -25px; + left: 228px; + padding-left: 6px; + background-color: @main_color_p; + div:not(.disable){ + .hand; + } + } + } + + &.required{ + .required-symbol { + display: inline-flex; + } + .input-parameter { + width: 250px; + } + .action-button{ + left: 224px; + } + } + } + + + + } + } + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + } + + + .parameter-description{ + background-color: @func_color_r; + border-left: 4px solid @main_color_a; + padding: 10px 30px; + } +} + +.header-info{ + float: right; +} + +.parameter-description-popover{ + z-index: 1100; + min-width: 210px; + .arrow{ + left: 20px !important; + border-width: 7px; + bottom: -8px !important; + } + .popover-content{ + .f-type._13_m;; + } +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts new file mode 100644 index 0000000000..476af4ada9 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts @@ -0,0 +1,153 @@ +'use strict'; +import {ValidationUtils} from "app/utils"; +import {ArtifactModel, HeatParameterModel, Component} from "app/models"; + +export interface IEnvParametersFormViewModelScope extends ng.IScope { + isLoading:boolean; + type:string; + heatParameters:Array<HeatParameterModel>; + forms:any; + artifactResource:ArtifactModel; + buttons:Array<any>; + envParametersModal:ng.ui.bootstrap.IModalServiceInstance; + tableHeadersList:Array<any>; + selectedParameter:HeatParameterModel; + templatePopover:string; + + getValidationPattern(type:string):RegExp; + isInstance():boolean; + validateJson(json:string):boolean; + close():void; + save():void; + openDescPopover(selectedParam:HeatParameterModel):void; + closeDescriptionPopover():void; +} + +export class EnvParametersFormViewModel { + + static '$inject' = [ + '$scope', + '$templateCache', + '$state', + '$uibModalInstance', + 'artifact', + 'ValidationUtils', + 'component' + ]; + + constructor(private $scope:IEnvParametersFormViewModelScope, + private $templateCache:ng.ITemplateCacheService, + private $state:any, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private artifact:ArtifactModel, + private ValidationUtils:ValidationUtils, + private component:Component) { + + + this.initScope(); + } + + private updateInstanceHeat = ():void => { + let success = (responseArtifact:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + }; + + let error = ():void => { + this.$scope.isLoading = false; + console.info('Failed to load save artifact'); + }; + + this.component.addOrUpdateInstanceArtifact(this.$scope.artifactResource).then(success, error); + }; + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.envParametersModal = this.$uibModalInstance; + this.$scope.artifactResource = this.artifact; + this.$scope.heatParameters = angular.copy(this.artifact.heatParameters); + + this.$scope.tableHeadersList = [ + {title: "Parameter", property: "name"}, + {title: "Default Value", property: "defaultValue", info: "DEFAULT_VALUE_INFO"}, + {title: "Current Value", property: "currentValue", info: "CURRENT_VALUE_INFO"} + ]; + + this.$templateCache.put("env-parametr-description-popover.html", require('app/view-models/forms/env-parameters-form/env-parametr-description-popover.html')); + this.$scope.templatePopover = "env-parametr-description-popover.html"; + + 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.isInstance = ():boolean => { + return !!this.component.selectedInstance; + }; + + this.$scope.save = ():void => { + this.$scope.buttons[0].disabled = true;//prevent double click (DE246266) + this.$scope.isLoading = true; + this.artifact.heatParameters = this.$scope.heatParameters; + this.artifact.heatParameters.forEach((parameter:any):void => { + /* if ("" === parameter.currentValue) { + parameter.currentValue = null; + }else */ + if (!parameter.currentValue && parameter.defaultValue) { + parameter.currentValue = parameter.defaultValue; + } + }); + + if (this.$scope.isInstance()) { + this.updateInstanceHeat(); + return; + } + + let success = (responseArtifact:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + + }; + + let error = ():void => { + this.$scope.isLoading = false; + console.info('Failed to load save artifact'); + }; + + this.component.addOrUpdateArtifact(this.$scope.artifactResource).then(success, error); + }; + + this.$scope.close = ():void => { + //this.artifact.heatParameters.forEach((parameter:any):void => { + // if (!parameter.currentValue && parameter.defaultValue) { + // parameter.currentValue = parameter.defaultValue; + // } + //}); + this.$uibModalInstance.dismiss(); + }; + + this.$scope.openDescPopover = (selectedParam:HeatParameterModel):void => { + this.$scope.selectedParameter = selectedParam; + }; + + this.$scope.closeDescriptionPopover = ():void => { + this.$scope.selectedParameter = null; + }; + + this.$scope.buttons = [ + {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.buttons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + }; +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html new file mode 100644 index 0000000000..ed127c6bfb --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html @@ -0,0 +1,4 @@ +<div> + <span data-tests-id='popover-x-button' data-ng-click='closeDescriptionPopover()' class='tlv-sprite tlv-x-btn close-popover-btn'></span> + {{selectedParameter.description}} +</div> diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts new file mode 100644 index 0000000000..e87e5c6c7d --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts @@ -0,0 +1,126 @@ +'use strict'; +import {FormState, PROPERTY_TYPES, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS} from "app/utils"; +import {InputModel} from "app/models"; + +export interface IInputEditModel { + editInput:InputModel; +} + +export interface IInputFormViewModelScope extends ng.IScope { + forms:any; + editForm:ng.IFormController; + footerButtons:Array<any>; + isService:boolean; + modalInstanceInput:ng.ui.bootstrap.IModalServiceInstance; + isLoading:boolean; + inputEditModel:IInputEditModel; + myValue:any; + maxLength:number; + + save():void; + close():void; + validateIntRange(value:string):boolean; + validateJson(json:string):boolean; + getValidationPattern(type:string):RegExp; + showSchema():boolean; +} + +export class InputFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'ValidationUtils', + 'input' + ]; + + private formState:FormState; + + + constructor(private $scope:IInputFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private ValidationUtils:ValidationUtils, + private input:InputModel) { + this.initScope(); + this.initMyValue(); + } + + private initMyValue = ():void => { + switch (this.$scope.inputEditModel.editInput.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : []; + break; + } + }; + + private initDefaultValueMaxLength = ():void => { + switch (this.$scope.inputEditModel.editInput.type) { + case PROPERTY_TYPES.MAP: + case PROPERTY_TYPES.LIST: + this.$scope.maxLength = this.$scope.inputEditModel.editInput.schema.property.type == PROPERTY_TYPES.JSON ? + PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH : + PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + break; + case PROPERTY_TYPES.JSON: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH; + break; + default: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + } + }; + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.modalInstanceInput = this.$uibModalInstance; + this.$scope.inputEditModel = { + editInput: null + }; + this.$scope.inputEditModel.editInput = this.input; + this.initDefaultValueMaxLength(); + + //scope methods + this.$scope.save = ():void => { + if (this.$scope.showSchema()) { + this.$scope.inputEditModel.editInput.defaultValue = JSON.stringify(this.$scope.myValue); + } + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.ValidationUtils.validateJson(json); + }; + + this.$scope.showSchema = ():boolean => { + return ['list', 'map'].indexOf(this.$scope.inputEditModel.editInput.type) > -1; + }; + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.ValidationUtils.getValidationPattern(type); + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + }; +} + diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html new file mode 100644 index 0000000000..1bf6dc4ca9 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html @@ -0,0 +1,125 @@ +<sdc-modal modal="modalInstanceInput" type="classic" class="sdc-edit-input-container" buttons="footerButtons" header="Update Input" show-close-button="true"> + + <div class="sdc-edit-input-form-container" > + <form novalidate class="w-sdc-form two-columns" name="forms.editForm" > + + <div class="w-sdc-form-columns-wrapper"> + + <div class="w-sdc-form-column"> + + <!-- Name --> + <div class="i-sdc-form-item"> + <label class="i-sdc-form-label">Name</label> + <input class="i-sdc-form-input" + data-tests-id="inputName" + data-ng-maxlength="50" + data-ng-disabled="true" + maxlength="50" + data-ng-model="inputEditModel.editInput.name" + type="text" + name="inputName" + autofocus /> + </div> + + <!-- Description --> + <div class="i-sdc-form-item"> + <label class="i-sdc-form-label">Description</label> + <textarea class="i-sdc-form-textarea" + data-ng-disabled="true" + name="description" + data-ng-model="inputEditModel.editInput.description" + data-tests-id="description"></textarea> + </div> + + + </div> + + <div class="w-sdc-form-column"> + <!-- Type --> + <div class="i-sdc-form-item"> + <label class="i-sdc-form-label">Type</label> + <input class="i-sdc-form-input" + data-tests-id="type" + data-ng-disabled="true" + data-ng-model="inputEditModel.editInput.type" + type="text" + name="type"/> + </div> + <!-- schema --> + <div class="i-sdc-form-item" + data-ng-if="showSchema()"> + <label class="i-sdc-form-label">Entry Schema</label> + <input class="i-sdc-form-input" + data-tests-id="schema" + data-ng-disabled="true" + data-ng-model="inputEditModel.editInput.schema.property.type" + type="text" + name="schema"/> + </div> + <!-- Default value --> + <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> + <div data-ng-switch="inputEditModel.editInput.type"> + <div ng-switch-when="map"> + <type-map value-obj-ref="myValue" + schema-property="inputEditModel.editInput.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="'input-value-'" + read-only="true" + default-value="" + types="[]" + max-length="maxLength"></type-map> + </div> + <div ng-switch-when="list"> + <type-list value-obj-ref="myValue" + schema-property="inputEditModel.editInput.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="'input-value-'" + read-only="true" + default-value="" + types="[]" + max-length="maxLength"></type-list> + </div> + <div ng-switch-default> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}"> + <input class="i-sdc-form-input" + data-tests-id="defaultvalue" + ng-if="inputEditModel.editInput.type != 'boolean'" + data-ng-maxlength="maxLength" + data-ng-disabled="true" + maxlength="{{maxLength}}" + data-ng-model="inputEditModel.editInput.defaultValue" + type="text" + name="value" + data-ng-pattern="getValidationPattern(input.type)" + data-ng-model-options="{ debounce: 200 }" + data-ng-change="('json'==inputEditModel.editInput.type && forms.editForm.value.$setValidity('pattern', validateJson(inputEditModel.editInput.defaultValue))) + ||(!forms.editForm.value.$error.pattern && ('integer'==inputEditModel.editInput.type && forms.editForm.value.$setValidity('pattern', validateIntRange(inputEditModel.editInput.defaultValue)) || onValueChange()))" + autofocus /> + <select class="i-sdc-form-select" + data-tests-id="booleantype" + ng-if="inputEditModel.editInput.type == 'boolean'" + data-ng-disabled="true" + name="value" + data-ng-model="inputEditModel.editInput.defaultValue"> + <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.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span> + <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span> + </div> + </div> + </div> + </div> + </div> + + </div> + + </div> + + </form> + </div> + +</sdc-modal> diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts new file mode 100644 index 0000000000..1ba5a90bb4 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts @@ -0,0 +1,204 @@ +/** + * Created by obarda on 1/19/2017. + */ +'use strict'; +import {DataTypesService} from "app/services/data-types-service"; +import {PropertyModel, DataTypesMap, Component} from "app/models"; +import {ValidationUtils, PROPERTY_DATA} from "app/utils"; + +export interface IPropertyFormBaseViewScope extends ng.IScope { + + forms:any; + editForm:ng.IFormController; + + property:PropertyModel; + types:Array<string>; + nonPrimitiveTypes:Array<string>; + simpleTypes:Array<string>; + + footerButtons:Array<any>; + modalPropertyFormBase:ng.ui.bootstrap.IModalServiceInstance; + currentPropertyIndex:number; + isLastProperty:boolean; + innerViewSrcUrl:string; + + //Disabling filed - each child controller can change this when needed + isNew:boolean; + isTypeSelectorDisable:boolean; + isDeleteDisable:boolean; + isNameDisable:boolean; + isDescriptionDisable:boolean; + isPropertyValueDisable:boolean; + isArrowsDisabled:boolean; + + //Validation pattern + validationPattern:RegExp; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + numberValidationPattern:RegExp; + + dataTypes:DataTypesMap; + + isLoading:boolean; + + save():void; + close():void; + getNext():void; + getPrev():void; + getValidationPattern(type:string):RegExp; +} + +export abstract class PropertyFormBaseView { + + + constructor(protected $scope:IPropertyFormBaseViewScope, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + protected filteredProperties:Array<PropertyModel>, + protected DataTypesService:DataTypesService) { + + this.initScope(); + + } + + protected validationUtils:ValidationUtils; + + protected isPropertyValueOwner = ():boolean => { + return this.component.isService() || !!this.component.selectedInstance; + }; + + private isDisable = ():boolean => { + return this.isPropertyValueOwner() || this.$scope.property.readonly; + }; + + + //This is the difault state, Childs screens can change if needed + protected initButtonsState = ():void => { + let isDisable = this.isDisable(); + + this.$scope.isArrowsDisabled = false; + this.$scope.isDeleteDisable = isDisable; + this.$scope.isDescriptionDisable = isDisable; + this.$scope.isNameDisable = isDisable; + this.$scope.isTypeSelectorDisable = isDisable; + this.$scope.isPropertyValueDisable = this.$scope.property.readonly && !this.isPropertyValueOwner(); + }; + + protected initValidations = ():void => { + + this.$scope.validationPattern = this.$injector.get('ValidationPattern'); + this.$scope.propertyNameValidationPattern = this.$injector.get('PropertyNameValidationPattern'); + this.$scope.commentValidationPattern = this.$injector.get('CommentValidationPattern'); + this.$scope.numberValidationPattern = this.$injector.get('NumberValidationPattern'); + this.validationUtils = this.$injector.get('ValidationUtils'); + }; + + //Functions implemented on child's scope if needed + abstract save(doNotCloseModal?:boolean):ng.IPromise<boolean>; + + protected onPropertyChange():void { + }; + + private updatePropertyByIndex = (index:number):void => { + this.$scope.property = new PropertyModel(this.filteredProperties[index]); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + this.onPropertyChange(); + }; + + private initScope = ():void => { + + this.$scope.forms = {}; + this.$scope.isLoading = false; + this.$scope.property = new PropertyModel(this.originalProperty); //we create a new Object so if user press cance we won't update the property + this.$scope.types = PROPERTY_DATA.TYPES; //All types - simple type + map + list + this.$scope.simpleTypes = PROPERTY_DATA.SIMPLE_TYPES; //All simple types + this.$scope.dataTypes = this.DataTypesService.getAllDataTypes(); //Get all data types in service + this.$scope.modalPropertyFormBase = this.$uibModalInstance; + this.$scope.isNew = !angular.isDefined(this.$scope.property.name); + + this.initValidations(); + this.initButtonsState(); + this.filteredProperties = _.sortBy(this.filteredProperties, 'name'); + this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, propety => propety.uniqueId == this.$scope.property.uniqueId); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + + this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> { + return this.$scope.types.indexOf(type) == -1; + }); + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.save = ():void => { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + this.$scope.isLoading = true; + this.save(true).then(onSuccess, onFailed); // Child controller implement save logic + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + + this.$scope.getPrev = ():void=> { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.updatePropertyByIndex(--this.$scope.currentPropertyIndex); + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + if (!this.$scope.property.readonly) { + this.$scope.isLoading = true; + this.save(false).then(onSuccess, onFailed); + + } else { + this.updatePropertyByIndex(--this.$scope.currentPropertyIndex); + } + + }; + + this.$scope.getNext = ():void=> { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.updatePropertyByIndex(++this.$scope.currentPropertyIndex); + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + if (!this.$scope.property.readonly) { + this.$scope.isLoading = true; + this.save(false).then(onSuccess, onFailed); + } else { + this.updatePropertyByIndex(++this.$scope.currentPropertyIndex); + } + + }; + + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.validationUtils.getValidationPattern(type); + }; + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html new file mode 100644 index 0000000000..7cb05bf4ca --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html @@ -0,0 +1,132 @@ +<sdc-modal modal="modalPropertyFormBase" type="classic" class="sdc-edit-property-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true" data-tests-id="sdc-edit-property-container"> + <loader data-display="isLoading" relative="false" size="medium"></loader> + <div class="sdc-modal-top-bar" data-ng-if="!isNew"> + <div class="sdc-modal-top-bar-buttons"> + <span ng-click="delete(property)" data-ng-class="{'disabled' : isDeleteDisable}" class="sprite-new delete-btn" data-tests-id="delete_property" sdc-smart-tooltip="">Delete</span> + <span class="delimiter"></span> + <span data-ng-click="getPrev()" data-ng-class="{'disabled' : !currentPropertyIndex || forms.editForm.$invalid || isArrowsDisabled}" class="sprite-new left-arrow" data-tests-id="get-prev" sdc-smart-tooltip="">Previous</span> + <span data-ng-click="getNext()" data-ng-class="{'disabled' : isLastProperty || forms.editForm.$invalid || isArrowsDisabled}" class="sprite-new right-arrow" data-tests-id="get-next" sdc-smart-tooltip="">Next</span> + </div> + </div> + + <div class="sdc-edit-property-form-container" > + <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container"> + <form class="w-sdc-form two-columns" name="forms.editForm" > + + <div class="w-sdc-form-columns-wrapper"> + + <div class="w-sdc-form-column"> + + <!-- Name --> + <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-tests-id="propertyName" + data-ng-maxlength="50" + data-ng-disabled="isNameDisable" + maxlength="50" + data-ng-model="property.name" + type="text" + name="propertyName" + data-ng-pattern="propertyNameValidationPattern" + data-required + data-ng-model-options="{ debounce: 200 }" + 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': '50' }"></span> + <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + </div> + </div> + </div> + + <div class="w-sdc-form-column"> + <div class="w-sdc-form-columns-wrapper"> + <div class="w-sdc-form-column"> + <!-- Type --> + <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-tests-id="propertyType" + data-required + data-ng-disabled="isTypeSelectorDisable" + name="type" + data-ng-change="onTypeChange()" + data-ng-model="property.type"> + <option value="">Choose Type</option> + <option data-ng-repeat="type in types" + value="{{type}}">{{type}}</option> + <option data-ng-repeat="type in nonPrimitiveTypes" + value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</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> + <div class="w-sdc-form-column" data-ng-if="showSchema()"> + <!-- Entry Schema --> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}"> + <label class="i-sdc-form-label required">Entry Schema</label> + <select class="i-sdc-form-select" + data-required + data-tests-id="schema-type" + data-ng-disabled="isPropertyValueOwner() || property.readonly" + name="schemaType" + data-ng-change="onSchemaTypeChange()" + data-ng-model="property.schema.property.type"> + <option value="">Choose Schema Type</option> + <option data-ng-repeat="type in simpleTypes" + value="{{type}}">{{type}}</option> + <option data-ng-repeat="type in nonPrimitiveTypes" + value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</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> + </div> + + <!-- Constraints by type --> + <div class="i-sdc-form-item" data-ng-if="false"> + <label class="i-sdc-form-label required">Constraints by type</label> + <div> + Should be constraints by type(TBD) + </div> + </div> + + </div> + + </div> + <!-- Description --> + <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-disabled="isDescriptionDisable" + maxlength="256" + data-ng-pattern="commentValidationPattern" + name="description" + data-ng-model="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> + <!-- Default value - insert in dynamic template url --> + <ng-include src="innerViewSrcUrl"></ng-include> + <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span> + </form> + </perfect-scrollbar> + </div> + +</sdc-modal> diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less new file mode 100644 index 0000000000..15e30af4ee --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less @@ -0,0 +1,63 @@ +.sdc-edit-property-container { + .scrollbar-container{ + height: 415px; + width: 830px; + .perfect-scrollbar; + } + + form{ + width: 813px; + [name="description"]{ + min-height:50px; + } + } + + .sdc-modal-top-bar{ + height: 40px; + .sdc-modal-top-bar-buttons { + float: right; + + > span:not(.delimiter){ + vertical-align: middle; + .hand; + + &.sprite-new { + text-indent: 100%; + } + &.disabled, &:hover.disabled { + pointer-events: none; + } + } + + .delete-btn{ + margin-right: 6px; + } + + .left-arrow{ + margin-right: 8px; + } + + .delimiter { + height: 20px; + width: 1px; + background-color: #959595; + display: inline-block; + vertical-align: middle; + margin-right: 10px; + } + } + } + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .default-value-section{ + border-top: solid 1px @main_color_a; + padding-top: 15px; + margin-top: 15px; + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts new file mode 100644 index 0000000000..b3557b055f --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts @@ -0,0 +1,322 @@ +'use strict'; +import { + PROPERTY_TYPES, ModalsHandler, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA} from "app/utils"; +import {DataTypesService} from "app/services"; +import {PropertyModel, DataTypesMap, Component} from "app/models"; + +export interface IEditPropertyModel { + property:PropertyModel; + types:Array<string>; + simpleTypes:Array<string>; +} + +interface IPropertyFormViewModelScope extends ng.IScope { + forms:any; + editForm:ng.IFormController; + footerButtons:Array<any>; + isNew:boolean; + isLoading:boolean; + isService:boolean; + validationPattern:RegExp; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + editPropertyModel:IEditPropertyModel; + modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance; + currentPropertyIndex:number; + isLastProperty:boolean; + myValue:any; + nonPrimitiveTypes:Array<string>; + dataTypes:DataTypesMap; + isTypeDataType:boolean; + maxLength:number; + isPropertyValueOwner:boolean; + + validateJson(json:string):boolean; + save(doNotCloseModal?:boolean):void; + getValidationPattern(type:string):RegExp; + validateIntRange(value:string):boolean; + close():void; + onValueChange():void; + onSchemaTypeChange():void; + onTypeChange(resetSchema:boolean):void; + showSchema():boolean; + delete(property:PropertyModel):void; + getPrev():void; + getNext():void; + isSimpleType(typeName:string):boolean; + getDefaultValue():any; +} + +export class PropertyFormViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.DataTypesService', + '$uibModalInstance', + 'property', + 'ValidationPattern', + 'PropertyNameValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'component', + '$filter', + 'ModalsHandler', + 'filteredProperties', + '$timeout', + 'isPropertyValueOwner' + ]; + + private formState:FormState; + + constructor(private $scope:IPropertyFormViewModelScope, + private DataTypesService:DataTypesService, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private property:PropertyModel, + private ValidationPattern:RegExp, + private PropertyNameValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils, + private component:Component, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private filteredProperties:Array<PropertyModel>, + private $timeout:ng.ITimeoutService, + private isPropertyValueOwner:boolean) { + + this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initResource = ():void => { + this.$scope.editPropertyModel.property = new PropertyModel(this.property); + this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null; + this.setMaxLength(); + this.initAddOnLabels(); + }; + + //init property add-ons labels that show up at the left side of the input. + private initAddOnLabels = () => { + if (this.$scope.editPropertyModel.property.name == 'network_role' && this.$scope.isService) { + //the server sends back the normalized name. Remove it (to prevent interference with validation) and set the addon label to the component name directly. + //Note: this cant be done in properties.ts because we dont have access to the component + if (this.$scope.editPropertyModel.property.value) { + let splitProp = this.$scope.editPropertyModel.property.value.split(new RegExp(this.component.normalizedName + '.', "gi")); + this.$scope.editPropertyModel.property.value = splitProp.pop(); + } + this.$scope.editPropertyModel.property.addOn = this.component.name; + } + } + + private initEditPropertyModel = ():void => { + this.$scope.editPropertyModel = { + property: null, + types: PROPERTY_DATA.TYPES, + simpleTypes: PROPERTY_DATA.SIMPLE_TYPES + }; + + this.initResource(); + }; + + private initForNotSimpleType = ():void => { + let property = this.$scope.editPropertyModel.property; + this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property); + if (property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type) == -1) { + if (!(property.value || property.defaultValue)) { + switch (property.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = []; + break; + default: + this.$scope.myValue = {}; + } + } else { + this.$scope.myValue = JSON.parse(property.value || property.defaultValue); + } + } + }; + + private setMaxLength = ():void => { + switch (this.$scope.editPropertyModel.property.type) { + case PROPERTY_TYPES.MAP: + case PROPERTY_TYPES.LIST: + this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ? + PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH : + PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + break; + case PROPERTY_TYPES.JSON: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH; + break; + default: + this.$scope.maxLength =PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + } + }; + + + private initScope = ():void => { + + //scope properties + this.$scope.forms = {}; + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + this.$scope.isLoading = false; + this.$scope.isNew = (this.formState === FormState.CREATE); + this.$scope.isService = this.component.isService(); + this.$scope.modalInstanceProperty = this.$uibModalInstance; + this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + this.$scope.dataTypes = this.DataTypesService.getAllDataTypes(); + this.$scope.isPropertyValueOwner = this.isPropertyValueOwner; + this.initEditPropertyModel(); + + this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> { + return this.$scope.editPropertyModel.types.indexOf(type) == -1; + }); + this.initForNotSimpleType(); + + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.ValidationUtils.validateJson(json); + }; + + + //scope methods + this.$scope.save = (doNotCloseModal?:boolean):void => { + let property:PropertyModel = this.$scope.editPropertyModel.property; + this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description); + //if read only - or no changes made - just closes the modal + //need to check for property.value changes manually to detect if map properties deleted + if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner) + || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) { + this.$uibModalInstance.close(); + return; + } + + this.$scope.isLoading = true; + + let onPropertyFaild = (response):void => { + console.info('onFaild', response); + this.$scope.isLoading = false; + }; + + let onPropertySuccess = (propertyFromBE:PropertyModel):void => { + console.info('onPropertyResourceSuccess : ', propertyFromBE); + this.$scope.isLoading = false; + + if (!doNotCloseModal) { + this.$uibModalInstance.close(); + } else { + this.$scope.forms.editForm.$setPristine(); + this.$scope.editPropertyModel.property = new PropertyModel(); + } + }; + + //in case we have uniqueId we call update method + if (this.$scope.isPropertyValueOwner) { + if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) { + let myValueString:string = JSON.stringify(this.$scope.myValue); + property.value = myValueString; + } + this.component.updateInstanceProperty(property).then(onPropertySuccess, onPropertyFaild); + } else { + if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) { + let myValueString:string = JSON.stringify(this.$scope.myValue); + property.defaultValue = myValueString; + } else { + this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value; + } + this.component.addOrUpdateProperty(property).then(onPropertySuccess, onPropertyFaild); + } + }; + + this.$scope.getPrev = ():void=> { + this.property = this.filteredProperties[--this.$scope.currentPropertyIndex]; + this.initResource(); + this.initForNotSimpleType(); + this.$scope.isLastProperty = false; + }; + + this.$scope.getNext = ():void=> { + this.property = this.filteredProperties[++this.$scope.currentPropertyIndex]; + this.initResource(); + this.initForNotSimpleType(); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + }; + + this.$scope.isSimpleType = (typeName:string):boolean=> { + return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1; + }; + + this.$scope.showSchema = ():boolean => { + return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1; + }; + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.ValidationUtils.getValidationPattern(type); + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + // put default value when instance value is empty + this.$scope.onValueChange = ():void => { + if (!this.$scope.editPropertyModel.property.value) { + if (this.$scope.isPropertyValueOwner) { + this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue; + } + } + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Save', '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; + }); + + this.$scope.getDefaultValue = ():any => { + return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null; + }; + + this.$scope.onTypeChange = ():void => { + this.$scope.editPropertyModel.property.value = ''; + this.$scope.editPropertyModel.property.defaultValue = ''; + this.setMaxLength(); + this.initForNotSimpleType(); + }; + + this.$scope.onSchemaTypeChange = ():void => { + if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) { + this.$scope.myValue = {'': null}; + } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) { + this.$scope.myValue = []; + } + this.setMaxLength(); + }; + + this.$scope.delete = (property:PropertyModel):void => { + let onOk = ():void => { + this.component.deleteProperty(property.uniqueId).then( + this.$scope.close + ); + }; + 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/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html new file mode 100644 index 0000000000..f92d9a5ddc --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html @@ -0,0 +1,201 @@ +<sdc-modal modal="modalInstanceProperty" type="classic" class="sdc-edit-property-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true" data-tests-id="sdc-edit-property-container"> + <loader data-display="isLoading" relative="false" size="medium"></loader> + <div class="sdc-modal-top-bar" data-ng-if="!isNew"> + <div class="sdc-modal-top-bar-buttons"> + <span ng-click="delete(editPropertyModel.property)" data-ng-class="{'disabled' : isPropertyValueOwner || editPropertyModel.property.readonly}" class="sprite-new delete-btn" data-tests-id="delete_property" sdc-smart-tooltip="">Delete</span> + <span class="delimiter"></span> + <span data-ng-click="getPrev()" data-ng-class="{'disabled' : !currentPropertyIndex }" class="sprite-new left-arrow" data-tests-id="get-prev" sdc-smart-tooltip="">Previous</span> + <span data-ng-click="getNext()" data-ng-class="{'disabled' : isLastProperty }" class="sprite-new right-arrow" data-tests-id="get-next" sdc-smart-tooltip="">Next</span> + </div> + </div> + + <div class="sdc-edit-property-form-container" > + <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container"> + <form novalidate class="w-sdc-form two-columns" name="forms.editForm" > + + <div class="w-sdc-form-columns-wrapper"> + + <div class="w-sdc-form-column"> + + <!-- Name --> + <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-tests-id="propertyName" + 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 }" + 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': '50' }"></span> + <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + </div> + </div> + </div> + + <div class="w-sdc-form-column"> + <div class="w-sdc-form-columns-wrapper"> + <div class="w-sdc-form-column"> + <!-- Type --> + <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-tests-id="propertyType" + data-required + data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly" + name="type" + data-ng-change="onTypeChange()" + data-ng-model="editPropertyModel.property.type"> + <option value="">Choose Type</option> + <option data-ng-repeat="type in editPropertyModel.types" + value="{{type}}">{{type}}</option> + <option data-ng-repeat="type in nonPrimitiveTypes" + value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</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> + <div class="w-sdc-form-column" data-ng-if="showSchema()"> + <!-- Entry Schema --> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}"> + <label class="i-sdc-form-label required">Entry Schema</label> + <select class="i-sdc-form-select" + data-required + data-tests-id="schema-type" + data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly" + name="schemaType" + data-ng-change="onSchemaTypeChange()" + data-ng-model="editPropertyModel.property.schema.property.type"> + <option value="">Choose Schema Type</option> + <option data-ng-repeat="type in editPropertyModel.simpleTypes" + value="{{type}}">{{type}}</option> + <option data-ng-repeat="type in nonPrimitiveTypes" + value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</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> + </div> + + <!-- Constraints by type --> + <div class="i-sdc-form-item" data-ng-if="false"> + <label class="i-sdc-form-label required">Constraints by type</label> + <div> + Should be constraints by type(TBD) + </div> + </div> + + </div> + + </div> + <!-- Description --> + <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="400" + data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly" + maxlength="400" + 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> + <!-- Default value --> + + <div class="default-value-section i-sdc-form-item"> + <label class="i-sdc-form-label">Default Value</label> + <div data-ng-if="isTypeDataType"> + <fields-structure value-obj-ref="myValue" + type-name="editPropertyModel.property.type" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="editPropertyModel.property.readonly && !isPropertyValueOwner" + default-value="{{getDefaultValue()}}" + expand-by-default="true"></fields-structure> + + </div> + <div data-ng-if="!isTypeDataType" ng-switch="editPropertyModel.property.type"> + <div ng-switch-when="map"> + <type-map value-obj-ref="myValue" + schema-property="editPropertyModel.property.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="editPropertyModel.property.readonly && !isPropertyValueOwner" + default-value="{{getDefaultValue()}}" + max-length="maxLength"></type-map> + </div> + <div ng-switch-when="list"> + <type-list value-obj-ref="myValue" + schema-property="editPropertyModel.property.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="editPropertyModel.property.readonly && !isPropertyValueOwner" + default-value="{{getDefaultValue()}}" + max-length="maxLength"></type-list> + </div> + <div ng-switch-default> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid), 'input-group' : editPropertyModel.property.addOn}"> + <span ng-if="editPropertyModel.property.addOn" class="input-group-addon">{{editPropertyModel.property.addOn}}</span> + <input class="i-sdc-form-input" + data-tests-id="defaultvalue" + ng-if="!((editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean')" + data-ng-maxlength="maxLength" + data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner" + maxlength="{{maxLength}}" + data-ng-model="editPropertyModel.property.value" + type="text" + name="value" + data-ng-pattern="getValidationPattern((editPropertyModel.property.simpleType||editPropertyModel.property.type))" + data-ng-model-options="{ debounce: 200 }" + data-ng-change="('json'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateJson(editPropertyModel.property.value))) + ||(!forms.editForm.value.$error.pattern && ('integer'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editPropertyModel.property.value)) || onValueChange()))" + data-ng-change="" + autofocus /> + <select class="i-sdc-form-select" + data-tests-id="booleantype" + ng-if="(editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean'" + data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner" + name="value" + data-ng-change="onValueChange()" + data-ng-model="editPropertyModel.property.value"> + <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': '{{maxLength}}' }"></span> + <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span> + </div> + </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> + </perfect-scrollbar> + </div> + +</sdc-modal> diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts new file mode 100644 index 0000000000..7359ac0e91 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts @@ -0,0 +1,206 @@ +/** + * Created by obarda on 1/18/2017. + */ +'use strict'; +import {PropertyModel, DisplayModule, Component, Resource, Service, ComponentInstance} from "app/models"; +import {UNIQUE_GROUP_PROPERTIES_NAME} from "app/utils"; +import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model"; +import {DataTypesService} from "app/services/data-types-service"; + +export interface IModulePropertyViewScope extends IPropertyFormBaseViewScope { + onValueChange():void; +} + +export class ModulePropertyView extends PropertyFormBaseView { + + static '$inject' = [ + '$scope', + '$templateCache', + '$uibModalInstance', + '$injector', + 'originalProperty', + 'component', + 'selectedModule', + 'Sdc.Services.DataTypesService', + '$q' + ]; + + constructor(protected $scope:IModulePropertyViewScope, + protected $templateCache:ng.ITemplateCacheService, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + private selectedModule:DisplayModule, + protected DataTypesService:DataTypesService, + private $q:ng.IQService) { + super($scope, $uibModalInstance, $injector, originalProperty, component, selectedModule.properties, DataTypesService); + + this.$templateCache.put("module-property-view.html", require('app/view-models/forms/property-forms/module-property-modal/module-property-view.html')); + this.$scope.innerViewSrcUrl = "module-property-view.html"; + this.initChildScope(); + } + + private findPropertyByName = (propertyName:string):PropertyModel => { + let property:PropertyModel = _.find(this.filteredProperties, (property:PropertyModel) => { + return property.name === propertyName; + }); + return property; + }; + + save(isNeedToCloseModal):ng.IPromise<boolean> { + + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array<PropertyModel>):void => { + deferred.resolve(true); + if (isNeedToCloseModal === true) { + this.$scope.close(); + } + }; + + let onFailed = ():void => { + deferred.resolve(false); + }; + + let property = _.find(this.selectedModule.properties, (property) => { + return property.uniqueId === this.$scope.property.uniqueId; + }); + if (property.value !== this.$scope.property.value) { + if (this.component.isResource()) { + (<Resource>this.component).updateResourceGroupProperties(this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time + } + if (this.component.isService()) { + // Find the component instance of the group instance + let componentInstance:ComponentInstance = _.find(this.component.componentInstances, (componentInstance:ComponentInstance) => { + let groupInstance = _.find(componentInstance.groupInstances, {uniqueId: this.selectedModule.groupInstanceUniqueId}); + return groupInstance !== undefined; + + }); + (<Service>this.component).updateGroupInstanceProperties(componentInstance.uniqueId, this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time + } + } else { + deferred.resolve(true); + } + return deferred.promise; + } + + onPropertyChange():void { + this.initValidation(); + } + + protected initValidation = ():void => { + + this.$scope.isDeleteDisable = true; + this.$scope.isNameDisable = true; + this.$scope.isTypeSelectorDisable = true; + this.$scope.isDescriptionDisable = true; + + switch (this.$scope.property.name) { + case UNIQUE_GROUP_PROPERTIES_NAME.IS_BASE: + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_TYPE: + case UNIQUE_GROUP_PROPERTIES_NAME.VOLUME_GROUP: + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_LABEL: + this.$scope.property.readonly = true; + break; + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_DESCRIPTION: + if (this.component.isService()) { + this.$scope.property.readonly = true; + } else { + this.$scope.property.readonly = false; + } + break; + } + }; + + private isUniqueProperty = ():boolean => { + return this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES || + this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES || + this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT; + }; + + + private initChildScope = ():void => { + + this.initValidation(); + + // put default value when instance value is empty + this.$scope.onValueChange = ():void => { + + if (!this.$scope.property.value) { // Resetting to default value + if (this.isPropertyValueOwner()) { + if (this.component.isService()) { + this.$scope.property.value = this.$scope.property.parentValue; + } else { + this.$scope.property.value = this.$scope.property.defaultValue; + } + } + } + + if (this.isUniqueProperty()) { + + let isValid = true; + let maxProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES); + let minProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES); + let initialCountProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT); + + let maxPropertyValue = parseInt(maxProperty.value); + let minPropertyValue = parseInt(minProperty.value); + let initialCountPropertyValue = parseInt(initialCountProperty.value); + let propertyValue = parseInt(this.$scope.property.value); + let parentPropertyValue = parseInt(this.$scope.property.parentValue); + + switch (this.$scope.property.name) { + + case UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES: + if (!maxPropertyValue || maxPropertyValue === null) { + isValid = propertyValue <= initialCountPropertyValue; + } + else { + isValid = propertyValue && (propertyValue <= maxPropertyValue && propertyValue <= initialCountPropertyValue); + } + this.$scope.forms.editForm["value"].$setValidity('maxValidation', isValid); + if (this.component.isService()) { + if (!parentPropertyValue || parentPropertyValue === null) { + isValid = true; + } else { + isValid = propertyValue >= parentPropertyValue; + this.$scope.forms.editForm["value"].$setValidity('minValidationVfLevel', isValid); + } + } + break; + case UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES: + if (!minPropertyValue || minPropertyValue === null) { + isValid = propertyValue >= initialCountPropertyValue; + } else { + isValid = !propertyValue || (propertyValue >= minPropertyValue && propertyValue >= initialCountPropertyValue); + } + this.$scope.forms.editForm["value"].$setValidity('minValidation', isValid); + if (this.component.isService()) { + if (!parentPropertyValue || parentPropertyValue === null) { + isValid = true; + } + else { + isValid = propertyValue <= parentPropertyValue; + this.$scope.forms.editForm["value"].$setValidity('maxValidationVfLevel', isValid); + } + } + break; + case UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT: + if ((!minPropertyValue || minPropertyValue === null) && (!maxPropertyValue || maxPropertyValue === null)) { + isValid = true; + } else if (!minPropertyValue || minPropertyValue === null) { + isValid = propertyValue <= maxPropertyValue; + } else if (!maxPropertyValue || maxPropertyValue === null) { + isValid = propertyValue >= minPropertyValue; + } else { + isValid = minPropertyValue <= propertyValue && propertyValue <= maxPropertyValue; + } + this.$scope.forms.editForm["value"].$setValidity('minOrMaxValidation', isValid); + break; + } + } + ; + } + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html new file mode 100644 index 0000000000..175f4c199b --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html @@ -0,0 +1,41 @@ +<div class="default-value-section i-sdc-form-item"> + <label class="i-sdc-form-label">Default Value</label> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}"> + <input class="i-sdc-form-input" + data-tests-id="defaultvalue" + ng-if="!((property.simpleType||property.type) == 'boolean')" + data-ng-maxlength="maxLength" + data-ng-disabled="property.readonly && !isPropertyValueOwner()" + maxlength="100" + data-ng-model="property.value" + type="text" + name="value" + data-ng-pattern="getValidationPattern(property.type)" + data-ng-model-options="{ debounce: 200 }" + data-ng-change="onValueChange()" + /> + <select class="i-sdc-form-select" + data-tests-id="booleantype" + ng-if="(property.simpleType||property.type) == 'boolean'" + data-ng-disabled="property.readonly && !isPropertyValueOwner()" + name="value" + data-ng-model="property.value"> + <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': '{{maxLength}}' }"></span> + <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span> + <span ng-show="forms.editForm.value.$error.minValidation" translate="MIN_VALIDATION_ERROR"></span> + <span ng-show="forms.editForm.value.$error.maxValidation" translate="MAX_VALIDATION_ERROR"></span> + <span ng-show="forms.editForm.value.$error.minOrMaxValidation" translate="MIN_MAX_VALIDATION"></span> + <span ng-show="forms.editForm.value.$error.minValidationVfLevel" translate="MIN_VALIDATION_VF_LEVE_ERROR"></span> + <span ng-show="forms.editForm.value.$error.maxValidationVfLevel" translate="MAX_VALIDATION_VF_LEVE_ERROR"></span> + + </div> + </div> +</div> diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts new file mode 100644 index 0000000000..48aa47fdd0 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts @@ -0,0 +1,97 @@ +'use strict'; +import {DataTypesService} from "app/services/data-types-service"; +import {PropertyModel, InputPropertyBase, Component} from "app/models"; +import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model"; +import {PROPERTY_TYPES} from "app/utils/constants"; + +interface ISelectDataTypeViewModelScope extends IPropertyFormBaseViewScope { + selectedPropertiesName:string; + dataTypesService:DataTypesService; + path:string; + isTypeDataType:boolean; + myValue:any; + isReadOnly:boolean; +} + +export class SelectDataTypeViewModel extends PropertyFormBaseView { + + static '$inject' = [ + '$scope', + '$templateCache', + '$uibModalInstance', + '$injector', + 'originalProperty', + 'component', + 'filteredProperties', + 'Sdc.Services.DataTypesService', + 'propertiesMap', + '$q' + ]; + + constructor(protected $scope:ISelectDataTypeViewModelScope, + protected $templateCache:ng.ITemplateCacheService, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + protected filteredProperties:Array<PropertyModel>, + protected DataTypesService:DataTypesService, + private propertiesMap:Array<InputPropertyBase>, + private $q:ng.IQService) { + super($scope, $uibModalInstance, $injector, originalProperty, component, filteredProperties, DataTypesService); + + this.$templateCache.put("select-datatype-modal-view.html", require('app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html')); + this.$scope.innerViewSrcUrl = "select-datatype-modal-view.html"; + this.initChildScope(); + } + + //scope methods + save(isNeedToCloseModal):ng.IPromise<boolean> { + let deferred = this.$q.defer(); + this.$scope.property.propertiesName = this.DataTypesService.selectedPropertiesName; + this.$scope.property.input = this.DataTypesService.selectedInput; + this.$scope.property.isAlreadySelected = true; + this.$uibModalInstance.close(this.$scope.property); + deferred.resolve(true); + return deferred.promise; + }; + + private initForNotSimpleType = ():void => { + let property = this.$scope.property; + this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.property); + if (property.type && this.$scope.simpleTypes.indexOf(property.type) == -1) { + if (!(property.value || property.defaultValue)) { + switch (property.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = []; + break; + default: + this.$scope.myValue = {}; + } + } else { + this.$scope.myValue = JSON.parse(property.value || property.defaultValue); + } + } + }; + + //remove selection property on the modal + private removeSelected = ():void => { + this.DataTypesService.selectedPropertiesName = null; + this.DataTypesService.selectedInput = null; + }; + + private initChildScope = ():void => { + //scope properties + this.$scope.forms = {}; + this.$scope.path = this.$scope.property.name; + this.$scope.isArrowsDisabled = true; + this.DataTypesService.alreadySelectedProperties = this.propertiesMap; + this.$scope.dataTypesService = this.DataTypesService; + this.$scope.isReadOnly = true; + this.initForNotSimpleType(); + this.removeSelected(); + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html new file mode 100644 index 0000000000..acb0f292ff --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html @@ -0,0 +1,74 @@ +<!--<div>selectedPropertiesName - {{dataTypesService.selectedPropertiesName}}</div>--> +<!--<div>selectedInput - {{dataTypesService.selectedInput}}</div>--> + +<div data-ng-if="dataTypes" class="default-value-section i-sdc-form-item"> + <label class="i-sdc-form-label">Default Value</label> + <div data-ng-if="isTypeDataType"> + <select-fields-structure value-obj-ref="myValue" + type-name="property.type" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="true" + default-value="{{getDefaultValue()}}" + path="{{property.name}}" + is-parent-already-input="false" + expand-by-default="true"></select-fields-structure> + + </div> + <div data-ng-if="!isTypeDataType" ng-switch="property.type"> + <div ng-switch-when="map"> + + <select-type-map value-obj-ref="myValue" + schema-property="property.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="true" + default-value="{{getDefaultValue()}}" + max-length="maxLength"></select-type-map> + </div> + <div ng-switch-when="list"> + <select-type-list value-obj-ref="myValue" + schema-property="property.schema.property" + parent-form-obj="forms.editForm" + fields-prefix-name="currentPropertyIndex" + read-only="true" + default-value="{{getDefaultValue()}}" + max-length="maxLength"></select-type-list> + </div> + <div ng-switch-default> + <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}"> + <input class="i-sdc-form-input" + data-tests-id="defaultvalue" + ng-if="!((property.simpleType||property.type) == 'boolean')" + data-ng-maxlength="maxLength" + data-ng-disabled="isReadOnly" + maxlength="{{maxLength}}" + data-ng-model="property.value" + type="text" + name="value" + data-ng-pattern="getValidationPattern((property.simpleType||property.type))" + data-ng-model-options="{ debounce: 200 }" + data-ng-change="('json'==property.type && forms.editForm.value.$setValidity('pattern', validateJson(property.value))) + ||(!forms.editForm.value.$error.pattern && ('integer'==property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(property.value)) || onValueChange()))" + data-ng-change="" + autofocus /> + <select class="i-sdc-form-select" + data-tests-id="booleantype" + ng-if="(property.simpleType||property.type) == 'boolean'" + data-ng-disabled="isReadOnly" + name="value" + data-ng-change="onValueChange()" + data-ng-model="property.value"> + <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': '{{maxLength}}' }"></span> + <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span> + </div> + </div> + </div> + </div> +</div> diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less new file mode 100644 index 0000000000..15e30af4ee --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less @@ -0,0 +1,63 @@ +.sdc-edit-property-container { + .scrollbar-container{ + height: 415px; + width: 830px; + .perfect-scrollbar; + } + + form{ + width: 813px; + [name="description"]{ + min-height:50px; + } + } + + .sdc-modal-top-bar{ + height: 40px; + .sdc-modal-top-bar-buttons { + float: right; + + > span:not(.delimiter){ + vertical-align: middle; + .hand; + + &.sprite-new { + text-indent: 100%; + } + &.disabled, &:hover.disabled { + pointer-events: none; + } + } + + .delete-btn{ + margin-right: 6px; + } + + .left-arrow{ + margin-right: 8px; + } + + .delimiter { + height: 20px; + width: 1px; + background-color: #959595; + display: inline-block; + vertical-align: middle; + margin-right: 10px; + } + } + } + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .default-value-section{ + border-top: solid 1px @main_color_a; + padding-top: 15px; + margin-top: 15px; + } +} diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts new file mode 100644 index 0000000000..869e3db584 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts @@ -0,0 +1,95 @@ +'use strict'; +import {ComponentInstanceFactory} from "app/utils"; +import {ComponentInstance} from "app/models"; +import {Requirement, Component, Capability} from "app/models"; + +interface IResourceInstanceViewModelScope extends ng.IScope { + + componentInstanceModel:ComponentInstance; + validationPattern:RegExp; + oldName:string; + isAlreadyPressed:boolean; + footerButtons:Array<any>; + forms:any; + modalInstanceName:ng.ui.bootstrap.IModalServiceInstance; + + save():void; + close():void; +} + +export class ResourceInstanceNameViewModel { + + static '$inject' = [ + '$scope', + 'ValidationPattern', + '$uibModalInstance', + 'component' + ]; + + + constructor(private $scope:IResourceInstanceViewModelScope, + private ValidationPattern:RegExp, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private component:Component) { + + this.initScope(); + } + + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.componentInstanceModel = ComponentInstanceFactory.createComponentInstance(this.component.selectedInstance); + this.$scope.oldName = this.component.selectedInstance.name; + this.$scope.modalInstanceName = this.$uibModalInstance; + + this.$scope.isAlreadyPressed = false; + + + this.$scope.close = ():void => { + this.$uibModalInstance.dismiss(); + }; + + this.$scope.save = ():void => { + + let onFailed = () => { + this.$scope.isAlreadyPressed = true; + }; + + let onSuccess = (componentInstance:ComponentInstance) => { + this.$uibModalInstance.close(); + this.$scope.isAlreadyPressed = false; + this.$scope.componentInstanceModel = componentInstance; + //this.component.name = componentInstance.name;//DE219124 + this.component.selectedInstance.name = componentInstance.name; + //update requirements and capabilities owner name + _.forEach(this.component.selectedInstance.requirements, (requirementsArray:Array<Requirement>) => { + _.forEach(requirementsArray, (requirement:Requirement):void => { + requirement.ownerName = componentInstance.name; + }); + }); + + _.forEach(this.component.selectedInstance.capabilities, (capabilitiesArray:Array<Capability>) => { + _.forEach(capabilitiesArray, (capability:Capability):void => { + capability.ownerName = componentInstance.name; + }); + }); + + }; + + this.$scope.isAlreadyPressed = true; + if (this.$scope.oldName != this.$scope.componentInstanceModel.name) { + this.component.updateComponentInstance(this.$scope.componentInstanceModel).then(onSuccess, onFailed); + } + }; + + this.$scope.footerButtons = [ + {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("[forms.editNameForm.$invalid,componentInstanceModel.name,isAlreadyPressed]", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editNameForm.$invalid || this.$scope.isAlreadyPressed || this.$scope.componentInstanceModel.name === this.$scope.oldName; + }); + } +} diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html new file mode 100644 index 0000000000..e04343adbd --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html @@ -0,0 +1,72 @@ +<sdc-modal modal="modalInstanceName" type="classic" class="w-sdc-modal-resource-instance-name modal-type-confirmation" buttons="footerButtons" header="Instance Name" show-close-button="true"> + + <form novalidate class="w-sdc-form" name="forms.editNameForm"> + <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}"> + <label class="i-sdc-form-label required">Instance Name</label> + <input class="w-sdc-modal-resource-instance-input i-sdc-form-input" + name="componentInstanceName" + data-ng-maxlength="50" + data-ng-model="componentInstanceModel.name" + type="text" + data-required + data-ng-pattern="validationPattern" + maxlength="50" + autofocus + placeholder="Enter instance name..." + data-tests-id="instanceName" + /> + + <div class="input-error" data-ng-show="forms.editNameForm.componentInstanceName.$dirty && forms.editNameForm.componentInstanceName.$invalid"> + <span ng-show="forms.editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span> + <span ng-show="forms.editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span> + <span ng-show="forms.editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + </div> + + </div> + </form> + +</sdc-modal> + + + +<!-- + +<div class="w-sdc-modal w-sdc-modal-resource-instance-name"> + <header> + <div class="w-sdc-modal-head"> + Instance Name + </div> + </header> + <div> + <form novalidate class="w-sdc-modal-body w-sdc-form" name="editNameForm"> + <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}"> + <label class="i-sdc-form-label required">Instance Name</label> + <input class="w-sdc-modal-resource-instance-input i-sdc-form-input" + name="componentInstanceName" + data-ng-maxlength="50" + data-ng-model="componentInstanceModel.name" + type="text" + data-required + data-ng-pattern="validationPattern" + maxlength="50" + autofocus + placeholder="Enter instance name..." + data-tests-id="instanceName" + /> + + <div class="input-error" data-ng-show="editNameForm.componentInstanceName.$dirty && editNameForm.componentInstanceName.$invalid"> + <span ng-show="editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span> + <span ng-show="editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span> + <span ng-show="editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> + </div> + + </div> + </form> + + <div class="w-sdc-modal-action"> + <button class="w-sdc-btn-blue" data-ng-click="save()" type="button" data-ng-disabled="(!componentInstanceModel.name || componentInstanceModel.name === oldName) || isAlreadyPressed">Ok</button> + <button class="w-sdc-btn-dark-gray" data-ng-click="close()" type="button">Cancel</button> + </div> + </div> +</div> +--> diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less new file mode 100644 index 0000000000..57698bef17 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less @@ -0,0 +1,29 @@ +.w-sdc-modal-resource-instance-name { + + .w-sdc-modal-body { + overflow: visible; + } + + .w-sdc-modal-action { + display: flex; + align-items: center; + justify-content: center; + } + + .w-sdc-modal-resource-instance-input { + .p_1; + border: solid 1px @color_p; + height: 45px; + padding: 0 20px; + margin: 0 auto 0 auto; + display: block; + } + .w-sdc-modal-body { + border-bottom: none; + } + + .w-sdc-form .i-sdc-form-item.error::after { + top: 13px; + } + +} |