summaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/view-models/workspace
diff options
context:
space:
mode:
authormiriame <miriam.eini@amdocs.com>2019-03-04 13:49:15 +0200
committerAvi Gaffa <avi.gaffa@amdocs.com>2019-03-05 06:57:57 +0000
commit41ee9cb182dd5f730c8eb21282004ce6ee4e2927 (patch)
treed92483e28aa1997ec207e6d7d9734b4464fef3ad /catalog-ui/src/app/view-models/workspace
parent11e9d33f2f50ad3990905fba184b7c10d255070a (diff)
Add 'Req & Cap' screen for VF/PNF/Service - UI
Issue-ID: SDC-2142 Change-Id: I23a2de18862e18389f801cbec3e452d7094df8e9 Signed-off-by: miriame <miriam.eini@amdocs.com>
Diffstat (limited to 'catalog-ui/src/app/view-models/workspace')
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-editable-view.html197
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts355
-rw-r--r--catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less193
-rw-r--r--catalog-ui/src/app/view-models/workspace/workspace-view-model.ts8
4 files changed, 721 insertions, 32 deletions
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-editable-view.html b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-editable-view.html
new file mode 100644
index 0000000000..14bc49e28b
--- /dev/null
+++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-editable-view.html
@@ -0,0 +1,197 @@
+<!--
+ ~ Copyright © 2016-2018 European Support Limited
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+
+<div class="workspace-req-and-cap-editable">
+ <loader data-display="isLoading"></loader>
+
+ <div class="tabs-header">
+ <div class="req-and-cap-tabs">
+ <div data-tests-id="req-tab" data-ng-click="onSwitchTab()" class="tab"
+ data-ng-class="{'selected':mode=='requirements'}">Requirements
+ </div>
+ <div data-tests-id="cap-tab" data-ng-click="onSwitchTab()" class="tab"
+ data-ng-class="{'selected':mode=='capabilities'}">Capabilities
+ </div>
+ </div>
+ <div class="buttons-in-right" data-ng-if="!isListEmpty()">
+ <div class="search">
+ <input id="search-box" data-ng-if="filter.show" data-tests-id="search-box" placeholder="Search"
+ data-ng-model-options="{debounce: 200}" data-ng-model="filter.txt" data-ng-change="onFilter()"/>
+ <div class="search-icon-container" data-tests-id="search-icon">
+ <svg-icon
+ class="hand"
+ [name]="'search-o'"
+ [mode]="'primary'"
+ [size]="'small'"
+ [clickable]="'true'"
+ data-ng-click="onSearchIconClick()">
+ </svg-icon>
+ </div>
+ </div>
+ <div class="add-button-icon-and-label" data-ng-if="isEditable" data-ng-click="onAddBtnClicked()"
+ data-ng-class="{'disabled': isReadonly()}" data-tests-id="add-button">
+ <svg-icon
+ name="plus"
+ mode="primary"
+ size="small"
+ clickable="true"
+ [disabled]="isReadonly()"
+ labelPlacement="top">
+ </svg-icon>
+ <span class="icon-label-txt">{{mode === 'requirements' ? 'Add Requirement' : 'Add Capability'}}</span>
+ </div>
+ </div>
+ </div>
+
+ <div class="empty-list-container" data-ng-if="isListEmpty() && !isLoading" data-tests-id="empty-list-container">
+ <div class="empty-list-add-btn add-button-icon-and-label" data-ng-class="{'disabled': isReadonly()}"
+ data-ng-click="onAddBtnClicked()" data-tests-id="empty-list-add-btn">
+ <svg-icon
+ name="plus-circle"
+ mode="primary"
+ size="x_large"
+ clickable="true"
+ [disabled]="isReadonly()">
+ </svg-icon>
+ <div class="icon-label-txt">{{mode === 'requirements' ? 'Add Requirement' : 'Add Capability'}}</div>
+ </div>
+ </div>
+
+ <div class="table-container-flex requirements-table" data-ng-if="mode=='requirements' && !isListEmpty()">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}" data-tests-id="requirement-table">
+ <div class="head flex-container">
+ <div data-ng-repeat="header in editableRequirementsTableHeadersList track by $index"
+ data-ng-click="sort(header.property, requirementsSortTableDefined)"
+ class="table-header head-row hand flex-item {{header.property}}"
+ data-tests-id="table-header-{{header.property}}">
+ {{header.title}}
+ <span data-ng-if="requirementsSortTableDefined.sortByField === header.property"
+ class="table-header-sort-arrow" data-tests-id="table-header-sort-arrow"
+ data-ng-class="{'down': requirementsSortTableDefined.reverse, 'up':!requirementsSortTableDefined.reverse}"> </span>
+ </div>
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="editable-table-data">
+ <div data-ng-if="filteredRequirementsList.length === 0" class="no-row-text"
+ data-tests-id="no-rows-in-table">
+ There are no requirements to display
+
+ </div>
+ <div data-ng-repeat="req in filteredRequirementsList | orderBy:requirementsSortTableDefined.sortByField:requirementsSortTableDefined.reverse track by $index"
+ data-tests-id="reqRow">
+ <div class="flex-container data-row" data-ng-class="{'editable-row': req.isCreatedManually}"
+ data-ng-click="req.isCreatedManually && onEditRequirement(req)">
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{req.name}}">
+ <span data-tests-id="{{req.name}}">{{req.name}}</span>
+ </div>
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{req.capability}}">
+ <span data-tests-id="{{req.capability}}">{{req.capability && cutToscaTypePrefix(req.capability, 'capabilities.')}}</span>
+ </div>
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{req.node}}">
+ <span data-tests-id="{{req.node}}">{{req.node && cutToscaTypePrefix(req.node, "nodes.")}}</span>
+ </div>
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{req.relationship}}">
+ <span data-tests-id="{{req.relationship}}">{{req.relationship && cutToscaTypePrefix(req.relationship, "relationships.")}}</span>
+ </div>
+ <div class="table-col-general flex-item text ellipsis-text occurrences-col" tooltips
+ tooltip-content="{{req.minOccurrences}} - {{req.maxOccurrences}}">
+ <span data-tests-id="{{req.minOccurrences}} - {{req.maxOccurrences}}">{{req.minOccurrences}} - {{req.maxOccurrences}}</span>
+ </div>
+ <div class="table-col-general flex-item text other-col" data-tests-id="delete-req"
+ data-ng-class="{'disabled': isReadonly()}">
+ <svg-icon name="trash-o" class="trash-icon" size="small"
+ data-ng-if="req.isCreatedManually && !isReadonly()"
+ data-ng-click="onDeleteReq($event, req)"></svg-icon>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+ <div class="table-container-flex capabilities-table" data-ng-if="mode=='capabilities' && !isListEmpty()"
+ data-tests-id="capabilities-table">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div data-ng-repeat="header in editableCapabilitiesTableHeadersList track by $index"
+ data-ng-click="sort(header.property, capabilitiesSortTableDefined)"
+ class="table-header head-row hand flex-item {{header.property}}"
+ data-tests-id="header-{{header.property}}">
+ {{header.title}}
+ <span data-ng-if="capabilitiesSortTableDefined.sortByField === header.property"
+ class="table-header-sort-arrow" data-tests-id=="table-header-sort-arrow"
+ data-ng-class="{'down': capabilitiesSortTableDefined.reverse, 'up':!capabilitiesSortTableDefined.reverse}"> </span>
+ </div>
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="editable-table-data">
+ <div data-ng-if="filteredCapabilitiesList.length === 0" class="no-row-text"
+ data-tests-id="no-rows-in-table">
+ There are no capabilities to display
+
+ </div>
+ <div data-ng-repeat="capability in filteredCapabilitiesList | orderBy:capabilitiesSortTableDefined.sortByField:capabilitiesSortTableDefined.reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': capability.selected, 'editable-row': capability.isCreatedManually}"
+ data-ng-click="capability.isCreatedManually && onEditCapability(capability)"
+ data-tests-id="capabilities-table-row">
+
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{capability.name}}">
+ <span data-tests-id="{{capability.name}}">{{capability.name}}</span>
+ </div>
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{capability.type}}">
+ <span data-tests-id="{{capability.type}}">{{capability.type && cutToscaTypePrefix(capability.type, 'capabilities.')}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text description-col">
+ <div data-tests-id="{{capability.description}}" class="multiline-ellipsis"
+ ellipsis="capability.description" max-chars="60">{{capability.description}}
+ </div>
+ </div>
+
+ <div class="table-col-general flex-item text ellipsis-text" tooltips
+ tooltip-content="{{capability.validSourceTypes.join(',')}}">
+ <span data-tests-id="{{capability.validSourceTypes.join(',')}}">{{capability.validSourceTypes.join(',')}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text ellipsis-text occurrences-col" tooltips
+ tooltip-content="{{capability.minOccurrences}} - {{capability.maxOccurrences}}">
+ <span data-tests-id="{{capability.minOccurrences}} - {{capability.maxOccurrences}}">{{capability.minOccurrences}} - {{capability.maxOccurrences}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text other-col" data-tests-id="delete-cap"
+ data-ng-class="{'disabled': isReadonly()}">
+ <svg-icon name="trash-o" class="trash-icon" size="small"
+ data-ng-if="capability.isCreatedManually && !isReadonly()"
+ data-ng-click="onDeleteCap($event, capability)"></svg-icon>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+</div>
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts
index 6eaae44eb2..165578d008 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts
+++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts
@@ -23,33 +23,82 @@
*/
'use strict';
import * as _ from "lodash";
+import {ComponentRef} from '@angular/core';
import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
-import {ModalsHandler} from "app/utils";
-import {Capability, PropertyModel, Requirement} from "app/models";
-import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response";
-import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service";
+import {ModalsHandler, ResourceType} from "app/utils";
+import {ComponentType} from "app/utils/constants";
+import {
+ Capability, PropertyModel, Requirement, Resource,
+ RelationshipTypesMap, NodeTypesMap, CapabilityTypesMap
+} from "app/models";
+import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response";
+import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service";
+import {ToscaTypesServiceNg2} from "app/ng2/services/tosca-types.service";
+import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
+import {ModalService} from 'app/ng2/services/modal.service';
+import {RequirementsEditorComponent} from 'app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.component';
+import {CapabilitiesEditorComponent} from 'app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.component';
+import {ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service";
+import {IModalConfig} from "sdc-ui/lib/angular/modals/models/modal-config";
+import {ModalButtonComponent} from "sdc-ui/lib/angular/components";
export class SortTableDefined {
reverse:boolean;
sortByField:string;
}
+class RequirementUI extends Requirement {
+ isCreatedManually: boolean;
+
+ constructor(input: Requirement, componentUniqueId: string) {
+ super(input);
+ this.isCreatedManually = input.ownerId === componentUniqueId;
+ }
+}
+class CapabilityUI extends Capability {
+ isCreatedManually: boolean;
+
+ constructor(input: Capability, componentUniqueId: string) {
+ super(input);
+ this.isCreatedManually = input.ownerId === componentUniqueId;
+ }
+}
+
interface IReqAndCapabilitiesViewModelScope extends IWorkspaceViewModelScope {
requirementsTableHeadersList:Array<any>;
+ editableRequirementsTableHeadersList: Array<any>;
capabilitiesTableHeadersList:Array<any>;
+ editableCapabilitiesTableHeadersList: Array<any>;
capabilityPropertiesTableHeadersList:Array<any>;
requirementsSortTableDefined:SortTableDefined;
capabilitiesSortTableDefined:SortTableDefined;
propertiesSortTableDefined:SortTableDefined;
- requirements:Array<Requirement>;
- capabilities:Array<Capability>;
+ requirements: Array<RequirementUI>;
+ filteredRequirementsList: Array<RequirementUI>;
+ capabilities: Array<CapabilityUI>;
+ filteredCapabilitiesList: Array<CapabilityUI>;
mode:string;
filteredProperties:Array<Array<PropertyModel>>;
searchText:string;
+ isEditable: boolean;
+ modalInstance: ComponentRef<ModalComponent>;
+ filter: {txt: string; show: boolean};
sort(sortBy:string, sortByTableDefined:SortTableDefined):void;
+ sortByIsCreatedManually(arrToSort: Array<RequirementUI|CapabilityUI>): Array<any>;
updateProperty(property:PropertyModel, indexInFilteredProperties:number):void;
allCapabilitiesSelected(selected:boolean):void;
+ onAddBtnClicked(): void;
+ onEditRequirement(req: RequirementUI): void;
+ onEditCapability(cap: CapabilityUI): void;
+ onDeleteReq(event, req: RequirementUI): void;
+ onDeleteCap(event, cap: CapabilityUI): void;
+ onFilter(): void;
+ isListEmpty(): boolean;
+ onSwitchTab(): void;
+ onSearchIconClick(): void;
+ cutToscaTypePrefix(valToCut: string, textToStartCut: string): string;
+ isReadonly(): boolean;
}
export class ReqAndCapabilitiesViewModel {
@@ -58,33 +107,37 @@ export class ReqAndCapabilitiesViewModel {
'$scope',
'$filter',
'ModalsHandler',
- 'ComponentServiceNg2'
+ 'ComponentServiceNg2',
+ 'ToscaTypesServiceNg2',
+ 'ModalServiceNg2',
+ 'ModalServiceSdcUI'
];
constructor(private $scope:IReqAndCapabilitiesViewModelScope,
private $filter:ng.IFilterService,
private ModalsHandler:ModalsHandler,
- private ComponentServiceNg2: ComponentServiceNg2) {
+ private ComponentServiceNg2: ComponentServiceNg2,
+ private ToscaTypesServiceNg2: ToscaTypesServiceNg2,
+ private ModalServiceNg2: ModalService,
+ private ModalServiceSdcUI: ModalServiceSdcUI) {
this.initCapabilitiesAndRequirements();
+ this.fetchCapabilitiesRelatedData();
}
private initCapabilitiesAndRequirements = (): void => {
- if(!this.$scope.component.capabilities || !this.$scope.component.requirements) {
- this.$scope.isLoading = true;
- this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.component.componentType, this.$scope.component.uniqueId).subscribe((response:ComponentGenericResponse) => {
- this.$scope.component.capabilities = response.capabilities;
- this.$scope.component.requirements = response.requirements;
- this.initScope();
- this.$scope.isLoading = false;
- }, () => {
- this.$scope.isLoading = false;
- });
- } else {
+ this.$scope.isEditable = this.getIsEditableByComponentType();
+ this.$scope.isLoading = true;
+ this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.component.componentType, this.$scope.component.uniqueId).subscribe((response: ComponentGenericResponse) => {
+ this.$scope.component.capabilities = response.capabilities;
+ this.$scope.component.requirements = response.requirements;
this.initScope();
- }
+ this.$scope.isLoading = false;
+ }, () => {
+ this.$scope.isLoading = false;
+ });
}
@@ -98,15 +151,18 @@ export class ReqAndCapabilitiesViewModel {
});
};
- private initScope = ():void => {
-
+ private initScope = (currentMode = 'requirements'): void => {
+ this.$scope.isReadonly = (): boolean => {
+ return this.$scope.isViewMode() || !this.$scope.isDesigner();
+ };
+ this.$scope.filter = {txt: '', show: false};
this.$scope.requirementsSortTableDefined = {
reverse: false,
- sortByField: 'name'
+ sortByField: this.$scope.isEditable ? 'other' : 'name'
};
this.$scope.capabilitiesSortTableDefined = {
reverse: false,
- sortByField: 'name'
+ sortByField: this.$scope.isEditable ? 'other' : 'name'
};
this.$scope.propertiesSortTableDefined = {
reverse: false,
@@ -129,6 +185,22 @@ export class ReqAndCapabilitiesViewModel {
{title: 'Valid Source', property: ''},
{title: 'Occurrences', property: ''}
];
+ this.$scope.editableRequirementsTableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Capability', property: 'capability'},
+ {title: 'Node', property: 'node'},
+ {title: 'Relationship', property: 'relationship'},
+ {title: 'Occurrences', property: 'occurrences'},
+ {title: '●●●', property: 'other'}
+ ];
+ this.$scope.editableCapabilitiesTableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Description', property: 'description'},
+ {title: 'Valid Sources', property: 'valid-sources'},
+ {title: 'Occurrences', property: 'occurrences'},
+ {title: '●●●', property: 'other'}
+ ];
this.$scope.capabilityPropertiesTableHeadersList = [
{title: 'Name', property: 'name'},
{title: 'Type', property: 'type'},
@@ -137,17 +209,26 @@ export class ReqAndCapabilitiesViewModel {
];
this.$scope.filteredProperties = [];
- this.$scope.mode = 'requirements';
+ this.$scope.mode = currentMode;
this.$scope.requirements = [];
_.forEach(this.$scope.component.requirements, (req:Array<Requirement>, capName)=> {
- this.$scope.requirements = this.$scope.requirements.concat(req);
+ let reqUIList: Array<RequirementUI> = _.map(req, reqObj => new RequirementUI(reqObj, this.$scope.component.uniqueId));
+ this.$scope.requirements = this.$scope.requirements.concat(reqUIList);
});
+ this.$scope.filteredRequirementsList = this.$scope.requirements;
this.$scope.capabilities = [];
_.forEach(this.$scope.component.capabilities, (cap:Array<Capability>, capName)=> {
- this.$scope.capabilities = this.$scope.capabilities.concat(cap);
+ let capUIList: Array<CapabilityUI> = _.map(cap, capObj => new CapabilityUI(capObj, this.$scope.component.uniqueId));
+ this.$scope.capabilities = this.$scope.capabilities.concat(capUIList);
});
+ this.$scope.sortByIsCreatedManually = (arrToSort: Array<RequirementUI|CapabilityUI>): Array<any> => {
+ return arrToSort.sort((elem1: RequirementUI|CapabilityUI, elem2: RequirementUI|CapabilityUI) => +elem2.isCreatedManually - (+elem1.isCreatedManually));
+ };
+ this.$scope.filteredCapabilitiesList = this.$scope.sortByIsCreatedManually(this.$scope.capabilities);
+ this.$scope.filteredRequirementsList = this.$scope.sortByIsCreatedManually(this.$scope.requirements);
+
this.$scope.sort = (sortBy:string, sortByTableDefined:SortTableDefined):void => {
sortByTableDefined.reverse = (sortByTableDefined.sortByField === sortBy) ? !sortByTableDefined.reverse : false;
sortByTableDefined.sortByField = sortBy;
@@ -162,6 +243,226 @@ export class ReqAndCapabilitiesViewModel {
cap.selected = selected;
});
};
+ this.$scope.onAddBtnClicked = (): void => {
+ switch (this.$scope.mode) {
+ case 'requirements':
+ this.openRequirementsModal();
+ break;
+ case 'capabilities':
+ this.openCapabilitiesModal();
+ break;
+ }
+ };
+ this.$scope.onEditRequirement = (req: RequirementUI): void => {
+ this.openRequirementsModal(req);
+ };
+ this.$scope.onEditCapability = (cap: CapabilityUI): void => {
+ this.openCapabilitiesModal(cap);
+ };
+ this.$scope.onDeleteReq = (event: Event, req: RequirementUI): void => {
+ event.stopPropagation();
+ this.ModalServiceSdcUI.openAlertModal('Delete Requirement',
+ `Are you sure you want to delete requirement: ${req.name}?`, 'OK', () => this.deleteRequirement(req), 'Cancel');
+ };
+ this.$scope.onDeleteCap = (event: Event, cap: CapabilityUI): void => {
+ event.stopPropagation();
+ this.ModalServiceSdcUI.openAlertModal('Delete Capability',
+ `Are you sure you want to delete capability: ${cap.name}?`, 'OK', () => this.deleteCapability(cap), 'Cancel');
+ };
+ this.$scope.onSearchIconClick = (): void => {
+ this.$scope.filter.show = !!this.$scope.filter.txt || !this.$scope.filter.show;
+ };
+ this.$scope.onFilter = (): void => {
+ switch (this.$scope.mode) {
+ case 'requirements':
+ this.$scope.filteredRequirementsList = _.filter(this.$scope.requirements, req => req.name.includes(this.$scope.filter.txt));
+ break;
+ case 'capabilities':
+ this.$scope.filteredCapabilitiesList = _.filter(this.$scope.capabilities, cap => cap.name.includes(this.$scope.filter.txt));
+ break;
+ }
+ };
+ this.$scope.isListEmpty = (): boolean => {
+ switch (this.$scope.mode) {
+ case 'requirements':
+ return this.$scope.requirements.length === 0;
+ case 'capabilities':
+ return this.$scope.capabilities.length === 0;
+ }
+ };
+ this.$scope.onSwitchTab = (): void => {
+ this.$scope.mode = this.$scope.mode === 'requirements' ? 'capabilities' : 'requirements';
+ this.$scope.filter.txt = '';
+ this.$scope.filter.show = false;
+ this.$scope.filteredRequirementsList = this.$scope.requirements;
+ this.$scope.filteredCapabilitiesList = this.$scope.capabilities;
+ };
+ this.$scope.cutToscaTypePrefix = (valToCut: string, textToStartCut: string): string => {
+ let index = valToCut.indexOf(textToStartCut);
+ return index !== -1 ? valToCut.substr(index + textToStartCut.length) : valToCut;
+ };
+ };
+
+ private getIsEditableByComponentType() {
+ if (this.$scope.componentType === ComponentType.SERVICE) {
+ return true;
+ }
+ if (this.$scope.component.isResource()) {
+ let componentAsResource: Resource = <Resource>this.$scope.component;
+ return componentAsResource.resourceType === ResourceType.VF ||
+ componentAsResource.resourceType === ResourceType.PNF;
+ }
+ return false;
+ };
+
+ private fetchCapabilitiesRelatedData() {
+ if (this.$scope.isEditable) {
+ this.$scope.capabilityTypesList = [];
+ this.ToscaTypesServiceNg2.fetchCapabilityTypes().subscribe((result: CapabilityTypesMap) => {
+ _.forEach(result, capabilityType => this.$scope.capabilityTypesList.push(capabilityType));
+ });
+ this.$scope.nodeTypesList = [];
+ this.ToscaTypesServiceNg2.fetchNodeTypes().subscribe((result: NodeTypesMap) => {
+ _.forEach(result, nodeType => this.$scope.nodeTypesList.push(nodeType));
+ });
+ this.$scope.relationshipTypesList = [];
+ this.ToscaTypesServiceNg2.fetchRelationshipTypes().subscribe((result: RelationshipTypesMap) => {
+ _.forEach(result, relshipType => this.$scope.relationshipTypesList.push(relshipType));
+ });
+ }
+ }
+
+ private openRequirementsModal(req?: RequirementUI) {
+ let modalConfig: IModalConfig = {
+ size: 'md',
+ title: (req ? 'Update' : 'Add') + ' Requirement',
+ type: 'custom',
+ buttons: [
+ {
+ id: 'saveButton',
+ text: (req ? 'Update' : 'Create'),
+ size: "'x-small'",
+ callback: () => this.createOrUpdateRequirement(),
+ closeModal: true
+ },
+ {text: "Cancel", size: "'x-small'", closeModal: true}]
+ };
+ let modalInputs = {
+ requirement: req,
+ relationshipTypesList: this.$scope.relationshipTypesList,
+ nodeTypesList: this.$scope.nodeTypesList,
+ capabilityTypesList: this.$scope.capabilityTypesList,
+ isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(),
+ validityChangedCallback: this.getDisabled
+ };
+
+ this.ModalServiceSdcUI.openCustomModal(modalConfig, RequirementsEditorComponent, {input: modalInputs});
+ }
+
+ private openCapabilitiesModal(cap?: CapabilityUI) {
+ let modalConfig: IModalConfig = {
+ size: 'md',
+ title: (cap ? 'Update' : 'Add') + ' Capability',
+ type: 'custom',
+ buttons: [
+ {
+ id: 'saveButton',
+ text: (cap ? 'Update' : 'Create'),
+ size: "'x-small'",
+ callback: () => this.createOrUpdateCapability(),
+ closeModal: true
+ },
+ {text: "Cancel", size: "'x-small'", closeModal: true}]
+ };
+ let modalInputs = {
+ capability: cap,
+ capabilityTypesList: this.$scope.capabilityTypesList,
+ isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(),
+ validityChangedCallback: this.getDisabled
+ };
+
+ this.ModalServiceSdcUI.openCustomModal(modalConfig, CapabilitiesEditorComponent, {input: modalInputs});
+ }
+
+ getDisabled = (shouldEnable: boolean): void => {
+ let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton');
+ saveButton.disabled = this.$scope.isViewMode() || !this.$scope.isDesigner() || !shouldEnable;
+ };
+
+ private createOrUpdateRequirement() {
+ let requirement = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance.requirementData;
+ this.$scope.isLoading = true;
+ if (!requirement.uniqueId) {
+ this.ComponentServiceNg2.createRequirement(this.$scope.component, requirement).subscribe(result => {
+ this.$scope.requirements.unshift(new RequirementUI(result[0], this.$scope.component.uniqueId));
+ this.$scope.isLoading = false;
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }
+ else {
+ this.ComponentServiceNg2.updateRequirement(this.$scope.component, requirement).subscribe(result => {
+ let index = this.$scope.requirements.findIndex(req => result[0].uniqueId === req.uniqueId);
+ this.$scope.requirements[index] = new RequirementUI(result[0], this.$scope.component.uniqueId);
+ this.$scope.isLoading = false;
+ this.$scope.$apply();
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }
+ }
+
+ private createOrUpdateCapability() {
+ let capability = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance.capabilityData;
+ this.$scope.isLoading = true;
+ if (!capability.uniqueId) {
+ this.ComponentServiceNg2.createCapability(this.$scope.component, capability).subscribe(result => {
+ this.$scope.capabilities.unshift(new CapabilityUI(result[0], this.$scope.component.uniqueId));
+ this.$scope.isLoading = false;
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }
+ else {
+ this.ComponentServiceNg2.updateCapability(this.$scope.component, capability).subscribe(result => {
+ let index = this.$scope.capabilities.findIndex(cap => result[0].uniqueId === cap.uniqueId);
+ this.$scope.capabilities[index] = new CapabilityUI(result[0], this.$scope.component.uniqueId);
+ this.$scope.isLoading = false;
+ this.$scope.$apply();
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }
+ }
+
+ private deleteRequirement(req) {
+ this.$scope.isLoading = true;
+ this.ComponentServiceNg2.deleteRequirement(this.$scope.component, req.uniqueId).subscribe(() => {
+ this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.componentType, this.$scope.component.uniqueId).subscribe(response => {
+ this.$scope.component.requirements = response.requirements;
+ this.initScope('requirements');
+ this.$scope.isLoading = false;
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }
+
+ private deleteCapability(cap) {
+ this.$scope.isLoading = true;
+ this.ComponentServiceNg2.deleteCapability(this.$scope.component, cap.uniqueId).subscribe(() => {
+ this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.componentType, this.$scope.component.uniqueId).subscribe(response => {
+ this.$scope.component.capabilities = response.capabilities;
+ this.initScope('capabilities');
+ this.$scope.isLoading = false;
+ }, () => {
+ this.$scope.isLoading = false;
+ });
+ }, () => {
+ this.$scope.isLoading = false;
+ });
}
}
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less
index 9b52fad411..fa6623f089 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less
+++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less
@@ -65,13 +65,14 @@
}
}
}
- .expand-collapse-buttons{
+ .add-button {
+ color: @main_color_a;
+ }
+ .add-button, .expand-collapse-buttons {
float: right;
- width: 44px;
margin-left: 11px;
margin-top: 10px;
- span{
- vertical-align: bottom;
+ &, span {
.hand;
}
}
@@ -108,6 +109,28 @@
white-space: nowrap;
}
+ .editable-table-data {
+ max-height: 430px;
+ }
+
+ .data-row {
+ &:not(.editable-row) {
+ background: @tlv_color_t;
+ color: @main_color_n;
+ }
+ &.editable-row {
+ cursor: pointer;
+ }
+ .sprite-new.delete-icon {
+ visibility: hidden;
+ }
+ &:hover {
+ .sprite-new.delete-icon {
+ visibility: visible;
+ }
+ }
+ }
+
&.requirements-table{
border-top: 4px solid @main_color_a;
.flex-item:nth-child(1) {
@@ -194,3 +217,165 @@
}
}
+
+.workspace-req-and-cap-editable {
+ .tabs-header {
+ display: flex;
+ justify-content: space-between;
+ border-bottom: 1px solid @main_color_o;
+ .req-and-cap-tabs {
+ display: flex;
+ .tab {
+ font-family: @font-opensans-regular;
+ font-size: 22px;
+ padding: 5px;
+ .hand;
+ &:first-of-type {
+ margin-right: 35px;
+ }
+ &.selected {
+ color: @main_color_a;
+ border-bottom: 2px solid @main_color_a;
+ }
+ }
+ }
+ .buttons-in-right {
+ display: flex;
+ .search {
+ display: flex;
+ height: min-content;
+ margin-top: 10px;
+ padding-right: 11px;
+ border-right: 1px solid @main_color_o;
+ #search-box {
+ border: none;
+ border-bottom: 1px solid @main_color_o;
+ text-indent: 10px;
+ &:focus {
+ outline: none;
+ }
+ }
+ .search-icon-container {
+ margin-top: 3px;
+ padding-top: 4px;
+ }
+
+ }
+ .add-button-icon-and-label {
+ font-size: 14px;
+ margin-left: 11px;
+ margin-top: 10px;
+ padding-top: 5px;
+ /deep/ svg-icon {
+ vertical-align: bottom;
+ }
+ &:hover {
+ &:not(.disabled) {
+ cursor: pointer;
+ color: @sdcui_color_light-blue;
+ }
+ }
+ }
+ }
+ }
+ .add-button-icon-and-label {
+ .icon-label-txt {
+ text-transform: uppercase;
+ font-family: @font-opensans-medium;
+ color: @main_color_a;
+ &:hover {
+ &:not(.disabled) {
+ color: @sdcui_color_light-blue;
+ }
+ }
+ }
+ }
+ .empty-list-container {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+
+ .empty-list-add-btn {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border: 1px solid @main_color_o;
+ margin-top: 50px;
+ height: 229px;
+ width: 480px;
+ &.disabled {
+ pointer-events: none;
+ }
+ &:hover {
+ &:not(.disabled) {
+ border: 1px solid @main_color_a;
+ cursor: pointer;
+ }
+ }
+ .icon-label-txt {
+ margin-top: 15px;
+ font-size: 16px;
+ }
+ }
+ }
+ .table-container-flex .table .head .head-row {
+ text-align: left;
+ &.description {
+ flex: 2;
+ }
+ &.other {
+ flex: 0.25;
+ text-align: center;
+ }
+ &.occurrences {
+ flex: 0.75;
+ }
+ }
+ .data-row {
+ .ellipsis-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ &:not(.editable-row) {
+ background: @tlv_color_t;
+ cursor: default;
+ color: @main_color_n;
+ }
+ &.editable-row {
+ cursor: pointer;
+ .table-col-general:hover {
+ color: @main_color_b;
+ }
+ }
+ .description-col {
+ flex: 2;
+ }
+ .occurrences-col {
+ flex: 0.75;
+ }
+ .other-col {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex: 0.25;
+ .trash-icon {
+ visibility: hidden;
+ }
+ }
+ &:hover {
+ .trash-icon {
+ visibility: visible;
+ }
+ }
+ .multiline-ellipsis {
+ line-height: 1.5em;
+ padding: 1px 0 1px 0;
+ /deep/ .ellipsis-directive-more-less {
+ float: none;
+ margin-left: 5px;
+ color: @main_color_a;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts
index 676a2d38d3..9429022b48 100644
--- a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts
+++ b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts
@@ -23,7 +23,10 @@
*/
'use strict';
import * as _ from "lodash";
-import {IUserProperties, IAppMenu, Resource, Component, Plugin, PluginsConfiguration, PluginDisplayOptions} from "app/models";
+import {
+ IUserProperties, IAppMenu, Resource, Component, Plugin, PluginsConfiguration, PluginDisplayOptions,
+ RelationshipTypeModel, NodeTypeModel, CapabilityTypeModel
+} from "app/models";
import {
WorkspaceMode, ComponentFactory, ChangeLifecycleStateHandler, Role, ComponentState, MenuItemGroup, MenuHandler,
MenuItem, ModalsHandler, States, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ResourceType, PREVIOUS_CSAR_COMPONENT
@@ -78,6 +81,9 @@ export interface IWorkspaceViewModelScope extends ng.IScope {
unsavedChanges:boolean;
unsavedChangesCallback:Function;
unsavedFile:boolean;
+ capabilityTypesList: Array<CapabilityTypeModel>;
+ relationshipTypesList: Array<RelationshipTypeModel>;
+ nodeTypesList: Array<NodeTypeModel>;
startProgress(message:string):void;