Interface operation screen enhancements 24/75724/3
authorArielk <Ariel.Kenan@amdocs.com>
Sun, 13 Jan 2019 16:31:13 +0000 (18:31 +0200)
committerAvi Gaffa <avi.gaffa@amdocs.com>
Mon, 14 Jan 2019 11:10:24 +0000 (11:10 +0000)
Change-Id: I2b510a4bf27ddf5730ed044cf77aebd955ad5862
Issue-ID: SDC-2044
Signed-off-by: Arielk <Ariel.Kenan@amdocs.com>
17 files changed:
catalog-ui/src/app/models/operation.ts
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts
catalog-ui/src/app/ng2/services/component-services/component.service.ts
catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
catalog-ui/src/app/ng2/services/workflow.service.ts
catalog-ui/src/app/utils/common-utils.ts
catalog-ui/src/assets/languages/en_US.json

index 2aa1332..6eeccec 100644 (file)
@@ -3,15 +3,15 @@
 export class OperationParameter {
     name: string;
     type: String;
-    property: string;
-    mandatory: boolean;
+    inputId: string;
+    required: boolean;
 
     constructor(param?: OperationParameter) {
         if (param) {
             this.name = param.name;
             this.type = param.type;
-            this.property = param.property;
-            this.mandatory = param.mandatory;
+            this.inputId = param.inputId;
+            this.required = param.required;
         }
     }
 }
@@ -26,13 +26,13 @@ export class WORKFLOW_ASSOCIATION_OPTIONS {
     static EXISTING = 'EXISTING';
 }
 
-export class OperationModel {
-    operationType: string;
+export class BEOperationModel {
+    name: string;
     description: string;
     uniqueId: string;
 
-    inputParams: IOperationParamsList;
-    outputParams: IOperationParamsList;
+    inputs: IOperationParamsList;
+    outputs: IOperationParamsList;
 
     workflowAssociationType: string;
     workflowId: string;
@@ -40,12 +40,12 @@ export class OperationModel {
 
     constructor(operation?: any) {
         if (operation) {
-            this.operationType = operation.operationType;
+            this.name = operation.name;
             this.description = operation.description;
             this.uniqueId = operation.uniqueId;
 
-            this.inputParams = operation.inputParams;
-            this.outputParams = operation.outputParams;
+            this.inputs = operation.inputs;
+            this.outputs = operation.outputs;
 
             this.workflowAssociationType = operation.workflowAssociationType;
             this.workflowId = operation.workflowId;
@@ -53,23 +53,66 @@ export class OperationModel {
         }
     }
 
-    public createInputParamsList(inputParams: Array<OperationParameter>): void {
-        this.inputParams = {
-            listToscaDataDefinition: inputParams
+    public createInputsList(inputs: Array<OperationParameter>): void {
+        this.inputs = {
+            listToscaDataDefinition: inputs
         };
     }
 
-    public createOutputParamsList(outputParams: Array<OperationParameter>): void {
-        this.outputParams = {
-            listToscaDataDefinition: _.map(outputParams, output => {
-                const newOutput = {...output};
-                delete newOutput.property;
-                return newOutput;
+    public createOutputsList(outputs: Array<OperationParameter>): void {
+        this.outputs = {
+            listToscaDataDefinition: _.map(outputs, output => {
+                delete output.inputId;
+                return output;
             })
         };
     }
 }
 
-export interface CreateOperationResponse extends OperationModel {
+export class OperationModel extends BEOperationModel {
+    interfaceType: string;
+    interfaceId: string;
+
+    constructor(operation?: any) {
+        super(operation);
+        if (operation) {
+            this.interfaceId = operation.interfaceId;
+            this.interfaceType = operation.interfaceType;
+        }
+    }
+
+    public displayName(): string {
+        const lastDot = this.name ? this.name.lastIndexOf('.') : -1;
+        return lastDot === -1 ? this.name : this.name.substr(lastDot + 1);
+    }
+}
+
+export class CreateOperationResponse extends OperationModel {
     artifactUUID: string;
+
+    constructor(operation?: any) {
+        super(operation);
+        if (operation) {
+            this.artifactUUID = operation.artifactUUID;
+        }
+    }
+}
+
+export class InterfaceModel {
+    type: string;
+    uniqueId: string;
+    operations: Array<OperationModel>;
+
+    constructor(interf?: any) {
+        if (interf) {
+            this.type = interf.type;
+            this.uniqueId = interf.uniqueId;
+            this.operations = interf.operations;
+        }
+    }
+
+    public displayType(): string {
+        const lastDot = this.type ? this.type.lastIndexOf('.') : -1;
+        return lastDot === -1 ? this.type : this.type.substr(lastDot + 1);
+    }
 }
index 3414769..6292d85 100644 (file)
@@ -1,7 +1,9 @@
 import {NgModule} from "@angular/core";
 import {CommonModule} from "@angular/common";
 import {InterfaceOperationComponent} from "./interface-operation.page.component";
+import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index";
 import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+import {TranslateModule} from "app/ng2/shared/translator/translate.module";
 
 @NgModule({
     declarations: [
@@ -9,7 +11,9 @@ import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
     ],
     imports: [
         CommonModule,
-        UiElementsModule
+        SdcUiComponentsModule,
+        UiElementsModule,
+        TranslateModule
     ],
     exports: [],
     entryComponents: [
index c9fdf94..afeff48 100644 (file)
 <!--
-  ~ 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.
-  -->
-  
+    ~ 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="interface-operation">
     <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
 
     <div
-        class="add-btn"
+        class="top-add-btn add-btn"
+        *ngIf="!isListEmpty()"
         [ngClass]="{'disabled': readonly}"
         data-tests-id="addInterfaceOperation"
         (click)="onEditOperation()">
-        Add Operation
+        {{ 'INTERFACE_ADD_OPERATION' | translate }}
     </div>
 
     <div class="operation-list">
 
         <div
-            class="operation-row"
-            *ngFor="let operation of operationList"
-            (click)="onEditOperation(operation)">
-
-            <span class="operation-info-container">
-                <span class="operation-title">
-                    <p class="operation-text" data-tests-id="interfaceOperationType">{{operation.operationType}}</p>
-                </span>
-
-                <span class="operation-description">
-                    <p class="operation-text" data-tests-id="interfaceOperationDescription">{{operation.description}}</p>
-                </span>
-            </span>
-
-            <span class="operation-dumbo" *ngIf="!readonly">
-                <span
-                    class="sprite-new delete-item-icon"
-                    data-tests-id="deleteInterfaceOperation"
-                    (click)="onRemoveOperation($event, operation)">
-                </span>
-            </span>
+            class="empty-list-container"
+            *ngIf="isListEmpty() && !isLoading">
+            <div
+                class="empty-list-add-btn"
+                [ngClass]="{'disabled': readonly}"
+                (click)="onEditOperation()">
+                <svg-icon
+                    name="plus-circle"
+                    mode="primary"
+                    size="x_large"
+                    [disabled]="readonly">
+                </svg-icon>
+                <div class="button-text">{{ 'INTERFACE_ADD_OPERATION' | translate }}</div>
+            </div>
+        </div>
+
+        <div *ngIf="!isListEmpty()">
+
+            <div class="expand-collapse">
+                <a
+                    class="link"
+                    [ngClass]="{'disabled': isAllExpanded()}"
+                    (click)="collapseAll(false)">
+                    {{ 'INTERFACE_EXPAND_ALL' | translate }}
+                </a> |
+                <a
+                    class="link"
+                    [ngClass]="{'disabled': isAllCollapsed()}"
+                    (click)="collapseAll()">
+                    {{ 'INTERFACE_COLLAPSE_ALL' | translate }}
+                </a>
+            </div>
+
+            <div
+                class="interface-row"
+                *ngFor="let interface of interfaces">
 
+                <div
+                    class="interface-accordion"
+                    (click)="interface.toggleCollapse()">
+                    <span
+                        class="chevron-container"
+                        [ngClass]="{'isCollapsed': interface.isCollapsed}">
+                        <svg-icon
+                            name="caret1-down-o"
+                            mode="primary"
+                            size="small">
+                        </svg-icon>
+                    </span>
+                    <span class="interface-name">{{interface.displayType()}}</span>
+                </div>
+
+                <div
+                    class="generic-table"
+                    *ngIf="!interface.isCollapsed">
+                    <div class="header-row table-row">
+                        <span
+                            class="cell header-cell field-name header-name">
+                            {{ 'INTERFACE_HEADER_NAME' | translate }}
+                        </span>
+                        <span
+                            class="cell header-cell field-description header-description">
+                            {{ 'INTERFACE_HEADER_DESCRIPTION' | translate }}
+                        </span>
+                        <span
+                            class="cell header-cell field-actions header-actions">
+                            ●●●
+                        </span>
+                    </div>
+
+                    <div
+                        class="data-row"
+                        *ngFor="let operation of interface.operations"
+                        (click)="onEditOperation(operation)">
+                        <span
+                            class="cell field-name">
+                            {{operation.name}}
+                        </span>
+                        <span
+                            class="cell field-description"
+                            [ngClass]="{'collapsed': operation.isCollapsed}">
+                            {{operation.getDescriptionEllipsis()}}
+                            <span
+                                class="more-or-less link"
+                                (click)="operation.toggleCollapsed($event)">
+                                {{!operation.isEllipsis ? '' : operation.isCollapsed ? 'More' : 'Less'}}
+                            </span>
+                        </span>
+                        <span class="cell field-actions">
+                            <span
+                                class="delete-action"
+                                data-tests-id="deleteOperation"
+                                (click)="onRemoveOperation($event, operation)">
+                                <svg-icon
+                                    *ngIf="!readonly"
+                                    name="trash-o"
+                                    mode="info"
+                                    size="small"
+                                    [clickable]="true">
+                                </svg-icon>
+                            </span>
+                        </span>
+                    </div>
+
+                </div>
+            </div>
         </div>
+
     </div>
 
 </div>
index 4355020..b79e2f5 100644 (file)
 @import '../../../../assets/styles/variables.less';
+@import '../../../../assets/styles/override.less';
 
 .interface-operation {
-    .add-btn {
+    font-size: 14px;
+
+    .top-add-btn {
         position: relative;
         top: -31px;
         text-transform: uppercase;
         font-size: 14px;
+        font-family: @font-opensans-medium;
     }
 
-    a:not(.disabled) {
-        &:hover {
-            cursor: pointer;
+    .link {
+        color: @sdcui_color_blue;
+        text-decoration: underline;
+        font-family: @font-opensans-regular;
+
+        &:not(.disabled) {
+            &:not(.empty-list-add-btn) {
+                &:hover {
+                    color: @sdcui_color_dark-blue;
+                    cursor: pointer;
+                }
+            }
         }
     }
 
     .operation-list {
         border-top: 1px solid @main_color_o;
-        padding-top: 25px;
+        padding-top: 5px;
 
-        .operation-row {
+        .empty-list-container {
             width: 100%;
-            border: 1px solid @main_color_o;
             display: flex;
-            justify-content: space-between;
-            align-items: center;
-            height: 100px;
+            justify-content: center;
 
-            &:hover {
-                border-color: @main_color_c;
-                cursor: pointer;
+            .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 @sdcui_color_blue;
+                        cursor: pointer;
+                    }
+                }
+
+                .button-text {
+                    margin-top: 9px;
+                    font-family: @font-opensans-medium;
+                    font-size: 16px;
+                    text-transform: uppercase;
+                    color: @sdcui_color_blue;
+                }
             }
+        }
+
+        .expand-collapse {
+            margin-top: 4px;
+            margin-bottom: 18px;
+            color: @sdcui_color_light-gray;
+        }
+
+        .interface-row {
+            width: 100%;
+            margin-top: 13px;
+            border-bottom: 1px solid @main_color_o;
+            padding-left: 4px;
+            min-height: 37px;
+
+
+            .interface-accordion {
+                cursor: pointer;
 
-            &:not(:first-child) {
-                margin-top: 20px;
+                .chevron-container {
+                    position: relative;
+                    margin-right: 5px;
+
+                    &.isCollapsed {
+                        right: -6px;
+                        top: 0;
+                        * {
+                            transform: rotate(270deg);
+                        }
+                    }
+                    &:not(.isCollapsed) {
+                        top: 6px;
+                    }
+                    * {
+                        &:hover {
+                            cursor: pointer;
+                        }
+                    }
+                }
+                .interface-name {
+                    font-size: 18px;
+                    font-family: @font-opensans-bold;
+                    margin-bottom: 15px;
+                }
             }
 
-            .operation-info-container {
-                height: 100%;
-                display: flex;
-                align-items: center;
-                margin-right: 60px;
+            .generic-table {
+                margin-bottom: 24px;
+                margin-top: 10px;
+                margin-left: 22px;
+                font-size: 14px;
 
-                .operation-title, .operation-description {
-                    display: flex;
-                    align-items: center;
+                .header-row, .data-row {
+                    .cell {
+                        &.field-description {
+                            flex: 2.5;
+                        }
 
-                    .operation-text {
-                        overflow: hidden;
-                        margin-bottom: 0;
-                        max-height: 5rem;
+                        &.field-actions {
+                            flex-basis: 72px;
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+                        }
                     }
                 }
 
-                .operation-title {
-                    flex-shrink: 0;
-                    width: 200px;
-                    height: calc(100% - 13px);
-                    border-right: 1px solid @main_color_o;
-                    margin: 4px 0;
-                    padding: 0 30px;
+                .header-row {
+                    .cell {
+                        background: @sdcui_color_silver;
 
-                    .operation-text {
-                        white-space: nowrap;
-                        text-overflow: ellipsis;
+                        &.field-actions {
+                            font-size: 10px;
+                        }
                     }
                 }
 
-                .operation-description {
-                    padding-left: 30px;
-                    text-align: left;
-                    font-family: @font-opensans-regular;
+                .data-row {
+                    cursor: pointer;
+
+                    &:hover {
+                        background: @sdcui_color_light-silver;
 
-                    .operation-text {
-                        word-break: break-word;
+                        .cell {
+                            &.field-name {
+                                color: @sdcui_color_dark-blue;
+                            }
+                        }
                     }
+
+                    &:not(:hover) {
+                       .field-actions {
+                            visibility: hidden;
+                        }
+                    }
+
+                    .cell {
+                        white-space: nowrap;
+                        text-overflow: ellipsis;
+                        overflow: hidden;
+
+                        &.field-description {
+                            &:not(.collapsed) {
+                                white-space: normal;
+                            }
+                            &.collapsed {
+                                text-overflow: clip;
+                            }
+                            .more-or-less {
+                                margin-left: 5px;
+                            }
+                        }
+
+                        &.field-actions {
+                            .delete-action {
+                                position: relative;
+                                top: 2px;
+                            }
+                        }
+                    }
+
                 }
             }
 
-            .operation-dumbo {
-                padding-right: 20px;
-                display: flex;
-                flex-direction: column;
-                align-items: left;
-            }
         }
     }
 }
index e19d345..e9b2001 100644 (file)
@@ -3,12 +3,17 @@ import {Component, Input, Output, ComponentRef, Inject} from '@angular/core';
 import {Component as IComponent} from 'app/models/components/component';
 
 import {SdcConfigToken, ISdcConfig} from "app/ng2/config/sdc-config.config";
+import {TranslateService} from "app/ng2/shared/translator/translate.service";
 
 import {Observable} from "rxjs/Observable";
 
 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
 import {ModalService} from 'app/ng2/services/modal.service';
-import {ModalModel, ButtonModel, InputBEModel, OperationModel, CreateOperationResponse, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
+import {ModalModel, ButtonModel, InputBEModel, OperationModel, InterfaceModel, CreateOperationResponse, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
+
+import {IModalConfig, IModalButtonComponent} from "sdc-ui/lib/angular/modals/models/modal-config";
+import {SdcUiComponents} from "sdc-ui/lib/angular";
+import {ModalButtonComponent} from "sdc-ui/lib/angular/components";
 
 import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
 import {ComponentGenericResponse} from 'app/ng2/services/responses/component-generic-response';
@@ -16,21 +21,96 @@ import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
 
 import {OperationCreatorComponent, OperationCreatorInput} from './operation-creator/operation-creator.component';
 
+export class UIOperationModel extends OperationModel {
+    isCollapsed: boolean = true;
+    isEllipsis: boolean;
+    MAX_LENGTH = 75;
+    _description: string;
+
+    constructor(operation: OperationModel) {
+        super(operation);
+
+        if (!operation.description) {
+            this.description = '';
+        }
+
+        if (this.description.length > this.MAX_LENGTH) {
+            this.isEllipsis = true;
+        } else {
+            this.isEllipsis = false;
+        }
+    }
+
+    getDescriptionEllipsis(): string {
+        if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
+            return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
+        }
+        return this.description;
+    }
+
+    toggleCollapsed(e) {
+        e.stopPropagation();
+        this.isCollapsed = !this.isCollapsed;
+    }
+}
+
+class ModalTranslation {
+    CREATE_TITLE: string;
+    EDIT_TITLE: string;
+    DELETE_TITLE: string;
+    CANCEL_BUTTON: string;
+    SAVE_BUTTON: string;
+    CREATE_BUTTON: string;
+    DELETE_BUTTON: string;
+    deleteText: Function;
+
+    constructor(private TranslateService: TranslateService) {
+        this.TranslateService.languageChangedObservable.subscribe(lang => {
+            this.CREATE_TITLE = this.TranslateService.translate("INTERFACE_CREATE_TITLE");
+            this.EDIT_TITLE = this.TranslateService.translate("INTERFACE_EDIT_TITLE");
+            this.DELETE_TITLE = this.TranslateService.translate("INTERFACE_DELETE_TITLE");
+            this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
+            this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
+            this.CREATE_BUTTON = this.TranslateService.translate("INTERFACE_CREATE_BUTTON");
+            this.DELETE_BUTTON = this.TranslateService.translate("INTERFACE_DELETE_BUTTON");
+            this.deleteText = (operationName) => this.TranslateService.translate("INTERFACE_DELETE_TEXT", {operationName});
+        });
+    }
+}
+
+export class UIInterfaceModel extends InterfaceModel {
+    isCollapsed: boolean = false;
+
+    constructor(interf?: any) {
+        super(interf);
+        this.operations = _.map(
+            this.operations,
+            operation => new UIOperationModel(operation)
+        );
+    }
+
+    toggleCollapse() {
+        this.isCollapsed = !this.isCollapsed;
+    }
+}
+
 @Component({
     selector: 'interface-operation',
     templateUrl: './interface-operation.page.component.html',
     styleUrls: ['interface-operation.page.component.less'],
-    providers: [ModalService]
+    providers: [ModalService, TranslateService]
 })
 
 export class InterfaceOperationComponent {
 
+    interfaces: Array<UIInterfaceModel>;
     modalInstance: ComponentRef<ModalComponent>;
-    operationList: Array<OperationModel> = [];
     openOperation: OperationModel;
     enableWorkflowAssociation: boolean;
     inputs: Array<InputBEModel>;
     isLoading: boolean;
+    interfaceTypes:{ [interfaceType: string]: Array<string> };
+    modalTranslation: ModalTranslation;
 
     @Input() component: IComponent;
     @Input() readonly: boolean;
@@ -40,40 +120,79 @@ export class InterfaceOperationComponent {
     constructor(
         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
         @Inject("$state") private $state: ng.ui.IStateService,
+        private TranslateService: TranslateService,
         private ComponentServiceNg2: ComponentServiceNg2,
         private WorkflowServiceNg2: WorkflowServiceNg2,
         private ModalServiceNg2: ModalService,
+        private ModalServiceSdcUI: SdcUiComponents.ModalService
     ) {
         this.enableWorkflowAssociation = sdcConfig.enableWorkflowAssociation;
+        this.modalTranslation = new ModalTranslation(TranslateService);
     }
 
     ngOnInit(): void {
         this.isLoading = true;
         Observable.forkJoin(
-            this.ComponentServiceNg2.getInterfaceOperations(this.component),
-            this.ComponentServiceNg2.getComponentInputs(this.component)
-        ).subscribe((response) => {
+            this.ComponentServiceNg2.getInterfaces(this.component),
+            this.ComponentServiceNg2.getComponentInputs(this.component),
+            this.ComponentServiceNg2.getInterfaceTypes(this.component)
+        ).subscribe((response: Array<any>) => {
             this.isLoading = false;
-            this.component.interfaceOperations = response[0].interfaceOperations;
-            this.operationList = _.toArray(response[0].interfaceOperations).sort((a, b) => a.operationType.localeCompare(b.operationType));
+            this.initInterfaces(response[0].interfaces);
+            this.sortInterfaces();
             this.inputs = response[1].inputs;
+            this.interfaceTypes = response[2];
+        });
+    }
+
+    initInterfaces(interfaces: Array<InterfaceModel>): void {
+        this.interfaces = _.map(interfaces, interf => new UIInterfaceModel(interf));
+    }
+
+    sortInterfaces(): void {
+        this.interfaces = _.filter(this.interfaces, interf => interf.operations && interf.operations.length > 0); // remove empty interfaces
+        this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
+        _.forEach(this.interfaces, interf => {
+            interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
+        });
+    }
+
+    collapseAll(value: boolean = true): void {
+        _.forEach(this.interfaces, interf => {
+            interf.isCollapsed = value;
         });
     }
 
+    isAllCollapsed(): boolean {
+        return _.every(this.interfaces, interf => interf.isCollapsed);
+    }
+
+    isAllExpanded(): boolean {
+        return _.every(this.interfaces, interf => !interf.isCollapsed);
+    }
+
+    isListEmpty(): boolean {
+        return _.filter(
+            this.interfaces,
+            interf => interf.operations && interf.operations.length > 0
+        ).length === 0;
+    }
+
     getDisabled = (): boolean => {
         return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
     }
 
     onEditOperation = (operation?: OperationModel): void => {
+
         const modalMap = {
             create: {
-                modalTitle: 'Create a New Operation',
-                saveBtnText: 'Create',
+                modalTitle: this.modalTranslation.CREATE_TITLE,
+                saveBtnText: this.modalTranslation.CREATE_BUTTON,
                 submitCallback: this.createOperation,
             },
             edit: {
-                modalTitle: 'Edit Operation',
-                saveBtnText: 'Save',
+                modalTitle: this.modalTranslation.EDIT_TITLE,
+                saveBtnText: this.modalTranslation.SAVE_BUTTON,
                 submitCallback: this.updateOperation,
             }
         };
@@ -86,23 +205,28 @@ export class InterfaceOperationComponent {
             }
         }
 
-        const cancelButton: ButtonModel = new ButtonModel(
-            'Cancel',
-            'outline white',
-            () => {
+        const cancelButton: IModalButtonComponent = {
+            id: 'cancelButton',
+            text: this.modalTranslation.CANCEL_BUTTON,
+            type: 'secondary',
+            size: 'small',
+            closeModal: true,
+            callback: () => {
                 this.openOperation = null;
-                this.ModalServiceNg2.closeCurrentModal();
             },
-        );
-
-        const saveButton: ButtonModel = new ButtonModel(
-            modalData.saveBtnText,
-            'blue',
-            () => {
-                this.modalInstance.instance.dynamicContent.instance.createParamLists();
-                this.ModalServiceNg2.closeCurrentModal();
+        };
 
-                const {operation, isUsingExistingWF} = this.modalInstance.instance.dynamicContent.instance;
+        const saveButton: IModalButtonComponent = {
+            id: 'saveButton',
+            text: modalData.saveBtnText,
+            type: 'primary',
+            size: 'small',
+            closeModal: true,
+            callback: () => {
+                const modalInstance = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance;
+
+                const {operation, isUsingExistingWF, createParamLists} = modalInstance;
+                createParamLists();
                 this.openOperation = {...operation};
 
                 if (this.enableWorkflowAssociation && !isUsingExistingWF()) {
@@ -111,74 +235,81 @@ export class InterfaceOperationComponent {
                 }
 
                 modalData.submitCallback(operation);
-            },
-            this.getDisabled,
-        );
-
-        const modalModel: ModalModel = new ModalModel(
-            'l',
-            modalData.modalTitle,
-            '',
-            [saveButton, cancelButton],
-            'standard',
-        );
-
-        this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
+            }
+        };
 
-        let input: OperationCreatorInput = {
-            operation,
+        const input: OperationCreatorInput = {
+            inputOperation: operation,
             inputProperties: this.inputs,
             enableWorkflowAssociation: this.enableWorkflowAssociation,
             readonly: this.readonly,
-            isService: this.component.isService()
-        }
+            isService: this.component.isService(),
+            interfaceTypes: this.interfaceTypes,
+            validityChangedCallback: this.enableOrDisableSaveButton
+        };
 
-        this.ModalServiceNg2.addDynamicContentToModal(
-            this.modalInstance,
-            OperationCreatorComponent,
-            input,
-        );
+        const modalConfig: IModalConfig = {
+            title: modalData.modalTitle,
+            size: 'l',
+            type: 'custom',
+            buttons: [saveButton, cancelButton] as IModalButtonComponent[]
+        };
+
+        this.ModalServiceSdcUI.openCustomModal(modalConfig, OperationCreatorComponent, input);
 
-        this.modalInstance.instance.open();
+    }
+
+    private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
+        let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton');
+        saveButton.disabled = !shouldEnable;
     }
 
     onRemoveOperation = (event: Event, operation: OperationModel): void => {
         event.stopPropagation();
 
         const confirmCallback = () => {
-            this.ModalServiceNg2.closeCurrentModal();
             this.ComponentServiceNg2
                 .deleteInterfaceOperation(this.component, operation)
                 .subscribe(() => {
-                    const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
-                    this.operationList.splice(index, 1);
-                    this.component.interfaceOperations = this.operationList;
+                    const curInterf = _.find(this.interfaces, interf => interf.type === operation.interfaceType);
+                    const index = _.findIndex(curInterf.operations, el => el.uniqueId === operation.uniqueId);
+                    curInterf.operations.splice(index, 1);
+                    if (!curInterf.operations.length) {
+                        const interfIndex = _.findIndex(this.interfaces, interf => interf.type === operation.interfaceType);
+                        this.interfaces.splice(interfIndex, 1);
+                    }
                 });
         }
 
-        this.modalInstance = this.ModalServiceNg2.createActionModal(
-            operation.operationType,
-            'Are you sure you want to delete this operation?',
-            'Delete',
+        this.ModalServiceSdcUI.openAlertModal(
+            this.modalTranslation.DELETE_TITLE,
+            this.modalTranslation.deleteText(operation.name),
+            this.modalTranslation.DELETE_BUTTON,
             confirmCallback,
-            'Cancel',
+            'deleteOperationModal'
         );
-
-        this.modalInstance.instance.open();
     }
 
     private createOperation = (operation: OperationModel): void => {
         this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: CreateOperationResponse) => {
             this.openOperation = null;
-            this.operationList.push(new OperationModel(response));
-            this.operationList.sort((a, b) => a.operationType.localeCompare(b.operationType));
+            let curInterf = _.find(
+                this.interfaces,
+                interf => interf.type === operation.interfaceType
+            )
+            if (!curInterf) {
+                curInterf = new UIInterfaceModel({
+                    type: response.interfaceType,
+                    uniqueId: response.uniqueId,
+                    operations: []
+                });
+                this.interfaces.push(curInterf);
+            }
+            curInterf.operations.push(new UIOperationModel(response));
+            this.sortInterfaces();
 
             if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
-                const operationId = response.uniqueId;
-                const workflowId = response.workflowId;
-                const versionId = response.workflowVersionId;
-                const artifactId = response.artifactUUID;
-                this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, operationId, workflowId, versionId, artifactId).subscribe();
+                this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
             } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
                 this.$state.go('workspace.plugins', { path: 'workflowDesigner' });
             }
@@ -188,16 +319,22 @@ export class InterfaceOperationComponent {
     private updateOperation = (operation: OperationModel): void => {
         this.ComponentServiceNg2.updateInterfaceOperation(this.component, operation).subscribe(newOperation => {
             this.openOperation = null;
-            const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
-            this.operationList.splice(index, 1, newOperation);
-            this.component.interfaceOperations = this.operationList;
+
+            _.forEach(this.interfaces, interf => {
+                _.forEach(interf.operations, op => {
+                    if (op.uniqueId === newOperation.uniqueId) {
+                        const oldIndex = _.findIndex(interf.operations, el => el.uniqueId === op.uniqueId);
+                        interf.operations.splice(oldIndex, 1);
+                    }
+                })
+            });
+
+            const newInterf = _.find(this.interfaces, interf => interf.type === operation.interfaceType);
+            newInterf.operations.push(new UIOperationModel(newOperation));
+            this.sortInterfaces();
 
             if (newOperation.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
-                const operationId = newOperation.uniqueId;
-                const workflowId = newOperation.workflowId;
-                const versionId = newOperation.workflowVersionId;
-                const artifactId = newOperation.artifactUUID;
-                this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, operationId, workflowId, versionId, artifactId).subscribe();
+                this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, newOperation).subscribe();
             }
         });
     }
index 441875b..81a33c4 100644 (file)
     <form class="w-sdc-form">
 
         <div class="side-by-side">
-            <div class="i-sdc-form-item">
-                <label class="i-sdc-form-label" [ngClass]="{'required': !isEditMode}">{{ 'OPERATION_NAME' | translate }}</label>
-                <input
-                    type="text"
-                    name="type"
-                    data-tests-id="operationType"
-                    [(ngModel)]="operation.operationType"
-                    [attr.maxLength]="200"
-                    [ngClass]="{'disabled':isEditMode}" />
+            <div class="form-item">
+                <sdc-dropdown
+                    label="{{ 'OPERATION_INTERFACE_TYPE' | translate }}"
+                    [required]="true"
+                    testId="interface-name"
+                    selectedOption="{{operation.interfaceType}}"
+                    placeHolder="Select..."
+                    [disabled]="readonly"
+                    (changed)="onSelectInterface($event)"
+                    [options]="interfaceNames">
+                </sdc-dropdown>
             </div>
 
-            <div class="i-sdc-form-item">
-                <label class="i-sdc-form-label">{{ 'OPERATION_DESCRIPTION' | translate }}</label>
-                <input
-                    type="text"
-                    data-tests-id="operationDescription"
-                    name="description"
-                    [ngClass]="{'disabled': readonly}"
-                    [(ngModel)]="operation.description"
-                    [attr.maxLength]="200" />
+            <div class="form-item" *ngIf="!isInterfaceOther()">
+                <sdc-dropdown
+                    label="{{ 'OPERATION_NAME' | translate }}"
+                    [required]="true"
+                    testId="operation-name"
+                    selectedOption="{{operation.name}}"
+                    placeHolder="Select..."
+                    [disabled]="readonly"
+                    (changed)="onSelectOperationName($event)"
+                    [options]="operationNames">
+                </sdc-dropdown>
             </div>
+            <div class="form-item" *ngIf="isInterfaceOther()">
+                <sdc-input
+                    label="{{ 'OPERATION_NAME' | translate }}"
+                    [(value)]="operation.name"
+                    testId="operationName">
+                </sdc-input>
+            </div>
+
         </div>
 
-        <div class="side-by-side association-options">
-            <div class="i-sdc-form-item" *ngIf="enableWorkflowAssociation">
-                <label class="i-sdc-form-label">{{ 'OPERATION_WORKFLOW_ASSIGNMENT' | translate }}</label>
-                <ui-element-dropdown
-                    data-tests-id="association-type"
-                    [(value)]="operation.workflowAssociationType"
-                    [values]="associationOptions"
-                    (valueChange)="toggleAssociateWorkflow()"
-                    [readonly]="readonly">
-                </ui-element-dropdown>
-            </div>
-            <div></div>
+        <div class="i-sdc-form-item sdc-input">
+            <span class="sdc-input__label">{{ 'OPERATION_DESCRIPTION' | translate }}</span>
+            <textarea
+                data-tests-id="operationDescription"
+                rows="2"
+                name="description"
+                [(ngModel)]="descriptionValue"
+                [ngClass]="{'disabled': readonly}">
+            </textarea>
         </div>
 
-        <div class="side-by-side" *ngIf="isUsingExistingWF()">
-            <div class="i-sdc-form-item">
-                <label class="i-sdc-form-label required">{{ 'OPERATION_WORKFLOW' | translate }}
+        <div class="side-by-side" *ngIf="enableWorkflowAssociation">
+            <div class="form-item">
+                <sdc-dropdown
+                    label="{{ 'OPERATION_WORKFLOW_ASSIGNMENT' | translate }}"
+                    placeHolder="Select..."
+                    testId="association-type"
+                    selectedOption="{{workflowAssociationType}}"
+                    [options]="associationOptions"
+                    (changed)="toggleAssociateWorkflow($event)"
+                    [disabled]="readonly">
+                </sdc-dropdown>
+            </div>
+
+            <div class="form-item" *ngIf="!isUsingExistingWF()"></div>
+
+            <div class="form-item sdc-input" *ngIf="isUsingExistingWF()">
+                <label class="sdc-input__label required">{{ 'OPERATION_WORKFLOW' | translate }}
                     <span class="archive-warning" *ngIf="archivedWorkflowId === operation.workflowId">({{ 'OPERATION_WORKFLOW_ARCHIVED' | translate }})</span>
+                    <span class="no-workflow-warning" *ngIf="!workflows.length">{{ 'OPERATION_NO_WORKFLOW_ERROR' | translate }}</span>
                 </label>
-                <ui-element-dropdown
-                    data-tests-id="associated-workflow"
-                    [readonly]="readonly"
-                    [values]="workflows"
-                    [(value)]="operation.workflowId"
-                    (valueChange)="onSelectWorkflow()">
-                </ui-element-dropdown>
+                <sdc-dropdown
+                    placeHolder="Select..."
+                    testId="associated-workflow"
+                    selectedOption="{{operation.workflowId}}"
+                    [options]="workflows"
+                    (changed)="onSelectWorkflow($event)"
+                    [disabled]="readonly || !workflows.length">
+                </sdc-dropdown>
             </div>
 
-            <div class="i-sdc-form-item">
-                <label class="i-sdc-form-label required">{{ 'OPERATION_WORKFLOW_VERSION' | translate }}</label>
-                <ui-element-dropdown
-                    data-tests-id="associated-workflow-version"
-                    [readonly]="!operation.workflowId || archivedWorkflowId === operation.workflowId || readonly"
-                    [values]="workflowVersions"
-                    [(value)]="operation.workflowVersionId"
-                    (valueChange)="changeWorkflowVersion()">
-                </ui-element-dropdown>
+            <div class="form-item sdc-input" *ngIf="isUsingExistingWF()">
+                <sdc-dropdown
+                    *ngIf="workflows.length"
+                    label="{{ 'OPERATION_WORKFLOW_VERSION' | translate }}"
+                    testId="associated-workflow-version"
+                    selectedOption="{{operation.workflowVersionId}}"
+                    [options]="workflowVersions"
+                    (changed)="changeWorkflowVersion($event)"
+                    [disabled]="!operation.workflowId || archivedWorkflowId === operation.workflowId || readonly">
+                </sdc-dropdown>
             </div>
         </div>
 
                 *ngIf="!isUsingExistingWF() && !readonly"
                 data-tests-id="addInputParameter"
                 [ngClass]="{'disabled':!canAdd()}"
-                (click)="addParam()">{{ 'OPERATION_ADD_PARAMS' | translate }}</a>
+                (click)="addParam()">{{ currentTab === TYPE_INPUT ? 'Add Input' : 'Add Output' }}</a>
         </div>
 
         <div class="generic-table">
                         <span class="bold-message">{{ 'EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_1' | translate }}</span>
                         <span>{{ 'EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_2' | translate }}</span>
                     </div>
-                    <div *ngIf="!workflows.length" [innerHTML]="'EMPTY_PARAM_TABLE_NO_WORKFLOWS' | translate"></div>
                 </div>
             </div>
 
                 [param]="param"
                 [inputProps]="inputProperties"
                 [onRemoveParam]="onRemoveParam"
-                [readonly]="readonly">
+                [readonly]="readonly"
+                [validityChanged]="validityChanged">
             </param-row>
         </div>
 
index 10976ef..1d65d98 100644 (file)
@@ -1,4 +1,5 @@
 @import '../../../../../assets/styles/variables.less';
+@import '../../../../../assets/styles/override.less';
 
 .operation-creator {
     font-family: @font-opensans-regular;
         font-size: 12px;
     }
 
-    .w-sdc-form .i-sdc-form-item {
+    .w-sdc-form .form-item {
         margin-bottom: 15px;
     }
 
-    .side-by-side {
-        display: flex;
+    textarea {
+        min-height: 74px;
+        margin-bottom: 18px;
+    }
+
+    /deep/ .sdc-dropdown__component-container {
+        .sdc-dropdown__header {
+            height: 38px;
+            line-height: 35px;
+
+            svg-icon {
+                margin: 13px 6px;
+            }
+        }
+    }
+
+    /deep/ .sdc-input {
+        margin-bottom: 0;
 
-        &.association-options {
-            margin-top: 5px;
+        .sdc-input__input {
+            height: 38px;
         }
+    }
+
+    .side-by-side {
+        display: flex;
 
-        .i-sdc-form-item {
-            flex-basis: 100%;
+        .form-item {
+            flex: 1;
 
             &:first-child {
-                flex-basis: 40%;
-                margin-right: 10px;
+                margin-right: 14px;
+                flex-basis: 37%;
+                flex-grow: 0;
+                flex-shrink: 0;
             }
 
-            .archive-warning {
-                font-family: @font-opensans-bold;
-                color: @main_color_i;
+            &:nth-child(3) {
+                margin-left: 14px;
+                flex: 0.4;
             }
+
         }
     }
 
+    .archive-warning {
+        font-family: @font-opensans-bold;
+        color: @main_color_i;
+    }
+
+    .no-workflow-warning {
+        font-family: @font-opensans-bold;
+        color: @sdcui_color_red;
+        float: right;
+    }
+
     .input-param-title {
         font-size: 16px;
         text-transform: uppercase;
                 cursor: pointer;
             }
         }
+
+        .tab {
+            width: 84px;
+            text-align: center;
+        }
     }
 
     .generic-table {
-        max-height: 233px;
+        max-height: 244px;
         min-height: 91px;
         background: @main_color_p;
 
         .header-row .header-cell {
-            &.remove {
-                padding: 8px;
-            }
             .info-icon {
                 float: right;
+                position: relative;
+                top: 2px;
             }
             /deep/ .tooltip-inner {
-                max-width: 280px;
+                padding: 2px;
+                max-width: 270px;
                 font-size: 11px;
             }
+            &.remove {
+                padding: 10px;
+                font-size: 10px;
+            }
         }
 
         .data-row {
index a304f1a..e1b2b4e 100644 (file)
@@ -7,15 +7,37 @@ import {TranslateService} from "app/ng2/shared/translator/translate.service";
 import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
 import {OperationModel, OperationParameter, InputBEModel, RadioButtonModel, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
 
+import {IDropDownOption} from "sdc-ui/lib/angular/form-elements/dropdown/dropdown-models";
 import {Tabs, Tab} from "app/ng2/components/ui/tabs/tabs.component";
 import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
 
+export class DropDownOption implements IDropDownOption {
+    value: string;
+    label: string;
+
+    constructor(value: string, label?: string) {
+        this.value = value;
+        this.label = label || value;
+    }
+}
+
+class TypedDropDownOption extends DropDownOption {
+    type: number;
+
+    constructor(value: string, label?: string, type?: number) {
+        super(value, label);
+        this.type = type;
+    }
+}
+
 export interface OperationCreatorInput {
-    operation: OperationModel,
+    inputOperation: OperationModel,
     inputProperties: Array<InputBEModel>,
     enableWorkflowAssociation: boolean,
     readonly: boolean,
-    isService: boolean
+    isService: boolean,
+    interfaceTypes: { [interfaceType: string]: Array<string> },
+    validityChangedCallback: Function
 }
 
 @Component({
@@ -28,7 +50,12 @@ export interface OperationCreatorInput {
 export class OperationCreatorComponent {
 
     input: OperationCreatorInput;
+    inputOperation: OperationModel;
     operation: OperationModel;
+    interfaceNames: Array<TypedDropDownOption> = [];
+    interfaceTypes: { [interfaceType: string]: Array<string> };
+    operationNames: Array<DropDownOption> = [];
+    validityChangedCallback: Function;
 
     workflows: Array<DropdownValue> = [];
     workflowVersions: Array<DropdownValue> = [];
@@ -45,7 +72,8 @@ export class OperationCreatorComponent {
 
     tableParameters: Array<OperationParameter> = [];
 
-    associationOptions: Array<DropdownValue>;
+    associationOptions: Array<DropdownValue> = [];
+    workflowAssociationType: string;
 
     enableWorkflowAssociation: boolean;
     isEditMode: boolean = false;
@@ -57,6 +85,9 @@ export class OperationCreatorComponent {
     TYPE_INPUT = 'Inputs';
     TYPE_OUTPUT = 'Outputs';
 
+    INTERFACE_OTHER_HEADER = 'Local Interfaces';
+    INTERFACE_OTHER = 'Local';
+
     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
     currentTab: String;
 
@@ -65,24 +96,41 @@ export class OperationCreatorComponent {
             this.propertyTooltipText = this.translateService.translate("OPERATION_PROPERTY_TOOLTIP_TEXT");
 
             this.associationOptions = [
-                new DropdownValue(WORKFLOW_ASSOCIATION_OPTIONS.NONE, this.translateService.translate("NO_WORKFLOW_ASSOCIATION")),
-                new DropdownValue(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION"))
+                new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.NONE, this.translateService.translate("NO_WORKFLOW_ASSOCIATION")),
+                new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION"))
             ];
+
+            this.workflowAssociationType = this.operation.workflowAssociationType || WORKFLOW_ASSOCIATION_OPTIONS.NONE;
         });
 
         this.currentTab = this.TYPE_INPUT;
     }
 
-    ngOnInit() {
-        this.readonly = this.input.readonly;
-        this.enableWorkflowAssociation = this.input.enableWorkflowAssociation;
-        this.inputProperties = this.input.inputProperties;
+    createInterfaceDropdown(type: string) {
+        let label = type;
+        const lastDot = label.lastIndexOf('.');
+        if (lastDot > -1) {
+            label = label.substr(lastDot + 1);
+        }
+        return new TypedDropDownOption(type, label);
+    }
 
-        const inputOperation = this.input.operation;
+    ngOnInit() {
+        this.interfaceNames = _.map(
+            _.keys(this.interfaceTypes),
+            type => this.createInterfaceDropdown(type)
+        );
+        this.interfaceNames.unshift(new TypedDropDownOption('Existing Interfaces', 'Existing Interfaces', 1));
+        this.interfaceNames = this.interfaceNames.concat([
+            new TypedDropDownOption(' ', ' ', 3),
+            new TypedDropDownOption(this.INTERFACE_OTHER_HEADER, this.INTERFACE_OTHER_HEADER, 1),
+            new TypedDropDownOption(this.INTERFACE_OTHER)
+        ]);
+
+        const inputOperation = this.inputOperation;
         this.operation = new OperationModel(inputOperation || {});
-        if (!inputOperation) {
-            this.operation.workflowAssociationType = WORKFLOW_ASSOCIATION_OPTIONS.NONE;
-        }
+        this.onSelectInterface(new DropDownOption(this.operation.interfaceType));
+        this.validityChanged();
 
         if (this.enableWorkflowAssociation) {
             this.isLoading = true;
@@ -113,13 +161,16 @@ export class OperationCreatorComponent {
     }
 
     reconstructOperation = () => {
-        const inputOperation = this.input.operation;
+        const inputOperation = this.inputOperation;
         if (inputOperation) {
             if (this.enableWorkflowAssociation && inputOperation.workflowVersionId && this.isUsingExistingWF(inputOperation)) {
-                this.onSelectWorkflow(inputOperation.workflowVersionId).add(() => {
-                    this.buildParams();
-                    this.updateTable();
-                });
+                const sub = this.onSelectWorkflow(new DropDownOption(inputOperation.workflowId), inputOperation.workflowVersionId);
+                if (sub) {
+                    sub.add(() => {
+                        this.buildParams();
+                        this.updateTable();
+                    });
+                }
             } else {
                 this.inputParameters = this.noAssignInputParameters;
                 this.outputParameters = this.noAssignOutputParameters;
@@ -132,14 +183,15 @@ export class OperationCreatorComponent {
             }
         }
         this.updateTable();
+        this.validityChanged();
     }
 
     buildParams = () => {
-        if (this.input.operation.outputParams) {
+        if (this.inputOperation.outputs) {
             this.currentTab = this.TYPE_OUTPUT;
             this.updateTable();
             _.forEach(
-                [...this.input.operation.outputParams.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
+                [...this.inputOperation.outputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
                 (output: OperationParameter) => {
                     this.addParam(output);
                 }
@@ -147,9 +199,9 @@ export class OperationCreatorComponent {
         }
         this.currentTab = this.TYPE_INPUT;
         this.updateTable();
-        if (this.input.operation.inputParams) {
+        if (this.inputOperation.inputs) {
             _.forEach(
-                [...this.input.operation.inputParams.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
+                [...this.inputOperation.inputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
                 (input: OperationParameter) => {
                     this.addParam(input);
                 }
@@ -157,15 +209,53 @@ export class OperationCreatorComponent {
         }
     }
 
-    onSelectWorkflow(selectedVersionId?: string): Subscription {
+    isInterfaceOther(): boolean {
+        return this.operation.interfaceType === this.INTERFACE_OTHER;
+    }
+
+    onSelectInterface(interf: IDropDownOption) {
+        if (interf && this.operation.interfaceType !== interf.value) {
+            this.operation.name = undefined;
+        }
+        this.operation.interfaceType = interf && interf.value;
+        this.operationNames = !this.operation.interfaceType ? [] : (
+            _.map(
+                this.interfaceTypes[this.operation.interfaceType],
+                name => new DropDownOption(name)
+            )
+        );
+        this.validityChanged();
+    }
+
+    onSelectOperationName(name: IDropDownOption) {
+        if (name) {
+            this.operation.name = name.value;
+            this.validityChanged();
+        }
+    }
+
+    get descriptionValue() {
+        return this.operation.description;
+    }
+
+    set descriptionValue(v) {
+        this.operation.description = v;
+        this.validityChanged();
+    }
 
-        this.operation.workflowVersionId = selectedVersionId || null;
+    onSelectWorkflow(workflowId: DropDownOption, selectedVersionId?: string): Subscription {
+
+        if (_.isUndefined(workflowId) || workflowId.value === this.operation.workflowId) {
+            return;
+        }
+        this.operation.workflowId = workflowId.value;
         if (!this.assignInputParameters[this.operation.workflowId]) {
             this.assignInputParameters[this.operation.workflowId] = {};
             this.assignOutputParameters[this.operation.workflowId] = {};
         }
 
         this.isLoading = true;
+        this.validityChanged();
         return this.workflowServiceNg2.getWorkflowVersions(this.operation.workflowId).subscribe((versions: Array<any>) => {
             this.isLoading = false;
 
@@ -197,18 +287,34 @@ export class OperationCreatorComponent {
                 this.operation.workflowVersionId = _.last(this.workflowVersions).value;
             }
 
-            this.changeWorkflowVersion();
+            this.changeWorkflowVersion(new DropDownOption(this.operation.workflowVersionId));
+            this.validityChanged();
         });
 
     }
 
-    changeWorkflowVersion() {
+    changeWorkflowVersion(versionId: DropDownOption) {
+
+        if (_.isUndefined(versionId)) {
+            return;
+        }
+
+        this.operation.workflowVersionId = versionId.value;
         this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
         this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
         this.updateTable();
+        this.validityChanged();
+
     }
 
-    toggleAssociateWorkflow() {
+    toggleAssociateWorkflow(type: DropDownOption) {
+
+        if (_.isUndefined(type)) {
+            return;
+        }
+
+        this.operation.workflowAssociationType = type.value;
+        this.workflowAssociationType = this.operation.workflowAssociationType;
 
         if (!this.isUsingExistingWF()) {
             this.inputParameters = this.noAssignInputParameters;
@@ -224,15 +330,19 @@ export class OperationCreatorComponent {
         }
 
         this.updateTable();
+        this.validityChanged();
 
     }
 
     tabChanged = (event) => {
+
         this.currentTab = event.title;
         this.updateTable();
+
     }
 
     updateTable() {
+
         switch (this.currentTab) {
             case this.TYPE_INPUT:
                 this.tableParameters = this.inputParameters;
@@ -241,17 +351,20 @@ export class OperationCreatorComponent {
                 this.tableParameters = this.outputParameters;
                 break;
         }
+
     }
 
     addParam(param?: OperationParameter): void {
+        this.validityChanged();
         this.tableParameters.push(new OperationParameter(param));
     }
 
-    canAdd(): boolean {
+    canAdd = (): boolean => {
+
         let valid = true;
         if (this.currentTab === this.TYPE_INPUT) {
             _.forEach(this.inputParameters, param => {
-                if (!param.name || !param.property) {
+                if (!param.name || !param.inputId) {
                     valid = false;
                 }
             });
@@ -262,13 +375,16 @@ export class OperationCreatorComponent {
                 }
             });
         }
+
         return valid;
+
     }
 
-    isParamsValid(): boolean {
+    isParamsValid = (): boolean => {
+
         let valid = true;
         _.forEach(this.inputParameters, param => {
-            if (!param.name || !param.property) {
+            if (!param.name || !param.inputId) {
                 valid = false;
             }
         });
@@ -277,7 +393,9 @@ export class OperationCreatorComponent {
                 valid = false;
             }
         });
+
         return valid;
+
     }
 
     onRemoveParam = (param: OperationParameter): void => {
@@ -285,9 +403,9 @@ export class OperationCreatorComponent {
         this.tableParameters.splice(index, 1);
     }
 
-    createParamLists(): void {
-        this.operation.createInputParamsList(this.inputParameters);
-        this.operation.createOutputParamsList(this.outputParameters);
+    createParamLists = () => {
+        this.operation.createInputsList(this.inputParameters);
+        this.operation.createOutputsList(this.outputParameters);
     }
 
     isUsingExistingWF = (operation?: OperationModel): boolean => {
@@ -295,15 +413,20 @@ export class OperationCreatorComponent {
         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING;
     }
 
-    shouldCreateWF(operation?: OperationModel): boolean {
+    shouldCreateWF = (operation?: OperationModel): boolean => {
         operation = operation || this.operation;
         return this.operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW;
     }
 
-    checkFormValidForSubmit(): boolean {
-        return this.operation.operationType &&
+    checkFormValidForSubmit = (): boolean => {
+        return this.operation.name &&
             (!this.isUsingExistingWF() || this.operation.workflowVersionId) &&
             this.isParamsValid();
     }
 
+    validityChanged = () => {
+        let validState = this.checkFormValidForSubmit();
+        this.validityChangedCallback(validState);
+    }
+
 }
index 7d88123..0b6f833 100644 (file)
@@ -3,8 +3,9 @@ import {CommonModule} from "@angular/common";
 
 import {FormsModule} from "@angular/forms";
 import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module";
-import {TranslateModule} from "app/ng2/shared/translator/translate.module";
+import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index";
 import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+import {TranslateModule} from "app/ng2/shared/translator/translate.module";
 
 import {OperationCreatorComponent} from "./operation-creator.component";
 import {ParamRowComponent} from './param-row/param-row.component';
@@ -16,6 +17,7 @@ import {ParamRowComponent} from './param-row/param-row.component';
     ],
     imports: [
         CommonModule,
+        SdcUiComponentsModule,
         FormsModule,
         FormElementsModule,
         TranslateModule,
index 9a5c101..1128d60 100644 (file)
@@ -19,6 +19,7 @@
         *ngIf="!isAssociateWorkflow"
         data-tests-id="paramName"
         [(value)]="param.name"
+        (valueChange)="onChangeName()"
         [readonly]="readonly">
     </ui-element-input>
     <span *ngIf="isAssociateWorkflow">{{param.name}}</span>
         *ngIf="filteredInputProps.length || !isAssociateWorkflow"
         data-tests-id="paramProperty"
         [values]="filteredInputProps"
-        [(value)]="param.property"
+        value="paramId"
+        (valueChange)="onChangeProperty($event)"
         [readonly]="readonly">
     </ui-element-dropdown>
     <span
         *ngIf="!filteredInputProps.length && isAssociateWorkflow"
         class="no-properties-error">
-        No available properties of this type.
+        {{ 'PARAM_NONE_OF_TYPE' | translate }}
     </span>
 </div>
 
     <checkbox
         *ngIf="!isAssociateWorkflow"
         data-tests-id="paramMandatory"
-        [(checked)]="param.mandatory"
+        [(checked)]="param.required"
         [ngClass]="{'disabled':readonly}">
     </checkbox>
 </div>
 
 <div class="cell remove" *ngIf="!isAssociateWorkflow && !readonly">
-    <span
-        class="sprite-new delete-item-icon"
-        data-tests-id="removeInputParam"
-        (click)="onRemoveParam(param)">
-    </span>
+    <svg-icon
+      name="trash-o"
+      mode="info"
+      size="small"
+      testId="removeInputParam"
+      (click)="onRemoveParam(param)"
+      [clickable]="true">
+  </svg-icon>
 </div>
index 2c2625d..99a54bb 100644 (file)
@@ -5,7 +5,10 @@
     align-items: center;
     justify-content: center;
 
-    .delete-item-icon {
+    svg-icon {
+        position: relative;
+        right: -3px;
+
         &:hover {
             cursor: pointer;
         }
index a12425d..8837a17 100644 (file)
@@ -1,6 +1,7 @@
 import {Component, Input} from '@angular/core';
 import {DataTypeService} from "app/ng2/services/data-type.service";
 import {OperationParameter, InputBEModel} from 'app/models';
+import {DropDownOption} from "../operation-creator.component";
 import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
 
 @Component({
@@ -16,8 +17,10 @@ export class ParamRowComponent {
     @Input() isAssociateWorkflow: boolean;
     @Input() readonly: boolean;
     @Input() isInputParam: boolean;
+    @Input() validityChanged: Function;
 
-    propTypeEnum: Array<String> = [];
+    paramId: string;
+    propTypeEnum: Array<DropDownOption> = [];
     filteredInputProps: Array<DropdownValue> = [];
 
     constructor(private dataTypeService: DataTypeService) {}
@@ -26,20 +29,48 @@ export class ParamRowComponent {
         this.propTypeEnum = _.uniq(
             _.map(
                 this.getPrimitiveSubtypes(),
-                prop => prop.type
+                prop => new DropDownOption(prop.type)
             )
         );
         this.onChangeType();
+        this.validityChanged();
     }
 
-    onChangeType() {
+    onChangeName() {
+        this.validityChanged();
+    }
+
+    onChangeType(paramId?: string) {
         this.filteredInputProps = _.map(
             _.filter(
                 this.getPrimitiveSubtypes(),
-                prop => prop.type === this.param.type
+                prop => !this.param.type || prop.type === this.param.type
             ),
             prop => new DropdownValue(prop.uniqueId, prop.name)
         );
+        if (paramId) {
+            this.paramId = paramId;
+        }
+    }
+
+    onChangeProperty(paramId: string) {
+        this.param.inputId = paramId;
+        const newProp = _.find(
+            this.getPrimitiveSubtypes(),
+            prop => this.param.inputId === prop.uniqueId
+        );
+
+        if (!this.param.type) {
+            this.param.type = newProp.type;
+            this.onChangeType(paramId);
+        } else {
+            this.paramId = paramId;
+        }
+
+        if (!this.param.name) {
+            this.param.name = newProp.name;
+        }
+        this.validityChanged();
     }
 
     getPrimitiveSubtypes(): Array<InputBEModel> {
index 26b0291..97e62da 100644 (file)
@@ -24,9 +24,9 @@ import {Observable} from 'rxjs/Observable';
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/toPromise';
 import {Response, URLSearchParams} from '@angular/http';
-import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, CreateOperationResponse} from "app/models";
+import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, BEOperationModel, CreateOperationResponse} from "app/models";
 import {downgradeInjectable} from '@angular/upgrade/static';
-import {COMPONENT_FIELDS} from "app/utils";
+import {COMPONENT_FIELDS, CommonUtils} from "app/utils";
 import {ComponentGenericResponse} from "../responses/component-generic-response";
 import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map";
 import {API_QUERY_PARAMS} from "app/utils";
@@ -122,38 +122,79 @@ export class ComponentServiceNg2 {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_PROPERTIES]);
     }
 
-    getInterfaceOperations(component:Component):Observable<ComponentGenericResponse> {
+    getInterfaces(component:Component):Observable<ComponentGenericResponse> {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]);
     }
 
     getInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
-        return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
-            .map((res:Response) => res.json());
+        return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId)
+            .map((res:Response) => {
+                return res.json();
+            });
     }
 
     createInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
         const operationList = {
-            'interfaceOperations': {
-                [operation.operationType]: operation
+            'interfaces': {
+                [operation.interfaceType]: {
+                    'type': operation.interfaceType,
+                    'operations': {
+                        [operation.name]: new BEOperationModel(operation)
+                    }
+                }
             }
         };
         return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
-            .map((res:Response) => res.json());
+            .map((res:Response) => {
+                const interf = _.find(res.json().interfaces, (interf: any) => interf.type === operation.interfaceType);
+                const newOperation = _.find(interf.operations, (op:OperationModel) => op.name === operation.name);
+                return new CreateOperationResponse({
+                    ...newOperation,
+                    interfaceType: interf.type,
+                    interfaceId: interf.uniqueId
+                });
+            });
     }
 
     updateInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
         const operationList = {
-            'interfaceOperations': {
-                [operation.operationType]: operation
+            'interfaces': {
+                [operation.interfaceType]: {
+                    'type': operation.interfaceType,
+                    'operations': {
+                        [operation.name]: new BEOperationModel(operation)
+                    }
+                }
             }
         };
         return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
-            .map((res:Response) => res.json());
+            .map((res:Response) => {
+                const interf = _.find(res.json().interfaces, (interf: any) => interf.type === operation.interfaceType);
+                const newOperation = _.find(interf.operations, (op:OperationModel) => op.name === operation.name);
+                return new CreateOperationResponse({
+                    ...newOperation,
+                    interfaceType: interf.type,
+                    interfaceId: interf.uniqueId
+                });
+            });
     }
 
     deleteInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
-        return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
-            .map((res:Response) => res.json());
+        return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId)
+            .map((res:Response) => {
+                return res.json();
+            });
+    }
+
+    getInterfaceTypes(component:Component):Observable<{[id:string]: Array<string>}> {
+        return this.http.get(this.baseUrl + 'interfaceLifecycleTypes')
+            .map((res:Response) => {
+                const interfaceMap = {};
+                _.forEach(res.json(), (interf:any) => {
+                    interfaceMap[interf.toscaPresentation.type] = _.keys(interf.toscaPresentation.operations);
+                });
+                return interfaceMap;
+            });
     }
 
     getCapabilitiesAndRequirements(componentType: string, componentId:string):Observable<ComponentGenericResponse> {
index a77133e..647cc92 100644 (file)
@@ -92,6 +92,7 @@ export class ComponentGenericResponse  implements Serializable<ComponentGenericR
             this.toscaArtifacts = new ArtifactGroupModel(response.toscaArtifacts);
         }
         if(response.interfaces) {
+            this.interfaces = CommonUtils.initInterfaces(response.interfaces);
             this.interfaceOperations = CommonUtils.initInterfaceOperations(response.interfaces);
         }
         if(response.metadata) {
index 1fc5c78..36d56d6 100644 (file)
@@ -3,7 +3,17 @@ import { Response } from "@angular/http";
 import { Observable } from "rxjs/Observable";
 import { HttpService } from "./http.service";
 import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config";
-import { Component } from "app/models";
+import { Component, CreateOperationResponse } from "app/models";
+
+interface WorkflowOutputParameter {
+    name: string,
+    type: string,
+    mandatory: boolean
+}
+
+interface WorkflowInputParameter extends WorkflowOutputParameter {
+    property: string;
+}
 
 @Injectable()
 export class WorkflowServiceNg2 {
@@ -20,9 +30,9 @@ export class WorkflowServiceNg2 {
         this.catalogBaseUrl = sdcConfig.api.POST_workflow_artifact;
     }
 
-    public associateWorkflowArtifact(component: Component, operationId: string, workflowId: string, workflowVersionId: string, artifactUuid: string): Observable<any> {
-        return this.http.post(this.baseUrl + '/workflows/' + workflowId + '/versions/' + workflowVersionId + '/artifact-deliveries', {
-                endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operationId + '/artifacts/' + artifactUuid,
+    public associateWorkflowArtifact(component: Component, operation: CreateOperationResponse): Observable<any> {
+        return this.http.post(this.baseUrl + '/workflows/' + operation.workflowId + '/versions/' + operation.workflowVersionId + '/artifact-deliveries', {
+                endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId + '/artifacts/' + operation.artifactUUID,
                 method: 'POST'
             })
             .map((res:Response) => {
@@ -40,7 +50,7 @@ export class WorkflowServiceNg2 {
     public getWorkflowVersions(workflowId: string, filterCertified: boolean = true): Observable<any> {
         return this.http.get(this.baseUrl + '/workflows/' + workflowId + '/versions' + (filterCertified ? '?state=' + this.VERSION_STATE_CERTIFIED : ''))
             .map((res:Response) => {
-                return res.json().items;
+                return _.map(res.json().items, version => version);
             });
     }
 
index 7ba50fd..c5259f0 100644 (file)
@@ -19,9 +19,9 @@
  */
 
 import * as _ from "lodash";
-import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel, OperationModel} from "../models";
-import {ComponentInstanceFactory} from "./component-instance-factory";
-import {InputBEModel, PropertyBEModel, RelationshipModel} from "app/models";
+import { ComponentInstanceFactory } from "./component-instance-factory";
+import { Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel, InterfaceModel, OperationModel } from "../models";
+import { InputBEModel, PropertyBEModel, RelationshipModel } from "app/models";
 import { PolicyInstance } from "app/models/graph/zones/policy-instance";
 import { GroupInstance } from "../models/graph/zones/group-instance";
 
@@ -139,44 +139,55 @@ export class CommonUtils {
         return groups;
     }
 
-    static initInterfaceOperations(interfaces: any): Array<OperationModel> {
+    static initInterfaces(interfaces: Array<InterfaceModel>): Array<InterfaceModel> {
 
-        return _.reduce(interfaces, (acc, interf: any) => {
+        return _.map(interfaces, (interf: InterfaceModel) => {
+
+            return new InterfaceModel({
+                type: interf.type,
+                uniqueId: interf.uniqueId,
+                operations: _.map(interf.operations,
+                    (operation: OperationModel) => {
+                        const newOperation = new OperationModel(operation);
+                        newOperation.interfaceType = interf.type;
+                        newOperation.interfaceId = interf.uniqueId;
+
+                        const {inputs, outputs} = operation;
+                        if (inputs) {
+                            newOperation.createInputsList(inputs.listToscaDataDefinition);
+                        }
+                        if (outputs) {
+                            newOperation.createOutputsList(outputs.listToscaDataDefinition);
+                        }
+
+                        return newOperation;
+                    }
+                )
+            });
+
+        });
+    }
+
+    static initInterfaceOperations(interfaces: Array<InterfaceModel>): Array<OperationModel> {
+
+        return _.reduce(interfaces, (acc, interf: InterfaceModel) => {
 
             return acc.concat(
                 _.map(interf.operations,
-                    ({description, name, uniqueId, inputs, outputs, workflowId, workflowVersionId, workflowAssociationType}) => {
-                        const operation = new OperationModel({
-                            description,
-                            operationType: name,
-                            uniqueId,
-                            workflowAssociationType,
-                            workflowId,
-                            workflowVersionId
-                        });
+                    (operation: OperationModel) => {
+                        const newOperation = new OperationModel(operation);
+                        newOperation.interfaceType = interf.type;
+                        newOperation.interfaceId = interf.uniqueId;
+
+                        const {inputs, outputs} = operation;
                         if (inputs) {
-                            const inputParams = _.map(inputs.listToscaDataDefinition, (input:any) => {
-                                return {
-                                    name: input.name,
-                                    property: input.inputId,
-                                    type: input.type,
-                                    mandatory: input.mandatory
-                                };
-                            });
-                            operation.createInputParamsList(inputParams);
+                            newOperation.createInputsList(inputs.listToscaDataDefinition);
                         }
                         if (outputs) {
-                            const outputParams = _.map(outputs.listToscaDataDefinition, (output:any) => {
-                                return {
-                                    name: output.name,
-                                    property: output.inputId,
-                                    type: output.type,
-                                    mandatory: output.mandatory
-                                };
-                            });
-                            operation.createOutputParamsList(outputParams);
+                            newOperation.createOutputsList(outputs.listToscaDataDefinition);
                         }
-                        return operation;
+
+                        return newOperation;
                     }
                 )
             );
index 02bc58f..8e43f18 100644 (file)
     "SERVICE_PATH_SELECTOR_HIDE_ALL_VALUE" : "⚊ Hide all ⚊",
     "SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE" : "⚊ Show all ⚊",
 
+    "=========== INTERFACE OPERATION ==========": "",
+    "INTERFACE_ADD_OPERATION": "Add Operation",
+    "INTERFACE_EXPAND_ALL": "Expand All",
+    "INTERFACE_COLLAPSE_ALL": "Collapse All",
+    "INTERFACE_HEADER_NAME": "Name",
+    "INTERFACE_HEADER_DESCRIPTION": "Description",
+
+    "=========== INTERFACE OPERATION MODAL ATTRIBUTES =========": "",
+    "INTERFACE_CREATE_TITLE": "Create New Operation",
+    "INTERFACE_EDIT_TITLE": "Edit Operation",
+    "INTERFACE_DELETE_TITLE": "Delete Operation",
+    "INTERFACE_CANCEL_BUTTON": "Cancel",
+    "INTERFACE_SAVE_BUTTON": "Save",
+    "INTERFACE_CREATE_BUTTON": "Create",
+    "INTERFACE_DELETE_BUTTON": "Delete",
+    "INTERFACE_DELETE_TEXT": "Are you sure you want to delete {{operationName}}?",
+
     "=========== OPERATION CREATOR ============": "",
     "OPERATION_PROPERTY_TOOLTIP_TEXT": "VNF properties are defined by the input parameter type. In case you can't find a certain parameter, it might be due to a wrong type selection.",
     "SERVICE_OPERATION_PROPERTY_TOOLTIP_TEXT": "Service properties are defined by the input parameter type. In case you can't find a certain parameter, it might be due to a wrong type selection.",
     "NEW_WORKFLOW_ASSOCIATION": "New Workflow",
     "EXISTING_WORKFLOW_ASSOCIATION": "Existing Workflow",
 
+    "OPERATION_INTERFACE_TYPE": "Interface Name",
     "OPERATION_NAME": "Operation Name",
     "OPERATION_DESCRIPTION": "Description",
     "OPERATION_WORKFLOW_ASSIGNMENT": "Workflow Assignment",
     "OPERATION_WORKFLOW": "Workflow",
+    "OPERATION_NO_WORKFLOW_ERROR": "No Certified Versions Available",
     "OPERATION_WORKFLOW_ARCHIVED": "Archived",
     "OPERATION_WORKFLOW_VERSION": "Workflow Version",
     "OPERATION_ADD_PARAMS": "Add Paramaters",
     "OPERATION_PARAM_TYPE": "Type",
     "OPERATION_PARAM_PROPERTY": "Property",
     "OPERATION_PARAM_MANDATORY": "Mandatory",
+
     "EMPTY_PARAM_TABLE_HEADER": "NO PARAMETERS TO SHOW",
     "EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_1": "Select Workflow and Workflow Version above",
     "EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_2": "in order to see the parameters",
     "EMPTY_PARAM_TABLE_NO_WORKFLOWS": "Only <b>certified</b> workflow versions can be assigned to an operation",
 
+    "PARAM_NONE_OF_TYPE": "No available properties of this type.",
+
 
     "=========== PLUGIN NOT CONNECTED ===========": "",
     "PLUGIN_NOT_CONNECTED_ERROR_MAIN": "The \"{{pluginName}}\" plugin is currently unavailable.",