From ed64b5edff15e702493df21aa3230b81593e6133 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Fri, 9 Jun 2017 03:19:04 +0300 Subject: [SDC-29] catalog 1707 rebase commit. Change-Id: I43c3dc5cf44abf5da817649bc738938a3e8388c1 Signed-off-by: Michael Lando --- .../add-category-modal-view-model.ts | 72 ++ .../add-category-modal-view.html | 41 + .../add-category-modal-view.less | 3 + .../admin-dashboard/admin-dashboard-view-model.ts | 61 ++ .../admin-dashboard/admin-dashboard-view.html | 26 + .../admin-dashboard/admin-dashboard.less | 49 ++ .../category-management-view-model.ts | 176 +++++ .../category-management-view.html | 53 ++ .../category-management/category-management.less | 118 +++ .../admin-dashboard/ecomp/ecomp-view.html | 1 + .../user-management/user-management-view-model.ts | 202 +++++ .../user-management/user-management-view.html | 103 +++ .../user-management/user-management.less | 251 ++++++ .../app/view-models/catalog/catalog-view-model.ts | 303 +++++++ .../src/app/view-models/catalog/catalog-view.html | 199 +++++ .../src/app/view-models/catalog/catalog.less | 304 +++++++ .../view-models/dashboard/dashboard-view-model.ts | 387 +++++++++ .../app/view-models/dashboard/dashboard-view.html | 112 +++ .../src/app/view-models/dashboard/dashboard.less | 420 ++++++++++ .../view-models/dcae-app/dcae-app-view-model.ts | 112 +++ .../app/view-models/dcae-app/dcae-app-view.html | 17 + .../src/app/view-models/dcae-app/dcae-app.less | 303 +++++++ .../artifact-form/artifact-form-view-model.ts | 358 +++++++++ .../forms/artifact-form/artifact-form-view.html | 169 ++++ .../forms/artifact-form/artifact-form.less | 44 ++ .../forms/attribute-form/attribute-form-view.html | 152 ++++ .../attribute-form/attribute-from-view-model.ts | 241 ++++++ .../env-parameters-form/env-parameters-form.html | 92 +++ .../env-parameters-form/env-parameters-form.less | 178 +++++ .../env-parameters-form/env-parameters-form.ts | 153 ++++ .../env-parametr-description-popover.html | 4 + .../forms/input-form/input-form-view-modal.ts | 126 +++ .../forms/input-form/input-form-view.html | 125 +++ .../base-property-form/property-form-base-model.ts | 204 +++++ .../property-form-base-view.html | 132 ++++ .../base-property-form/property-form-base.less | 63 ++ .../property-form-view-model.ts | 322 ++++++++ .../property-form-view.html | 201 +++++ .../module-property-modal/module-property-model.ts | 206 +++++ .../module-property-view.html | 41 + .../select-datatype-modal-view-model.ts | 97 +++ .../select-datatype-modal-view.html | 74 ++ .../select-datatype-modal.less | 63 ++ .../resource-instance-name-model.ts | 95 +++ .../resource-instance-name-view.html | 72 ++ .../resource-instance-name.less | 29 + .../confirmation-modal-view-model.ts | 73 ++ .../confirmation-modal-view.html | 29 + .../confirmation-modal/confirmation-modal.less | 30 + .../modals/email-modal/email-modal-view-model.ts | 96 +++ .../modals/email-modal/email-modal-view.html | 77 ++ .../modals/email-modal/email-modal.less | 57 ++ .../modals/error-modal/error-403-view.html | 4 + .../modals/error-modal/error-view-model.ts | 19 + .../app/view-models/modals/error-modal/error.less | 13 + .../message-modal/message-base-modal-model.ts | 43 + .../client-message-modal-view-model.ts | 22 + .../client-message-modal-view.html | 16 + .../message-client-modal/client-message-modal.less | 0 .../server-message-modal-view-model.ts | 24 + .../server-message-modal-view.html | 17 + .../message-server-modal/server-message-modal.less | 0 .../onboarding-modal-view-model.ts | 249 ++++++ .../onboarding-modal/onboarding-modal-view.html | 144 ++++ .../modals/onboarding-modal/onboarding-modal.less | 148 ++++ .../onboard-vendor/onboard-vendor-view-model.ts | 125 +++ .../onboard-vendor/onboard-vendor-view.html | 16 + .../view-models/onboard-vendor/onboard-vendor.less | 303 +++++++ .../view-models/preloading/preloading-view.html | 9 + .../app/view-models/preloading/preloading-view.ts | 27 + .../app/view-models/support/support-view-model.ts | 16 + .../src/app/view-models/support/support-view.html | 31 + .../src/app/view-models/support/support.less | 8 + .../src/app/view-models/tabs/general-tab.less | 122 +++ .../tabs/hierarchy/hierarchy-view-model.ts | 98 +++ .../view-models/tabs/hierarchy/hierarchy-view.html | 107 +++ .../app/view-models/tabs/hierarchy/hierarchy.less | 101 +++ .../app/view-models/tutorial-end/tutorial-end.html | 10 + .../app/view-models/tutorial-end/tutorial-end.less | 41 + .../app/view-models/tutorial-end/tutorial-end.ts | 20 + .../src/app/view-models/welcome/welcome-view.html | 9 + .../src/app/view-models/welcome/welcome-view.ts | 57 ++ .../conformance-level-modal-view-model.ts | 29 + .../conformance-level-modal-view.html | 22 + .../conformance-level-modal.less | 3 + .../workspace/tabs/activity-log/activity-log.html | 85 ++ .../workspace/tabs/activity-log/activity-log.less | 83 ++ .../workspace/tabs/activity-log/activity-log.ts | 103 +++ .../tabs/attributes/attributes-view-model.ts | 80 ++ .../workspace/tabs/attributes/attributes-view.html | 52 ++ .../workspace/tabs/attributes/attributes.less | 54 ++ .../tabs/composition/composition-view-model.ts | 242 ++++++ .../tabs/composition/composition-view.html | 95 +++ .../workspace/tabs/composition/composition.less | 873 +++++++++++++++++++++ .../tabs/artifacts/artifacts-view-model.ts | 301 +++++++ .../composition/tabs/artifacts/artifacts-view.html | 67 ++ .../tabs/composition/tabs/artifacts/artifacts.less | 172 ++++ .../composition/tabs/details/details-view-model.ts | 132 ++++ .../composition/tabs/details/details-view.html | 136 ++++ .../tabs/composition/tabs/details/details.less | 72 ++ .../properties-view-model.ts | 217 +++++ .../properties-and-attributes/properties-view.html | 117 +++ .../tabs/properties-and-attributes/properties.less | 38 + .../tabs/relations/relations-view-model.ts | 156 ++++ .../composition/tabs/relations/relations-view.html | 61 ++ .../tabs/composition/tabs/relations/relations.less | 14 + .../composition/tabs/structure/structure-view.html | 13 + .../composition/tabs/structure/structure-view.ts | 14 + .../deployment-artifacts-description-popover.html | 23 + .../deployment-artifacts-view-model.ts | 276 +++++++ .../deployment-artifacts-view.html | 126 +++ .../deployment-artifacts/deployment-artifacts.less | 196 +++++ .../tabs/deployment/deployment-view-model.ts | 127 +++ .../workspace/tabs/deployment/deployment-view.html | 10 + .../workspace/tabs/deployment/deployment.less | 33 + .../disribution-status-modal-view-model.ts | 83 ++ .../disribution-status-modal-view.html | 130 +++ .../disribution-status-modal.less | 40 + .../tabs/distribution/distribution-view-model.ts | 111 +++ .../tabs/distribution/distribution-view.html | 181 +++++ .../workspace/tabs/distribution/distribution.less | 362 +++++++++ .../workspace/tabs/general/general-view-model.ts | 349 ++++++++ .../workspace/tabs/general/general-view.html | 354 +++++++++ .../workspace/tabs/general/general.less | 64 ++ .../workspace/tabs/icons/icons-view-model.ts | 111 +++ .../workspace/tabs/icons/icons-view.html | 26 + .../view-models/workspace/tabs/icons/icons.less | 65 ++ .../information-artifacts-view-model.ts | 138 ++++ .../information-artifacts-view.html | 66 ++ .../information-artifacts.less | 57 ++ .../view-models/workspace/tabs/inputs/inputs.less | 225 ++++++ .../resource-input/resource-inputs-view-model.ts | 117 +++ .../resource-input/resource-inputs-view.html | 86 ++ .../inputs/resource-input/resource-inputs.less | 9 + .../service-input/service-inputs-view-model.ts | 378 +++++++++ .../inputs/service-input/service-inputs-view.html | 134 ++++ .../tabs/inputs/service-input/service-inputs.less | 47 ++ .../management-workflow-view-model.ts | 127 +++ .../management-workflow-view.html | 3 + .../network-call-flow-view-model.ts | 79 ++ .../network-call-flow/network-call-flow-view.html | 3 + .../product-hierarchy-view-model.ts | 109 +++ .../product-hierarchy/product-hierarchy-view.html | 40 + .../tabs/product-hierarchy/product-hierarchy.less | 130 +++ .../tabs/properties/properties-view-model.ts | 92 +++ .../workspace/tabs/properties/properties-view.html | 59 ++ .../workspace/tabs/properties/properties.less | 115 +++ .../req-and-capabilities-view-model.ts | 147 ++++ .../req-and-capabilities-view.html | 143 ++++ .../req-and-capabilities/req-and-capabilities.less | 196 +++++ .../tosca-artifacts/tosca-artifacts-view-model.ts | 84 ++ .../tabs/tosca-artifacts/tosca-artifacts-view.html | 49 ++ .../tabs/tosca-artifacts/tosca-artifacts.less | 78 ++ .../view-models/workspace/workspace-view-model.ts | 718 +++++++++++++++++ .../app/view-models/workspace/workspace-view.html | 78 ++ .../src/app/view-models/workspace/workspace.less | 150 ++++ 156 files changed, 18270 insertions(+) create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view-model.ts create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view.html create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard.less create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view-model.ts create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view.html create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management.less create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/ecomp/ecomp-view.html create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view.html create mode 100644 catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management.less create mode 100644 catalog-ui/src/app/view-models/catalog/catalog-view-model.ts create mode 100644 catalog-ui/src/app/view-models/catalog/catalog-view.html create mode 100644 catalog-ui/src/app/view-models/catalog/catalog.less create mode 100644 catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts create mode 100644 catalog-ui/src/app/view-models/dashboard/dashboard-view.html create mode 100644 catalog-ui/src/app/view-models/dashboard/dashboard.less create mode 100644 catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts create mode 100644 catalog-ui/src/app/view-models/dcae-app/dcae-app-view.html create mode 100644 catalog-ui/src/app/view-models/dcae-app/dcae-app.less create mode 100644 catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html create mode 100644 catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less create mode 100644 catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html create mode 100644 catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html create mode 100644 catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less create mode 100644 catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts create mode 100644 catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html create mode 100644 catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts create mode 100644 catalog-ui/src/app/view-models/forms/input-form/input-form-view.html create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html create mode 100644 catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less create mode 100644 catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts create mode 100644 catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html create mode 100644 catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less create mode 100644 catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view.html create mode 100644 catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal.less create mode 100644 catalog-ui/src/app/view-models/modals/email-modal/email-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/email-modal/email-modal-view.html create mode 100644 catalog-ui/src/app/view-models/modals/email-modal/email-modal.less create mode 100644 catalog-ui/src/app/view-models/modals/error-modal/error-403-view.html create mode 100644 catalog-ui/src/app/view-models/modals/error-modal/error-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/error-modal/error.less create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-base-modal-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal.less create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html create mode 100644 catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal.less create mode 100644 catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view.html create mode 100644 catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal.less create mode 100644 catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts create mode 100644 catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view.html create mode 100644 catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor.less create mode 100644 catalog-ui/src/app/view-models/preloading/preloading-view.html create mode 100644 catalog-ui/src/app/view-models/preloading/preloading-view.ts create mode 100644 catalog-ui/src/app/view-models/support/support-view-model.ts create mode 100644 catalog-ui/src/app/view-models/support/support-view.html create mode 100644 catalog-ui/src/app/view-models/support/support.less create mode 100644 catalog-ui/src/app/view-models/tabs/general-tab.less create mode 100644 catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts create mode 100644 catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html create mode 100644 catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy.less create mode 100644 catalog-ui/src/app/view-models/tutorial-end/tutorial-end.html create mode 100644 catalog-ui/src/app/view-models/tutorial-end/tutorial-end.less create mode 100644 catalog-ui/src/app/view-models/tutorial-end/tutorial-end.ts create mode 100644 catalog-ui/src/app/view-models/welcome/welcome-view.html create mode 100644 catalog-ui/src/app/view-models/welcome/welcome-view.ts create mode 100644 catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-description-popover.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/general/general.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/icons/icons.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/inputs.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/properties/properties.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less create mode 100644 catalog-ui/src/app/view-models/workspace/workspace-view-model.ts create mode 100644 catalog-ui/src/app/view-models/workspace/workspace-view.html create mode 100644 catalog-ui/src/app/view-models/workspace/workspace.less (limited to 'catalog-ui/src/app/view-models') diff --git a/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts new file mode 100644 index 0000000000..c421e632da --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts @@ -0,0 +1,72 @@ +'use strict'; +import {ICategoryResourceClass, ICategoryResource} from "../../../services/category-resource-service"; + +interface IAddCategoryModalViewModelScope extends ng.IScope { + category:ICategoryResource; + modelType:string; + footerButtons:Array; + forms:any; + + save():void; + close():void; +} + +export class AddCategoryModalViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.CategoryResourceService', + '$uibModalInstance', + 'parentCategory', + 'type' + ]; + + constructor(private $scope:IAddCategoryModalViewModelScope, + private categoryResourceService:ICategoryResourceClass, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private parentCategory:ICategoryResource, + private type:string) { + this.initScope(); + } + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.modelType = this.parentCategory ? 'sub category' : 'category'; + this.$scope.category = new this.categoryResourceService(); + + this.$scope.close = ():void => { + this.$uibModalInstance.dismiss(); + }; + + this.$scope.save = ():void => { + + let onOk = (newCategory:ICategoryResource):void => { + this.$uibModalInstance.close(newCategory); + }; + + let onCancel = ():void => { + //error + }; + + if (!this.parentCategory) { + this.$scope.category.$save({types: this.type + "s"}, onOk, onCancel); + } else { + this.$scope.category.$saveSubCategory({ + types: this.type + "s", + categoryId: this.parentCategory.uniqueId + }, onOk, onCancel); + } + + }; + + this.$scope.footerButtons = [ + {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save, 'disabled': true}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + } +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html new file mode 100644 index 0000000000..a9df3e6009 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html @@ -0,0 +1,41 @@ + + +
+ +
+
+ + + +
+ + + +
+ +
+ +
+ +
+ +
diff --git a/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less new file mode 100644 index 0000000000..39d84aab23 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less @@ -0,0 +1,3 @@ +.i-sdc-admin-add-category-modal { + +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view-model.ts b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view-model.ts new file mode 100644 index 0000000000..c8503bce42 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view-model.ts @@ -0,0 +1,61 @@ +'use strict'; +import {CacheService} from "app/services"; +import {IAppConfigurtaion} from "app/models"; + +interface IAdminDashboardViewModelScope extends ng.IScope { + version:string; + sdcConfig:IAppConfigurtaion; + isLoading:boolean; + currentTab:string; + templateUrl:string; + monitorUrl:string; + moveToTab(tab:string):void; + isSelected(tab:string):boolean; +} + + +export class AdminDashboardViewModel { + static '$inject' = [ + '$scope', + '$templateCache', + 'Sdc.Services.CacheService', + 'sdcConfig' + ]; + + constructor(private $scope:IAdminDashboardViewModelScope, + private $templateCache:ng.ITemplateCacheService, + private cacheService:CacheService, + private sdcConfig:IAppConfigurtaion) { + + this.initScope(); + } + + private initScope = ():void => { + + this.$scope.version = this.cacheService.get('version'); + this.$scope.sdcConfig = this.sdcConfig; + this.$scope.monitorUrl = this.$scope.sdcConfig.api.kibana; + this.$scope.isSelected = (tab:string):boolean => { + return tab === this.$scope.currentTab; + } + + this.$scope.moveToTab = (tab:string):void => { + if (tab === this.$scope.currentTab) { + return; + } + else if (tab === 'USER_MANAGEMENT') { + this.$scope.templateUrl="user-management-view.html"; + this.$templateCache.put("user-management-view.html", require('app/view-models/admin-dashboard/user-management/user-management-view.html')); + } + else if (tab === 'CATEGORY_MANAGEMENT') { + this.$scope.templateUrl="category-management-view.html"; + this.$templateCache.put("category-management-view.html", require('app/view-models/admin-dashboard/category-management/category-management-view.html')); + } + this.$scope.currentTab = tab; + }; + + this.$scope.moveToTab('USER_MANAGEMENT'); + + + } +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view.html b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view.html new file mode 100644 index 0000000000..150f7c2554 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard-view.html @@ -0,0 +1,26 @@ +
+ + + + + +
+ +
+ + +
diff --git a/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard.less b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard.less new file mode 100644 index 0000000000..874a02c431 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/admin-dashboard.less @@ -0,0 +1,49 @@ +.sdc-admin-container{ + height: 100%; + + .sdc-admin-top-bar-menu{ + .bg_k; + height: @top_nav_admin_height; + padding-left:260px; + .box-shadow(-1px 0px 3px 0px rgba(0, 0, 0, 0.33)); + position: absolute; + top: @header_height; + left: 0; + right: 0; + z-index: 2; + + .sdc-admin-top-bar-menu-tab{ + .b_17; + .hand; + height: 44px; + background-color: transparent; + position: relative; + padding: 0px 10px 0px 10px; + border: none; + outline: none; + margin-right: 15px; + &.selected { + outline: none; + border-bottom: solid 4px @color_t; + } + } + .sdc-admin-top-bar-menu-monitor-btn{ + .bg_a; + .c_6; + float: right; + border: none; + position: relative; + padding: 11px 24px; + } + } + + .sdc-admin-body{ + .bg_n; + padding: 40px 260px 60px 260px; + position: absolute; + top: @top_nav_admin_height; + left: 0; + right: 0; + bottom: 0; + } +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view-model.ts b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view-model.ts new file mode 100644 index 0000000000..ba390c4bee --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view-model.ts @@ -0,0 +1,176 @@ +'use strict'; +import {ModalsHandler, ValidationUtils} from "app/utils"; +import {CacheService, ICategoryResource} from "app/services"; +import {IAppConfigurtaion} from "app/models"; +import {ComponentType} from "../../../utils/constants"; + +interface ICategoryManagementViewModelScope extends ng.IScope { + SERVICE:string; + RESOURCE:string; + categoriesToShow:Array; + serviceCategories:Array; + resourceCategories:Array; + selectedCategory:ICategoryResource; + selectedSubCategory:ICategoryResource; + modalInstance:ng.ui.bootstrap.IModalServiceInstance; + isLoading:boolean; + type:string; + namePattern:RegExp; + + selectCategory(category:ICategoryResource):void; + selectSubCategory(subcategory:ICategoryResource):void; + selectType(type:string):void; + deleteCategory(category:ICategoryResource, subCategory:ICategoryResource):void; + createCategoryModal(parentCategory:ICategoryResource):void; +} + +export class CategoryManagementViewModel { + static '$inject' = [ + '$scope', + 'sdcConfig', + 'Sdc.Services.CacheService', + '$uibModal', + '$filter', + 'ValidationUtils', + 'ModalsHandler' + ]; + + constructor(private $scope:ICategoryManagementViewModelScope, + private sdcConfig:IAppConfigurtaion, + private cacheService:CacheService, + private $uibModal:ng.ui.bootstrap.IModalService, + private $filter:ng.IFilterService, + private ValidationUtils:ValidationUtils, + private ModalsHandler:ModalsHandler) { + + this.initScope(); + this.$scope.selectType(ComponentType.SERVICE.toLocaleLowerCase()); + + } + + private initScope = ():void => { + let scope:ICategoryManagementViewModelScope = this.$scope; + scope.SERVICE = ComponentType.SERVICE.toLocaleLowerCase(); + scope.RESOURCE = ComponentType.RESOURCE.toLocaleLowerCase(); + + scope.namePattern = this.ValidationUtils.getValidationPattern('category'); + + scope.selectCategory = (category:ICategoryResource) => { + if (scope.selectedCategory !== category) { + scope.selectedSubCategory = null; + } + scope.selectedCategory = category; + }; + scope.selectSubCategory = (subcategory:ICategoryResource) => { + scope.selectedSubCategory = subcategory; + }; + scope.selectType = (type:string):void => { + if (scope.type !== type) { + scope.selectedCategory = null; + scope.selectedSubCategory = null; + } + + scope.type = type; + scope.categoriesToShow = scope[type + 'Categories']; + }; + + scope.createCategoryModal = (parentCategory:ICategoryResource):void => { + //can't create a sub category for service + if (parentCategory && scope.type === ComponentType.SERVICE.toLowerCase()) { + return; + } + + let type:string = scope.type; + + let onOk = (newCategory:ICategoryResource):void => { + if (!parentCategory) { + scope[type + 'Categories'].push(newCategory); + } else { + if (!parentCategory.subcategories) { + parentCategory.subcategories = []; + } + parentCategory.subcategories.push(newCategory); + } + }; + + let onCancel = ():void => { + + }; + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + template: 'src/app/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html', + controller: 'Sdc.ViewModels.AddCategoryModalViewModel', + size: 'sdc-xsm', + backdrop: 'static', + scope: scope, + resolve: { + parentCategory: function () { + return parentCategory; + }, + type: function () { + return type; + } + } + }; + + scope.modalInstance = this.$uibModal.open(modalOptions); + scope.modalInstance.result.then(onOk, onCancel); + + }; + + scope.deleteCategory = (category:ICategoryResource, subCategory:ICategoryResource):void => { + + let onOk = ():void => { + + scope.isLoading = true; + let type:string = scope.type; + + let onError = (response):void => { + scope.isLoading = false; + console.info('onFaild', response); + }; + + let onSuccess = (response:any):void => { + let arr:Array; + + if (!subCategory) { + arr = this.$scope[type + 'Categories']; + arr.splice(arr.indexOf(category), 1); + if (category === scope.selectedCategory) { + scope.selectedCategory = null; + scope.selectedSubCategory = null; + } + } else { + arr = category.subcategories; + arr.splice(arr.indexOf(subCategory), 1); + } + + scope.isLoading = false; + }; + + if (!subCategory) { + category.$delete({ + types: type + "s", + categoryId: category.uniqueId + } + , onSuccess, onError); + } else { + category.$deleteSubCategory({ + types: type + "s", + categoryId: category.uniqueId, + subCategoryId: subCategory.uniqueId, + } + , onSuccess, onError); + } + }; + let modelType:string = subCategory ? 'sub category' : 'category'; + let title:string = this.$filter('translate')("DELETE_CATEGORY_MODAL_HEADER", "{'modelType': '" + modelType + "' }"); + let message:string = this.$filter('translate')("DELETE_CATEGORY_MODAL_CATEGORY_NAME", "{'modelType': '" + modelType + "' }"); + + this.ModalsHandler.openConfirmationModal(title, message, false, 'sdc-xsm').then(onOk); + }; + + this.$scope.serviceCategories = this.cacheService.get('serviceCategories'); + this.$scope.resourceCategories = this.cacheService.get('resourceCategories'); + } +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view.html b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view.html new file mode 100644 index 0000000000..95a002d3d7 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management-view.html @@ -0,0 +1,53 @@ +
+ + + +
+ +
+ +

+ + +

+ + + +
    +
  • + {{category.name}} + + + +
  • +
+
+
+ +
+ +

+ + + +
    +
  • + {{subcategory.name}} + + + +
  • +
+
+
+ +
+
diff --git a/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management.less b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management.less new file mode 100644 index 0000000000..011122c9e8 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/category-management/category-management.less @@ -0,0 +1,118 @@ + +.category-management { + + .row { + display: table; + width: 70%; + min-width: 800px; + margin: auto; + + [class*="col-"] { + float: none; + display: table-cell; + vertical-align: top; + background-color: white; + border: 1px solid #c1c1c1; + padding: 0; + + &:not(:last-child) { + border-right: none; + } + + h4 { + float:left; + color:white; + background-color: rgb(155,168,176); + margin: 0; + padding: 0px 0px 0px 16px; + width: 100%; + height: 31px; + font-size: 15px; + + span{ + display: inline-block; + line-height: 30px; + margin-right: 5px; + padding: 0 7px; + + &.selected { + border-bottom: 2px #067ab4 solid; + } + } + } + h4+span{ + .hand; + float:right; + display: inline-block; + background-color: rgb(59,124,156); + text-align: center; + padding: 5.5px 0px; + width: 60px; + color: white; + position: absolute; + top: 0; + right: 0; + z-index: 1; + color: white; + } + + + .perfect-scrollbar { + width: 100%; + height: 500px; + margin-top: 40px; + margin-bottom: 15px; + } + + ul { + clear: both; + margin: 5px 0 10px 0; + list-style-type: none; + padding: 0; + position: relative; + + li{ + .hand; + padding: 0 8px; + text-indent: 9px; + font-size: 13px; + line-height: 25px; + border: 1px solid white; + border-right: none; + border-left: none; + margin-right: 5px; + + button { + background-color: transparent; + border: none; + float: right; + margin: 5px 3px; + display: none; + } + + &:hover { + background-color: #d9e6ec; + color: #3b7b9b; + border-color: #d9e6ec; + + button { + display: inline-block; + } + } + &.selected { + background-color: rgb(219,230,236); + color: #3b7b9b; + border-color: rgba(59, 123, 155, 0.42); + + &.gray { + background-color: rgba(155, 168, 176, 0.09); + border-color: white; + } + } + + } + } + } + } + +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/ecomp/ecomp-view.html b/catalog-ui/src/app/view-models/admin-dashboard/ecomp/ecomp-view.html new file mode 100644 index 0000000000..7c89b545c5 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/ecomp/ecomp-view.html @@ -0,0 +1 @@ +
diff --git a/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts new file mode 100644 index 0000000000..82cc3a74da --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts @@ -0,0 +1,202 @@ +'use strict'; +import {ModalsHandler} from "app/utils"; +import {IUserResource, IUserResourceClass} from "app/services"; +import {User, IUserProperties, IUser, IAppConfigurtaion} from "app/models"; + +interface IUserManagementViewModelScope extends ng.IScope { + sdcConfig:IAppConfigurtaion; + usersList:Array; + isLoading:boolean; + isNewUser:boolean; + sortBy:string; + reverse:boolean; + tableHeadersList:any; + roles:Array; + newUser:IUser; + currentUser:IUserResource; + userIdValidationPattern:RegExp; + editForm:ng.IFormController; + getAllUsers():void; + editUserRole(user:IUserProperties); + sort(sortBy:string):void; + createUser():void; + deleteUser(userId:string):void; + onEditUserPressed(user:IUserProperties):void; + saveUserChanges(user:IUserProperties):void; + getTitle(role:string):string; + clearForm():void; + +} + + +export class UserManagementViewModel { + static '$inject' = [ + '$scope', + 'sdcConfig', + 'Sdc.Services.UserResourceService', + 'UserIdValidationPattern', + '$filter', + 'ModalsHandler' + ]; + + constructor(private $scope:IUserManagementViewModelScope, + private sdcConfig:IAppConfigurtaion, + private userResourceService:IUserResourceClass, + private UserIdValidationPattern:RegExp, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler) { + + this.initScope(); + + } + + + private getAllUsers = ():void => { + this.$scope.isLoading = true; + + let onError = (response) => { + this.$scope.isLoading = false; + console.info('onFaild', response); + }; + let onSuccess = (response:Array) => { + this.$scope.usersList = response; + _.forEach(this.$scope.usersList, (user:any, i:number)=> { + user.index = i; + }); + this.$scope.isLoading = false; + }; + this.userResourceService.getAllUsers(onSuccess, onError); + }; + + private updateUserFilterTerm = (user:IUserProperties):void => { + user.filterTerm = user.firstName + ' ' + user.lastName + ' ' + user.userId + ' ' + user.email + ' ' + user.role + ' ' + this.$filter('date')(user.lastLoginTime, "MM/dd/yyyy"); + }; + + private initScope = ():void => { + let self = this; + + this.$scope.tableHeadersList = [{title: "First Name", property: 'firstName'}, { + title: "Last Name", + property: 'lastName' + }, + { + title: this.$filter('translate')("USER_MANAGEMENT_TABLE_HEADER_USER_ID"), + property: 'userId' + }, {title: "Email", property: 'email'}, {title: "Role", property: 'role'}, { + title: "Last Active", + property: 'lastLoginTime' + }]; + this.$scope.userIdValidationPattern = this.UserIdValidationPattern; + this.$scope.sortBy = 'lastLoginTime'; + this.$scope.reverse = false; + this.$scope.roles = this.sdcConfig.roles; + this.$scope.isNewUser = false; + this.$scope.currentUser = this.userResourceService.getLoggedinUser(); + this.getAllUsers(); + + let resource:IUserResource = {}; + this.$scope.newUser = new User(resource); + + this.$scope.sort = (sortBy:string):void => {//default sort by descending last update. default for alphabetical = ascending + this.$scope.isNewUser = false; + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? ( !this.$scope.reverse) : this.$scope.reverse = false; + this.$scope.sortBy = sortBy; + }; + + this.$scope.createUser = ():void => { + + let onError = (response) => { + this.$scope.isLoading = false; + console.info('onFaild', response); + }; + + let onSuccess = (response:IUserProperties) => { + this.$scope.newUser.resource['index'] = this.$scope.usersList.length; + this.$scope.newUser.resource.lastLoginTime = "0"; + this.$scope.newUser.resource.status = response.status; + this.updateUserFilterTerm(this.$scope.newUser.resource); + this.$scope.usersList.unshift(this.$scope.newUser.resource); + this.$scope.isNewUser = true; + this.$scope.sortBy = 'index'; + this.$scope.reverse = true; + this.$scope.isLoading = false; + this.$scope.newUser = new User(null); + this.$scope.editForm.$setPristine(); + let _self = this; + setTimeout(function () { + _self.$scope.isNewUser = false; + }, 7000); + }; + this.userResourceService.createUser({ + userId: this.$scope.newUser.resource.userId, + role: this.$scope.newUser.resource.role + }, onSuccess, onError); + }; + + + this.$scope.onEditUserPressed = (user:IUserProperties):void => { + user.isInEditMode = true; + user.tempRole = user.role; + }; + + this.$scope.editUserRole = (user:IUserProperties):void => { + let roleBeforeUpdate:string = user.role; + user.role = user.tempRole; + + let onError = (response) => { + this.$scope.isLoading = false; + user.role = roleBeforeUpdate; + console.info('onFaild', response); + }; + let onSuccess = (response:any) => { + this.$scope.isLoading = false; + user.tempRole = user.role; + this.updateUserFilterTerm(user); + }; + + this.userResourceService.editUserRole({id: user.userId, role: user.role}, onSuccess, onError); + }; + + this.$scope.saveUserChanges = (user:IUserProperties):void => { + if (user.tempRole != user.role) { + this.$scope.editUserRole(user) + } + user.isInEditMode = false; + }; + + this.$scope.deleteUser = (userId:string):void => { + + let onOk = ():void => { + this.$scope.isLoading = true; + + let onError = (response):void => { + this.$scope.isLoading = false; + console.info('onFaild', response); + }; + + let onSuccess = (response:any):void => { + _.remove(this.$scope.usersList, {userId: userId}); + this.$scope.isLoading = false; + }; + this.userResourceService.deleteUser({id: userId}, onSuccess, onError); + }; + + let title:string = this.$filter('translate')("USER_MANAGEMENT_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("USER_MANAGEMENT_VIEW_DELETE_MODAL_TEXT"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + this.$scope.getTitle = (role:string):string => { + return role.toLowerCase().replace('governor', 'governance_Rep').replace('_', ' '); + }; + + this.$scope.clearForm = ():void => { + if (!this.$scope.editForm['contactId'].$viewValue && !this.$scope.editForm['role'].$viewValue) { + this.$scope.editForm.$setPristine(); + } + //if(this.$scope.editForm['contactId'].$viewValue === '' && this.$scope.editForm['role'].$viewValue){ + // this.$scope.editForm.$setPristine(); + //} + }; + } +} diff --git a/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view.html b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view.html new file mode 100644 index 0000000000..d2983873cc --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view.html @@ -0,0 +1,103 @@ +
+ +
+
+ + + +
+
+
+
+
+ +
+
+ + +
+ + +
+
+
+ +
+ +
+
+ +
+
+
+ + +
+ +
+
+
{{header.title}} + +
+
+
+
+ +
+ +
+ +
{{user.firstName || '---'}}
+
{{user.lastName || '---' }}
+
{{user.userId || '---'}}
+
{{user.email || '---'}}
+
+
+ +
+
{{user.lastLoginTime == 0 ? 'Waiting' : (user.lastLoginTime | date:'MM/dd/yyyy')}}
+
+ + +
+
+ +
+ +
+
+
+ +
+
+
diff --git a/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management.less b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management.less new file mode 100644 index 0000000000..934faab9e7 --- /dev/null +++ b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management.less @@ -0,0 +1,251 @@ +.sdc-user-management-top-bar { + display: flex; + width: 100%; + label { + .i_17; + } + .sdc-user-management-top-bar-form-input, + .sdc-user-management-top-bar-form-select { + .b_9; + color: @color_b; + height: 28px; + padding-left: 10px; + border-radius: 2px; + border: 1px solid @border_color_f; + } + + .sdc-user-management-top-bar-search-container { + display: flex; + flex-direction: column; + position: relative; + width: 400px; + + label { + margin-bottom: 20px; + } + + .w-sdc-search-icon { + right: 11px; + top: 49px; + } + } + .vertical-border-container { + min-width: 50px; + margin: 0px auto; + + .vertical-border { + + width: 1px; + height: 70px; + background-color: @color_e; + display: table; + margin: 0 auto; + } + } + + .sdc-user-management-top-bar-wrapper { + display: flex; + } + + .sdc-user-management-top-bar-title { + .i_17; + font-weight: bold; + } + + .sdc-user-management-top-bar-create-user-container { + + display: flex; + flex-direction: column; + position: relative; + float: right; + padding-top: 0px; + text-align: left; + width: 650px; + + label { + margin-bottom: 20px; + } + + .sdc-user-management-top-bar-form-container { + width: 233px; + margin-right: 35px; + } + + .sdc-user-management-top-bar-create-btn { + .w-sdc-btn-light-green; + height: 30px; + width: 100px; + line-height: 0px; + padding-bottom: 3px; + margin-right: 0px; + } + } +} + + +.sdc-user-management-table-container-flex { + height: 650px; + margin-top: 35px; + .sdc-user-management-table { + width: 100%; + border: 1px solid @color_m; + .head { + .bg_m; + .head-row { + .c_18; + font-weight: bold; + + border-right: 1px solid @border_color_d; + + .sdc-user-management-table-header-sort-arrow { + display: inline-block; + background-color: transparent; + border: none; + .c_9; + width: 0; + height: 0; + float: right; + margin: 8px 8px 0px 0px; + &.up { + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid; + } + &.down { + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid; + } + } + } + .sdc-user-management-table-header:hover { + .bg_j; + } + + } + .body { + .scrollbar-container { + max-height: 421px; + .perfect-scrollbar; + } + .b_9; + + .data-row { + &:nth-of-type(odd) { + .bg_c; + } + &.sdc-user-management-table-new-user-row { + + animation: change 7s step-end both; + + @keyframes change { + from { + color: @color_z + } + to { + color: @color_b + } + } + } + &.sdc-user-management-table-row-edit-mode { + .bg_j; + } + div { + + border-right: 1px solid @border_color_d; + + &:last-child { + border-right: none; + } + + .sdc-user-management-table-role-select { + background-color: transparent; + border: 0; + width: 100%; + + } + .sdc-user-management-table-role-label { + margin-left:4px; + } + + } + + } + .data-row:hover { + .bg_j; + } + + } + + .sdc-user-management-table-btn-col { + + line-height: 0px; + text-align: center; + .sdc-user-management-table-delete-btn { + background-color: transparent; + border: none; + .sprite; + .sprite.e-sdc-small-icon-delete; + opacity: 0.7; + } + .sdc-user-management-table-edit-btn { + background-color: transparent; + border: none; + .sprite; + .e-sdc-small-icon-pencil; + opacity: 0.7; + } + .sdc-user-management-table-save-btn { + background-color: transparent; + border: none; + .sprite; + .sprite.e-sdc-green-save; + } + } + + } + + .sdc-user-management-flex-container { + display: flex; + } + + .sdc-user-management-flex-item { + width:10px; + padding: 5px; + text-align: center; + } + + .sdc-user-management-flex-item:nth-child(1) { + flex-grow: 5; + } + + .sdc-user-management-flex-item:nth-child(2) { + flex-grow: 7; + } + + .sdc-user-management-flex-item:nth-child(3) { + flex-grow: 4; + } + + .sdc-user-management-flex-item:nth-child(4) { + flex-grow: 8; + } + + .sdc-user-management-flex-item:nth-child(5) { + flex-grow: 8; + } + + .sdc-user-management-flex-item:nth-child(6) { + flex-grow: 8; + } + + .sdc-user-management-flex-item:nth-child(7) { + flex-grow: 1; + } + + .sdc-user-management-flex-item:nth-child(8) { + flex-grow: 1; + } + +} + diff --git a/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts b/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts new file mode 100644 index 0000000000..0e7e4aaeae --- /dev/null +++ b/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts @@ -0,0 +1,303 @@ +'use strict'; +import {Component, IMainCategory, IGroup, IConfigStatuses, IAppMenu, IAppConfigurtaion, IUserProperties, ISubCategory} from "app/models"; +import {EntityService, IUserResourceClass, CacheService} from "app/services"; +import {ComponentFactory, ResourceType, MenuHandler, ChangeLifecycleStateHandler} from "app/utils"; + + +interface Checkboxes { + componentTypes:Array; + resourceSubTypes:Array; +} + +interface CheckboxesFilter { + // Types + selectedComponentTypes:Array; + selectedResourceSubTypes:Array; + // Categories + selectedCategoriesModel:Array; + // Statuses + selectedStatuses:Array; +} + +interface Gui { + isLoading:boolean; + onResourceSubTypesClick:Function; + onComponentTypeClick:Function; + onCategoryClick:Function; + onSubcategoryClick:Function; + onGroupClick:Function; +} + +export interface ICatalogViewModelScope extends ng.IScope { + checkboxes:Checkboxes; + checkboxesFilter:CheckboxesFilter; + gui:Gui; + + categories:Array; + confStatus:IConfigStatuses; + sdcMenu:IAppMenu; + catalogFilterdItems:Array; + expandedSection:Array; + actionStrategy:any; + user:IUserProperties; + catalogMenuItem:any; + version:string; + sortBy:string; + reverse:boolean; + vfcmtType:string; + + //this is for UI paging + numberOfItemToDisplay:number; + isAllItemDisplay:boolean; + + changeLifecycleState(entity:any, state:string):void; + sectionClick (section:string):void; + order(sortBy:string):void; + getNumOfElements(num:number):string; + goToComponent(component:Component):void; + raiseNumberOfElementToDisplay():void; +} + +export class CatalogViewModel { + static '$inject' = [ + '$scope', + '$filter', + 'Sdc.Services.EntityService', + 'sdcConfig', + 'sdcMenu', + '$state', + '$q', + 'Sdc.Services.UserResourceService', + 'Sdc.Services.CacheService', + 'ComponentFactory', + 'ChangeLifecycleStateHandler', + 'MenuHandler' + ]; + + constructor(private $scope:ICatalogViewModelScope, + private $filter:ng.IFilterService, + private EntityService:EntityService, + private sdcConfig:IAppConfigurtaion, + private sdcMenu:IAppMenu, + private $state:ng.ui.IStateService, + private $q:ng.IQService, + private userResourceService:IUserResourceClass, + private cacheService:CacheService, + private ComponentFactory:ComponentFactory, + private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, + private MenuHandler:MenuHandler) { + + + this.initScopeMembers(); + this.initCatalogData(); // Async task to get catalog from server. + this.initScopeMethods(); + } + + private initCatalogData = ():void => { + let onSuccess = (followedResponse:Array):void => { + this.$scope.catalogFilterdItems = followedResponse; + this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length; + this.$scope.categories = this.cacheService.get('serviceCategories').concat(this.cacheService.get('resourceCategories')).concat(this.cacheService.get('productCategories')); + this.$scope.gui.isLoading = false; + }; + + let onError = ():void => { + console.info('Failed to load catalog CatalogViewModel::initCatalog'); + this.$scope.gui.isLoading = false; + }; + this.EntityService.getCatalog().then(onSuccess, onError); + }; + + + private initScopeMembers = ():void => { + // Gui init + this.$scope.gui = {}; + this.$scope.gui.isLoading = true; + this.$scope.numberOfItemToDisplay = 0; + //this.$scope.categories = this.cacheService.get('categoriesMap'); + this.$scope.sdcMenu = this.sdcMenu; + this.$scope.confStatus = this.sdcMenu.statuses; + this.$scope.expandedSection = ["type", "category", "product-category", "status"]; + this.$scope.user = this.userResourceService.getLoggedinUser(); + this.$scope.catalogMenuItem = this.sdcMenu.catalogMenuItem; + this.$scope.version = this.cacheService.get('version'); + this.$scope.sortBy = 'lastUpdateDate'; + this.$scope.reverse = true; + + + // Checklist init + this.$scope.checkboxes = {}; + this.$scope.checkboxes.componentTypes = ['Resource', 'Service', 'Product']; + this.$scope.checkboxes.resourceSubTypes = ['VF', 'VFC', 'CP', 'VL']; + + // Checkboxes filter init + this.$scope.checkboxesFilter = {}; + this.$scope.checkboxesFilter.selectedComponentTypes = []; + this.$scope.checkboxesFilter.selectedResourceSubTypes = []; + this.$scope.checkboxesFilter.selectedCategoriesModel = []; + this.$scope.checkboxesFilter.selectedStatuses = []; + + // this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length; + + this.$scope.vfcmtType = ResourceType.VFCMT; + }; + + private initScopeMethods = ():void => { + this.$scope.sectionClick = (section:string):void => { + let index:number = this.$scope.expandedSection.indexOf(section); + if (index !== -1) { + this.$scope.expandedSection.splice(index, 1); + } else { + this.$scope.expandedSection.push(section); + } + }; + + + this.$scope.order = (sortBy:string):void => {//default sort by descending last update. default for alphabetical = ascending + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : (sortBy === 'lastUpdateDate') ? true : false; + this.$scope.sortBy = sortBy; + }; + + + this.$scope.goToComponent = (component:Component):void => { + this.$scope.gui.isLoading = true; + this.$state.go('workspace.general', {id: component.uniqueId, type: component.componentType.toLowerCase()}); + }; + + + // Will print the number of elements found in catalog + this.$scope.getNumOfElements = (num:number):string => { + if (!num || num === 0) { + return "No Elements found"; + } else if (num === 1) { + return "1 Element found"; + } else { + return num + " Elements found"; + } + }; + + /** + * Select | unselect sub resource when resource is clicked | unclicked. + * @param type + */ + this.$scope.gui.onComponentTypeClick = (type:string):void => { + if (type === 'Resource') { + if (this.$scope.checkboxesFilter.selectedComponentTypes.indexOf('Resource') === -1) { + // If the resource was not selected, unselect all childs. + this.$scope.checkboxesFilter.selectedResourceSubTypes = []; + } else { + // If the resource was selected, select all childs + this.$scope.checkboxesFilter.selectedResourceSubTypes = angular.copy(this.$scope.checkboxes.resourceSubTypes); + } + } + }; + + /** + * Selecting | unselect resources when sub resource is clicked | unclicked. + */ + this.$scope.gui.onResourceSubTypesClick = ():void => { + if (this.$scope.checkboxesFilter.selectedResourceSubTypes && this.$scope.checkboxesFilter.selectedResourceSubTypes.length === this.$scope.checkboxes.resourceSubTypes.length) { + this.$scope.checkboxesFilter.selectedComponentTypes.push('Resource'); + } else { + this.$scope.checkboxesFilter.selectedComponentTypes = _.without(this.$scope.checkboxesFilter.selectedComponentTypes, 'Resource'); + } + }; + + this.$scope.gui.onCategoryClick = (category:IMainCategory):void => { + // Select | Unselect all childs + if (this.isCategorySelected(category.uniqueId)) { + this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(category.subcategories, (item) => { + return item.uniqueId; + }))); + if (category.subcategories) { + category.subcategories.forEach((sub:ISubCategory)=> { // Loop on all selected subcategories and mark the childrens + this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(sub.groupings, (item) => { + return item.uniqueId; + }))); + }); + } + } else { + this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(category.subcategories, (item) => { + return item.uniqueId; + })); + if (category.subcategories) { + category.subcategories.forEach((sub:ISubCategory)=> { // Loop on all selected subcategories and un mark the childrens + this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(sub.groupings, (item) => { + return item.uniqueId; + })); + }); + } + } + }; + + this.$scope.gui.onSubcategoryClick = (category:IMainCategory, subCategory:ISubCategory):void => { + // Select | Unselect all childs + if (this.isCategorySelected(subCategory.uniqueId)) { + this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(subCategory.groupings, (item) => { + return item.uniqueId; + }))); + } else { + this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(subCategory.groupings, (item) => { + return item.uniqueId; + })); + } + + // Mark | Un mark the parent when all childs selected. + if (this.areAllCategoryChildsSelected(category)) { + // Add the category to checkboxesFilter.selectedCategoriesModel + this.$scope.checkboxesFilter.selectedCategoriesModel.push(category.uniqueId); + } else { + this.$scope.checkboxesFilter.selectedCategoriesModel = _.without(this.$scope.checkboxesFilter.selectedCategoriesModel, category.uniqueId); + } + + }; + + this.$scope.raiseNumberOfElementToDisplay = ():void => { + this.$scope.numberOfItemToDisplay = this.$scope.numberOfItemToDisplay + 35; + if (this.$scope.catalogFilterdItems) { + this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length; + } + }; + + this.$scope.gui.onGroupClick = (subCategory:ISubCategory):void => { + // Mark | Un mark the parent when all childs selected. + if (this.areAllSubCategoryChildsSelected(subCategory)) { + // Add the category to checkboxesFilter.selectedCategoriesModel + this.$scope.checkboxesFilter.selectedCategoriesModel.push(subCategory.uniqueId); + } else { + this.$scope.checkboxesFilter.selectedCategoriesModel = _.without(this.$scope.checkboxesFilter.selectedCategoriesModel, subCategory.uniqueId); + } + }; + + + }; + + private areAllCategoryChildsSelected = (category:IMainCategory):boolean => { + if (!category.subcategories) { + return false; + } + let allIds = _.map(category.subcategories, (sub:ISubCategory)=> { + return sub.uniqueId; + }); + let total = _.intersection(this.$scope.checkboxesFilter.selectedCategoriesModel, allIds); + return total.length === category.subcategories.length ? true : false; + }; + + private areAllSubCategoryChildsSelected = (subCategory:ISubCategory):boolean => { + if (!subCategory.groupings) { + return false; + } + let allIds = _.map(subCategory.groupings, (group:IGroup)=> { + return group.uniqueId; + }); + let total = _.intersection(this.$scope.checkboxesFilter.selectedCategoriesModel, allIds); + return total.length === subCategory.groupings.length ? true : false; + }; + + private isCategorySelected = (uniqueId:string):boolean => { + if (this.$scope.checkboxesFilter.selectedCategoriesModel.indexOf(uniqueId) !== -1) { + return true; + } + return false; + }; +} diff --git a/catalog-ui/src/app/view-models/catalog/catalog-view.html b/catalog-ui/src/app/view-models/catalog/catalog-view.html new file mode 100644 index 0000000000..03ca4cb81f --- /dev/null +++ b/catalog-ui/src/app/view-models/catalog/catalog-view.html @@ -0,0 +1,199 @@ +
+ + + + +
+ + + +
+ +
+
+ + Type +
+
+
    +
  • + + + +
      +
    • + + + +
    • +
    +
  • +
+
+
+ +
+
+ + Categories +
+
+ +
    +
  • + + + + +
      +
    • + + + + +
        +
      • + + + +
      • +
      +
    • +
    +
  • +
+ +
+
+ + +
+
+ + Status +
+ +
+
    + + +
  • + + + +
    + +
  • +
+
+
+ +
+
+ + + + + +
+
+ {{getNumOfElements((catalogFilterdItems| filter:{resourceType:('!'+vfcmtType)} | entityFilter:checkboxesFilter | filter:search).length)}} +
+
+    +   + +  |  +   + +
+
+ +
+ +
+ + +
+ +
+
+
{{component.getComponentSubType()}}
+
S
+
+
+
+
+
+
+ +
+ + +
+ + +
+ +
+
+ +
+ + + + + +
diff --git a/catalog-ui/src/app/view-models/catalog/catalog.less b/catalog-ui/src/app/view-models/catalog/catalog.less new file mode 100644 index 0000000000..9db9192167 --- /dev/null +++ b/catalog-ui/src/app/view-models/catalog/catalog.less @@ -0,0 +1,304 @@ +.sdc-catalog-container { + + .i-sdc-categories-list-item { + font-weight: normal; + } + + // Checkboxes + .i-sdc-designer-leftbar-section-content-ul { + padding: 0; + margin: 0; + + .i-sdc-catalog-subcategories-checkbox { + padding: 0 0 0 20px; + margin: 0; + + .i-sdc-catalog-grouping-checkbox { + padding: 0 0 0 20px; + margin: 0; + } + + } + + } + + .i-sdc-designer-leftbar-section-content-li { + &:last-child { + .i-sdc-categories-list-item { + margin: 0; + } + } + } + + .i-sdc-categories-list-item { + display: block; + //margin-bottom: 5px; + //padding-left: 15px; + //text-indent: -24px; + vertical-align: top; + font-weight: bold; + } + + .i-sdc-subcategories-list-item { + display: block; + //padding-left: 20px; + vertical-align: top; + font-weight: normal; + margin: 0; + //text-indent: -10px; + } + + /*Added by - Ikram */ + .i-sdc-product-input, + .i-sdc-product-select { + border: 1px solid @border_color_f; + min-height: 30px; + padding: 0; + width: 100%; + margin: 1px 0; + background-color: #F2F2F2; + outline: none; + + &:disabled { + .disabled; + } + optgroup{ + color: @color_u; + option{ + color: @color_b; + } + } + } + + .i-sdc-categories-list-item-icon { + display: inline-block; + float: right; + position: relative; + right: -8px; + top: 6px; + } + + .i-sdc-categories-list-item { + margin-top: 7px; + &.NOT_CERTIFIED_CHECKOUT, + &.NOT_CERTIFIED_CHECKIN { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2889px; + width: 14px; + height: 14px; + + } + } + + &.CERTIFIED { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -3034px; + width: 14px; + height: 16px; + } + } + + &.READY_FOR_CERTIFICATION { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2985px; + width: 14px; + height: 16px; + } + } + + &.CERTIFICATION_IN_PROGRESS { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2934px; + width: 14px; + height: 16px; + } + } + + &.DISTRIBUTED, + &.TBD { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -43px -3087px; + width: 24px; + height: 14px; + + } + } + } + + .i-sdc-categories-list-input { + margin: 8px; + + } + + .i-sdc-subcategories-list-input { + + margin: 8px; + } + .i-sdc-subcategories-list-input-container { + margin: 0px 0px 0px 20px; + padding: 2px; + } + + .w-sdc-header-catalog-search-container { + display: table; + padding: 21px 0; + position: relative; + + .w-sdc-designer-leftbar-search-input { + color: #000; + width: 300px; + } + + // .magnification { + // .sprite; + // .sprite.magnification-glass; + // .hand; + // position: absolute; + // top: 40px; + // right: 42px; + // } + } + + .w-sdc-catalog-main { + padding: 10px 12px; + } + .w-sdc-dashboard-catalog-header { + .b_9; + display: inline-block; + font-style: italic; + font-weight: bold; + padding-left: 10px; + } + + .w-sdc-dashboard-catalog-header-order { + .b_9; + font-weight: 800; + } + + .w-sdc-dashboard-catalog-sort { + .b_9; + font-weight: bold; + white-space:pre; + &:hover{ + .hand; + text-decoration: none; + .a_9; + } + &.blue { + .a_9; + } + } + + .w-sdc-catalog-sort-arrow{ + display: inline-block; + &.up{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid ; + } + &.down{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid; + } + } + + + + + .w-sdc-dashboard-catalog-header-right{ + float: right; + display: inline-block; + padding-right:34px; + } + + .w-sdc-header-catalog-search-input { + width: 420px; + display: table-cell; + padding: 0 25px 1px 10px; + border: 1px solid #bcbcbc; + .border-radius(10px); + height: 30px; + margin: 10px 30px; + outline: none; + } + + .sdc-catalog-type-filter-container { + margin-top: -1px; + } + + .i-sdc-designer-leftbar-section-title { + text-transform: uppercase; + .l_14_m; + line-height: 30px; + } + + .i-sdc-designer-leftbar-section-title-icon { + .hand; + .tlv-sprite; + .footer-close; + transition: .3s all; + margin-top: -4px; + } + + .i-sdc-designer-leftbar-section-title-text { + margin-left: 20px; + } + + .seperator-left, + .seperator-right { + border-right: solid 1px @color_m; + display: table-cell; + width: 2px; + } + + // Rotate catalog left side arrows + .i-sdc-designer-leftbar-section-title.expanded .i-sdc-designer-leftbar-section-title-icon { + transform: rotate(180deg); + } + + // Transform catalog left side sections + .i-sdc-designer-leftbar-section-title + .i-sdc-designer-leftbar-section-content { + max-height: 0px; + margin: 0 auto; + transition: all .3s; + overflow: hidden; + padding: 0 10px 0 18px; + } + + .i-sdc-designer-leftbar-section-title.expanded + .i-sdc-designer-leftbar-section-content { + max-height: 9999px; + margin: 0 auto 1px; + transition: all .3s; + padding: 10px 18px 10px 18px; + overflow: hidden; + } + +} + +.w-sdc-search-icon{ + position: absolute; + right: 40px; + top: 40px; + &.leftbar{ + top: 19px; + right: 18px; + } + &.magnification { + .sprite; + .sprite.magnification-glass; + .hand; + } + + &.cancel { + .sprite; + .sprite.clear-text; + .hand; + } +} diff --git a/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts b/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts new file mode 100644 index 0000000000..d0b74a9062 --- /dev/null +++ b/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts @@ -0,0 +1,387 @@ +'use strict'; +import {IConfigRoles, IAppConfigurtaion, IAppMenu, IUserProperties, Component} from "app/models"; +import {EntityService, IUserResourceClass, SharingService, CacheService} from "app/services"; +import {ComponentType, ResourceType, MenuHandler, ModalsHandler, ChangeLifecycleStateHandler, SEVERITY, ComponentFactory} from "app/utils"; +import {IClientMessageModalModel} from "../modals/message-modal/message-client-modal/client-message-modal-view-model"; + +export interface IDashboardViewModelScope extends ng.IScope { + + isLoading:boolean; + components:Array; + folders:FoldersMenu; + roles:IConfigRoles; + user:IUserProperties; + sdcConfig:IAppConfigurtaion; + sdcMenu:IAppMenu; + sharingService:SharingService; + showTutorial:boolean; + isFirstTime:boolean; + version:string; + checkboxesFilter:CheckboxesFilter; + vfcmtType:string; + + + onImportVfc(file:any):void; + onImportVf(file:any):void; + openCreateModal(componentType:ComponentType, importedFile:any):void; + openWhatsNewModal(version:string):void; + openDesignerModal(isResource:boolean, uniqueId:string):void; + setSelectedFolder(folderItem:FoldersItemsMenu):void; + entitiesCount(folderItem:FoldersItemsMenu):number; + getCurrentFolderDistributed():Array; + changeLifecycleState(entity:any, data:any):void; + goToComponent(component:Component):void; + wizardDebugEdit:Function; + notificationIconCallback:Function; +} + +interface CheckboxesFilter { + // Statuses + selectedStatuses:Array; + // distributed + distributed:Array; +} + +export interface IItemMenu { + +} + +export interface IMenuItemProperties { + text:string; + group:string; + state:string; + dist:string; + groupname:string; + states:Array; +} + +export class FoldersMenu { + + private _folders:Array = []; + + constructor(folders:Array) { + let self = this; + folders.forEach(function (folder:IMenuItemProperties) { + if (folder.groupname) { + self._folders.push(new FoldersItemsMenuGroup(folder)); + } else { + self._folders.push(new FoldersItemsMenu(folder)); + } + }); + self._folders[0].setSelected(true); + } + + public getFolders = ():Array => { + return this._folders; + }; + + public getCurrentFolder = ():FoldersItemsMenu => { + let menuItem:FoldersItemsMenu = undefined; + this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + if (tmpFolder.isSelected()) { + menuItem = tmpFolder; + } + }); + return menuItem; + }; + + public setSelected = (folder:FoldersItemsMenu):void => { + this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + tmpFolder.setSelected(false); + }); + folder.setSelected(true); + } + +} + +export class FoldersItemsMenu implements IItemMenu { + + public text:string; + public group:string; + public state:string; + public dist:string; + public states:Array; + + private selected:boolean = false; + + constructor(menuProperties:IMenuItemProperties) { + this.text = menuProperties.text; + this.group = menuProperties.group; + this.state = menuProperties.state; + this.states = menuProperties.states; + this.dist = menuProperties.dist; + } + + public isSelected = ():boolean => { + return this.selected; + }; + + public setSelected = (value:boolean):void => { + this.selected = value; + }; + + public isGroup = ():boolean => { + return false; + } + +} + +export class FoldersItemsMenuGroup extends FoldersItemsMenu { + + public groupname:string; + + constructor(menuProperties:IMenuItemProperties) { + super(menuProperties); + this.groupname = menuProperties.groupname; + } + + public isGroup = ():boolean => { + return true; + } + +} + +export class DashboardViewModel { + static '$inject' = [ + '$scope', + '$filter', + 'Sdc.Services.EntityService', + '$http', + 'sdcConfig', + 'sdcMenu', + '$state', + '$stateParams', + 'Sdc.Services.UserResourceService', + 'Sdc.Services.SharingService', + 'Sdc.Services.CacheService', + '$q', + 'ComponentFactory', + 'ChangeLifecycleStateHandler', + 'ModalsHandler', + 'MenuHandler' + ]; + + private components:Array; + + constructor(private $scope:IDashboardViewModelScope, + private $filter:ng.IFilterService, + private entityService:EntityService, + private $http:ng.IHttpService, + private sdcConfig:IAppConfigurtaion, + private sdcMenu:IAppMenu, + private $state:any, + private $stateParams:any, + private userResourceService:IUserResourceClass, + private sharingService:SharingService, + private cacheService:CacheService, + private $q:ng.IQService, + private ComponentFactory:ComponentFactory, + private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, + private ModalsHandler:ModalsHandler, + private MenuHandler:MenuHandler) { + this.initScope(); + this.initFolders(); + this.initEntities(true); + + if (this.$stateParams) { + + if (this.$state.params.folder) { + let self = this; + let folderName = this.$state.params.folder.replaceAll("_", " "); + + this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + if (tmpFolder.text === folderName) { + self.$scope.setSelectedFolder(tmpFolder); + } + }); + } + + // Show the tutorial if needed when the dashboard page is opened. + // This is called from the welcome page. + else if (this.$stateParams.show === 'tutorial') { + this.$scope.showTutorial = true; + this.$scope.isFirstTime = true; + } + } + } + + private initFolders = ():void => { + if (this.$scope.user) { + this.$scope.folders = new FoldersMenu(this.$scope.roles[this.$scope.user.role].folder); + } + }; + + private initScope = ():void => { + let self = this; + + this.$scope.version = this.cacheService.get('version'); + this.$scope.sharingService = this.sharingService; + this.$scope.isLoading = false; + this.$scope.sdcConfig = this.sdcConfig; + this.$scope.sdcMenu = this.sdcMenu; + this.$scope.user = this.userResourceService.getLoggedinUser(); + this.$scope.roles = this.sdcMenu.roles; + this.$scope.showTutorial = false; + this.$scope.isFirstTime = false; + this.$scope.vfcmtType = ResourceType.VFCMT; + + // Open onboarding modal + this.$scope.notificationIconCallback = ():void => { + this.ModalsHandler.openOnboadrdingModal('Import').then(()=> { + // OK + }, ()=> { + // ERROR + }); + }; + + // Checkboxes filter init + this.$scope.checkboxesFilter = {}; + this.$scope.checkboxesFilter.selectedStatuses = []; + this.$scope.checkboxesFilter.distributed = []; + + this.$scope.onImportVf = (file:any):void => { + if (file && file.filename) { + // Check that the file has valid extension. + let fileExtension:string = file.filename.split(".").pop(); + if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VF + }); + } else { + let data:IClientMessageModalModel = { + title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE"), + message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS", "{'extensions': '" + this.sdcConfig.csarFileExtension + "'}"), + severity: SEVERITY.ERROR + }; + this.ModalsHandler.openClientMessageModal(data); + } + } + }; + + this.$scope.onImportVfc = (file:any):void => { + if (file && file.filename) { + // Check that the file has valid extension. + let fileExtension:string = file.filename.split(".").pop(); + if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + importedFile: file, + resourceType: ResourceType.VFC + }); + } else { + let data:IClientMessageModalModel = { + title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE"), + message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS", "{'extensions': '" + this.sdcConfig.toscaFileExtension + "'}"), + severity: SEVERITY.ERROR + }; + this.ModalsHandler.openClientMessageModal(data); + } + } + }; + + this.$scope.openCreateModal = (componentType:string, importedFile:any):void => { + if (importedFile) { + this.initEntities(true); // Return from import + } else { + this.$state.go('workspace.general', {type: componentType.toLowerCase()}); + } + + }; + + this.$scope.entitiesCount = (folderItem:FoldersItemsMenu):any => { + let self = this; + let total:number = 0; + if (folderItem.isGroup()) { + this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + if (tmpFolder.group && tmpFolder.group === (folderItem).groupname) { + total = total + self._getTotalCounts(tmpFolder, self); + } + }); + } else { + total = total + self._getTotalCounts(folderItem, self); + } + return total; + }; + + this.$scope.getCurrentFolderDistributed = ():Array => { + let self = this; + let states = []; + if (this.$scope.folders) { + let folderItem:FoldersItemsMenu = this.$scope.folders.getCurrentFolder(); + if (folderItem.isGroup()) { + this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) { + if (tmpFolder.group && tmpFolder.group === (folderItem).groupname) { + self._setStates(tmpFolder, states); + } + }); + } else { + self._setStates(folderItem, states); + } + } + return states; + }; + + this.$scope.setSelectedFolder = (folderItem:FoldersItemsMenu):void => { + this.$scope.folders.setSelected(folderItem); + }; + + this.$scope.goToComponent = (component:Component):void => { + this.$scope.isLoading = true; + this.$state.go('workspace.general', {id: component.uniqueId, type: component.componentType.toLowerCase()}); + }; + + }; + + private _getTotalCounts(tmpFolder, self):number { + let total:number = 0; + if (tmpFolder.dist !== undefined) { + let distributions = tmpFolder.dist.split(','); + distributions.forEach((item:any) => { + total = total + self.getEntitiesByStateDist(tmpFolder.state, item).length; + }); + } + else { + total = total + self.getEntitiesByStateDist(tmpFolder.state, tmpFolder.dist).length; + } + return total; + } + + private _setStates(tmpFolder, states) { + if (tmpFolder.states !== undefined) { + tmpFolder.states.forEach(function (item:any) { + states.push({"state": item.state, "dist": item.dist}); + }); + } else { + states.push({"state": tmpFolder.state, "dist": tmpFolder.dist}); + } + } + + private initEntities = (reload:boolean):void => { + this.$scope.isLoading = reload; + this.entityService.getAllComponents().then( + (components:Array) => { + this.components = components; + this.$scope.components = components; + this.$scope.isLoading = false; + }); + }; + + private getEntitiesByStateDist = (state:string, dist:string):Array => { + let gObj:Array; + if (this.components && (state || dist)) { + gObj = this.components.filter(function (obj:Component) { + if (dist !== undefined && obj.distributionStatus === dist && obj.lifecycleState === state) { + return true; + } else if (dist === undefined && obj.lifecycleState === state) { + return true; + } + return false; + }); + } else { + gObj = []; + } + return gObj; + } +} diff --git a/catalog-ui/src/app/view-models/dashboard/dashboard-view.html b/catalog-ui/src/app/view-models/dashboard/dashboard-view.html new file mode 100644 index 0000000000..806bb8138d --- /dev/null +++ b/catalog-ui/src/app/view-models/dashboard/dashboard-view.html @@ -0,0 +1,112 @@ + +
+ + + + +
+ + + +
+ + +
+
+
+
+ + + +
+
+
+ + +
+
+
+
+
Import VFC + +
+
Import VSP
+
Import DCAE asset + +
+
+
+
+ + +
+ +
+
+
{{component.getComponentSubType()}}
+
S
+
+
+
+
+
+
+ +
+ + +
+ + +
+ +
+ +
+
+ {{folder.text}} + + + + + {{entitiesCount(folder)}} +
+
+ +
+ + + +
+
+ + + diff --git a/catalog-ui/src/app/view-models/dashboard/dashboard.less b/catalog-ui/src/app/view-models/dashboard/dashboard.less new file mode 100644 index 0000000000..7993390769 --- /dev/null +++ b/catalog-ui/src/app/view-models/dashboard/dashboard.less @@ -0,0 +1,420 @@ +.sdc-dashboard-container { + .tlv-loader { + top: -110px; + left: 80px; + } + .sdc-hide-popover { + .popover { + display: none !important; + } + } +} + +.w-sdc-left-sidebar-nav { + margin-top: 46px; +} + +.w-sdc-main-right-container-element { + float: left; + height: 217px; + width: 217px; + margin: 10px; + position: relative; +} + +.w-sdc-main-right-container-element-details-container { + position: absolute; + top: 165px; + left: 50px; +} + +.w-sdc-main-right-container-element-name { + font-weight: bold; +} + +.w-sdc-main-right-container-element-owner { + +} + +//////////////////////////////Cards//////////////////// +.w-sdc-dashboard-card-new { + border: 2px dashed @color_m; + .border-radius(2px); + cursor: pointer; + display: inline-block; + height: 198px; + margin: 11px; + position: relative; + vertical-align: middle; + width: 202px; +} + +.w-sdc-dashboard-card-new-content { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + height: 100%; +} + +.w-sdc-dashboard-card-new-content-plus { + .sprite-new; + .add-icon; + position: relative; + margin-bottom: 20px; + + &:after { + .n_14_m; + content: 'ADD'; + position: absolute; + top: 25px; + left: -3px; + vertical-align: -50%; + } +} + +.w-sdc-dashboard-card-import-content-plus { + .sprite-new; + .import-icon; + position: relative; + margin-bottom: 20px; + + &:after { + .n_14_m; + content: 'IMPORT'; + position: absolute; + top: 25px; + left: -16px; + vertical-align: -50%; + } +} + +.sdc-dashboard-create-element-container, +.sdc-dashboard-import-element-container { + + width: 140px; + + .tlv-btn.import-dcae { + padding: 0; + } + + .tlv-btn { + position: relative; + width: 100%; + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + + input[type="file"] { + cursor: inherit; + filter: alpha(opacity=0); + opacity: 0; + position: absolute; + top: 0; + left: 0; + width: 138px; + height: 30px; + } +} + +.w-sdc-dashboard-card { + width: 204px; + height: 200px; + background-color: @main_color_p; + .border-radius(2px); + .box-shadow(0px 2px 2px 0px rgba(24, 24, 25, 0.05)); + display: inline-block; + margin: 10px; + position: relative; + vertical-align: middle; + border: solid 1px @main_color_p; + + &:hover { + border: solid 1px @main_color_o; + .box-shadow(3px 3px 2px 0px rgba(24, 24, 25, 0.05)); + } + + &:active { + border: solid 1px @main_color_c; + .box-shadow(3px 3px 2px 0px rgba(24, 24, 25, 0.05)); + } +} + +.w-sdc-dashboard-card-body { + .hand; + border-bottom: 1px solid @color_j; + height: 155px; + position: relative; + text-align: center; +} + +.w-sdc-dashboard-card-description { + .c_3; + .hand; + background-color: rgba(57, 73, 84, 0.9); + border-radius: 4px 4px 0 0; + bottom: 0; + left: 0; + opacity: 0; + padding: 10px; + position: absolute; + right: 0; + text-align: left; + top: 0; + word-wrap: break-word; + z-index: 4; + min-height: 100px; + overflow: hidden; +} + + +.w-sdc-dashboard-card-schema { + margin-top: 30px; +} + +.w-sdc-dashboard-card-edit { + .hand; + position: absolute; + right: 13px; + top: 15px; + z-index: 2; +} + +.w-sdc-dashboard-card-footer { + padding: 3px 12px 10px 12px; + position: relative; +} + +.w-sdc-dashboard-card-avatar { + .uppercase; + border-radius: 50%; + display: inline-block; + position: absolute; + left: -6px; + text-align: center; + top: -6px; + + span { + + background-color: @main_color_p; + .border-radius(15px); + color: @color_c; + content: ''; + height: 30px; + text-align: center; + display: block; + border: solid 2px #ECEFF3; + padding: 3px 10px 2px 10px; + + &.VF { + .j_14_m; + &::before { + content: 'VF'; + } + } + + &.VFC { + .j_14_m; + &::before { + content: 'VFC'; + } + } + + &.CP { + .j_14_m; + &::before { + content: 'CP'; + } + } + + &.VL { + .j_14_m; + &::before { + content: 'VL'; + } + } + + &.SERVICE { + .c_14_m; + &::before { + content: 'S'; + } + } + + &.PRODUCT { + .b_14_m; + &::before { + content: 'P'; + } + } + + &.green { + .d_12; + &::before { + content: 'R'; + } + } + &.red { + .r_12; + &::before { + content: 'S'; + } + } + &.dblack { + .s_12; + &::before { + content: 'P'; + } + } + } +} + +.w-sdc-dashboard-card-info { + display: inline-block; + vertical-align: middle; + max-width: 165px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.w-sdc-dashboard-card-info-name-container{ + position: absolute; + bottom: 0; + left: 0; + margin: 0 0 2px 10px; +} +.w-sdc-dashboard-card-info-name { + .m_14_m; + display: inline-block; + vertical-align: middle; + max-width: 165px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.w-sdc-dashboard-card-info-lifecycleState { + .m_13_m; + display: inline-block; + vertical-align: middle; + max-width: 165px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.w-sdc-dashboard-card-info-user { + .n_13_r; + line-height: 18px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; +} + +.w-sdc-dashboard-card-menu-button { + display: inline-block; + padding: 12px 0 0 10px; + position: absolute; + right: 12px; + top: 8px; + border-left: solid 1px @color_k; + height: 42px; + + &:hover { + .w-sdc-dashboard-card-menu { + display: block; + } + } +} + +.w-sdc-dashboard-card-menu { + .bg_c; + border-radius: 0 0 4px 4px; + border-top: 3px solid @color_a; + box-shadow: 0 2px 2px 0px rgba(0, 0, 0, 0.2); + color: @color_s; + display: none; + min-height: 30px; + padding: 9px 0; + position: absolute; + right: -27px; + width: 208px; + z-index: 9; + max-height: 164px; + + &::before { + //TODO: Missing image for small blue triangle. + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE1OTIzNDI1MENFQjExRTU4ODRERTI1MDM2REZCOUYzIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE1OTIzNDI2MENFQjExRTU4ODRERTI1MDM2REZCOUYzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU5MjM0MjMwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTU5MjM0MjQwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4gBXTlAAAAOElEQVR42mK0rp7NgASMgZgFiE/CBJjQJPcA8U4gNkdXAJMUAGJ+ZEVMaJIwAFfEhEUSRRFAgAEAVtgJyiLAPWAAAAAASUVORK5CYII='); + content: ''; + display: block; + height: 21px; + position: absolute; + right: 24px; + top: -24px; + width: 184px; + background-repeat: no-repeat; + background-position: 175px 16px; + } +} + +.i-sdc-dashboard-card-menu-item { + .hand; + line-height: 24px; + padding: 0 10px; + &:hover { .a_7; } +} + +.w-sdc-dashboard-card-info-lifecycleState-icon{ + position:absolute; + bottom:18px; + right:10px; +} + +// Same for dashboard and catalog view. +.w-sdc-dashboard-card-schema-image { + position: absolute; + top: 41%; + + //TODO: Israel - remove this after getting the services sprite. + height: 45px; + width: 53px; + background-repeat: no-repeat; + + // Center the icon vertical and horizontal. + margin: auto; + left: 0; + right: 0; + top: -10px; + bottom: 0; +} + +/* dashboard card main icons */ +.w-sdc-dashboard-card-schema-image.service { .s-sdc-service } +.w-sdc-dashboard-card-schema-image.resource { .s-sdc-resource } + +/* dashboard card statuses icons */ +.w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN; } +.w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT; } +.w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED; } +.w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION; } +.w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS; } +.w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED; } + +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN.green; } +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT.green; } +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED.green; } +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION.green; } +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS.green; } +.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED.green; } + +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN.red; } +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT.red; } +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED.red; } +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION.red; } +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS.red; } +.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED.red; } diff --git a/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts new file mode 100644 index 0000000000..9be7786f4d --- /dev/null +++ b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts @@ -0,0 +1,112 @@ +'use strict'; +import {MenuItemGroup, MenuItem} from "app/utils"; +import {BreadcrumbsPath, BreadcrumbsMenu} from "../onboard-vendor/onboard-vendor-view-model"; +import {CacheService} from "app/services"; +import {IUserProperties} from "app/models"; + +export class TestData { + breadcrumbs:BreadcrumbsPath; +} + +export interface IDcaeAppViewModelScope extends ng.IScope { + testData:TestData; + onTestEvent:Function; + topNavMenuModel:Array; + topNavRootMenu:MenuItemGroup; + user:IUserProperties; + version:string; +} + +export class DcaeAppViewModel { + static '$inject' = [ + '$scope', + '$q', + 'Sdc.Services.CacheService' + ]; + + private firstControlledTopNavMenu:MenuItemGroup; + + constructor(private $scope:IDcaeAppViewModelScope, + private $q:ng.IQService, + private cacheService:CacheService) { + + this.$scope.testData = { + breadcrumbs: { + selectedKeys: [] + } + }; + + this.$scope.version = this.cacheService.get('version'); + + this.$scope.onTestEvent = (eventName:string, data:any):void => { + switch (eventName) { + case 'breadcrumbsupdated': + this.handleBreadcrumbsUpdate(data); + break; + } + }; + + this.$scope.topNavMenuModel = []; + + this.$scope.user = this.cacheService.get('user'); + } + + updateBreadcrumbsPath = (selectedKeys:Array):ng.IPromise => { + let topNavMenuModel = this.$scope.topNavMenuModel; + let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu); + if (startIndex === -1) { + startIndex = topNavMenuModel.length; + } + topNavMenuModel.splice(startIndex + selectedKeys.length); + this.$scope.testData = { + breadcrumbs: {selectedKeys: selectedKeys} + }; + + return this.$q.when(true); + }; + + handleBreadcrumbsUpdate(breadcrumbsMenus:Array):void { + let selectedKeys = []; + let topNavMenus = breadcrumbsMenus.map((breadcrumbMenu, breadcrumbIndex) => { + let topNavMenu = new MenuItemGroup(); + topNavMenu.menuItems = breadcrumbMenu.menuItems.map(menuItem => + new MenuItem( + menuItem.displayText, + this.updateBreadcrumbsPath, + null, + null, + [selectedKeys.concat([menuItem.key])] + ) + ); + topNavMenu.selectedIndex = _.findIndex( + breadcrumbMenu.menuItems, + menuItem => menuItem.key === breadcrumbMenu.selectedKey + ); + selectedKeys.push(breadcrumbMenu.selectedKey); + return topNavMenu; + }); + + let topNavMenuModel = this.$scope.topNavMenuModel; + let len = topNavMenuModel.length; + let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu); + if (startIndex === -1) { + startIndex = len; + } + topNavMenuModel.splice(startIndex, len - startIndex); + topNavMenuModel.push.apply(topNavMenuModel, topNavMenus); + this.firstControlledTopNavMenu = topNavMenus[0]; + + if (startIndex === 1 && this.$scope.topNavRootMenu == null) { + let topNavRootMenu = topNavMenuModel[0]; + let onboardItem = topNavRootMenu.menuItems[topNavRootMenu.selectedIndex]; + let originalCallback = onboardItem.callback; + onboardItem.callback = (...args) => { + let ret = this.updateBreadcrumbsPath([]); + return originalCallback && originalCallback.apply(undefined, args) || ret; + }; + this.$scope.topNavRootMenu = topNavRootMenu; + } + + this.updateBreadcrumbsPath(selectedKeys); + } +} diff --git a/catalog-ui/src/app/view-models/dcae-app/dcae-app-view.html b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view.html new file mode 100644 index 0000000000..af0d06787a --- /dev/null +++ b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view.html @@ -0,0 +1,17 @@ +
+ + + + +
+ + + +
+
+ + + +
diff --git a/catalog-ui/src/app/view-models/dcae-app/dcae-app.less b/catalog-ui/src/app/view-models/dcae-app/dcae-app.less new file mode 100644 index 0000000000..4a16ca2b5b --- /dev/null +++ b/catalog-ui/src/app/view-models/dcae-app/dcae-app.less @@ -0,0 +1,303 @@ +.sdc-catalog-container { + + .i-sdc-categories-list-item { + font-weight: normal; + } + + // Checkboxes + .i-sdc-designer-leftbar-section-content-ul { + padding: 0; + margin: 0; + + .i-sdc-catalog-subcategories-checkbox { + padding: 0 0 0 20px; + margin: 0; + + .i-sdc-catalog-grouping-checkbox { + padding: 0 0 0 20px; + margin: 0; + } + + } + + } + + .i-sdc-designer-leftbar-section-content-li { + &:last-child { + .i-sdc-categories-list-item { + margin: 0; + } + } + } + + .i-sdc-categories-list-item { + display: block; + //margin-bottom: 5px; + //padding-left: 15px; + //text-indent: -24px; + vertical-align: top; + font-weight: bold; + } + + .i-sdc-subcategories-list-item { + display: block; + //padding-left: 20px; + vertical-align: top; + font-weight: normal; + margin: 0; + //text-indent: -10px; + } + + /*Added by - Ikram */ + .i-sdc-product-input, + .i-sdc-product-select { + border: 1px solid @border_color_f; + min-height: 30px; + padding: 0; + width: 100%; + margin: 1px 0; + background-color: #F2F2F2; + outline: none; + + &:disabled { + .disabled; + } + optgroup{ + color: @color_u; + option{ + color: @color_b; + } + } + } + + .i-sdc-categories-list-item-icon { + display: inline-block; + float: right; + position: relative; + right: -8px; + top: 6px; + } + + .i-sdc-categories-list-item { + margin-top: 7px; + &.NOT_CERTIFIED_CHECKOUT, + &.NOT_CERTIFIED_CHECKIN { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2889px; + width: 14px; + height: 14px; + + } + } + + &.CERTIFIED { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -3034px; + width: 14px; + height: 16px; + } + } + + &.READY_FOR_CERTIFICATION { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2985px; + width: 14px; + height: 16px; + } + } + + &.CERTIFICATION_IN_PROGRESS { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2934px; + width: 14px; + height: 16px; + } + } + + &.DISTRIBUTED, + &.TBD { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -43px -3087px; + width: 24px; + height: 14px; + + } + } + } + + .i-sdc-categories-list-input { + margin: 8px; + + } + + .i-sdc-subcategories-list-input { + + margin: 8px; + } + .i-sdc-subcategories-list-input-container { + margin: 0px 0px 0px 20px; + padding: 2px; + } + + .w-sdc-header-catalog-search-container { + display: table; + padding: 21px 0; + position: relative; + + .w-sdc-designer-leftbar-search-input { + color: #000; + width: 300px; + } + + // .magnification { + // .sprite; + // .sprite.magnification-glass; + // .hand; + // position: absolute; + // top: 40px; + // right: 42px; + // } + } + + .w-sdc-catalog-main { + padding: 10px 12px; + } + .w-sdc-dashboard-catalog-header { + .b_9; + display: inline-block; + font-style: italic; + font-weight: bold; + padding-left: 10px; + } + + .w-sdc-dashboard-catalog-header-order { + .b_9; + font-weight: 800; + } + + .w-sdc-dashboard-catalog-sort { + .b_9; + font-weight: bold; + white-space:pre; + &:hover{ + .hand; + text-decoration: none; + .a_9; + } + &.blue { + .a_9; + } + } + + .w-sdc-catalog-sort-arrow{ + display: inline-block; + &.up{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid ; + } + &.down{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid; + } + } + + + + + .w-sdc-dashboard-catalog-header-right{ + float: right; + display: inline-block; + padding-right:34px; + } + + .w-sdc-header-catalog-search-input { + width: 420px; + display: table-cell; + padding: 0 25px 1px 10px; + border: 1px solid #bcbcbc; + .border-radius(10px); + height: 30px; + margin: 10px 30px; + outline: none; + } + + .sdc-catalog-type-filter-container { + margin-top: -1px; + } + + .i-sdc-designer-leftbar-section-title { + text-transform: uppercase; + .l_14_m; + line-height: 30px; + } + + .i-sdc-designer-leftbar-section-title-icon { + .hand; + .tlv-sprite; + .footer-close; + transition: .3s all; + margin-top: -4px; + } + + .i-sdc-designer-leftbar-section-title-text { + margin-left: 20px; + } + + .seperator-left, + .seperator-right { + border-right: solid 1px @color_m; + display: table-cell; + width: 2px; + } + + // Rotate catalog left side arrows + .i-sdc-designer-leftbar-section-title.expanded .i-sdc-designer-leftbar-section-title-icon { + transform: rotate(180deg); + } + + // Transform catalog left side sections + .i-sdc-designer-leftbar-section-title + .i-sdc-designer-leftbar-section-content { + max-height: 0px; + margin: 0 auto; + transition: all .3s; + overflow: hidden; + padding: 0 10px 0 18px; + } + + .i-sdc-designer-leftbar-section-title.expanded + .i-sdc-designer-leftbar-section-content { + max-height: 9999px; + margin: 0 auto 1px; + transition: all .3s; + padding: 10px 18px 10px 18px; + overflow: hidden; + } + +} + +.w-sdc-search-icon{ + position: absolute; + right: 40px; + top: 40px; + &.leftbar{ + top: 19px; + right: 18px; + } + &.magnification { + .sprite; + .sprite.magnification-glass; + .hand; + } + &.cancel { + .sprite; + .sprite.clear-text; + .hand; + } +} diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts new file mode 100644 index 0000000000..3e912706e0 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts @@ -0,0 +1,358 @@ +'use strict'; +import {ArtifactModel, Resource, Component} from "app/models"; +import {ArtifactsUtils, FormState, ValidationUtils, ArtifactType} from "app/utils"; +import {CacheService} from "app/services"; + +export interface IEditArtifactModel { + artifactResource:ArtifactModel; + artifactTypes:Array; + artifactFile:any; +} + +export interface IArtifactResourceFormViewModelScope extends ng.IScope { + forms:any; + $$childTail:any; + isNew:boolean; + isLoading:boolean; + validationPattern:RegExp; + urlValidationPattern:RegExp; + labelValidationPattern:RegExp; + integerValidationPattern:RegExp; + commentValidationPattern:RegExp; + artifactType:string; + editArtifactResourceModel:IEditArtifactModel; + defaultHeatTimeout:number; + validExtensions:any; + originalArtifactName:string; + editForm:ng.IFormController; + footerButtons:Array; + modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance; + + fileExtensions():string; + save(doNotCloseModal?:boolean):void; + saveAndAnother():void; + close():void; + getOptions():Array; + isDeploymentHeat():boolean; + onFileChange():void; + setDefaultTimeout():void; + openEditEnvParametersModal(artifact:ArtifactModel):void; + getFormTitle():string; + fileUploadRequired():string; + isArtifactOwner():boolean; +} + +export class ArtifactResourceFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'artifact', + 'Sdc.Services.CacheService', + 'ValidationPattern', + 'UrlValidationPattern', + 'LabelValidationPattern', + 'IntegerValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + '$base64', + '$state', + 'ArtifactsUtils', + '$uibModal', + 'component' + ]; + + private formState:FormState; + private entityId:string; + + constructor(private $scope:IArtifactResourceFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private artifact:ArtifactModel, + private cacheService:CacheService, + private ValidationPattern:RegExp, + private UrlValidationPattern:RegExp, + private LabelValidationPattern:RegExp, + private IntegerValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils, + private $base64:any, + private $state:any, + private artifactsUtils:ArtifactsUtils, + private $uibModal:ng.ui.bootstrap.IModalService, + private component:Component) { + + + this.entityId = this.component.uniqueId; + this.formState = angular.isDefined(artifact.artifactLabel) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initEntity = ():void => { + this.$scope.editArtifactResourceModel.artifactResource = this.artifact; + this.$scope.originalArtifactName = this.artifact.artifactName; + }; + + + private initFooterButtons = ():void => { + + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save} + ]; + if (this.$scope.isNew) { + this.$scope.footerButtons.push({ + 'name': 'Add Another', + 'css': 'grey', + 'disabled': !this.$scope.isNew && 'deployment' === this.$scope.artifactType, + 'callback': this.$scope.saveAndAnother + }); + } + }; + + private filterDeploymentArtifactTypeByResourceType = (resourceType:string):any => { + let result = {}; + _.each(this.$scope.validExtensions, function (typeSettings:any, typeName:string) { + if (!typeSettings.validForResourceTypes || typeSettings.validForResourceTypes.indexOf(resourceType) > -1) { + result[typeName] = typeSettings; + } + }); + + return result; + }; + + private initArtifactTypes = ():void => { + + let artifactTypes:any = this.cacheService.get('UIConfiguration'); + + if ('deployment' === this.$scope.artifactType) { + + + if ('HEAT_ENV' == this.artifact.artifactType || this.component.selectedInstance) { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceInstanceDeploymentArtifacts; + } else if (this.component.isResource()) { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts; + this.$scope.validExtensions = this.filterDeploymentArtifactTypeByResourceType((this.component).resourceType); + } else { + this.$scope.validExtensions = artifactTypes.artifacts.deployment.serviceDeploymentArtifacts; + } + + if (this.$scope.validExtensions) { + this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions); + } + this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout; + if (this.$scope.isNew) { + let isHeat = 'HEAT_ENV' == this.artifact.artifactType; + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return 'HEAT' == item.substring(0, 4) || (!isHeat && item == "VF_MODULES_METADATA") || + _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item); + }); + } + + } + if (this.$scope.artifactType === 'informational') { + this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> { + return element.name; + }); + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> { + return _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) || + _.has(ArtifactType.TOSCA, item); + }) + } + + if (this.component.isResource() && (this.component).isCsarComponent()) { + _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => { + return this.artifactsUtils.isLicenseType(item); + }) + } + + }; + + private initEditArtifactResourceModel = ():void => { + this.$scope.editArtifactResourceModel = { + artifactResource: null, + artifactTypes: null, + artifactFile: {} + }; + + this.initEntity(); + }; + + private initScope = ():void => { + + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.urlValidationPattern = this.UrlValidationPattern; + this.$scope.labelValidationPattern = this.LabelValidationPattern; + this.$scope.integerValidationPattern = this.IntegerValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + this.$scope.isLoading = false; + this.$scope.isNew = (this.formState === FormState.CREATE); + this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name); + this.$scope.modalInstanceArtifact = this.$uibModalInstance; + + this.initEditArtifactResourceModel(); + this.initArtifactTypes(); + + // In case of edit, show the file name in browse. + if (this.artifact.artifactName !== "" && 'HEAT_ENV' !== this.artifact.artifactType) { + this.$scope.editArtifactResourceModel.artifactFile = {}; + this.$scope.editArtifactResourceModel.artifactFile.filename = this.artifact.artifactName; + } + + //scope methods + this.$scope.isDeploymentHeat = ():boolean => { + return !this.$scope.isNew && this.$scope.artifactType === 'deployment' + && this.$scope.editArtifactResourceModel.artifactResource.isHEAT(); + + }; + this.$scope.onFileChange = ():void => { + if (this.$scope.editArtifactResourceModel.artifactFile && this.$scope.editArtifactResourceModel.artifactFile.filename) { + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename; + } else { + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.originalArtifactName; + } + }; + this.$scope.setDefaultTimeout = ():void => { + if (this.$scope.isDeploymentHeat() && !this.$scope.editArtifactResourceModel.artifactResource.timeout) { + this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout; + } + + if (this.$scope.editArtifactResourceModel.artifactFile.filename) { + this.$scope.editArtifactResourceModel.artifactFile = {}; + this.$scope.forms.editForm.myArtifactFile.$setValidity('required', false); + } + }; + + this.$scope.fileExtensions = ():string => { + let type:string = this.$scope.editArtifactResourceModel.artifactResource.artifactType; + return type && this.$scope.validExtensions && this.$scope.validExtensions[type].acceptedTypes ? + this.$scope.validExtensions[type].acceptedTypes.join(',') : ""; + }; + + this.$scope.save = (doNotCloseModal?:boolean):void => { + this.$scope.isLoading = true; + this.$scope.editArtifactResourceModel.artifactResource.description = this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description); + + if (!this.$scope.isDeploymentHeat()) { + this.$scope.editArtifactResourceModel.artifactResource.timeout = null; + } + + if (this.$scope.editArtifactResourceModel.artifactFile) { + this.$scope.editArtifactResourceModel.artifactResource.payloadData = this.$scope.editArtifactResourceModel.artifactFile.base64; + this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename; + } + + let onFaild = (response):void => { + this.$scope.isLoading = false; + console.info('onFaild', response); + }; + + let onSuccess = (artifactResource:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$scope.originalArtifactName = ""; + + if (this.$scope.isDeploymentHeat()) { + if (artifactResource.heatParameters) { + this.$scope.openEditEnvParametersModal(artifactResource); + } + } + + if (!doNotCloseModal) { + this.$uibModalInstance.close(); + } else { + this.$scope.editArtifactResourceModel.artifactFile = null; + angular.element("input[type='file']").val(null); // for support chrome when upload the same file + this.artifactsUtils.addAnotherAfterSave(this.$scope); + } + + }; + + if ('HEAT_ENV' == this.artifact.artifactType) { + if (this.component.selectedInstance) { + this.component.uploadInstanceEnvFile(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } else { + this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + + } + } else if (this.$scope.isArtifactOwner()) { + this.component.addOrUpdateInstanceArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } else { + this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild); + } + }; + + this.$scope.isArtifactOwner = ():boolean=> { + return this.component.isService() && !!this.component.selectedInstance; + }; + + this.$scope.saveAndAnother = ():void => { + this.$scope.save(true); + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.fileUploadRequired = ():string => { + if (this.$scope.editArtifactResourceModel.artifactFile.filename) { + // This is edit mode + return 'false'; + } else { + return 'true'; + } + }; + + this.$scope.getFormTitle = ():string => { + if ('HEAT_ENV' == this.artifact.artifactType) { + return 'Update HEAT ENV'; + } + if (this.$scope.isDeploymentHeat()) { + if (!this.$scope.editArtifactResourceModel.artifactResource.artifactChecksum) { + return 'Add HEAT Template'; + } + return 'Update HEAT Template'; + } + if (this.$scope.isNew) { + return 'Add Artifact'; + } + return 'Update Artifact'; + }; + + this.$scope.openEditEnvParametersModal = (artifactResource:ArtifactModel):void => { + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../env-parameters-form/env-parameters-form.html', + controller: 'Sdc.ViewModels.EnvParametersFormViewModel', + size: 'sdc-md', + backdrop: 'static', + resolve: { + artifact: ():ArtifactModel => { + return artifactResource; + }, + component: ():Component => { + return this.component; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + modalInstance + .result + .then(():void => { + }); + }; + + this.$scope.forms = {}; + + this.initFooterButtons(); + + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + if(this.$scope.forms.editForm) { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + if (this.$scope.isNew) { + this.$scope.footerButtons[1].disabled = this.$scope.forms.editForm.$invalid; + } + } + }); + + } +} diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html new file mode 100644 index 0000000000..0984c6872d --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html @@ -0,0 +1,169 @@ + + + + +
+
+ + +
+ + + +
+ + + + +
+
+ + +
+ +
+ +
+ + + +
+ + + +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ + + +
+ +
+ +
+ +
+ +
+ + + +
+ + + +
+ +
+ +
+ +
+ +
+ + + +
+ + + +
+ +
+ UUID +
+ Version +
+
+ +
+ +
+ + + +
+
+
+ diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less new file mode 100644 index 0000000000..1f77958c88 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less @@ -0,0 +1,44 @@ +.sdc-edit-artifact-form-container { + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .i-sdc-form-textarea{ + min-height: 95px; + } + + .i-sdc-form-url { + padding-bottom: 0px; + } + + &.mandatory-artifact { + .w-sdc-form-column { + width: 100%; + padding: 0; + min-height: initial; + } + } + .w-sdc-form .i-sdc-form-item.upload input[type="file"] { + display: none + } + + .artifact-info { + text-align: left; + color: rgb(140, 140, 140); + font-size: 13px; + margin-top: -10px; + margin-bottom: 5px; + width: 100%; + min-height: initial; + + span { + color: #666666; + padding-left: 4px; + } + } + +} diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html new file mode 100644 index 0000000000..90b8f67df4 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html @@ -0,0 +1,152 @@ + + +
+
+ +
+ +
+ + +
+ + +
+ + + + +
+
+ + +
+ + +
+ + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+ + +
+ + + +
+ +
+
+ + +
+ + + +
+ + + + +
+
+ + +
+ + +
+
+ +
+ +
+
+ +
diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts new file mode 100644 index 0000000000..122cf10ed2 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts @@ -0,0 +1,241 @@ +'use strict'; +import {AttributeModel, Component} from "app/models"; +import {IMapRegex, ValidationUtils, FormState, PROPERTY_TYPES} from "app/utils"; + +export interface IEditAttributeModel { + attribute:AttributeModel; + types:Array; + simpleTypes:Array; +} + +export class attributeValue {//in order to solve DE226783, we update the value on another obj + value:string; +} + +interface IAttributeFormViewModelScope extends ng.IScope { + $$childTail:any; + forms:any; + editForm:ng.IFormController; + footerButtons:Array; + isService:boolean; + editAttributeModel:IEditAttributeModel; + modalInstanceAttribute:ng.ui.bootstrap.IModalServiceInstance; + isNew:boolean; + listRegex:IMapRegex; + mapRegex:IMapRegex; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + isLoading:boolean; + validationPattern:RegExp; + attributeValue:attributeValue; + + save():void; + close():void; + onTypeChange():void; + onValueChange():void; + isAttributeValueOwner():boolean; + validateIntRange(value:string):boolean; + validateUniqueKeys(viewValue:string):boolean; + getValidationTranslate():string; + showSchema():boolean; + isSchemaEditable():boolean; + validateName():void; +} + +export class AttributeFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'attribute', + 'ValidationUtils', + 'CommentValidationPattern', + 'PropertyNameValidationPattern', + 'component' + ]; + + private formState:FormState; + + + constructor(private $scope:IAttributeFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private attribute:AttributeModel, + private ValidationUtils:ValidationUtils, + private CommentValidationPattern:RegExp, + private PropertyNameValidationPattern:RegExp, + private component:Component) { + this.formState = angular.isDefined(attribute.name) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initResource = ():void => { + this.$scope.editAttributeModel.attribute = new AttributeModel(this.attribute); + if (this.$scope.editAttributeModel.types.indexOf(this.attribute.type) === -1) {//attribute defaulte type is string too? + this.attribute.type = "string"; + } + }; + + private initEditAttributeModel = ():void => { + this.$scope.editAttributeModel = { + attribute: null, + types: ['integer', 'string', 'float', 'boolean', 'list', 'map'], + simpleTypes: ['integer', 'string', 'float', 'boolean'] + }; + + this.initResource(); + }; + + private initScope = ():void => { + + //scope attributes + this.$scope.forms = {}; + this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + + this.$scope.modalInstanceAttribute = this.$uibModalInstance; + this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns(); + this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns(); + + this.$scope.isNew = (this.formState === FormState.CREATE); + this.$scope.isLoading = false; + this.$scope.attributeValue = new attributeValue(); + + this.initEditAttributeModel(); + this.setValidationPattern(); + + //scope methods + this.$scope.save = ():void => { + if (!this.$scope.forms.editForm.$invalid) { + let attribute:AttributeModel = this.$scope.editAttributeModel.attribute; + this.$scope.editAttributeModel.attribute.description = this.ValidationUtils.stripAndSanitize(this.$scope.editAttributeModel.attribute.description); + ////if read only - just closes the modal + if (this.$scope.editAttributeModel.attribute.readonly && !this.$scope.isAttributeValueOwner()) { + this.$uibModalInstance.close(); + return; + } + this.$scope.isLoading = true; + let onAttributeFaild = (response):void => { + console.info('onFaild', response); + this.$scope.isLoading = false; + }; + + let onAttributeSuccess = (attributeFromBE:AttributeModel):void => { + console.info('onAttributeResourceSuccess : ', attributeFromBE); + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + }; + + //in case we have uniqueId we call update method + if (this.$scope.isAttributeValueOwner()) { + attribute.value = this.$scope.attributeValue.value; + this.component.updateInstanceAttribute(attribute).then(onAttributeSuccess, onAttributeFaild); + } else { + attribute.defaultValue = this.$scope.attributeValue.value; + this.component.addOrUpdateAttribute(attribute).then(onAttributeSuccess, onAttributeFaild); + } + } + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.validateName = ():void => { + let existsAttr:AttributeModel = _.find(this.component.attributes, (attribute:AttributeModel) => { + return attribute.name === this.$scope.editAttributeModel.attribute.name; + }); + if (existsAttr) { + this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', false); + } else { + this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', true); + } + + }; + + this.$scope.onTypeChange = ():void => { + this.$scope.editAttributeModel.attribute.value = ''; + this.$scope.editAttributeModel.attribute.defaultValue = ''; + this.setValidationPattern(); + }; + + this.$scope.isAttributeValueOwner = ():boolean=> { + return this.component.isService() || !!this.component.selectedInstance; + }; + + this.$scope.onValueChange = ():void => { + if (!this.$scope.editAttributeModel.attribute.value) { + if (this.$scope.isAttributeValueOwner()) { + this.$scope.editAttributeModel.attribute.value = this.$scope.editAttributeModel.attribute.defaultValue; + } + } + }; + + + this.$scope.validateUniqueKeys = (viewValue:string):boolean => { + if (this.$scope.editAttributeModel.attribute.type === 'map') { + return this.ValidationUtils.validateUniqueKeys(viewValue); + } + else { + return true; //always valid if not a map + } + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.isSchemaEditable = ():boolean => { + let schemaType = this.$scope.editAttributeModel.attribute.schema.property.type; + return this.$scope.editAttributeModel.simpleTypes.indexOf(schemaType) > -1 || !schemaType; + }; + + this.$scope.showSchema = ():boolean => { + return ['list', 'map'].indexOf(this.$scope.editAttributeModel.attribute.type) > -1; + }; + + this.$scope.getValidationTranslate = ():string => { + let result = "ATTRIBUTE_EDIT_PATTERN"; + if (this.$scope.showSchema()) { + + result = "ATTRIBUTE_EDIT_" + this.$scope.editAttributeModel.attribute.type.toUpperCase(); + + if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.STRING) { + result += "_STRING"; + } else if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.BOOLEAN) { + result += "_BOOLEAN"; + } else { + result += "_GENERIC"; + } + } + + return result; + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + this.$scope.attributeValue.value = this.$scope.isAttributeValueOwner() ? this.$scope.editAttributeModel.attribute.value : this.$scope.editAttributeModel.attribute.defaultValue; + }; + + + private setValidationPattern = ():void => { + + if (this.$scope.editAttributeModel.attribute.type === 'list') { + this.$scope.validationPattern = this.$scope.listRegex[this.$scope.editAttributeModel.attribute.schema.property.type]; + } + else if (this.$scope.editAttributeModel.attribute.type === 'map') { + this.$scope.validationPattern = this.$scope.mapRegex[this.$scope.editAttributeModel.attribute.schema.property.type]; + } + else { + this.$scope.validationPattern = this.ValidationUtils.getValidationPattern(this.$scope.editAttributeModel.attribute.type); + } + + }; +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html new file mode 100644 index 0000000000..ae13844532 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html @@ -0,0 +1,92 @@ + +
+ +
+
+
+
+ + {{header.title}} +
+
+
+ + +
+
+ {{parameter.name}} + +
+ +
+ {{parameter.defaultValue}} +
+ + + + + +
+ +
+ * +
+ + +
+
+
+
+
+
+
+
+ + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less new file mode 100644 index 0000000000..a25a2c5f62 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less @@ -0,0 +1,178 @@ + +.sdc-env-form-container{ + .w-sdc-modal-body{ + padding: 20px 10px 2px 10px; + } + .w-sdc-modal-body-content{ + .b_6; + display: block; + } + + .env-file-generation-label{ + .p_9; + .bold; + margin-bottom: 20px; + } +} + +.w-sdc-env-form-container { + height: 650px; + + .w-sdc-env-search { + padding: 10px 20px 20px 0; + white-space: nowrap; + position: relative; + width: 60%; + height: 64px; + + .env-search-icon { + top: 9px; + right: 11px; + } + + .magnification-white { + .sprite-new; + .search-white-icon; + .hand; + } + + .search-icon-container { + width: 35px; + height: 30px; + background-color: @main_color_a; + white-space: nowrap; + float: right; + position: relative; + bottom: 31px; + right: 1px; + border-radius: 0px 4px 4px 0px; + .hand + } + + .w-sdc-env-search-input { + border: 1px solid @color_e; + .border-radius(4px); + height: 32px; + margin: 0; + padding: 0px 28px 3px 10px; + vertical-align: 4px; + width: 100%; + outline: none; + font-style: italic; + } + } + + .table-container-flex { + height: 570px; + + .table { + height: 100%; + .flex-item:nth-child(1) { + flex-grow: 20; + .show-desc{ + float: right; + top: 10px; + position: relative; + } + } + + .flex-item:nth-child(2) { + flex-grow: 10; + } + + .flex-item:nth-child(3) { + flex-grow: 10; + } + .scrollbar-container{ + max-height: 527px; + } + .left-column-container{ + .required-symbol { + .m_14_m; + color: #f33; + display: none; + position: relative; + left: -4px; + top: 3px; + } + + .i-sdc-form-item{ + border-right: none; + margin: 0px; + + .input-parameter{ + border: none; + height: 30px; + width: 254px; + float: right; + input{ + .m_13_m; + width: 100%; + display: inline-flex; + padding-right: 33px; + } + .action-button{ + border-left: solid 1px @main_color_o; + position: relative; + height: 20px; + width: 25px; + top: -25px; + left: 228px; + padding-left: 6px; + background-color: @main_color_p; + div:not(.disable){ + .hand; + } + } + } + + &.required{ + .required-symbol { + display: inline-flex; + } + .input-parameter { + width: 250px; + } + .action-button{ + left: 224px; + } + } + } + + + + } + } + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + } + + + .parameter-description{ + background-color: @func_color_r; + border-left: 4px solid @main_color_a; + padding: 10px 30px; + } +} + +.header-info{ + float: right; +} + +.parameter-description-popover{ + z-index: 1100; + min-width: 210px; + .arrow{ + left: 20px !important; + border-width: 7px; + bottom: -8px !important; + } + .popover-content{ + .f-type._13_m;; + } +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts new file mode 100644 index 0000000000..476af4ada9 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts @@ -0,0 +1,153 @@ +'use strict'; +import {ValidationUtils} from "app/utils"; +import {ArtifactModel, HeatParameterModel, Component} from "app/models"; + +export interface IEnvParametersFormViewModelScope extends ng.IScope { + isLoading:boolean; + type:string; + heatParameters:Array; + forms:any; + artifactResource:ArtifactModel; + buttons:Array; + envParametersModal:ng.ui.bootstrap.IModalServiceInstance; + tableHeadersList:Array; + selectedParameter:HeatParameterModel; + templatePopover:string; + + getValidationPattern(type:string):RegExp; + isInstance():boolean; + validateJson(json:string):boolean; + close():void; + save():void; + openDescPopover(selectedParam:HeatParameterModel):void; + closeDescriptionPopover():void; +} + +export class EnvParametersFormViewModel { + + static '$inject' = [ + '$scope', + '$templateCache', + '$state', + '$uibModalInstance', + 'artifact', + 'ValidationUtils', + 'component' + ]; + + constructor(private $scope:IEnvParametersFormViewModelScope, + private $templateCache:ng.ITemplateCacheService, + private $state:any, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private artifact:ArtifactModel, + private ValidationUtils:ValidationUtils, + private component:Component) { + + + this.initScope(); + } + + private updateInstanceHeat = ():void => { + let success = (responseArtifact:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + }; + + let error = ():void => { + this.$scope.isLoading = false; + console.info('Failed to load save artifact'); + }; + + this.component.addOrUpdateInstanceArtifact(this.$scope.artifactResource).then(success, error); + }; + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.envParametersModal = this.$uibModalInstance; + this.$scope.artifactResource = this.artifact; + this.$scope.heatParameters = angular.copy(this.artifact.heatParameters); + + this.$scope.tableHeadersList = [ + {title: "Parameter", property: "name"}, + {title: "Default Value", property: "defaultValue", info: "DEFAULT_VALUE_INFO"}, + {title: "Current Value", property: "currentValue", info: "CURRENT_VALUE_INFO"} + ]; + + this.$templateCache.put("env-parametr-description-popover.html", require('app/view-models/forms/env-parameters-form/env-parametr-description-popover.html')); + this.$scope.templatePopover = "env-parametr-description-popover.html"; + + this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => { + return this.ValidationUtils.getValidationPattern(validationType, parameterType); + }; + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.ValidationUtils.validateJson(json); + }; + + this.$scope.isInstance = ():boolean => { + return !!this.component.selectedInstance; + }; + + this.$scope.save = ():void => { + this.$scope.buttons[0].disabled = true;//prevent double click (DE246266) + this.$scope.isLoading = true; + this.artifact.heatParameters = this.$scope.heatParameters; + this.artifact.heatParameters.forEach((parameter:any):void => { + /* if ("" === parameter.currentValue) { + parameter.currentValue = null; + }else */ + if (!parameter.currentValue && parameter.defaultValue) { + parameter.currentValue = parameter.defaultValue; + } + }); + + if (this.$scope.isInstance()) { + this.updateInstanceHeat(); + return; + } + + let success = (responseArtifact:ArtifactModel):void => { + this.$scope.isLoading = false; + this.$uibModalInstance.close(); + + }; + + let error = ():void => { + this.$scope.isLoading = false; + console.info('Failed to load save artifact'); + }; + + this.component.addOrUpdateArtifact(this.$scope.artifactResource).then(success, error); + }; + + this.$scope.close = ():void => { + //this.artifact.heatParameters.forEach((parameter:any):void => { + // if (!parameter.currentValue && parameter.defaultValue) { + // parameter.currentValue = parameter.defaultValue; + // } + //}); + this.$uibModalInstance.dismiss(); + }; + + this.$scope.openDescPopover = (selectedParam:HeatParameterModel):void => { + this.$scope.selectedParameter = selectedParam; + }; + + this.$scope.closeDescriptionPopover = ():void => { + this.$scope.selectedParameter = null; + }; + + this.$scope.buttons = [ + {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.buttons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + }; +} diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html new file mode 100644 index 0000000000..ed127c6bfb --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html @@ -0,0 +1,4 @@ +
+ + {{selectedParameter.description}} +
diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts new file mode 100644 index 0000000000..e87e5c6c7d --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts @@ -0,0 +1,126 @@ +'use strict'; +import {FormState, PROPERTY_TYPES, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS} from "app/utils"; +import {InputModel} from "app/models"; + +export interface IInputEditModel { + editInput:InputModel; +} + +export interface IInputFormViewModelScope extends ng.IScope { + forms:any; + editForm:ng.IFormController; + footerButtons:Array; + isService:boolean; + modalInstanceInput:ng.ui.bootstrap.IModalServiceInstance; + isLoading:boolean; + inputEditModel:IInputEditModel; + myValue:any; + maxLength:number; + + save():void; + close():void; + validateIntRange(value:string):boolean; + validateJson(json:string):boolean; + getValidationPattern(type:string):RegExp; + showSchema():boolean; +} + +export class InputFormViewModel { + + static '$inject' = [ + '$scope', + '$uibModalInstance', + 'ValidationUtils', + 'input' + ]; + + private formState:FormState; + + + constructor(private $scope:IInputFormViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private ValidationUtils:ValidationUtils, + private input:InputModel) { + this.initScope(); + this.initMyValue(); + } + + private initMyValue = ():void => { + switch (this.$scope.inputEditModel.editInput.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : []; + break; + } + }; + + private initDefaultValueMaxLength = ():void => { + switch (this.$scope.inputEditModel.editInput.type) { + case PROPERTY_TYPES.MAP: + case PROPERTY_TYPES.LIST: + this.$scope.maxLength = this.$scope.inputEditModel.editInput.schema.property.type == PROPERTY_TYPES.JSON ? + PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH : + PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + break; + case PROPERTY_TYPES.JSON: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH; + break; + default: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + } + }; + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.modalInstanceInput = this.$uibModalInstance; + this.$scope.inputEditModel = { + editInput: null + }; + this.$scope.inputEditModel.editInput = this.input; + this.initDefaultValueMaxLength(); + + //scope methods + this.$scope.save = ():void => { + if (this.$scope.showSchema()) { + this.$scope.inputEditModel.editInput.defaultValue = JSON.stringify(this.$scope.myValue); + } + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.ValidationUtils.validateJson(json); + }; + + this.$scope.showSchema = ():boolean => { + return ['list', 'map'].indexOf(this.$scope.inputEditModel.editInput.type) > -1; + }; + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.ValidationUtils.getValidationPattern(type); + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + }; +} + diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html new file mode 100644 index 0000000000..1bf6dc4ca9 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html @@ -0,0 +1,125 @@ + + +
+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+
+ +
+
+
+ + + +
+ + +
+
+
+
+
+ +
+ +
+ +
+
+ +
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts new file mode 100644 index 0000000000..1ba5a90bb4 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts @@ -0,0 +1,204 @@ +/** + * Created by obarda on 1/19/2017. + */ +'use strict'; +import {DataTypesService} from "app/services/data-types-service"; +import {PropertyModel, DataTypesMap, Component} from "app/models"; +import {ValidationUtils, PROPERTY_DATA} from "app/utils"; + +export interface IPropertyFormBaseViewScope extends ng.IScope { + + forms:any; + editForm:ng.IFormController; + + property:PropertyModel; + types:Array; + nonPrimitiveTypes:Array; + simpleTypes:Array; + + footerButtons:Array; + modalPropertyFormBase:ng.ui.bootstrap.IModalServiceInstance; + currentPropertyIndex:number; + isLastProperty:boolean; + innerViewSrcUrl:string; + + //Disabling filed - each child controller can change this when needed + isNew:boolean; + isTypeSelectorDisable:boolean; + isDeleteDisable:boolean; + isNameDisable:boolean; + isDescriptionDisable:boolean; + isPropertyValueDisable:boolean; + isArrowsDisabled:boolean; + + //Validation pattern + validationPattern:RegExp; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + numberValidationPattern:RegExp; + + dataTypes:DataTypesMap; + + isLoading:boolean; + + save():void; + close():void; + getNext():void; + getPrev():void; + getValidationPattern(type:string):RegExp; +} + +export abstract class PropertyFormBaseView { + + + constructor(protected $scope:IPropertyFormBaseViewScope, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + protected filteredProperties:Array, + protected DataTypesService:DataTypesService) { + + this.initScope(); + + } + + protected validationUtils:ValidationUtils; + + protected isPropertyValueOwner = ():boolean => { + return this.component.isService() || !!this.component.selectedInstance; + }; + + private isDisable = ():boolean => { + return this.isPropertyValueOwner() || this.$scope.property.readonly; + }; + + + //This is the difault state, Childs screens can change if needed + protected initButtonsState = ():void => { + let isDisable = this.isDisable(); + + this.$scope.isArrowsDisabled = false; + this.$scope.isDeleteDisable = isDisable; + this.$scope.isDescriptionDisable = isDisable; + this.$scope.isNameDisable = isDisable; + this.$scope.isTypeSelectorDisable = isDisable; + this.$scope.isPropertyValueDisable = this.$scope.property.readonly && !this.isPropertyValueOwner(); + }; + + protected initValidations = ():void => { + + this.$scope.validationPattern = this.$injector.get('ValidationPattern'); + this.$scope.propertyNameValidationPattern = this.$injector.get('PropertyNameValidationPattern'); + this.$scope.commentValidationPattern = this.$injector.get('CommentValidationPattern'); + this.$scope.numberValidationPattern = this.$injector.get('NumberValidationPattern'); + this.validationUtils = this.$injector.get('ValidationUtils'); + }; + + //Functions implemented on child's scope if needed + abstract save(doNotCloseModal?:boolean):ng.IPromise; + + protected onPropertyChange():void { + }; + + private updatePropertyByIndex = (index:number):void => { + this.$scope.property = new PropertyModel(this.filteredProperties[index]); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + this.onPropertyChange(); + }; + + private initScope = ():void => { + + this.$scope.forms = {}; + this.$scope.isLoading = false; + this.$scope.property = new PropertyModel(this.originalProperty); //we create a new Object so if user press cance we won't update the property + this.$scope.types = PROPERTY_DATA.TYPES; //All types - simple type + map + list + this.$scope.simpleTypes = PROPERTY_DATA.SIMPLE_TYPES; //All simple types + this.$scope.dataTypes = this.DataTypesService.getAllDataTypes(); //Get all data types in service + this.$scope.modalPropertyFormBase = this.$uibModalInstance; + this.$scope.isNew = !angular.isDefined(this.$scope.property.name); + + this.initValidations(); + this.initButtonsState(); + this.filteredProperties = _.sortBy(this.filteredProperties, 'name'); + this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, propety => propety.uniqueId == this.$scope.property.uniqueId); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + + this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> { + return this.$scope.types.indexOf(type) == -1; + }); + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.save = ():void => { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + this.$scope.isLoading = true; + this.save(true).then(onSuccess, onFailed); // Child controller implement save logic + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + + this.$scope.getPrev = ():void=> { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.updatePropertyByIndex(--this.$scope.currentPropertyIndex); + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + if (!this.$scope.property.readonly) { + this.$scope.isLoading = true; + this.save(false).then(onSuccess, onFailed); + + } else { + this.updatePropertyByIndex(--this.$scope.currentPropertyIndex); + } + + }; + + this.$scope.getNext = ():void=> { + + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.updatePropertyByIndex(++this.$scope.currentPropertyIndex); + }; + let onFailed = ():void => { + this.$scope.isLoading = false; + }; + + if (!this.$scope.property.readonly) { + this.$scope.isLoading = true; + this.save(false).then(onSuccess, onFailed); + } else { + this.updatePropertyByIndex(++this.$scope.currentPropertyIndex); + } + + }; + + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.validationUtils.getValidationPattern(type); + }; + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html new file mode 100644 index 0000000000..7cb05bf4ca --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html @@ -0,0 +1,132 @@ + + +
+
+ Delete + + Previous + Next +
+
+ +
+ +
+ +
+ +
+ + +
+ + + +
+ + + +
+
+
+ +
+
+
+ +
+ + + +
+ +
+
+
+
+ +
+ + + +
+ +
+
+
+
+ + +
+ +
+ Should be constraints by type(TBD) +
+
+ +
+ +
+ +
+ + + +
+ + + +
+
+ + + +
+
+
+ +
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less new file mode 100644 index 0000000000..15e30af4ee --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less @@ -0,0 +1,63 @@ +.sdc-edit-property-container { + .scrollbar-container{ + height: 415px; + width: 830px; + .perfect-scrollbar; + } + + form{ + width: 813px; + [name="description"]{ + min-height:50px; + } + } + + .sdc-modal-top-bar{ + height: 40px; + .sdc-modal-top-bar-buttons { + float: right; + + > span:not(.delimiter){ + vertical-align: middle; + .hand; + + &.sprite-new { + text-indent: 100%; + } + &.disabled, &:hover.disabled { + pointer-events: none; + } + } + + .delete-btn{ + margin-right: 6px; + } + + .left-arrow{ + margin-right: 8px; + } + + .delimiter { + height: 20px; + width: 1px; + background-color: #959595; + display: inline-block; + vertical-align: middle; + margin-right: 10px; + } + } + } + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .default-value-section{ + border-top: solid 1px @main_color_a; + padding-top: 15px; + margin-top: 15px; + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts new file mode 100644 index 0000000000..b3557b055f --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts @@ -0,0 +1,322 @@ +'use strict'; +import { + PROPERTY_TYPES, ModalsHandler, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA} from "app/utils"; +import {DataTypesService} from "app/services"; +import {PropertyModel, DataTypesMap, Component} from "app/models"; + +export interface IEditPropertyModel { + property:PropertyModel; + types:Array; + simpleTypes:Array; +} + +interface IPropertyFormViewModelScope extends ng.IScope { + forms:any; + editForm:ng.IFormController; + footerButtons:Array; + isNew:boolean; + isLoading:boolean; + isService:boolean; + validationPattern:RegExp; + propertyNameValidationPattern:RegExp; + commentValidationPattern:RegExp; + editPropertyModel:IEditPropertyModel; + modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance; + currentPropertyIndex:number; + isLastProperty:boolean; + myValue:any; + nonPrimitiveTypes:Array; + dataTypes:DataTypesMap; + isTypeDataType:boolean; + maxLength:number; + isPropertyValueOwner:boolean; + + validateJson(json:string):boolean; + save(doNotCloseModal?:boolean):void; + getValidationPattern(type:string):RegExp; + validateIntRange(value:string):boolean; + close():void; + onValueChange():void; + onSchemaTypeChange():void; + onTypeChange(resetSchema:boolean):void; + showSchema():boolean; + delete(property:PropertyModel):void; + getPrev():void; + getNext():void; + isSimpleType(typeName:string):boolean; + getDefaultValue():any; +} + +export class PropertyFormViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.DataTypesService', + '$uibModalInstance', + 'property', + 'ValidationPattern', + 'PropertyNameValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'component', + '$filter', + 'ModalsHandler', + 'filteredProperties', + '$timeout', + 'isPropertyValueOwner' + ]; + + private formState:FormState; + + constructor(private $scope:IPropertyFormViewModelScope, + private DataTypesService:DataTypesService, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private property:PropertyModel, + private ValidationPattern:RegExp, + private PropertyNameValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils, + private component:Component, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private filteredProperties:Array, + private $timeout:ng.ITimeoutService, + private isPropertyValueOwner:boolean) { + + this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE; + this.initScope(); + } + + private initResource = ():void => { + this.$scope.editPropertyModel.property = new PropertyModel(this.property); + this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null; + this.setMaxLength(); + this.initAddOnLabels(); + }; + + //init property add-ons labels that show up at the left side of the input. + private initAddOnLabels = () => { + if (this.$scope.editPropertyModel.property.name == 'network_role' && this.$scope.isService) { + //the server sends back the normalized name. Remove it (to prevent interference with validation) and set the addon label to the component name directly. + //Note: this cant be done in properties.ts because we dont have access to the component + if (this.$scope.editPropertyModel.property.value) { + let splitProp = this.$scope.editPropertyModel.property.value.split(new RegExp(this.component.normalizedName + '.', "gi")); + this.$scope.editPropertyModel.property.value = splitProp.pop(); + } + this.$scope.editPropertyModel.property.addOn = this.component.name; + } + } + + private initEditPropertyModel = ():void => { + this.$scope.editPropertyModel = { + property: null, + types: PROPERTY_DATA.TYPES, + simpleTypes: PROPERTY_DATA.SIMPLE_TYPES + }; + + this.initResource(); + }; + + private initForNotSimpleType = ():void => { + let property = this.$scope.editPropertyModel.property; + this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property); + if (property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type) == -1) { + if (!(property.value || property.defaultValue)) { + switch (property.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = []; + break; + default: + this.$scope.myValue = {}; + } + } else { + this.$scope.myValue = JSON.parse(property.value || property.defaultValue); + } + } + }; + + private setMaxLength = ():void => { + switch (this.$scope.editPropertyModel.property.type) { + case PROPERTY_TYPES.MAP: + case PROPERTY_TYPES.LIST: + this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ? + PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH : + PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + break; + case PROPERTY_TYPES.JSON: + this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH; + break; + default: + this.$scope.maxLength =PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH; + } + }; + + + private initScope = ():void => { + + //scope properties + this.$scope.forms = {}; + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + this.$scope.isLoading = false; + this.$scope.isNew = (this.formState === FormState.CREATE); + this.$scope.isService = this.component.isService(); + this.$scope.modalInstanceProperty = this.$uibModalInstance; + this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + this.$scope.dataTypes = this.DataTypesService.getAllDataTypes(); + this.$scope.isPropertyValueOwner = this.isPropertyValueOwner; + this.initEditPropertyModel(); + + this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> { + return this.$scope.editPropertyModel.types.indexOf(type) == -1; + }); + this.initForNotSimpleType(); + + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.ValidationUtils.validateJson(json); + }; + + + //scope methods + this.$scope.save = (doNotCloseModal?:boolean):void => { + let property:PropertyModel = this.$scope.editPropertyModel.property; + this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description); + //if read only - or no changes made - just closes the modal + //need to check for property.value changes manually to detect if map properties deleted + if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner) + || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) { + this.$uibModalInstance.close(); + return; + } + + this.$scope.isLoading = true; + + let onPropertyFaild = (response):void => { + console.info('onFaild', response); + this.$scope.isLoading = false; + }; + + let onPropertySuccess = (propertyFromBE:PropertyModel):void => { + console.info('onPropertyResourceSuccess : ', propertyFromBE); + this.$scope.isLoading = false; + + if (!doNotCloseModal) { + this.$uibModalInstance.close(); + } else { + this.$scope.forms.editForm.$setPristine(); + this.$scope.editPropertyModel.property = new PropertyModel(); + } + }; + + //in case we have uniqueId we call update method + if (this.$scope.isPropertyValueOwner) { + if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) { + let myValueString:string = JSON.stringify(this.$scope.myValue); + property.value = myValueString; + } + this.component.updateInstanceProperty(property).then(onPropertySuccess, onPropertyFaild); + } else { + if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) { + let myValueString:string = JSON.stringify(this.$scope.myValue); + property.defaultValue = myValueString; + } else { + this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value; + } + this.component.addOrUpdateProperty(property).then(onPropertySuccess, onPropertyFaild); + } + }; + + this.$scope.getPrev = ():void=> { + this.property = this.filteredProperties[--this.$scope.currentPropertyIndex]; + this.initResource(); + this.initForNotSimpleType(); + this.$scope.isLastProperty = false; + }; + + this.$scope.getNext = ():void=> { + this.property = this.filteredProperties[++this.$scope.currentPropertyIndex]; + this.initResource(); + this.initForNotSimpleType(); + this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1); + }; + + this.$scope.isSimpleType = (typeName:string):boolean=> { + return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1; + }; + + this.$scope.showSchema = ():boolean => { + return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1; + }; + + this.$scope.getValidationPattern = (type:string):RegExp => { + return this.ValidationUtils.getValidationPattern(type); + }; + + this.$scope.validateIntRange = (value:string):boolean => { + return !value || this.ValidationUtils.validateIntRange(value); + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + // put default value when instance value is empty + this.$scope.onValueChange = ():void => { + if (!this.$scope.editPropertyModel.property.value) { + if (this.$scope.isPropertyValueOwner) { + this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue; + } + } + }; + + // Add the done button at the footer. + this.$scope.footerButtons = [ + {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid; + }); + + this.$scope.getDefaultValue = ():any => { + return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null; + }; + + this.$scope.onTypeChange = ():void => { + this.$scope.editPropertyModel.property.value = ''; + this.$scope.editPropertyModel.property.defaultValue = ''; + this.setMaxLength(); + this.initForNotSimpleType(); + }; + + this.$scope.onSchemaTypeChange = ():void => { + if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) { + this.$scope.myValue = {'': null}; + } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) { + this.$scope.myValue = []; + } + this.setMaxLength(); + }; + + this.$scope.delete = (property:PropertyModel):void => { + let onOk = ():void => { + this.component.deleteProperty(property.uniqueId).then( + this.$scope.close + ); + }; + let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html new file mode 100644 index 0000000000..f92d9a5ddc --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html @@ -0,0 +1,201 @@ + + +
+
+ Delete + + Previous + Next +
+
+ +
+ +
+ +
+ +
+ + +
+ + + +
+ + + +
+
+
+ +
+
+
+ +
+ + + +
+ +
+
+
+
+ +
+ + + +
+ +
+
+
+
+ + +
+ +
+ Should be constraints by type(TBD) +
+
+ +
+ +
+ +
+ + + +
+ + + +
+
+ + +
+ +
+ + +
+
+
+ +
+
+ +
+
+
+ {{editPropertyModel.property.addOn}} + + + +
+ + + +
+
+
+
+
+ +
+
+
+ +
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts new file mode 100644 index 0000000000..7359ac0e91 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts @@ -0,0 +1,206 @@ +/** + * Created by obarda on 1/18/2017. + */ +'use strict'; +import {PropertyModel, DisplayModule, Component, Resource, Service, ComponentInstance} from "app/models"; +import {UNIQUE_GROUP_PROPERTIES_NAME} from "app/utils"; +import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model"; +import {DataTypesService} from "app/services/data-types-service"; + +export interface IModulePropertyViewScope extends IPropertyFormBaseViewScope { + onValueChange():void; +} + +export class ModulePropertyView extends PropertyFormBaseView { + + static '$inject' = [ + '$scope', + '$templateCache', + '$uibModalInstance', + '$injector', + 'originalProperty', + 'component', + 'selectedModule', + 'Sdc.Services.DataTypesService', + '$q' + ]; + + constructor(protected $scope:IModulePropertyViewScope, + protected $templateCache:ng.ITemplateCacheService, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + private selectedModule:DisplayModule, + protected DataTypesService:DataTypesService, + private $q:ng.IQService) { + super($scope, $uibModalInstance, $injector, originalProperty, component, selectedModule.properties, DataTypesService); + + this.$templateCache.put("module-property-view.html", require('app/view-models/forms/property-forms/module-property-modal/module-property-view.html')); + this.$scope.innerViewSrcUrl = "module-property-view.html"; + this.initChildScope(); + } + + private findPropertyByName = (propertyName:string):PropertyModel => { + let property:PropertyModel = _.find(this.filteredProperties, (property:PropertyModel) => { + return property.name === propertyName; + }); + return property; + }; + + save(isNeedToCloseModal):ng.IPromise { + + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array):void => { + deferred.resolve(true); + if (isNeedToCloseModal === true) { + this.$scope.close(); + } + }; + + let onFailed = ():void => { + deferred.resolve(false); + }; + + let property = _.find(this.selectedModule.properties, (property) => { + return property.uniqueId === this.$scope.property.uniqueId; + }); + if (property.value !== this.$scope.property.value) { + if (this.component.isResource()) { + (this.component).updateResourceGroupProperties(this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time + } + if (this.component.isService()) { + // Find the component instance of the group instance + let componentInstance:ComponentInstance = _.find(this.component.componentInstances, (componentInstance:ComponentInstance) => { + let groupInstance = _.find(componentInstance.groupInstances, {uniqueId: this.selectedModule.groupInstanceUniqueId}); + return groupInstance !== undefined; + + }); + (this.component).updateGroupInstanceProperties(componentInstance.uniqueId, this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time + } + } else { + deferred.resolve(true); + } + return deferred.promise; + } + + onPropertyChange():void { + this.initValidation(); + } + + protected initValidation = ():void => { + + this.$scope.isDeleteDisable = true; + this.$scope.isNameDisable = true; + this.$scope.isTypeSelectorDisable = true; + this.$scope.isDescriptionDisable = true; + + switch (this.$scope.property.name) { + case UNIQUE_GROUP_PROPERTIES_NAME.IS_BASE: + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_TYPE: + case UNIQUE_GROUP_PROPERTIES_NAME.VOLUME_GROUP: + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_LABEL: + this.$scope.property.readonly = true; + break; + case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_DESCRIPTION: + if (this.component.isService()) { + this.$scope.property.readonly = true; + } else { + this.$scope.property.readonly = false; + } + break; + } + }; + + private isUniqueProperty = ():boolean => { + return this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES || + this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES || + this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT; + }; + + + private initChildScope = ():void => { + + this.initValidation(); + + // put default value when instance value is empty + this.$scope.onValueChange = ():void => { + + if (!this.$scope.property.value) { // Resetting to default value + if (this.isPropertyValueOwner()) { + if (this.component.isService()) { + this.$scope.property.value = this.$scope.property.parentValue; + } else { + this.$scope.property.value = this.$scope.property.defaultValue; + } + } + } + + if (this.isUniqueProperty()) { + + let isValid = true; + let maxProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES); + let minProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES); + let initialCountProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT); + + let maxPropertyValue = parseInt(maxProperty.value); + let minPropertyValue = parseInt(minProperty.value); + let initialCountPropertyValue = parseInt(initialCountProperty.value); + let propertyValue = parseInt(this.$scope.property.value); + let parentPropertyValue = parseInt(this.$scope.property.parentValue); + + switch (this.$scope.property.name) { + + case UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES: + if (!maxPropertyValue || maxPropertyValue === null) { + isValid = propertyValue <= initialCountPropertyValue; + } + else { + isValid = propertyValue && (propertyValue <= maxPropertyValue && propertyValue <= initialCountPropertyValue); + } + this.$scope.forms.editForm["value"].$setValidity('maxValidation', isValid); + if (this.component.isService()) { + if (!parentPropertyValue || parentPropertyValue === null) { + isValid = true; + } else { + isValid = propertyValue >= parentPropertyValue; + this.$scope.forms.editForm["value"].$setValidity('minValidationVfLevel', isValid); + } + } + break; + case UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES: + if (!minPropertyValue || minPropertyValue === null) { + isValid = propertyValue >= initialCountPropertyValue; + } else { + isValid = !propertyValue || (propertyValue >= minPropertyValue && propertyValue >= initialCountPropertyValue); + } + this.$scope.forms.editForm["value"].$setValidity('minValidation', isValid); + if (this.component.isService()) { + if (!parentPropertyValue || parentPropertyValue === null) { + isValid = true; + } + else { + isValid = propertyValue <= parentPropertyValue; + this.$scope.forms.editForm["value"].$setValidity('maxValidationVfLevel', isValid); + } + } + break; + case UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT: + if ((!minPropertyValue || minPropertyValue === null) && (!maxPropertyValue || maxPropertyValue === null)) { + isValid = true; + } else if (!minPropertyValue || minPropertyValue === null) { + isValid = propertyValue <= maxPropertyValue; + } else if (!maxPropertyValue || maxPropertyValue === null) { + isValid = propertyValue >= minPropertyValue; + } else { + isValid = minPropertyValue <= propertyValue && propertyValue <= maxPropertyValue; + } + this.$scope.forms.editForm["value"].$setValidity('minOrMaxValidation', isValid); + break; + } + } + ; + } + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html new file mode 100644 index 0000000000..175f4c199b --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html @@ -0,0 +1,41 @@ +
+ +
+ + + +
+ + + + + + + + + +
+
+
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts new file mode 100644 index 0000000000..48aa47fdd0 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts @@ -0,0 +1,97 @@ +'use strict'; +import {DataTypesService} from "app/services/data-types-service"; +import {PropertyModel, InputPropertyBase, Component} from "app/models"; +import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model"; +import {PROPERTY_TYPES} from "app/utils/constants"; + +interface ISelectDataTypeViewModelScope extends IPropertyFormBaseViewScope { + selectedPropertiesName:string; + dataTypesService:DataTypesService; + path:string; + isTypeDataType:boolean; + myValue:any; + isReadOnly:boolean; +} + +export class SelectDataTypeViewModel extends PropertyFormBaseView { + + static '$inject' = [ + '$scope', + '$templateCache', + '$uibModalInstance', + '$injector', + 'originalProperty', + 'component', + 'filteredProperties', + 'Sdc.Services.DataTypesService', + 'propertiesMap', + '$q' + ]; + + constructor(protected $scope:ISelectDataTypeViewModelScope, + protected $templateCache:ng.ITemplateCacheService, + protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + protected $injector:ng.auto.IInjectorService, + protected originalProperty:PropertyModel, + protected component:Component, + protected filteredProperties:Array, + protected DataTypesService:DataTypesService, + private propertiesMap:Array, + private $q:ng.IQService) { + super($scope, $uibModalInstance, $injector, originalProperty, component, filteredProperties, DataTypesService); + + this.$templateCache.put("select-datatype-modal-view.html", require('app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html')); + this.$scope.innerViewSrcUrl = "select-datatype-modal-view.html"; + this.initChildScope(); + } + + //scope methods + save(isNeedToCloseModal):ng.IPromise { + let deferred = this.$q.defer(); + this.$scope.property.propertiesName = this.DataTypesService.selectedPropertiesName; + this.$scope.property.input = this.DataTypesService.selectedInput; + this.$scope.property.isAlreadySelected = true; + this.$uibModalInstance.close(this.$scope.property); + deferred.resolve(true); + return deferred.promise; + }; + + private initForNotSimpleType = ():void => { + let property = this.$scope.property; + this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.property); + if (property.type && this.$scope.simpleTypes.indexOf(property.type) == -1) { + if (!(property.value || property.defaultValue)) { + switch (property.type) { + case PROPERTY_TYPES.MAP: + this.$scope.myValue = {'': null}; + break; + case PROPERTY_TYPES.LIST: + this.$scope.myValue = []; + break; + default: + this.$scope.myValue = {}; + } + } else { + this.$scope.myValue = JSON.parse(property.value || property.defaultValue); + } + } + }; + + //remove selection property on the modal + private removeSelected = ():void => { + this.DataTypesService.selectedPropertiesName = null; + this.DataTypesService.selectedInput = null; + }; + + private initChildScope = ():void => { + //scope properties + this.$scope.forms = {}; + this.$scope.path = this.$scope.property.name; + this.$scope.isArrowsDisabled = true; + this.DataTypesService.alreadySelectedProperties = this.propertiesMap; + this.$scope.dataTypesService = this.DataTypesService; + this.$scope.isReadOnly = true; + this.initForNotSimpleType(); + this.removeSelected(); + } +} diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html new file mode 100644 index 0000000000..acb0f292ff --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html @@ -0,0 +1,74 @@ + + + +
+ +
+ + +
+
+
+ + +
+
+ +
+
+
+ + + +
+ + + +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less new file mode 100644 index 0000000000..15e30af4ee --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less @@ -0,0 +1,63 @@ +.sdc-edit-property-container { + .scrollbar-container{ + height: 415px; + width: 830px; + .perfect-scrollbar; + } + + form{ + width: 813px; + [name="description"]{ + min-height:50px; + } + } + + .sdc-modal-top-bar{ + height: 40px; + .sdc-modal-top-bar-buttons { + float: right; + + > span:not(.delimiter){ + vertical-align: middle; + .hand; + + &.sprite-new { + text-indent: 100%; + } + &.disabled, &:hover.disabled { + pointer-events: none; + } + } + + .delete-btn{ + margin-right: 6px; + } + + .left-arrow{ + margin-right: 8px; + } + + .delimiter { + height: 20px; + width: 1px; + background-color: #959595; + display: inline-block; + vertical-align: middle; + margin-right: 10px; + } + } + } + + .w-sdc-form-note { + .h_9; + display: block; + position: relative; + top: 13px; + } + + .default-value-section{ + border-top: solid 1px @main_color_a; + padding-top: 15px; + margin-top: 15px; + } +} diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts new file mode 100644 index 0000000000..869e3db584 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts @@ -0,0 +1,95 @@ +'use strict'; +import {ComponentInstanceFactory} from "app/utils"; +import {ComponentInstance} from "app/models"; +import {Requirement, Component, Capability} from "app/models"; + +interface IResourceInstanceViewModelScope extends ng.IScope { + + componentInstanceModel:ComponentInstance; + validationPattern:RegExp; + oldName:string; + isAlreadyPressed:boolean; + footerButtons:Array; + forms:any; + modalInstanceName:ng.ui.bootstrap.IModalServiceInstance; + + save():void; + close():void; +} + +export class ResourceInstanceNameViewModel { + + static '$inject' = [ + '$scope', + 'ValidationPattern', + '$uibModalInstance', + 'component' + ]; + + + constructor(private $scope:IResourceInstanceViewModelScope, + private ValidationPattern:RegExp, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private component:Component) { + + this.initScope(); + } + + + private initScope = ():void => { + this.$scope.forms = {}; + this.$scope.validationPattern = this.ValidationPattern; + this.$scope.componentInstanceModel = ComponentInstanceFactory.createComponentInstance(this.component.selectedInstance); + this.$scope.oldName = this.component.selectedInstance.name; + this.$scope.modalInstanceName = this.$uibModalInstance; + + this.$scope.isAlreadyPressed = false; + + + this.$scope.close = ():void => { + this.$uibModalInstance.dismiss(); + }; + + this.$scope.save = ():void => { + + let onFailed = () => { + this.$scope.isAlreadyPressed = true; + }; + + let onSuccess = (componentInstance:ComponentInstance) => { + this.$uibModalInstance.close(); + this.$scope.isAlreadyPressed = false; + this.$scope.componentInstanceModel = componentInstance; + //this.component.name = componentInstance.name;//DE219124 + this.component.selectedInstance.name = componentInstance.name; + //update requirements and capabilities owner name + _.forEach(this.component.selectedInstance.requirements, (requirementsArray:Array) => { + _.forEach(requirementsArray, (requirement:Requirement):void => { + requirement.ownerName = componentInstance.name; + }); + }); + + _.forEach(this.component.selectedInstance.capabilities, (capabilitiesArray:Array) => { + _.forEach(capabilitiesArray, (capability:Capability):void => { + capability.ownerName = componentInstance.name; + }); + }); + + }; + + this.$scope.isAlreadyPressed = true; + if (this.$scope.oldName != this.$scope.componentInstanceModel.name) { + this.component.updateComponentInstance(this.$scope.componentInstanceModel).then(onSuccess, onFailed); + } + }; + + this.$scope.footerButtons = [ + {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save}, + {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close} + ]; + + this.$scope.$watch("[forms.editNameForm.$invalid,componentInstanceModel.name,isAlreadyPressed]", (newVal, oldVal) => { + this.$scope.footerButtons[0].disabled = this.$scope.forms.editNameForm.$invalid || this.$scope.isAlreadyPressed || this.$scope.componentInstanceModel.name === this.$scope.oldName; + }); + } +} diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html new file mode 100644 index 0000000000..e04343adbd --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html @@ -0,0 +1,72 @@ + + +
+
+ + + +
+ + + +
+ +
+
+ +
+ + + + diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less new file mode 100644 index 0000000000..57698bef17 --- /dev/null +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less @@ -0,0 +1,29 @@ +.w-sdc-modal-resource-instance-name { + + .w-sdc-modal-body { + overflow: visible; + } + + .w-sdc-modal-action { + display: flex; + align-items: center; + justify-content: center; + } + + .w-sdc-modal-resource-instance-input { + .p_1; + border: solid 1px @color_p; + height: 45px; + padding: 0 20px; + margin: 0 auto 0 auto; + display: block; + } + .w-sdc-modal-body { + border-bottom: none; + } + + .w-sdc-form .i-sdc-form-item.error::after { + top: 13px; + } + +} diff --git a/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts new file mode 100644 index 0000000000..3d8b6c3053 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts @@ -0,0 +1,73 @@ +'use strict'; +import {ValidationUtils, ModalType} from "app/utils"; + +export interface IConfirmationModalModel { + title:string; + message:string; + showComment:boolean; + type:ModalType; +} + +interface IConfirmationModalViewModelScope { + modalInstanceConfirmation:ng.ui.bootstrap.IModalServiceInstance; + confirmationModalModel:IConfirmationModalModel; + comment:any; + commentValidationPattern:RegExp; + editForm:ng.IFormController; + okButtonColor:string; + hideCancelButton:boolean; + ok():any; + cancel():void; +} + +export class ConfirmationModalViewModel { + + static '$inject' = ['$scope', '$uibModalInstance', 'confirmationModalModel', 'CommentValidationPattern', 'ValidationUtils']; + + constructor(private $scope:IConfirmationModalViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + confirmationModalModel:IConfirmationModalModel, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils) { + + this.initScope(confirmationModalModel); + } + + private initScope = (confirmationModalModel:IConfirmationModalModel):void => { + let self = this; + this.$scope.hideCancelButton = false; + this.$scope.modalInstanceConfirmation = this.$uibModalInstance; + this.$scope.confirmationModalModel = confirmationModalModel; + this.$scope.comment = {"text": ''}; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + + this.$scope.ok = ():any => { + self.$uibModalInstance.close(this.ValidationUtils.stripAndSanitize(self.$scope.comment.text)); + }; + + this.$scope.cancel = ():void => { + console.info('Cancel pressed on: ' + this.$scope.confirmationModalModel.title); + self.$uibModalInstance.dismiss(); + }; + + // Set the OK button color according to modal type (standard, error, alert) + let _okButtonColor = 'blue'; // Default + switch (confirmationModalModel.type) { + case ModalType.STANDARD: + _okButtonColor = 'blue'; + break; + case ModalType.ERROR: + _okButtonColor = 'red'; + break; + case ModalType.ALERT: + this.$scope.hideCancelButton = true; + _okButtonColor = 'grey'; + break; + default: + _okButtonColor = 'blue'; + break; + } + this.$scope.okButtonColor = _okButtonColor; + + } +} diff --git a/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view.html b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view.html new file mode 100644 index 0000000000..09c27f8cd3 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal-view.html @@ -0,0 +1,29 @@ + +
+ + +
+ + +
+ + +
+
+
+ +
diff --git a/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal.less b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal.less new file mode 100644 index 0000000000..666c41d5ed --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/confirmation-modal/confirmation-modal.less @@ -0,0 +1,30 @@ +.w-sdc-modal-confirmation { + form.w-sdc-form{ + padding: 0; + } + + .w-sdc-modal-body-content { + .b_6; + word-break: break-word; + + } + .w-sdc-modal-body { + height: auto; + /* padding: 47px 60px 20px 60px; */ + border-bottom: none; + } + .w-sdc-modal-body-content { + padding: 0; + } + .w-sdc-modal-body-comment { + width: 430px; + height: 127px; + border: solid 1px @color_e; + margin: 20px 0 0 0; + padding: 15px; + } + .w-sdc-modal-label { + .m_14_r; + text-align: left; + } +} diff --git a/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view-model.ts b/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view-model.ts new file mode 100644 index 0000000000..f1fb56d0ff --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view-model.ts @@ -0,0 +1,96 @@ +'use strict'; +import {IAppConfigurtaion, Component, AsdcComment} from "app/models"; +import {ValidationUtils} from "app/utils"; + +export interface IEmailModalModel_Email { + to:string; + subject:string; + message:string; +} + +export interface IEmailModalModel_Data { + component:Component; + stateUrl:string; +} + +export interface IEmailModalModel { + title:string; + email:IEmailModalModel_Email; + data:IEmailModalModel_Data; +} + +interface IEmailModalViewModelScope { + modalInstanceEmail:ng.ui.bootstrap.IModalServiceInstance; + emailModalModel:IEmailModalModel; + submitInProgress:boolean; + commentValidationPattern:RegExp; + isLoading:boolean; + submit():any; + cancel():void; + validateField(field:any):boolean; +} + +export class EmailModalViewModel { + + static '$inject' = ['$scope', '$filter', 'sdcConfig', '$uibModalInstance', 'emailModalModel', 'ValidationUtils', 'CommentValidationPattern']; + + constructor(private $scope:IEmailModalViewModelScope, + private $filter:ng.IFilterService, + private sdcConfig:IAppConfigurtaion, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private emailModalModel:IEmailModalModel, + private ValidationUtils:ValidationUtils, + private CommentValidationPattern:RegExp) { + + this.initScope(emailModalModel); + } + + private initScope = (emailModalModel:IEmailModalModel):void => { + this.$scope.emailModalModel = emailModalModel; + this.$scope.submitInProgress = false; + this.$scope.commentValidationPattern = this.CommentValidationPattern; + this.$scope.modalInstanceEmail = this.$uibModalInstance; + + this.$scope.submit = ():any => { + + let onSuccess = (component:Component) => { + this.$scope.isLoading = false; + this.$scope.submitInProgress = false; + // showing the outlook modal according to the config json + if (this.sdcConfig.showOutlook) { + let link:string = encodeURI(this.sdcConfig.api.baseUrl + "?folder=Ready_For_Testing"); + let outlook:string = this.$filter('translate')("EMAIL_OUTLOOK_MESSAGE", "{'to': '" + emailModalModel.email.to + "','subject': '" + emailModalModel.email.subject + "','message': '" + emailModalModel.email.message + "', 'entityNameAndVersion': '" + emailModalModel.email.subject + "','link': '" + link + "'}"); + window.location.href = outlook; // Open outlook with the email to send + } + this.$uibModalInstance.close(component); // Close the dialog + }; + + let onError = () => { + this.$scope.isLoading = false; + this.$scope.submitInProgress = false; + this.$uibModalInstance.close(); // Close the dialog + }; + + // Submit to server + // Prevent from user pressing multiple times on submit. + if (this.$scope.submitInProgress === false) { + this.$scope.isLoading = true; + this.$scope.submitInProgress = true; + let comment:AsdcComment = new AsdcComment(); + comment.userRemarks = emailModalModel.email.message; + emailModalModel.data.component.changeLifecycleState(emailModalModel.data.stateUrl, comment).then(onSuccess, onError); + } + }; + + this.$scope.cancel = ():void => { + this.$uibModalInstance.dismiss(); + }; + + this.$scope.validateField = (field:any):boolean => { + if (field && field.$dirty && field.$invalid) { + return true; + } + return false; + }; + } +} diff --git a/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view.html b/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view.html new file mode 100644 index 0000000000..bf65428033 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/email-modal/email-modal-view.html @@ -0,0 +1,77 @@ + diff --git a/catalog-ui/src/app/view-models/modals/email-modal/email-modal.less b/catalog-ui/src/app/view-models/modals/email-modal/email-modal.less new file mode 100644 index 0000000000..471089fa1a --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/email-modal/email-modal.less @@ -0,0 +1,57 @@ +.w-sdc-modal-email { + + .w-sdc-modal-body { + border-bottom: none; + } + + form.w-sdc-form{ + padding: 0; + + .i-sdc-form-item { + clear: both; + label { + min-height: 30px; + padding-top: 4px; + } + + .col-sm-10 { + padding-right: 0; + } + + } + + .w-sdc-modal-body-email { + border-style: solid; + border-width: 1px; + border-color: @color_e; + box-sizing: border-box; + width: 100%; + height: 127px; + margin-bottom: 20px; + } + + label {.m_14_m; text-align: left;} + input {.m_14_r;} + textarea {.m_14_r;} + /* I made the subject and to fields as input (for future use), but for now they look like labels: */ + input:disabled { + .bg_c; + border: none; + } + } + + .w-sdc-modal-action { + background-color: @main_color_p; + padding: 0 13px 0 0; + height: 90px; + line-height: 65px; + + button {width: 174px;} + } + + .w-sdc-form .i-sdc-form-item label.required::before { + position: absolute; + left: -13px; + } + +} diff --git a/catalog-ui/src/app/view-models/modals/error-modal/error-403-view.html b/catalog-ui/src/app/view-models/modals/error-modal/error-403-view.html new file mode 100644 index 0000000000..41b1c6df1d --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/error-modal/error-403-view.html @@ -0,0 +1,4 @@ +
+
+
+
diff --git a/catalog-ui/src/app/view-models/modals/error-modal/error-view-model.ts b/catalog-ui/src/app/view-models/modals/error-modal/error-view-model.ts new file mode 100644 index 0000000000..f622a6f53b --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/error-modal/error-view-model.ts @@ -0,0 +1,19 @@ +'use strict'; +import {CookieService} from "app/services"; + +interface IErrorViewModelScope { + mailto:string; +} + +export class ErrorViewModel { + + static ADMIN_EMAIL = 'dl-asdcaccessrequest@att.com'; + static SUBJECT_PRFIEX = 'SDC Access Request for'; + + static '$inject' = ['$scope', 'Sdc.Services.CookieService', '$window']; + + constructor($scope:IErrorViewModelScope, cookieService:CookieService, $window) { + let userDetails = cookieService.getFirstName() + ' ' + cookieService.getLastName() + ' (' + cookieService.getUserId() + ')'; + $scope.mailto = ErrorViewModel.ADMIN_EMAIL + '?subject=' + $window.encodeURIComponent(ErrorViewModel.SUBJECT_PRFIEX + ' ' + userDetails); + } +} diff --git a/catalog-ui/src/app/view-models/modals/error-modal/error.less b/catalog-ui/src/app/view-models/modals/error-modal/error.less new file mode 100644 index 0000000000..8297b5053d --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/error-modal/error.less @@ -0,0 +1,13 @@ +.sdc-error-403-container { + .bg_n; + width: 700px; + height: 400px; + margin: auto; + margin-top: 196px; + + .w-sdc-error-403-text { + .q_11; + margin-top: 20px; + } + +} diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-base-modal-model.ts b/catalog-ui/src/app/view-models/modals/message-modal/message-base-modal-model.ts new file mode 100644 index 0000000000..3c9e75238a --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/message-modal/message-base-modal-model.ts @@ -0,0 +1,43 @@ +'use strict'; +import {SEVERITY} from "app/utils"; + +export interface IMessageModalModel { + title:string; + message:string; + severity:SEVERITY; +} + +export interface IMessageModalViewModelScope extends ng.IScope { + footerButtons:Array; + messageModalModel:IMessageModalModel; + modalInstanceError:ng.ui.bootstrap.IModalServiceInstance; + ok():void; +} + +export class MessageModalViewModel { + + constructor(private $baseScope:IMessageModalViewModelScope, + private $baseModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private baseMessageModalModel:IMessageModalModel) { + + this.initScope(baseMessageModalModel); + } + + private initScope = (messageModalViewModel:IMessageModalModel):void => { + + this.$baseScope.messageModalModel = messageModalViewModel; + this.$baseScope.modalInstanceError = this.$baseModalInstance; + + this.$baseScope.ok = ():void => { + this.$baseModalInstance.close(); + }; + + this.$baseScope.footerButtons = [ + { + 'name': 'OK', + 'css': 'grey', + 'callback': this.$baseScope.ok + } + ]; + } +} diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts b/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts new file mode 100644 index 0000000000..053ea41ba3 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts @@ -0,0 +1,22 @@ +'use strict'; +import {IMessageModalModel, MessageModalViewModel, IMessageModalViewModelScope} from "../message-base-modal-model"; + +export interface IClientMessageModalModel extends IMessageModalModel { +} + +export interface IClientMessageModalViewModelScope extends IMessageModalViewModelScope { + clientMessageModalModel:IClientMessageModalModel; +} + +export class ClientMessageModalViewModel extends MessageModalViewModel { + + static '$inject' = ['$scope', '$uibModalInstance', 'clientMessageModalModel']; + + constructor(private $scope:IClientMessageModalViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private clientMessageModalModel:IClientMessageModalModel) { + + super($scope, $uibModalInstance, clientMessageModalModel); + } + +} diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html b/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html new file mode 100644 index 0000000000..cfb0a35f69 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html @@ -0,0 +1,16 @@ + + + +
+
+
+
+ +
+ +
diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal.less b/catalog-ui/src/app/view-models/modals/message-modal/message-client-modal/client-message-modal.less new file mode 100644 index 0000000000..e69de29bb2 diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts b/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts new file mode 100644 index 0000000000..5f1d5e7a92 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts @@ -0,0 +1,24 @@ +'use strict'; +import {IMessageModalModel, IMessageModalViewModelScope, MessageModalViewModel} from "../message-base-modal-model"; + +export interface IServerMessageModalModel extends IMessageModalModel { + status:string; + messageId:string; +} + +export interface IServerMessageModalViewModelScope extends IMessageModalViewModelScope { + serverMessageModalModel:IServerMessageModalModel; +} + +export class ServerMessageModalViewModel extends MessageModalViewModel { + + static '$inject' = ['$scope', '$uibModalInstance', 'serverMessageModalModel']; + + constructor(private $scope:IServerMessageModalViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private serverMessageModalModel:IServerMessageModalModel) { + + super($scope, $uibModalInstance, serverMessageModalModel); + } + +} diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html b/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html new file mode 100644 index 0000000000..294dc76c4c --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html @@ -0,0 +1,17 @@ + + + +
+
+
Error code: {{messageModalModel.messageId}}
+
Status code: {{messageModalModel.status}}
+
+
+
+ +
diff --git a/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal.less b/catalog-ui/src/app/view-models/modals/message-modal/message-server-modal/server-message-modal.less new file mode 100644 index 0000000000..e69de29bb2 diff --git a/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts new file mode 100644 index 0000000000..8e7e79c576 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts @@ -0,0 +1,249 @@ +'use strict'; +import {ComponentType, CHANGE_COMPONENT_CSAR_VERSION_FLAG, SEVERITY, FileUtils, ModalsHandler, ComponentFactory} from "app/utils"; +import {OnboardingService, CacheService} from "app/services"; +import {Component, IComponent, IUser, IAppConfigurtaion, Resource} from "app/models"; +import {IServerMessageModalModel} from "../message-modal/message-server-modal/server-message-modal-view-model"; +import {Dictionary} from "app/utils"; +import * as _ from 'underscore'; + +interface IOnboardingModalViewModelScope { + modalOnboarding:ng.ui.bootstrap.IModalServiceInstance; + componentsList:Array; + tableHeadersList:Array; + selectedComponent:Component; + componentFromServer:Component; + reverse:boolean; + sortBy:string; + searchBind:string; + okButtonText:string; + isCsarComponentExists:boolean; + user:IUser; + isLoading:boolean; + + //this is for UI paging + numberOfItemsToDisplay:number; + allItemsDisplayed:boolean; + + doSelectComponent(component:Component):void; + doUpdateCsar():void; + doImportCsar():void; + sort(sortBy:string):void; + downloadCsar(packageId:string):void; + increaseNumItemsToDisplay():void; +} + +export class OnboardingModalViewModel { + + static '$inject' = [ + '$scope', + '$filter', + '$state', + 'sdcConfig', + '$uibModalInstance', + 'Sdc.Services.OnboardingService', + 'okButtonText', + 'currentCsarUUID', + 'Sdc.Services.CacheService', + 'FileUtils', + 'ComponentFactory', + 'ModalsHandler' + ]; + + constructor(private $scope:IOnboardingModalViewModelScope, + private $filter:ng.IFilterService, + private $state:any, + private sdcConfig:IAppConfigurtaion, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private onBoardingService:OnboardingService, + private okButtonText:string, + private currentCsarUUID:string, + private cacheService:CacheService, + private fileUtils:FileUtils, + private componentFactory:ComponentFactory, + private modalsHandler:ModalsHandler) { + + this.init(); + } + + /** + * Called from controller constructor, this will call onboarding service to get list + * of "mini" components (empty components created from CSAR). + * The list is inserted to componentsList on $scope. + * And then call initScope method. + */ + private init = ():void => { + this.initOnboardingComponentsList(); + }; + + private initScope = ():void => { + + this.initSortedTableScope(); + this.initModalScope(); + this.$scope.sortBy = "name"; // Default sort by + this.$scope.user = this.cacheService.get('user'); + this.$scope.okButtonText = this.okButtonText; + this.$scope.numberOfItemsToDisplay = 0; + this.$scope.allItemsDisplayed = false; + + // Dismiss the modal and pass the "mini" component to workspace general page + this.$scope.doImportCsar = ():void => { + this.$uibModalInstance.dismiss(); + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + componentCsar: this.$scope.selectedComponent + }); + }; + + this.$scope.doUpdateCsar = ():void => { + // In case user select on update the checkin and submit for testing buttons (in general page) should be disabled. + // to do that we need to pass to workspace.general state parameter to know to disable the buttons. + this.$uibModalInstance.close(); + // Change the component version to the CSAR version we want to update. + /*(this.$scope.componentFromServer).csarVersion = (this.$scope.selectedComponent).csarVersion; + let component:Components.Component = this.componentFactory.createComponent(this.$scope.componentFromServer); + this.$state.go('workspace.general', {vspComponent: component, disableButtons: true });*/ + this.cacheService.set(CHANGE_COMPONENT_CSAR_VERSION_FLAG, (this.$scope.selectedComponent).csarVersion); + this.$state.go('workspace.general', { + id: this.$scope.componentFromServer.uniqueId, + type: this.$scope.componentFromServer.componentType.toLowerCase(), + disableButtons: true + }); + }; + + this.$scope.downloadCsar = (packageId:string):void => { + this.$scope.isLoading = true; + this.onBoardingService.downloadOnboardingCsar(packageId).then( + (file:any):void => { + this.$scope.isLoading = false; + if (file) { + this.fileUtils.downloadFile(file, packageId + '.csar'); + } + }, ():void => { + this.$scope.isLoading = false; + var data:IServerMessageModalModel = { + title: 'Download error', + message: "Error downloading file", + severity: SEVERITY.ERROR, + messageId: "", + status: "" + }; + this.modalsHandler.openServerMessageModal(data); + } + ); + }; + + this.$scope.increaseNumItemsToDisplay = ():void => { + this.$scope.numberOfItemsToDisplay = this.$scope.numberOfItemsToDisplay + 40; + if (this.$scope.componentsList) { + this.$scope.allItemsDisplayed = this.$scope.numberOfItemsToDisplay >= this.$scope.componentsList.length; + } + }; + + // When the user select a row, set the component as selectedComponent + this.$scope.doSelectComponent = (component:Component):void => { + + if (this.$scope.selectedComponent === component) { + // Collapse the item + this.$scope.selectedComponent = undefined; + return; + } + + this.$scope.isLoading = true; + this.$scope.componentFromServer = undefined; + this.$scope.selectedComponent = component; + + let onSuccess = (componentFromServer:Component):void => { + this.$scope.isLoading = false; + if (componentFromServer) { + this.$scope.componentFromServer = componentFromServer; + this.$scope.isCsarComponentExists = true; + } else { + this.$scope.componentFromServer = component; + this.$scope.isCsarComponentExists = false; + } + }; + + let onError = ():void => { + this.$scope.isLoading = false; + this.$scope.componentFromServer = component; + this.$scope.isCsarComponentExists = false; + }; + + this.onBoardingService.getComponentFromCsarUuid((component).csarUUID).then(onSuccess, onError); + }; + + }; + + private initSortedTableScope = ():void => { + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Vendor', property: 'vendorName'}, + {title: 'Category', property: 'categories'}, + {title: 'Version', property: 'csarVersion'}, + {title: '#', property: 'importAndUpdate'} + //{title: 'Date', property: 'componentDate'} + ]; + + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + }; + + private initModalScope = ():void => { + // Enable the modal directive to close + this.$scope.modalOnboarding = this.$uibModalInstance; + }; + + private initOnboardingComponentsList = ():void => { + let onSuccess = (onboardingResponse:Array):void => { + initMaxVersionOfItemsInList(onboardingResponse); + + if (this.currentCsarUUID) { + //this.$scope.componentsList = this.$filter('filter')(this.$scope.componentsList, {csarUUID: this.currentCsarUUID}); + this.$scope.componentsList = this.$filter('filter')(this.$scope.componentsList, + (input):boolean => { + return input.csarUUID === this.currentCsarUUID; + } + ); + } + this.initScope(); + }; + + let onError = ():void => { + console.log("Error getting onboarding list"); + this.initScope(); + }; + + let initMaxVersionOfItemsInList = (onboardingResponse:Array):void => { + // Get only the latest version of each item + this.$scope.componentsList = []; + + // Get all unique items from the list + let uniqueItems:Array = _.uniq(onboardingResponse, false, (item:any):void=>{ + return item.packageId; + }); + + // Loop on all the items with unique packageId + _.each(uniqueItems, (item:any):void=> { + // Find all the items that has same packageId + let ItemsFound:Array = _.filter(onboardingResponse, (inListItem:any):any => { + return inListItem.packageId === item.packageId; + }); + + // Loop on all the items with same packageId and find the max version. + let maxItem:any; + _.each(ItemsFound, (ItemFound:any):void=> { + if (!maxItem) { + maxItem = ItemFound; + } else if (maxItem && parseInt(maxItem.csarVersion) < parseInt(ItemFound.csarVersion)) { + maxItem = ItemFound; + } + }); + this.$scope.componentsList.push(maxItem); + }); + }; + + this.onBoardingService.getOnboardingComponents().then(onSuccess, onError); + }; +} diff --git a/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view.html b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view.html new file mode 100644 index 0000000000..3657fad017 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal-view.html @@ -0,0 +1,144 @@ + + +
+
+

Select one of the software product component below:

+
+ + +
+ +
+
+ + +
+
{{header.title}} + +
+
+ + +
+ + + +
+ There are no software product component to display +
+ +
+ + +
+ + +
+ + {{component.name}} +
+ + +
+ {{component.vendorName}} +
+ + +
+ {{component.categories[0].name}} {{component.categories[0].subcategories[0].name}} +
+ + +
+ {{component.csarVersion}} +
+ + +
+ +
+ +
+ +
+
VSP Description:
+ {{component.description}} +
+ +
+
+ +
Name: {{componentFromServer.name}}
+
Lifecycle: {{componentFromServer.lifecycleState}}
+
Creator: {{componentFromServer.creatorFullName}}
+
+
+ +
+
+ +
UUID: {{componentFromServer.uuid}}
+
Version: {{componentFromServer.version}}
+
Modifier: {{componentFromServer.lastUpdaterFullName}}
+
+ Designers cannot update a VSP if the VF is checked out by another user. +
+
+ Designers cannot update a VSP if the VF is in Ready for testing state. +
+
+
+ +
+ +
+ +
+ + + + + +
+ +
+
+ +
+
+
+
+ + +
diff --git a/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal.less b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal.less new file mode 100644 index 0000000000..c745a86888 --- /dev/null +++ b/catalog-ui/src/app/view-models/modals/onboarding-modal/onboarding-modal.less @@ -0,0 +1,148 @@ +.w-sdc-modal-onboarding { + + width: 100%; + display: inline-block; + + .general-info-button{ + position: relative; + top: -40px; + left: 86px; + float: left; + } + + .title-wrapper { + display: flex; + justify-content: space-between; + align-items: flex-end; + + .sub-title { + .m_14_r; + float:left; + } + } + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height: 472px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 10px; + + .table { + .body { + .data-row + div.item-opened { + word-wrap: break-word; + display: flex; + justify-content: space-between; + padding: 10px 0; + + .item-opened-description-title, + .item-opened-metadata-title { + .m_14_m; + } + + .item-opened-description, + .item-opened-metadata1, + .item-opened-metadata2, + .item-opened-metadata3 { + .th { .m_14_m; } + flex-basis: 0; + overflow: hidden; + padding: 5px 15px; + } + + .item-opened-description, + .item-opened-metadata3 { + border-right: 1px solid @main_color_o; + } + + .item-opened-metadata2 { + word-break: break-word; + .note { + color: @func_color_q; + } + } + + .item-opened-icon { + flex-basis: 0; + overflow: hidden; + padding: 5px 15px; + align-self: center; + } + + .item-opened-description {flex-grow: 25;} + .item-opened-metadata1 {flex-grow: 25;} + .item-opened-metadata2 {flex-grow: 30;} + .item-opened-metadata3 { + flex-grow: 10; + .info-button{ + float: right; + } + } + .item-opened-icon {flex-grow: 10;} + } + } + } + + .flex-item:nth-child(1) { + flex-grow: 25; + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) {flex-grow: 25;} + .flex-item:nth-child(3) {flex-grow: 30;} + .flex-item:nth-child(4) {flex-grow: 10; text-align: center; } + .flex-item:nth-child(5) {flex-grow: 10; } + + } + + .download-file-btn { + cursor: pointer; + margin-left: 4px; + } + + .refresh-file-btn, + .import-file-btn { + cursor: pointer; + margin-left: 20px; + } + + .top-search { + float: right; + position: relative; + + input.search-text { + .border-radius(2px); + width: 245px; + height: 32px; + line-height: 32px; + border: 1px solid @main_color_o; + margin: 0; + outline: none; + text-indent: 10px; + + &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */ + &:-moz-placeholder { font-style: italic; } /* Firefox 18- */ + &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */ + &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */ + &:-ms-input-placeholder { font-style: italic; } /* Edge */ + } + + .magnification { + position: absolute; + top: 10px; + right: 10px; + } + + } + +} diff --git a/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts new file mode 100644 index 0000000000..faeaefb5e5 --- /dev/null +++ b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts @@ -0,0 +1,125 @@ +'use strict'; +import {IUserProperties} from "app/models"; +import {MenuItemGroup, MenuItem} from "app/utils"; +import {CacheService} from "app/services"; + +export class BreadcrumbsMenuItem { + key:string; + displayText:string; +} + +export class BreadcrumbsMenu { + selectedKey:string; + menuItems:Array; +} + +export class BreadcrumbsPath { + selectedKeys:Array; +} + +export class VendorData { + breadcrumbs:BreadcrumbsPath; +} + +export interface IOnboardVendorViewModelScope extends ng.IScope { + vendorData:VendorData; + onVendorEvent:Function; + topNavMenuModel:Array; + topNavRootMenu:MenuItemGroup; + user:IUserProperties; + version:string; +} + +export class OnboardVendorViewModel { + static '$inject' = [ + '$scope', + '$q', + 'Sdc.Services.CacheService' + ]; + + private firstControlledTopNavMenu:MenuItemGroup; + + constructor(private $scope:IOnboardVendorViewModelScope, + private $q:ng.IQService, + private cacheService:CacheService) { + + this.$scope.vendorData = { + breadcrumbs: { + selectedKeys: [] + } + }; + + this.$scope.version = this.cacheService.get('version'); + + this.$scope.onVendorEvent = (eventName:string, data:any):void => { + switch (eventName) { + case 'breadcrumbsupdated': + this.handleBreadcrumbsUpdate(data); + break; + } + }; + + this.$scope.topNavMenuModel = []; + + this.$scope.user = this.cacheService.get('user'); + } + + updateBreadcrumbsPath = (selectedKeys:Array):ng.IPromise => { + let topNavMenuModel = this.$scope.topNavMenuModel; + let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu); + if (startIndex === -1) { + startIndex = topNavMenuModel.length; + } + topNavMenuModel.splice(startIndex + selectedKeys.length); + this.$scope.vendorData = { + breadcrumbs: {selectedKeys: selectedKeys} + }; + + return this.$q.when(true); + }; + + handleBreadcrumbsUpdate(breadcrumbsMenus:Array):void { + let selectedKeys = []; + let topNavMenus = breadcrumbsMenus.map((breadcrumbMenu, breadcrumbIndex) => { + let topNavMenu = new MenuItemGroup(); + topNavMenu.menuItems = breadcrumbMenu.menuItems.map(menuItem => + new MenuItem( + menuItem.displayText, + this.updateBreadcrumbsPath, + null, + null, + [selectedKeys.concat([menuItem.key])] + ) + ); + topNavMenu.selectedIndex = _.findIndex( + breadcrumbMenu.menuItems, + menuItem => menuItem.key === breadcrumbMenu.selectedKey + ); + selectedKeys.push(breadcrumbMenu.selectedKey); + return topNavMenu; + }); + + let topNavMenuModel = this.$scope.topNavMenuModel; + let len = topNavMenuModel.length; + let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu); + if (startIndex === -1) { + startIndex = len; + } + topNavMenuModel.splice(startIndex, len - startIndex); + topNavMenuModel.push.apply(topNavMenuModel, topNavMenus); + this.firstControlledTopNavMenu = topNavMenus[0]; + + if (startIndex === 1 && this.$scope.topNavRootMenu == null) { + let topNavRootMenu = topNavMenuModel[0]; + let onboardItem = topNavRootMenu.menuItems[topNavRootMenu.selectedIndex]; + let originalCallback = onboardItem.callback; + onboardItem.callback = (...args) => { + let ret = this.updateBreadcrumbsPath([]); + return originalCallback && originalCallback.apply(undefined, args) || ret; + }; + this.$scope.topNavRootMenu = topNavRootMenu; + } + + this.updateBreadcrumbsPath(selectedKeys); + } +} diff --git a/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view.html b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view.html new file mode 100644 index 0000000000..734fb93daf --- /dev/null +++ b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view.html @@ -0,0 +1,16 @@ +
+ + + + +
+ +
+ + + + + +
diff --git a/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor.less b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor.less new file mode 100644 index 0000000000..4a16ca2b5b --- /dev/null +++ b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor.less @@ -0,0 +1,303 @@ +.sdc-catalog-container { + + .i-sdc-categories-list-item { + font-weight: normal; + } + + // Checkboxes + .i-sdc-designer-leftbar-section-content-ul { + padding: 0; + margin: 0; + + .i-sdc-catalog-subcategories-checkbox { + padding: 0 0 0 20px; + margin: 0; + + .i-sdc-catalog-grouping-checkbox { + padding: 0 0 0 20px; + margin: 0; + } + + } + + } + + .i-sdc-designer-leftbar-section-content-li { + &:last-child { + .i-sdc-categories-list-item { + margin: 0; + } + } + } + + .i-sdc-categories-list-item { + display: block; + //margin-bottom: 5px; + //padding-left: 15px; + //text-indent: -24px; + vertical-align: top; + font-weight: bold; + } + + .i-sdc-subcategories-list-item { + display: block; + //padding-left: 20px; + vertical-align: top; + font-weight: normal; + margin: 0; + //text-indent: -10px; + } + + /*Added by - Ikram */ + .i-sdc-product-input, + .i-sdc-product-select { + border: 1px solid @border_color_f; + min-height: 30px; + padding: 0; + width: 100%; + margin: 1px 0; + background-color: #F2F2F2; + outline: none; + + &:disabled { + .disabled; + } + optgroup{ + color: @color_u; + option{ + color: @color_b; + } + } + } + + .i-sdc-categories-list-item-icon { + display: inline-block; + float: right; + position: relative; + right: -8px; + top: 6px; + } + + .i-sdc-categories-list-item { + margin-top: 7px; + &.NOT_CERTIFIED_CHECKOUT, + &.NOT_CERTIFIED_CHECKIN { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2889px; + width: 14px; + height: 14px; + + } + } + + &.CERTIFIED { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -3034px; + width: 14px; + height: 16px; + } + } + + &.READY_FOR_CERTIFICATION { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2985px; + width: 14px; + height: 16px; + } + } + + &.CERTIFICATION_IN_PROGRESS { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -53px -2934px; + width: 14px; + height: 16px; + } + } + + &.DISTRIBUTED, + &.TBD { + .i-sdc-categories-list-item-icon { + background: url('/assets/styles/images/sprites/sprite-global-old.png') no-repeat -43px -3087px; + width: 24px; + height: 14px; + + } + } + } + + .i-sdc-categories-list-input { + margin: 8px; + + } + + .i-sdc-subcategories-list-input { + + margin: 8px; + } + .i-sdc-subcategories-list-input-container { + margin: 0px 0px 0px 20px; + padding: 2px; + } + + .w-sdc-header-catalog-search-container { + display: table; + padding: 21px 0; + position: relative; + + .w-sdc-designer-leftbar-search-input { + color: #000; + width: 300px; + } + + // .magnification { + // .sprite; + // .sprite.magnification-glass; + // .hand; + // position: absolute; + // top: 40px; + // right: 42px; + // } + } + + .w-sdc-catalog-main { + padding: 10px 12px; + } + .w-sdc-dashboard-catalog-header { + .b_9; + display: inline-block; + font-style: italic; + font-weight: bold; + padding-left: 10px; + } + + .w-sdc-dashboard-catalog-header-order { + .b_9; + font-weight: 800; + } + + .w-sdc-dashboard-catalog-sort { + .b_9; + font-weight: bold; + white-space:pre; + &:hover{ + .hand; + text-decoration: none; + .a_9; + } + &.blue { + .a_9; + } + } + + .w-sdc-catalog-sort-arrow{ + display: inline-block; + &.up{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid ; + } + &.down{ + .b_9; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid; + } + } + + + + + .w-sdc-dashboard-catalog-header-right{ + float: right; + display: inline-block; + padding-right:34px; + } + + .w-sdc-header-catalog-search-input { + width: 420px; + display: table-cell; + padding: 0 25px 1px 10px; + border: 1px solid #bcbcbc; + .border-radius(10px); + height: 30px; + margin: 10px 30px; + outline: none; + } + + .sdc-catalog-type-filter-container { + margin-top: -1px; + } + + .i-sdc-designer-leftbar-section-title { + text-transform: uppercase; + .l_14_m; + line-height: 30px; + } + + .i-sdc-designer-leftbar-section-title-icon { + .hand; + .tlv-sprite; + .footer-close; + transition: .3s all; + margin-top: -4px; + } + + .i-sdc-designer-leftbar-section-title-text { + margin-left: 20px; + } + + .seperator-left, + .seperator-right { + border-right: solid 1px @color_m; + display: table-cell; + width: 2px; + } + + // Rotate catalog left side arrows + .i-sdc-designer-leftbar-section-title.expanded .i-sdc-designer-leftbar-section-title-icon { + transform: rotate(180deg); + } + + // Transform catalog left side sections + .i-sdc-designer-leftbar-section-title + .i-sdc-designer-leftbar-section-content { + max-height: 0px; + margin: 0 auto; + transition: all .3s; + overflow: hidden; + padding: 0 10px 0 18px; + } + + .i-sdc-designer-leftbar-section-title.expanded + .i-sdc-designer-leftbar-section-content { + max-height: 9999px; + margin: 0 auto 1px; + transition: all .3s; + padding: 10px 18px 10px 18px; + overflow: hidden; + } + +} + +.w-sdc-search-icon{ + position: absolute; + right: 40px; + top: 40px; + &.leftbar{ + top: 19px; + right: 18px; + } + &.magnification { + .sprite; + .sprite.magnification-glass; + .hand; + } + &.cancel { + .sprite; + .sprite.clear-text; + .hand; + } +} diff --git a/catalog-ui/src/app/view-models/preloading/preloading-view.html b/catalog-ui/src/app/view-models/preloading/preloading-view.html new file mode 100644 index 0000000000..c0512dd9ec --- /dev/null +++ b/catalog-ui/src/app/view-models/preloading/preloading-view.html @@ -0,0 +1,9 @@ +
+

+

+ +
+
Loading...
+
+ +
diff --git a/catalog-ui/src/app/view-models/preloading/preloading-view.ts b/catalog-ui/src/app/view-models/preloading/preloading-view.ts new file mode 100644 index 0000000000..f299f2a30f --- /dev/null +++ b/catalog-ui/src/app/view-models/preloading/preloading-view.ts @@ -0,0 +1,27 @@ +'use strict'; + +interface IPreLoadingViewScope { + startZoomIn:boolean; +} + +export class PreLoadingViewModel { + + static '$inject' = ['$scope']; + + constructor(private $scope:IPreLoadingViewScope) { + this.init($scope); + } + + private init = ($scope:IPreLoadingViewScope):void => { + this.animate($('.caption1'), 'fadeInUp', 400); + this.animate($('.caption2'), 'fadeInUp', 800); + }; + + private animate = (element:any, animation:string, when:number):void => { + window.setTimeout(()=> { + element.addClass("animated " + animation); + element[0].style = "visibility: visible;"; + }, when); + }; + +} diff --git a/catalog-ui/src/app/view-models/support/support-view-model.ts b/catalog-ui/src/app/view-models/support/support-view-model.ts new file mode 100644 index 0000000000..2f43d87b18 --- /dev/null +++ b/catalog-ui/src/app/view-models/support/support-view-model.ts @@ -0,0 +1,16 @@ +'use strict'; +import {CacheService} from "app/services"; + +interface ISupportViewModelScope { + version:string; +} + +export class SupportViewModel { + + static '$inject' = ['$scope', 'Sdc.Services.CacheService']; + + constructor(private $scope:ISupportViewModelScope, + private cacheService:CacheService) { + this.$scope.version = this.cacheService.get('version'); + } +} diff --git a/catalog-ui/src/app/view-models/support/support-view.html b/catalog-ui/src/app/view-models/support/support-view.html new file mode 100644 index 0000000000..88609774bb --- /dev/null +++ b/catalog-ui/src/app/view-models/support/support-view.html @@ -0,0 +1,31 @@ +
+ +
+ +
Support
+ +
+
+
+
+
In Design ({{numOfCheckOutEntities+numOfCheckInEntities}})
+
Checked Out ({{numOfCheckOutEntities}})
+
Checked In ({{numOfCheckInEntities}})
+
+
+
Completed Design ({{numOfReadyForCertificationEntities+numOfCertificationInProgressEntities+numOfCertifiedEntities}})
+
Ready For Certification ({{numOfReadyForCertificationEntities}})
+
Certification In Progress ({{numOfCertificationInProgressEntities}})
+
Certified ({{numOfCertifiedEntities}})
+
+
+
Catalog
+
Support
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/support/support.less b/catalog-ui/src/app/view-models/support/support.less new file mode 100644 index 0000000000..8159e38320 --- /dev/null +++ b/catalog-ui/src/app/view-models/support/support.less @@ -0,0 +1,8 @@ +.w-sdc-left-sidebar-in-progress, +.w-sdc-left-sidebar-following { + .b_7; +} + +.w-sdc-left-sidebar-following { + padding: 13px 0; +} diff --git a/catalog-ui/src/app/view-models/tabs/general-tab.less b/catalog-ui/src/app/view-models/tabs/general-tab.less new file mode 100644 index 0000000000..936b3e3414 --- /dev/null +++ b/catalog-ui/src/app/view-models/tabs/general-tab.less @@ -0,0 +1,122 @@ +.sdc-general-tab { + + display: flex; + min-height: 100%; + flex-flow: column; + + .sdc-edit-icon { + .sprite; + .e-sdc-small-icon-pencil; + } + .sdc-general-tab-title { + + .f-color.a; + .f-type._14_m; + padding: 0px 0px 15px 20px; + border-bottom: 1px solid @main_color_o; + } + + .sdc-general-tab-sub-title { + + .f-color.a; + .f-type._14_m; + padding: 15px 20px 15px 20px; + + } + + //scrollbar + .general-tab-scrollbar-container { + + .perfect-scrollbar; + width: 100%; + } + + //plus minus expand collapse + .general-tab-expand-collapse { + + &.expanded { + .expand-collapse-title { + .expand-collapse-title-icon { + .expand-collapse-minus-icon; + } + } + } + + .expand-collapse-title { + + padding: 8px 20px 4px 20px; + cursor: pointer; + &:hover { + background-color: @main_color_o; + } + + .expand-collapse-title-icon { + .hand; + .sprite-new; + .expand-collapse-plus-icon; + } + .expand-collapse-title-text { + max-width: 225px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding-left: 10px; + line-height: 15px; + } + } + .selected { + background-color: @main_color_a; + .f-color.p; + } + + } + + .expand-collapse-sub-title { + max-width: 190px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 10px 0 0 43px; + + } + + //resizable view + .resizable-container { + + flex: 1 1 auto; + display: flex; + flex-direction: column; + height: 90%; + + .resizable-section { + min-height: 50px; + flex: 1; + display: flex; + flex-flow: column; + &.resizable { + flex: 0 0 300px; + } + } + + //this is the resizable icon custom design for the angular resizable directive + .rg-top { + span { + margin-top: -5px; + &:before { + border-top: 1px dotted @main_color_m; + content: ''; + display: inline-block; + width: 39px; + height: 6px; + } + + border-top: 1px dotted @main_color_m; + border-bottom: 1px dotted @main_color_m; + width: 39px; + height: 4px; + } + } + } +} diff --git a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts new file mode 100644 index 0000000000..7c505512c4 --- /dev/null +++ b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts @@ -0,0 +1,98 @@ +'use strict'; +import {ModalsHandler} from "app/utils"; +import {PropertyModel, DisplayModule, Component, ComponentInstance, Tab, Module} from "app/models"; +import {ExpandCollapseListData} from "app/directives/utils/expand-collapse-list-header/expand-collapse-list-header"; + +export interface IHierarchyScope extends ng.IScope { + component:Component; + selectedIndex:number; + selectedModule:DisplayModule; + singleTab:Tab; + isLoading:boolean; + expandCollapseArtifactsList:ExpandCollapseListData; + expandCollapsePropertiesList:ExpandCollapseListData; + selectedInstanceId:string; + + onModuleSelected(moduleId:string, selectedIndex:number):void; + onModuleNameChanged(module:DisplayModule):void; + updateHeatName():void; + loadInstanceModules(instance:ComponentInstance):ng.IPromise; + openEditPropertyModal(property:PropertyModel):void; +} + +export class HierarchyViewModel { + + static '$inject' = [ + '$scope', + '$q', + 'ModalsHandler' + ]; + + constructor(private $scope:IHierarchyScope, private $q:ng.IQService, private ModalsHandler:ModalsHandler) { + this.$scope.component = this.$scope.singleTab.data; + this.$scope.isLoading = false; + this.$scope.expandCollapseArtifactsList = new ExpandCollapseListData(); + this.$scope.expandCollapsePropertiesList = new ExpandCollapseListData(); + this.initScopeMethods(); + } + + private initScopeMethods():void { + + let collapseModuleData = ():void => { + this.$scope.expandCollapseArtifactsList.expandCollapse = false; + this.$scope.expandCollapsePropertiesList.expandCollapse = false; + this.$scope.expandCollapseArtifactsList.orderByField = "artifactName"; + this.$scope.expandCollapsePropertiesList.orderByField = "name"; + }; + + this.$scope.onModuleSelected = (moduleId:string, selectedIndex:number, componentInstanceId?:string):void => { + + let onSuccess = (module:DisplayModule) => { + console.log("Module Loaded: ", module); + this.$scope.selectedModule = module; + this.$scope.isLoading = false; + collapseModuleData(); + }; + + let onFailed = () => { + this.$scope.isLoading = false; + }; + + this.$scope.selectedIndex = selectedIndex; + if (!this.$scope.selectedModule || (this.$scope.selectedModule && this.$scope.selectedModule.uniqueId != moduleId)) { + this.$scope.isLoading = true; + if (this.$scope.component.isService()) { + this.$scope.selectedInstanceId = componentInstanceId; + this.$scope.component.getModuleInstanceForDisplay(componentInstanceId, moduleId).then(onSuccess, onFailed); + } else { + this.$scope.component.getModuleForDisplay(moduleId).then(onSuccess, onFailed); + } + } + }; + + this.$scope.updateHeatName = () => { + this.$scope.isLoading = true; + + let originalName:string = this.$scope.selectedModule.name; + + let onSuccess = (module:Module) => { + console.log("Module name updated:", module.name); + this.$scope.selectedModule.name = module.name; + this.$scope.isLoading = false; + }; + + let onFailed = () => { + this.$scope.isLoading = false; + this.$scope.selectedModule.name = originalName; + }; + + this.$scope.selectedModule.updateName(); + this.$scope.component.updateGroupMetadata(new DisplayModule(this.$scope.selectedModule)).then(onSuccess, onFailed); + }; + + this.$scope.openEditPropertyModal = (property:PropertyModel):void => { + this.ModalsHandler.openEditModulePropertyModal(property, this.$scope.component, this.$scope.selectedModule).then(() => { + }); + } + } +} diff --git a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html new file mode 100644 index 0000000000..9eaa3a0f76 --- /dev/null +++ b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html @@ -0,0 +1,107 @@ +
+ +
+ +
+
+ + +
{{component.name}}
+ +
+
+ + +
+
+ +
+
+
{{memberName}}
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+
+ +
+
+
{{memberName}}
+
+
+
+
+
+ +
+ +
+
+
{{selectedModule.name}}
+
+ +
+
+
Module ID:
{{selectedModule.groupUUID}}
+
Customization ID:
{{selectedModule.customizationUUID}}
+
Invariant UUID:{{selectedModule.invariantUUID}}
+
Version: {{selectedModule.version}}
+
IsBase: {{selectedModule.isBase}}
+ +
+ +
+
+
+ {{property.name}} +
+
Type: {{property.type}}
+
Value: {{property.value}}
+
+
+ +
+
+
{{artifact.artifactName}}
+
UUID: {{artifact.artifactUUID}}
+
Version: {{artifact.artifactVersion}}
+
+
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy.less b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy.less new file mode 100644 index 0000000000..dee0eeb38b --- /dev/null +++ b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy.less @@ -0,0 +1,101 @@ +.hierarchy-tab{ + width: 100%; + .hierarchy-module-list-container{ + padding: 0px 20px 0px 20px; + } + .module-data-container{ + + width: 100%; + height: 100%; + background-color: #e6f6fb; + border: 1px solid #009fdb; + border-top: 4px solid #009fdb; + box-shadow: 0.3px 1px 2px rgba(24, 24, 25, 0.32); + + .module-data { + + .selectable; + .module-name { + .f-type._14_m; + width: 87%; + } + .f-type._14_r; + .f-color.a; + padding: 10px 0px 10px 0px; + margin: 0px 20px 0px 20px; + //border-bottom: 1px solid rgba(0, 159, 219, 0.6); + + .small-font{ + font-size: 12px; + } + } + + .list-item{ + padding: 10px 0px 10px 0px; + margin: 0px 20px 0px 20px; + &:not(.last){ + border-bottom: 1px solid rgba(0, 159, 219, 0.6); + } + } + + .artifact-data{ + .selectable; + .f-type._12_r; + .f-color.m; + .artifact-name { + .f-type._14_r; + font-weight: bold; + } + } + + .property-data{ + .property-name{ + width: 100%; + .f-type._14_m; + font-weight: 400; + color: @main_color_a; + } + .property-info{ + color: @func_color_s; + .f-type._14_r; + width: 100%; + } + } + + .module-text-overflow { + max-width: 240px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + //display: inline-block; + } + } + + .hierarchy-modules-list{ + .expand-collapse-title{ + .expand-collapse-title-text{ + max-width: 202px; + } + } + } + + .hierarchy-module-member-list { + overflow: hidden; + background-color: @main_color_p; + } + + .edit-name-container { + float: right; + border-left: 1px solid #5cc1e7; + height: 20px; + width: 12%; + + .sdc-edit-icon { + float: right; + cursor: pointer; + position: relative; + top: 4px; + right: 5px; + } + } +} diff --git a/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.html b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.html new file mode 100644 index 0000000000..6e478fc471 --- /dev/null +++ b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.html @@ -0,0 +1,10 @@ +
+ +

+

+
+ + +
+
+
diff --git a/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.less b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.less new file mode 100644 index 0000000000..30fa4f7e8b --- /dev/null +++ b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.less @@ -0,0 +1,41 @@ +.sdc-tutorial-end-page { + + .bg_s; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 999; + .opacity(0.8); + display: flex; + align-items: center; + + background-image: url('/assets/styles/images/welcome.png'); + background-repeat: no-repeat; + background-position: bottom left; + + .sdc-tutorial-end-page-main { + width: 600px; + height: 400px; + margin: 100px auto 100px auto; + padding: 80px; + } + + h2 { + .t_15; + margin: 0; + } + + .sdc-tutorial-end-page-description1 { + .c_2; + .opacity(0.8); + margin-top: 10px; + line-height: 22px; + } + + .w-sdc-btn-blue { + margin: 40px 10px 0 0; + } + +} diff --git a/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.ts b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.ts new file mode 100644 index 0000000000..1787cd5937 --- /dev/null +++ b/catalog-ui/src/app/view-models/tutorial-end/tutorial-end.ts @@ -0,0 +1,20 @@ +'use strict'; + +interface ITutorialEndViewModelScope extends ng.IScope { +} + +export class TutorialEndViewModel { + + static '$inject' = [ + '$scope' + ]; + + constructor(private $scope:ITutorialEndViewModelScope) { + this.init(); + } + + private init = ():void => { + + } + +} diff --git a/catalog-ui/src/app/view-models/welcome/welcome-view.html b/catalog-ui/src/app/view-models/welcome/welcome-view.html new file mode 100644 index 0000000000..18ca4d51cd --- /dev/null +++ b/catalog-ui/src/app/view-models/welcome/welcome-view.html @@ -0,0 +1,9 @@ +
+
+
+
+
+

Welcome to SDC

+
+
+
diff --git a/catalog-ui/src/app/view-models/welcome/welcome-view.ts b/catalog-ui/src/app/view-models/welcome/welcome-view.ts new file mode 100644 index 0000000000..154a70c301 --- /dev/null +++ b/catalog-ui/src/app/view-models/welcome/welcome-view.ts @@ -0,0 +1,57 @@ +'use strict'; + +export interface IWelcomeViewMode { + onCloseButtonClick():void; +} + +export class WelcomeViewModel { + + firstLoad:boolean = true; + alreadyAnimated:Array = []; + + static '$inject' = [ + '$scope', + '$state' + ]; + + constructor(private $scope:IWelcomeViewMode, + private $state:ng.ui.IStateService + ) { + this.init(); + this.initScope(); + window.setTimeout(():void => { + this.loadImages(():void=> { + window.setTimeout(():void =>{ + $(".sdc-welcome-new-page").addClass("animated fadeIn"); + },1000); + }); + },0); + } + + private initScope = ():void => { + this.$scope.onCloseButtonClick = ():void => { + this.$state.go("dashboard", {}); + }; + }; + + private init = ():void => { + let viewModelsHtmlBasePath:string = 'src/app/view-models/'; + $('body').keyup((e):void=> { + if (e.keyCode == 27) { // escape key maps to keycode `27` + this.$state.go('dashboard'); + } + }); + }; + + private loadImages = (callback:Function):void => { + let src = $('.sdc-welcome-wrapper').css('background-image'); + let url = src.match(/\((.*?)\)/)[1].replace(/('|")/g,''); + + let img = new Image(); + img.onload = function() { + callback(); + }; + img.src = url; + }; + +} diff --git a/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view-model.ts b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view-model.ts new file mode 100644 index 0000000000..61a83c88f7 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view-model.ts @@ -0,0 +1,29 @@ +'use strict'; + +export interface IConformanceLevelModalModelScope { + footerButtons:Array; + modalInstance:ng.ui.bootstrap.IModalServiceInstance; +} + +export class ConformanceLevelModalViewModel { + + static '$inject' = ['$scope', '$uibModalInstance']; + + constructor(private $scope:IConformanceLevelModalModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance) { + + this.initScope(); + } + + private initScope = ():void => { + + this.$scope.modalInstance = this.$uibModalInstance; + + this.$scope.footerButtons = [ + {'name': 'Continue', 'css': 'grey', 'callback': this.$uibModalInstance.close}, + {'name': 'Reject', 'css': 'blue', 'callback': this.$uibModalInstance.dismiss} + ]; + + }; + +} diff --git a/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view.html b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view.html new file mode 100644 index 0000000000..3577e4d77b --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal-view.html @@ -0,0 +1,22 @@ + + + +
+

+ You are about to distribute a service with models and artifacts created with an older version of the platform. + For such service, new properties, metadata and requirements needed by ECOMP components will not be available. +

+ It is highly recommended that you upgrade the service models and artifacts. +

+ Click "Continue" if you need to distribute the current service version.
+ Click "Reject" if you need to stop the distribution and manually upgrade the service. +

+
+
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal.less b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal.less new file mode 100644 index 0000000000..7f195ade83 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/conformance-level-modal/conformance-level-modal.less @@ -0,0 +1,3 @@ +.conformance-level-modal{ + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.html b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.html new file mode 100644 index 0000000000..23c08f6ec6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.html @@ -0,0 +1,85 @@ +
+ +
+ +
+ +
+
+ + +
+
{{header.title}} + +
+
+ + +
+ + + +
+ There are no logs to display +
+ + +
+ + +
+ {{item.dateFormat}} +
+ + +
+ {{item.ACTION}} +
+ + +
+ {{item.COMMENT}} +
+ + +
+ {{item.MODIFIER}} +
+ + +
+ {{item.STATUS}} + +
+ +
+ +
+
+
+
+ +
+ + + + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.less b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.less new file mode 100644 index 0000000000..61bb3e9f01 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.less @@ -0,0 +1,83 @@ +.activity-log { + + margin-top: 30px; + + .title-wrapper { + display: flex; + justify-content: flex-end; + } + + .table-container-flex .table .body .scrollbar-container { + max-height: 448px; + } + + .view-mode { + background-color: @main_color_p; + } + + .table{ + height: 490px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 10px; + + .flex-item:nth-child(1) { width: 200px; } + .flex-item:nth-child(2) { flex-grow: 20; } + .flex-item:nth-child(3) { flex-grow: 30; } + .flex-item:nth-child(4) { flex-grow: 20; } + .flex-item:nth-child(5) { width: 80px; } + + .success { + position: absolute; + top: 11px; + right: 20px; + .sprite-new; + .sdc-success; + } + + .error { + position: absolute; + top: 11px; + right: 20px; + .sprite-new; + .sdc-error; + } + + } + + .data-row { + position: relative; + } + + .top-search { + float: right; + position: relative; + + input.search-text { + .border-radius(2px); + width: 245px; + height: 32px; + line-height: 32px; + border: 1px solid @main_color_o; + margin: 0; + outline: none; + text-indent: 10px; + + &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */ + &:-moz-placeholder { font-style: italic; } /* Firefox 18- */ + &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */ + &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */ + &:-ms-input-placeholder { font-style: italic; } /* Edge */ + } + + .magnification { + position: absolute; + top: 10px; + right: 10px; + } + + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts new file mode 100644 index 0000000000..ed583dc4c0 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts @@ -0,0 +1,103 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {Activity} from "app/models"; +import {ActivityLogService} from "app/services"; + +export interface IActivityLogViewModelScope extends IWorkspaceViewModelScope { + activityDateArray:Array; //this is in order to sort the dates + activityLog:Array; + preVersion:string; + + tableHeadersList:Array; + reverse:boolean; + sortBy:string; + searchBind:string; + + getActivityLog(uniqueId:string):void; + onVersionChanged(version:any):void; + parseAction(action:string):string; + sort(sortBy:string):void; +} + +export class ActivityLogViewModel { + + static '$inject' = [ + '$scope', + '$state', + 'Sdc.Services.ActivityLogService' + ]; + + constructor(private $scope:IActivityLogViewModelScope, + private $state:ng.ui.IStateService, + private activityLogService:ActivityLogService) { + + this.initScope(); + this.$scope.setValidState(true); + this.initSortedTableScope(); + this.$scope.updateSelectedMenuItem(); + + // Set default sorting + this.$scope.sortBy = 'logDate'; + } + + private initScope():void { + + this.$scope.preVersion = this.$scope.component.version; + + this.$scope.onVersionChanged = (version:any):void => { + if (version.versionNumber != this.$scope.component.version) { + this.$scope.isLoading = true; + this.$scope.getActivityLog(version.versionId); + } + }; + + this.$scope.getActivityLog = (uniqueId:any):void => { + + let onError = (response) => { + this.$scope.isLoading = false; + console.info('onFaild', response); + + }; + + let onSuccess = (response:Array) => { + this.$scope.activityLog = _.sortBy(response, function (o) { + return o.TIMESTAMP; + }); //response; // + this.$scope.isLoading = false; + }; + + this.$scope.isLoading = true; + if (this.$scope.component.isResource()) { + this.activityLogService.getActivityLogService('resources', uniqueId).then(onSuccess, onError); + } + if (this.$scope.component.isService()) { + this.activityLogService.getActivityLogService('services', uniqueId).then(onSuccess, onError); + } + + }; + + if (!this.$scope.activityLog || this.$scope.preVersion != this.$scope.component.version) { + this.$scope.getActivityLog(this.$scope.component.uniqueId); + } + + this.$scope.parseAction = (action:string) => { + return action ? action.split(/(?=[A-Z])/).join(' ') : ''; + }; + + } + + private initSortedTableScope = ():void => { + this.$scope.tableHeadersList = [ + {title: 'Date', property: 'logDate'}, + {title: 'Action', property: 'logAction'}, + {title: 'Comment', property: 'logComment'}, + {title: 'Username', property: 'logUsername'}, + {title: 'Status', property: 'logStatus'} + ]; + + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view-model.ts new file mode 100644 index 0000000000..d8a60444be --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view-model.ts @@ -0,0 +1,80 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {Component, AttributeModel} from "app/models"; +import {ModalsHandler} from "app/utils"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; + +interface IAttributesViewModelScope extends IWorkspaceViewModelScope { + tableHeadersList:Array; + reverse:boolean; + sortBy:string; + + addOrUpdateAttribute(attribute?:AttributeModel):void; + delete(attribute:AttributeModel):void; + sort(sortBy:string):void; +} + +export class AttributesViewModel { + + static '$inject' = [ + '$scope', + '$filter', + '$uibModal', + 'ModalsHandler', + 'ComponentServiceNg2' + ]; + + + constructor(private $scope:IAttributesViewModelScope, + private $filter:ng.IFilterService, + private $uibModal:ng.ui.bootstrap.IModalService, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.initComponentAttributes(); + this.$scope.updateSelectedMenuItem(); + } + + private initComponentAttributes = () => { + if(this.$scope.component.attributes) { + this.initScope(); + } else { + this.ComponentServiceNg2.getComponentAttributes(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.attributes = response.attributes; + this.initScope(); + }); + } + } + + + private initScope = ():void => { + + this.$scope.sortBy = 'name'; + this.$scope.reverse = false; + this.$scope.setValidState(true); + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Type', property: 'type'}, + {title: 'Default Value', property: 'defaultValue'} + ]; + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + this.$scope.addOrUpdateAttribute = (attribute?:AttributeModel):void => { + this.ModalsHandler.openEditAttributeModal(attribute ? attribute : new AttributeModel(), this.$scope.component); + }; + + this.$scope.delete = (attribute:AttributeModel):void => { + + let onOk = ():void => { + this.$scope.component.deleteAttribute(attribute.uniqueId); + }; + let title:string = this.$filter('translate')("ATTRIBUTE_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("ATTRIBUTE_VIEW_DELETE_MODAL_TEXT", "{'name': '" + attribute.name + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view.html b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view.html new file mode 100644 index 0000000000..59ba933a0a --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes-view.html @@ -0,0 +1,52 @@ +
+
Add
+
+
+
+
{{header.title}} + +
+
+ +
+ +
+ +
+ There are no attributes to display
+ click here to add one + +
+
+ +
+ + {{attribute.name}} + +
+ +
+ +
+ +
+ +
+ + +
+
+
+
+
+
+ +
+
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.less b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.less new file mode 100644 index 0000000000..ffd28afce4 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.less @@ -0,0 +1,54 @@ +.workspace-attributes { + + width: 93%; + display: inline-block; + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height:490px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 27px; + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 9; + } + + .flex-item:nth-child(4) { + flex-grow: 3; + padding-top: 10px; + } + + .flex-item:nth-child(5) { + flex-grow: 1; + + } + + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts new file mode 100644 index 0000000000..e2d95280c8 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts @@ -0,0 +1,242 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +'use strict'; +import {Component, Product, ComponentInstance, IAppMenu} from "app/models"; +import {SharingService, CacheService, EventListenerService, LeftPaletteLoaderService} from "app/services"; +import {ModalsHandler, GRAPH_EVENTS, ComponentFactory, ChangeLifecycleStateHandler, MenuHandler} from "app/utils"; +import {IWorkspaceViewModelScope} from "../../workspace-view-model"; +import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; + +export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { + + currentComponent:Component; + selectedComponent:Component; + isLoading:boolean; + graphApi:any; + sharingService:SharingService; + sdcMenu:IAppMenu; + version:string; + isViewOnly:boolean; + isLoadingRightPanel:boolean; + onComponentInstanceVersionChange(component:Component); + isComponentInstanceSelected():boolean; + updateSelectedComponent():void + openUpdateModal(); + deleteSelectedComponentInstance():void; + onBackgroundClick():void; + setSelectedInstance(componentInstance:ComponentInstance):void; + printScreen():void; + + cacheComponentsInstancesFullData:Component; +} + +export class CompositionViewModel { + + static '$inject' = [ + '$scope', + '$log', + 'sdcMenu', + 'MenuHandler', + '$uibModal', + '$state', + 'Sdc.Services.SharingService', + '$filter', + 'Sdc.Services.CacheService', + 'ComponentFactory', + 'ChangeLifecycleStateHandler', + 'LeftPaletteLoaderService', + 'ModalsHandler', + 'EventListenerService', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:ICompositionViewModelScope, + private $log:ng.ILogService, + private sdcMenu:IAppMenu, + private MenuHandler:MenuHandler, + private $uibModal:ng.ui.bootstrap.IModalService, + private $state:ng.ui.IStateService, + private sharingService:SharingService, + private $filter:ng.IFilterService, + private cacheService:CacheService, + private ComponentFactory:ComponentFactory, + private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, + private LeftPaletteLoaderService:LeftPaletteLoaderService, + private ModalsHandler:ModalsHandler, + private eventListenerService:EventListenerService, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.$scope.setValidState(true); + this.initScope(); + this.initGraphData(); + this.$scope.updateSelectedMenuItem(); + this.registerGraphEvents(this.$scope); + } + + + private initGraphData = ():void => { + if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations ) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentInstancesAndRelation(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.componentInstances = response.componentInstances; + this.$scope.component.componentInstancesRelations = response.componentInstancesRelations; + this.$scope.isLoading = false; + this.initComponent(); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); + }); + } else { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); + } + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); + }; + + + private cacheComponentsInstancesFullData:Array; + + private initComponent = ():void => { + + this.$scope.currentComponent = this.$scope.component; + this.$scope.selectedComponent = this.$scope.currentComponent; + this.updateUuidMap(); + this.$scope.isViewOnly = this.$scope.isViewMode(); + }; + private registerGraphEvents = (scope:ICompositionViewModelScope):void => { + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick); + + }; + + private openUpdateComponentInstanceNameModal = ():void => { + this.ModalsHandler.openUpdateComponentInstanceNameModal(this.$scope.currentComponent).then(()=> { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance); + + }); + }; + + private removeSelectedComponentInstance = ():void => { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS); + }; + + private updateUuidMap = ():void => { + /** + * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined, + * but after loadService or loadResource this.sharingService.currentEntity will be defined. + * Need to update the uuidMap with the new resource or service. + */ + this.sharingService.addUuidValue(this.$scope.currentComponent.uniqueId, this.$scope.currentComponent.uuid); + }; + + private initScope = ():void => { + + this.$scope.sharingService = this.sharingService; + this.$scope.sdcMenu = this.sdcMenu; + this.$scope.isLoading = false; + this.$scope.isLoadingRightPanel = false; + this.$scope.graphApi = {}; + this.$scope.version = this.cacheService.get('version'); + this.initComponent(); + + this.cacheComponentsInstancesFullData = new Array(); + + this.$scope.isComponentInstanceSelected = ():boolean => { + return this.$scope.currentComponent && this.$scope.currentComponent.selectedInstance != undefined && this.$scope.currentComponent.selectedInstance != null; + }; + + this.$scope.updateSelectedComponent = ():void => { + if (this.$scope.currentComponent.selectedInstance) { + + let componentParent = _.find(this.cacheComponentsInstancesFullData, (component) => { + return component.uniqueId === this.$scope.currentComponent.selectedInstance.componentUid; + }); + if (componentParent) { + this.$scope.selectedComponent = componentParent; + } + else { + try { + let onSuccess = (component:Component) => { + this.$scope.isLoadingRightPanel = false; + this.$scope.selectedComponent = component; + this.cacheComponentsInstancesFullData.push(component); + }; + let onError = (component:Component) => { + console.log("Error updating selected component"); + this.$scope.isLoadingRightPanel = false; + }; + this.ComponentFactory.getComponentFromServer(this.$scope.currentComponent.selectedInstance.originType, this.$scope.currentComponent.selectedInstance.componentUid).then(onSuccess, onError); + } catch (e) { + console.log("Error updating selected component", e); + this.$scope.isLoadingRightPanel = false; + } + } + } + else { + + this.$scope.selectedComponent = this.$scope.currentComponent; + } + }; + + this.$scope.setSelectedInstance = (selectedComponent:ComponentInstance):void => { + + this.$log.debug('composition-view-model::onNodeSelected:: with id: ' + selectedComponent.uniqueId); + this.$scope.currentComponent.setSelectedInstance(selectedComponent); + this.$scope.updateSelectedComponent(); + + if (this.$state.current.name === 'workspace.composition.api') { + this.$state.go('workspace.composition.details'); + } + if (this.$state.current.name === 'workspace.composition.relations' && this.$scope.currentComponent.isProduct()) { + this.$state.go('workspace.composition.details'); + } + }; + + this.$scope.onBackgroundClick = ():void => { + this.$scope.currentComponent.selectedInstance = null; + this.$scope.selectedComponent = this.$scope.currentComponent; + + if (this.$state.current.name === 'workspace.composition.api') { + this.$state.go('workspace.composition.details'); + } + }; + + this.$scope.openUpdateModal = ():void => { + this.openUpdateComponentInstanceNameModal(); + }; + + this.$scope.deleteSelectedComponentInstance = ():void => { + let state = "deleteInstance"; + let onOk = ():void => { + this.removeSelectedComponentInstance(); + //this.$scope.graphApi.deleteSelectedNodes(); + }; + let title:string = this.$scope.sdcMenu.alertMessages[state].title; + let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([this.$scope.currentComponent.selectedInstance.name]); + this.ModalsHandler.openAlertModal(title, message).then(onOk); + }; + + this.$scope.onComponentInstanceVersionChange = (component:Product):void => { + this.$scope.currentComponent = component; + this.$scope.setComponent(this.$scope.currentComponent); + this.$scope.updateSelectedComponent(); + } + + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html new file mode 100644 index 0000000000..761ae53909 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html @@ -0,0 +1,95 @@ +
+ +
+ + + +
+ +
+ +
+ +
+ +
+
+
+
+
+
+ + +
+ +
+
+ +
+ + + + + + + + + + + + +
+ +
+ + + +
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less new file mode 100644 index 0000000000..501805be3f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less @@ -0,0 +1,873 @@ + +.composition{ + .sdc-workspace-container{ + .w-sdc-main-container{ + .w-sdc-main-right-container{ + left:0; + //overflow-y: scroll; + .sdc-workspace-top-bar { + padding-left: 295px; + .not-latest{ + left: 270px; + } + } + .w-sdc-main-container-body-content{ + padding: 0 0 0 247px; + } + + > div:first-child{ + padding: 0; + } + } + } + } +} + +.workspace-composition { + height:100%; + display: block; + text-align: left; + align-items: left; + padding: 0; + + + + // --------------------------------------------------------------------------------------------------- + // Sidebar + // --------------------------------------------------------------------------------------------------- + + + + .w-sdc-designer-sidebar-toggle { + background-color: @main_color_p; + border-left: 1px solid @main_color_o; + border-bottom: 1px solid @main_color_o; + height: 21px; + position: absolute; + right: 0; + top: 53px; + width: 17px; + transition: right 0.2s; + z-index: 10; + .box-shadow(-1px 1px 3px 0 @main_color_n); + + &.active { + right: 302px; + .w-sdc-designer-sidebar-toggle-icon{ + transform: rotate(180deg); + } + } + + } + + .w-sdc-designer-sidebar-toggle-icon { + margin-left: 6px; + margin-top: 6px; + } + + .w-sdc-designer-sidebar { + background-color:@main_color_p ; + .noselect; + bottom: @footer_height; + position: fixed; + right: -302px; + width: 302px; + top: 102px; + transition: right 0.2s; + z-index: 9; + .box-shadow(-7px -3px 6px -8px @main_color_n); + + } + + .w-sdc-designer-sidebar-toggle.active + .w-sdc-designer-sidebar { + right: 0; + + } + + .w-sdc-designer-sidebar-head { + padding: 36px 30px 30px 30px; + height: 120px; + } + + .w-sdc-designer-sidebar-logo-ph { + display: inline-block; + vertical-align: middle; + line-height: 48px; + height: 48px; + } + + .w-sdc-designer-sidebar-logo { + .g_6; + display: inline-block; + margin-left: 10px; + font-weight: 500; + } + + .w-sdc-designer-sidebar-logo-title { + .s_16_r; + .selectable; + vertical-align: middle; + text-overflow: ellipsis; + max-width: 167px; + display: inline-block; + white-space: nowrap; + overflow: hidden; + } + + .w-sdc-designer-update-resource-icon { + .hand; + position: absolute; + right: 20px; + top: 10px; + } + + .w-sdc-designer-delete-resource-icon { + .hand; + position: absolute; + right: 40px; + top: 10px; + } + + .w-sdc-designer-sidebar-tabs { + .bg_e; + } + + .w-sdc-designer-sidebar-tabs::after { + clear: both; + content: ''; + display: table; + } + + .i-sdc-designer-sidebar-tab { + background-color: @main_color_p; + border: 1px solid @tlv_color_u;; + border-left: none; + display: inline-block; + float: left; + height: 36px; + padding-top: 9px; + text-align: center; + width: 60px; + .hand; + + &:focus { + outline: none; + } + &.tab-disabled { + /* .disabled; */ + } + &.active, &:hover:enabled { + background-color: @tlv_color_u; + .i-sdc-designer-sidebar-tab-icon { + opacity: 1; + + + } + + } + + div& { + padding-top: 0; + } + /*for tooltip on disabled buttons*/ + } + + .i-sdc-designer-sidebar-tab-icon { + margin-top: 5px ; + // opacity: .4; + } + + .w-sdc-designer-sidebar-tab-content { + .perfect-scrollbar; + height: 100%; + } + + .w-sdc-designer-sidebar-tab-content-view { + position: absolute; + top: 156px; + bottom: 0; + width: 100%; + padding-bottom: 10px; + + } + + .w-sdc-designer-sidebar-section { + } + + .w-sdc-designer-sidebar-section-title { + .m_14_m; + background-color: @tlv_color_u; + .hand; + clear: both; + height: 32px; + line-height: 32px; + margin-top: 1px; + padding: 0 40px 0 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + position: relative; + width: 100%; + display: block; + + &.expanded { + .w-sdc-designer-sidebar-section-title-icon { + transform: rotate(180deg); + } + } + } + + .w-sdc-designer-sidebar-section-title-text { + max-width: 240px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + position: relative; + } + + .w-sdc-designer-sidebar-section-title-icon { + .hand; + .sprite-new; + .arrow-up; + right: 16px; + top: 10px; + transition: .3s all; + position: absolute; + } + + .w-sdc-designer-sidebar-section-content { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .w-sdc-designer-sidebar-section-title + .w-sdc-designer-sidebar-section-content { + margin: 0 auto; + } + + .w-sdc-designer-sidebar-section-title.expanded + .w-sdc-designer-sidebar-section-content { + margin: 0 auto 1px; + + } + + .i-sdc-designer-sidebar-section-content-item { + .b_7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + //max-width: 250px; + + &.description { + margin-top: 28px; + white-space: normal; + word-wrap: break-word; + } + } + + .i-sdc-designer-sidebar-section-content-item-tag { + .g_7; + .bg_c; + border-radius: 4px; + display: inline-block; + line-height: 25px; + margin: 0 4px 6px 0; + min-width: 50px; + padding: 0 9px; + text-align: center; + max-width: 280px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .w-sdc-designer-sidebar-section-footer { + margin-top: 10px; + text-align: center; + width: 100%; + } + + + + .w-sdc-designer-sidebar-section-footer-action { + width: 180px; + margin-top: 10px; + } + + //////////////////////Relationship + .w-sdc-designer-sidebar-section-requirements { + border-bottom: 1px solid @color_e; + margin: 0 13px 20px 13px; + padding: 15px 0 0; + } + + .w-sdc-designer-sidebar-section-requirements-item { + margin-bottom: 20px; + } + + .w-sdc-designer-sidebar-section-requirements-label { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + white-space: nowrap; + width: 102px; + } + + .w-sdc-designer-sidebar-section-requirements-select { + border: 1px solid @color_e; + min-height: 30px; + padding: 4px 13px; + width: 168px; + } + + //////////////////////Properties + .i-sdc-designer-sidebar-section-content-item-property-and-attribute { + .b_7; + border-bottom: 1px solid @color_e; + min-height: 72px; + padding: 15px 10px 10px 18px; + position: relative; + + &:first-child { + //margin-top: -18px; + } + + &:hover { + // .bg_c_hover; + .bg_c; + transition: all .3s; + + .i-sdc-designer-sidebar-section-content-item-button { + display: block; + } + } + } + + .i-sdc-designer-sidebar-section-content-item-property-and-attribute-label { + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; + white-space: nowrap; + display: inline-block; + &:hover { + .a_7; + } + } + + .i-sdc-designer-sidebar-section-content-item-property-value { + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; + display: inline-block; + white-space: nowrap; + + } + + .i-sdc-designer-sidebar-section-content-item-property-label-value { + } + + .i-sdc-designer-sidebar-section-content-item-button { + display: none; + position: absolute; + top: 25px; + + &.update { + background-color: transparent; + border: 0; + right: 60px; + } + + &.delete { + background-color: transparent; + border: 0; + right: 13px; + } + + &.download { + background-color: transparent; + border: 0; + right: 35px; + } + + &.download-env { + background-color: transparent; + border: 0; + right: 35px; + margin-top: 65px; + } + + &.update-env { + background-color: transparent; + border: 0; + right: 15px; + margin-top: 65px; + } + + &.attach { + background-color: transparent; + border: 0; + right: 15px; + } + } + + // --------------------------------------------------------------------------------------------------- + // Canvas + // --------------------------------------------------------------------------------------------------- + .w-sdc-designer-canvas { + height:100%; + .noselect; + .bg_c; + bottom: 0; + // position: fixed; + //right: 0; + //left: 240px; + //top: 94px; + .view-mode{ + background-color: #f8f8f8; + border:0; + } + } + + .w-sdc-designer-canvas.sidebaractive { + //right: 300px; + } + + .w-sdc-designer-element { + .hand; + width: 200px; + height: 100px; + position: absolute; + text-align: center; + top: 50%; + margin-top: -200px; + left: 50%; + margin-left: -50px; + } + + .w-sdc-designer-resource-label { + .q_7; + } + + .w-sdc-designer-resource-label-indicator { + .bg_q; + border-radius: 50%; + display: inline-block; + height: 10px; + margin-right: 6px; + vertical-align: middle; + width: 10px; + + &.valid { + .bg_l; + } + + &.invalid { + .bg_h; + } + } + + // --------------------------------------------------------------------------------------------------- + // Leftbar + // --------------------------------------------------------------------------------------------------- + .w-sdc-designer-leftbar { + background-color: @main_color_p; + bottom: 0; + left: 0; + overflow-y: scroll; + overflow-x: hidden; + position: absolute; + top: 0; + width: 244px; + .box-shadow(7px -3px 6px -8px @main_color_n); + + } + + .w-sdc-designer-leftbar-title { + + .p_16_m; + background-color: @main_color_n; + line-height: 40px; + padding: 0 17px; + } + + .w-sdc-designer-leftbar-title-count { + float: right; + } + + .w-scd-diagram-container { + // left: 240px; + //right: 300px; + } + + .w-sdc-designer-leftbar-search { + background-color: @tlv_color_u; + padding: 10px; + white-space: nowrap; + position: relative; + } + + .w-sdc-designer-leftbar-search-input { + border: 1px solid @color_e; + .border-radius(4px); + height: 30px; + margin: 0; + padding: 0px 28px 3px 10px; + vertical-align: 4px; + width: 100%; + outline: none; + font-style: italic; + } + + .w-sdc-designer-leftbar-search-filter { + + } + + .i-sdc-designer-leftbar-section { + .hand; + } + + .i-sdc-designer-leftbar-section-title { + .m_14_m; + background-color: @tlv_color_u; + .hand; + clear: both; + height: 40px; + line-height: 40px; + margin-top: 1px; + padding: 0 10px; + position: relative; + text-transform: uppercase; + font-weight: bold; + } + + .i-sdc-designer-leftbar-section-title-icon { + .hand; + .sprite-new; + .arrow-up; + width: 15px; + height: 9px; + position: absolute; + right: 13px; + top: 18px; + transition: .3s all; + } + + .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-title-icon { + transform: rotate(180deg); + margin-right: 2px; + } + + .i-sdc-designer-leftbar-section-content { + background-color: @main_color_o; + } + + .i-sdc-designer-leftbar-section-content-item { + background-color: @main_color_p; + overflow: hidden; + + &:hover { + background-color: @main_color_p; + } + + .cp{ + margin: 6px; + } + + .vl{ + margin: 6px; + } + } + + .i-sdc-designer-leftbar-section-content-subcat { + .m_14_m; + background-color: @tlv_color_t; + line-height: 35px; + padding: 0 10px; + cursor: default; + + + &:hover { + background-color: @func_color_r; + } + + + } + + .i-sdc-designer-leftbar-section .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-item { + max-height: 0px; + margin: 0 auto; + transition: all .3s; + } + + .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-item { + max-height: 64px; + margin: 0 auto 1px auto; + // padding: 4px 13px; + } + + .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-subcat { + margin: 0; + } + + .i-sdc-designer-leftbar-section-content-item-icon-ph { + display: inline-block; + margin: 12px 0 12px 10px; + pointer-events: auto; + + .non-certified { + position: relative; + left: 27px; + bottom: 6px; + .sprite; + .s-sdc-state-non-certified; + display: block; + + &.smaller-icon { + bottom: 6px; + left: 13px; + } + } + + + + } + + .non-certified { + position: relative; + left: 43px; + bottom: 3px; + .sprite; + .s-sdc-state-non-certified; + display: block; + + &.smaller-icon { + left: 35px; + bottom: -14px; + } + } + /* + .i-sdc-composition-leftbar-section-content-item-icon { + background-image: url('../../../styles/images/resource-icons/default.png'); + // position: absolute; + right: 20px; + top: 10px; + height: 40px; + width: 40px; + background-size: 40px; + } + */ + + .i-sdc-designer-leftbar-section-content-item-info { + display: inline-block; + // margin-left: 10px; + //overflow: hidden; + // vertical-align: middle; + width: 160px; + padding: 0 0 0 10px; + } + + .i-sdc-designer-leftbar-section-content-item-info-title { + .m_14_m; + line-height: 14px; + overflow: hidden; + text-overflow: ellipsis; + text-transform: uppercase; + max-width: 120px; + display: inline-block; + white-space: nowrap; + vertical-align: bottom; + } + + .i-sdc-designer-leftbar-section-content-item-info-text { + .p_3; + line-height: 15px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + //margin: -1px 0 2px 0; + } + + .i-sdc-designer-leftbar-section-content-item-info-text-link { + color: @color_s; + text-decoration: underline; + float: right; + position: absolute; + right: 17px; + // bottom: 5px; + } + + // --------------------------------------------------------------------------------------------------- + // Form actions + // --------------------------------------------------------------------------------------------------- + .w-sdc-form-actions-container.add-property { + text-align: center; + width: 100%; + margin-top: 2px; + margin-bottom: 12px; + + .w-sdc-form-action { + width: 245px; + } + .w-sdc-form-action.add-property-add-another { + .bg_a; + margin-left: 35px; + } + .w-sdc-form-action.add-property-done { + margin-left: 312px; + } + .w-sdc-form-action.save { + margin-left: 327px; + margin-bottom: 30px; + } + + } + + // --------------------------------------------------------------------------------------------------- + // Top menu + // --------------------------------------------------------------------------------------------------- + .w-sdc-header-menu { + padding: 25px 0; + text-align: center; + white-space: nowrap; + } + + .i-sdc-header-menu-item { + cursor: pointer; + display: inline-block; + height: 43px; + min-width: 93px; + padding: 0 38px; + position: relative; + vertical-align: middle; + + &::after { + border-right: 1px solid @color_m; + content: ''; + display: block; + height: 43px; + right: 0; + position: absolute; + top: 0; + width: 2px; + } + + &:first-child { + &::before { + border-right: 1px solid @color_m; + content: ''; + display: block; + height: 43px; + left: 0; + position: absolute; + top: 0; + width: 2px; + } + } + } + + .i-sdc-header-menu-item-icon { + display: inline-block; + height: 20px; + width: 28px; + } + + .i-sdc-header-menu-item-label { + .g_1; + line-height: 18px; + } + + // --------------------------------------------------------------------------------------------------- + // Canvas inline menu + // --------------------------------------------------------------------------------------------------- + .w-sdc-canvas-menu { + position: fixed; + z-index: 100; + + border-style: solid; + border-width: 1px; + border-color: #d8d8d8; + box-sizing: border-box; + background-color: #ffffff; + box-shadow: 0px 2px 2px 0px rgba(24, 24, 25, 0.1); + width: 91px; + + /* &.vl-type-select{ + width: 173px; + } + */ + + h3 { + color: @func_color_s; + font-size: 14px; + font-weight: bold; + margin: 0; + padding: 7px 11px; + border-bottom: 1px solid #e5e5e5; + } + + .w-sdc-canvas-menu-content { + padding: 5px 5px; + + &.vl-select{ + border-bottom: #d8d8d8 solid 1px; + line-height: 15px; + + .tlv-radio { + padding: 3px 0px; + + .tlv-radio-label { + padding: 3px 0px; + + &::before { + margin-right: 10px; + } + } + } + } + + .w-sdc-canvas-menu-content-update-button { + .sprite; + .sprite.e-sdc-small-icon-delete; + .hand; + position: absolute; + top: 15px; + right: 10px; + } + .w-sdc-canvas-menu-content-delete-button { + .sprite; + .sprite.e-sdc-small-icon-delete; + .hand; + margin: 0 8px 0 6px; + } + } + + .w-sdc-canvas-menu-arrow { + //TODO: Missing image for small blue triangle. + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE1OTIzNDI1MENFQjExRTU4ODRERTI1MDM2REZCOUYzIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE1OTIzNDI2MENFQjExRTU4ODRERTI1MDM2REZCOUYzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU5MjM0MjMwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTU5MjM0MjQwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4gBXTlAAAAOElEQVR42mK0rp7NgASMgZgFiE/CBJjQJPcA8U4gNkdXAJMUAGJ+ZEVMaJIwAFfEhEUSRRFAgAEAVtgJyiLAPWAAAAAASUVORK5CYII='); + content: ''; + display: block; + height: 21px; + position: absolute; + right: 12px; + top: -24px; + width: 184px; + background-repeat: no-repeat; + background-position: 175px 16px; + } + + } +} +/*.right-tab-loader { + border: 16px solid #f3f3f3; !* Light grey *! + border-top: 16px solid #3498db; !* Blue *! + border-radius: 50%; + width: 120px; + height: 120px; + animation: spin 2s linear infinite; +}*/ + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts new file mode 100644 index 0000000000..0ac5fd0799 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts @@ -0,0 +1,301 @@ +'use strict'; +import { + ArtifactModel, + Service, + IAppConfigurtaion, + Resource, + Component, + ComponentInstance, + ArtifactGroupModel, + IFileDownload +} from "app/models"; +import {ICompositionViewModelScope} from "../../composition-view-model"; +import {ArtifactsUtils, ModalsHandler, ArtifactGroupType} from "app/utils"; +import {GRAPH_EVENTS} from "app/utils/constants"; +import {EventListenerService} from "app/services/event-listener-service"; + +export interface IArtifactsViewModelScope extends ICompositionViewModelScope { + artifacts:Array; + artifactType:string; + downloadFile:IFileDownload; + isLoading:boolean; + + getTitle():string; + addOrUpdate(artifact:ArtifactModel):void; + delete(artifact:ArtifactModel):void; + download(artifact:ArtifactModel):void; + openEditEnvParametersModal(artifact:ArtifactModel):void; + getEnvArtifact(heatArtifact:ArtifactModel):any; + getEnvArtifactName(artifact:ArtifactModel):string; + isLicenseArtifact(artifact:ArtifactModel):boolean; + isVFiArtifact(artifact:ArtifactModel):boolean; +} + +export class ResourceArtifactsViewModel { + + static '$inject' = [ + '$scope', + '$filter', + '$state', + 'sdcConfig', + 'ArtifactsUtils', + 'ModalsHandler', + '$q', + 'EventListenerService' + ]; + + constructor(private $scope:IArtifactsViewModelScope, + private $filter:ng.IFilterService, + private $state:any, + private sdcConfig:IAppConfigurtaion, + private artifactsUtils:ArtifactsUtils, + private ModalsHandler:ModalsHandler, + private $q:ng.IQService, + private eventListenerService: EventListenerService) { + + this.initScope(); + } + + + private initArtifactArr = (artifactType:string):void => { + let artifacts:Array = []; + + if (this.$scope.selectedComponent) { + if ('interface' == artifactType) { + let interfaces = this.$scope.currentComponent.interfaces; + if (interfaces && interfaces.standard && interfaces.standard.operations) { + + angular.forEach(interfaces.standard.operations, (operation:any, interfaceName:string):void => { + let item:ArtifactModel = {}; + if (operation.implementation) { + item = operation.implementation; + } + item.artifactDisplayName = interfaceName; + item.artifactLabel = interfaceName; + item.mandatory = false; + artifacts.push(item); + }); + } + } else { + //init normal artifacts, deployment or api artifacts + let artifactsObj:ArtifactGroupModel; + switch (artifactType) { + case "api": + artifactsObj = (this.$scope.currentComponent).serviceApiArtifacts; + break; + case "deployment": + if (!this.$scope.isComponentInstanceSelected()) { + artifactsObj = this.$scope.currentComponent.deploymentArtifacts; + } else { + artifactsObj = this.$scope.currentComponent.selectedInstance.deploymentArtifacts; + } + break; + default: + //artifactsObj = this.$scope.selectedComponent.artifacts; + if (!this.$scope.isComponentInstanceSelected()) { + artifactsObj = this.$scope.currentComponent.artifacts; + } else { + artifactsObj = this.$scope.currentComponent.selectedInstance.artifacts; + } + break; + } + _.forEach(artifactsObj, (artifact:ArtifactModel, key) => { + artifacts.push(artifact); + }); + } + } + this.$scope.artifacts = artifacts; + }; + + + private convertToArtifactUrl = (artifactType:string):string => { + + switch (artifactType) { + case 'deployment': + return 'DEPLOYMENT'; + case 'api': + return 'SERVICE_API'; + default: + return 'INFORMATIONAL'; + } + + } + + private loadComponentArtifactIfNeeded = (forceLoad?: boolean) => { + + let onGetComponentArtifactsSuccess = (artifacts:ArtifactGroupModel)=> { + switch (this.$scope.artifactType) { + case 'deployment': + this.$scope.currentComponent.deploymentArtifacts = artifacts; + break; + case 'api': + (this.$scope.currentComponent).serviceApiArtifacts = artifacts; + break; + default: + this.$scope.currentComponent.artifacts = artifacts; + break; + } + this.$scope.isLoading = false; + this.initArtifactArr(this.$scope.artifactType); + } + + let onError = ()=> { + this.$scope.isLoading = false; + }; + + switch (this.$scope.artifactType) { + case 'deployment': + if(forceLoad || !this.$scope.currentComponent.deploymentArtifacts) { + this.$scope.component.getArtifactByGroupType(this.convertToArtifactUrl(this.$scope.artifactType)).then(onGetComponentArtifactsSuccess, onError); + } else { + this.initArtifactArr(this.$scope.artifactType); + } + + break; + case 'api': + if(!(this.$scope.currentComponent).serviceApiArtifacts) { + this.$scope.component.getArtifactByGroupType(this.convertToArtifactUrl(this.$scope.artifactType)).then(onGetComponentArtifactsSuccess, onError); + } else { + this.initArtifactArr(this.$scope.artifactType); + } + break; + default: + if(!this.$scope.currentComponent.artifacts) { + this.$scope.component.getArtifactByGroupType(this.convertToArtifactUrl(this.$scope.artifactType)).then(onGetComponentArtifactsSuccess, onError); + } else { + this.initArtifactArr(this.$scope.artifactType); + } + break; + } + } + private loadArtifacts = (forceLoad?: boolean):void => { + + let onGetInstanceArtifactsSuccess = (artifacts:ArtifactGroupModel)=> { + switch (this.$scope.artifactType) { + case 'deployment': + this.$scope.currentComponent.selectedInstance.deploymentArtifacts = artifacts; + break; + default: + this.$scope.currentComponent.selectedInstance.artifacts = artifacts; + break; + } + this.loadComponentArtifactIfNeeded(); + }; + + let onError = ()=> { + this.$scope.isLoading = false; + }; + + this.$scope.isLoading = true; + if (this.$scope.isComponentInstanceSelected()) { + this.$scope.component.getComponentInstanceArtifactsByGroupType(this.$scope.component.selectedInstance.uniqueId, this.convertToArtifactUrl(this.$scope.artifactType)).then(onGetInstanceArtifactsSuccess, onError); + } else { + this.loadComponentArtifactIfNeeded(forceLoad); + } + } + + private updateArtifactsIfNeeded = ():void => { + if (this.$scope.artifactType === "deployment") { + this.loadArtifacts(true); + } else { + this.initArtifactArr(this.$scope.artifactType); + } + }; + + private openEditArtifactModal = (artifact:ArtifactModel):void => { + this.ModalsHandler.openArtifactModal(artifact, this.$scope.currentComponent).then(():void => { + this.updateArtifactsIfNeeded(); + }); + }; + + private initScope = ():void => { + + this.$scope.isLoading = false; + this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name); + this.loadArtifacts(); + this.$scope.getTitle = ():string => { + return this.artifactsUtils.getTitle(this.$scope.artifactType, this.$scope.currentComponent); + }; + + this.$scope.isVFiArtifact = (artifact:ArtifactModel):boolean=> { + if (artifact.artifactGroupType === ArtifactGroupType.INFORMATION) {//fix DE256847 + return this.$scope.currentComponent.artifacts && (!this.$scope.currentComponent.artifacts[artifact.artifactLabel] || !this.$scope.currentComponent.artifacts[artifact.artifactLabel].artifactName); + } + return this.$scope.currentComponent.deploymentArtifacts && (!this.$scope.currentComponent.deploymentArtifacts[artifact.artifactLabel]);//fix DE251314 + }; + + this.$scope.addOrUpdate = (artifact:ArtifactModel):void => { + this.artifactsUtils.setArtifactType(artifact, this.$scope.artifactType); + let artifactCopy = new ArtifactModel(artifact); + this.openEditArtifactModal(artifactCopy); + }; + + + this.$scope.delete = (artifact:ArtifactModel):void => { + + let onOk = ():void => { + this.$scope.isLoading = true; + this.artifactsUtils.removeArtifact(artifact, this.$scope.artifacts); + + let success = (responseArtifact:ArtifactModel):void => { + this.initArtifactArr(this.$scope.artifactType); + this.$scope.isLoading = false; + }; + + let error = (error:any):void => { + console.log('Delete artifact returned error:', error); + this.initArtifactArr(this.$scope.artifactType); + this.$scope.isLoading = false; + }; + if (this.$scope.isComponentInstanceSelected()) { + this.$scope.currentComponent.deleteInstanceArtifact(artifact.uniqueId, artifact.artifactLabel).then(success, error); + } else { + this.$scope.currentComponent.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(success, error);//TODO simulate error (make sure error returns) + } + }; + let title:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + + this.$scope.getEnvArtifact = (heatArtifact:ArtifactModel):any=> { + return _.find(this.$scope.artifacts, (item:ArtifactModel)=> { + return item.generatedFromId === heatArtifact.uniqueId; + }); + }; + + this.$scope.getEnvArtifactName = (artifact:ArtifactModel):string => { + let envArtifact = this.$scope.getEnvArtifact(artifact); + if (envArtifact) { + return envArtifact.artifactDisplayName; + } + }; + + this.$scope.isLicenseArtifact = (artifact:ArtifactModel):boolean => { + let isLicense:boolean = false; + if (this.$scope.component.isResource() && (this.$scope.component).isCsarComponent()) { + isLicense = this.artifactsUtils.isLicenseType(artifact.artifactType); + } + + return isLicense; + }; + + this.$scope.openEditEnvParametersModal = (artifact:ArtifactModel):void => { + this.ModalsHandler.openEditEnvParametersModal(artifact, this.$scope.currentComponent).then(()=> { + this.updateArtifactsIfNeeded(); + }, ()=> { + // ERROR + }); + }; + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, this.loadArtifacts); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, this.loadArtifacts); + + this.$scope.$on('$destroy', () => { + + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_NODE_SELECTED, this.loadArtifacts); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, this.loadArtifacts); + }); + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html new file mode 100644 index 0000000000..b0d81b3437 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html @@ -0,0 +1,67 @@ + +
+ + +
+
+ +
+
+
+ +
+
+
+ +
+ + + +
+
+ +
+ Description:{{artifact.description}} +
+
+ + + + + + + + +
+
+ +
+ +
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less new file mode 100644 index 0000000000..7c8b8315d9 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less @@ -0,0 +1,172 @@ +.w-sdc-designer-sidebar-tab-content.artifacts { + + .i-sdc-designer-sidebar-section-content-item-artifact.hand { + .hand; + } + + .w-sdc-designer-sidebar-section-content { + padding: 0; + } + .w-sdc-designer-sidebar-section-title { + &.expanded { + margin-bottom: 0; + } + } + + .i-sdc-designer-sidebar-section-content-item-artifact-details { + display: inline-block; + margin-left: 5px; + vertical-align: middle; + width: 180px; + &.heat { + line-height: 18px; + width: 250px; + } + } + + .i-sdc-designer-sidebar-section-content-item-artifact-details-name { + .g_7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width:220px; + display: inline-block; + //text-transform: capitalize; + &.enabled { + &:hover { + .a_7; + } + } + + } + + .i-sdc-designer-sidebar-section-content-item-artifact-heat-env { + .g_7; + margin-top: 6px; + line-height: 42px; + padding-top: 10px; + border-top:1px solid #c8cdd1; + .enabled { + &:hover { + .hand; + .a_7; + } + } + } + + .i-sdc-designer-sidebar-section-content-item-artifact-filename { + .g_7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 225px; + display: inline-block; + .bold; + &.enabled { + &:hover { + .a_7; + } + } + } + + + .i-sdc-designer-sidebar-section-content-item-file-link{ + border-left: 1px #848586 solid; + height: 58px; + margin-left: -11px; + margin-top: 11px; + border-top: 1px #848586 solid; + border-bottom: 1px #848586 solid; + width: 12px; + float: left; + } + + .i-sdc-designer-sidebar-section-content-item-artifact-details-desc { + display: none; + line-height: 16px; + word-wrap: break-word; + white-space: normal; + } + + .i-sdc-designer-sidebar-section-content-item-artifact-details-desc-label { + .b_3; + } + + + .i-sdc-designer-sidebar-section-content-item-artifact { + border-bottom: 1px solid #c8cdd1; + padding: 5px 10px 5px 18px; + position: relative; + // line-height: 36px; + min-height: 61px; + //cursor: default; + display: flex; + align-items: center; + + + .i-sdc-designer-sidebar-section-content-item-button { + top: 20px; + line-height: 10px; + } + + &:hover { + //background-color: @color_c; + .bg_c; + transition: all .3s; + + .i-sdc-designer-sidebar-section-content-item-button { + display: block; + + } + + } + } + +} + +///////////////////Lifecycle Management +.i-sdc-designer-sidebar-section-content-item-lm { + .b_7; + border-bottom: 1px solid @color_e; + cursor: pointer; + height: 65px; + padding: 22px 0; + position: relative; + + &:hover { + .bg_c_hover; + margin-left: -10px; + margin-right: -10px; + padding: 22px 10px; + + .i-sdc-designer-sidebar-section-content-item-lm-icon { + right: 16px; + } + } +} + +.i-sdc-designer-sidebar-section-content-item-lm:first-child { + margin-top: -18px; +} + +.i-sdc-designer-sidebar-section-content-item-lm-icon { + position: absolute; + right: 6px; + + //TODO: Replace the icons. + &.icon-view { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAJCAYAAAACTR1pAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdGMDNBRUJDMDkxNjExRTVCMjRBOEI5QzMxQTlBQjY4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdGMDNBRUJEMDkxNjExRTVCMjRBOEI5QzMxQTlBQjY4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6N0YwM0FFQkEwOTE2MTFFNUIyNEE4QjlDMzFBOUFCNjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6N0YwM0FFQkIwOTE2MTFFNUIyNEE4QjlDMzFBOUFCNjgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4U2decAAABRUlEQVR42pyRPUvDUBSG39t2UyyaBgvWQZMqKoFq4lBH0U0UqXSxKPaPieCoWPAPKIgdjG1ohaghOrSR1iaLKMXpmnMhQVfPcLmc8zxwPhjnHFE4rx3eMO/w4rr4+vwQuZHRMcwqClaMVeRnplnEskg8O7/g96YJSZLgBz5+R0bKIAgC6IaBvdKukBP0HJ+cctu2cVCtQsrIUFUVlcp+LE6EItWIIVaIV9c3/PnRRqlcxkJeYV2vi+/hEO/9QSx6b56oEUMsOal6/TacQcXinCpaGE+n0QnljufFIuUoiAlZTk6iWFyD6zpoWi3RwvrmBlLJZCzRn3IUxBBLjlgO9e06T9ja3sGyrqPX66NtWQLWCgVks5Owmg1c1mpQ8vM4OqywP1s1w1PkpnJY0jTIsizy/sDHQ7sFmt0ITxJtlf33jj8CDADhB52tEX6ifAAAAABJRU5ErkJggg=='); + height: 9px; + top: 29px; + width: 14px; + } + + //TODO: Replace the icons. + &.icon-alert { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAANCAYAAAB2HjRBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjhBM0YxQTBCMDkyMDExRTVBNzlCQUYxNEYwMDUwOTQ5IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjhBM0YxQTBDMDkyMDExRTVBNzlCQUYxNEYwMDUwOTQ5Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OEEzRjFBMDkwOTIwMTFFNUE3OUJBRjE0RjAwNTA5NDkiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OEEzRjFBMEEwOTIwMTFFNUE3OUJBRjE0RjAwNTA5NDkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7exgceAAAB5klEQVR42oySy2tTQRTGfzN3cpvbpFTaUlzUjeCuVFyJf4DuCl34wL/CrbpUunLnwpUuFFy48IFiwYWhpYuW+gCLrbWKBQumaRIba5ub3MccJ5WECCV44MDMme873/lmBhGhnZulcn/3/sfrO7J/45R017pT0xUS2bC93vy2LAOLt5DfCeUn14RD4h/ysbHRDii/8pyo7DmFiP7VZ4Rbu9KT3I7i+qJklp7ibQt2J6HeNOhXV/gv8tDcbaLtXYLJKeTcZfxqSPPzvGu6ID3J1Q8z0ny/hIkM5swF8qenUFUfwixHCtd7Kwez04jOgEmc+hq6tuZ8N9E6xG5U+Pn2kRxKLs7eE7tRc0XlToRM2sD+2sGmDiY+kWsSFG528Kr1Xgfjfn0n5uElvFrrzVqVDHqkiTKjxCsV9LBx08QkgcY/eZH+89Oqo+x9eumkwQaQaoPEKWpsHHN8ArItgLt5UdiG6736mFLxixyQt+bviik8QPuaNFRIkjqQRZ2YJDo6QWydjTRtOcGPPdJKgnlxFfN9fVmGNhecR4U3mLrL6nPTRTQHXZO5+4jyyI4Iqs99GJvgBiKjcySlj3897y6/kbreI4w0QS7ASxrsa4/cXkxkIzK5PKmNqRMw7NfdeczA+Fn1R4ABAPnMAeCjkgf5AAAAAElFTkSuQmCC'); + height: 13px; + top: 27px; + width: 15px; + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts new file mode 100644 index 0000000000..a81bb9176e --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts @@ -0,0 +1,132 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +'use strict'; +import {Component} from "app/models"; +import {GRAPH_EVENTS} from "app/utils"; +import {LeftPaletteLoaderService, EventListenerService} from "app/services"; +import {ICompositionViewModelScope} from "../../composition-view-model"; +import {LeftPaletteComponent} from "../../../../../../models/components/displayComponent"; + +export interface IEditResourceVersion { + allVersions:any; + changeVersion:string; +} + +interface IDetailsViewModelScope extends ICompositionViewModelScope { + isLoading:boolean; + $parent:ICompositionViewModelScope; + expandedSection:Array; + editForm:ng.IFormController; + editResourceVersion:IEditResourceVersion; + + changeResourceVersion():void; +} + +export class DetailsViewModel { + + static '$inject' = [ + '$scope', + 'LeftPaletteLoaderService', + 'EventListenerService' + + ]; + + constructor(private $scope:IDetailsViewModelScope, + private LeftPaletteLoaderService:LeftPaletteLoaderService, + private eventListenerService:EventListenerService) { + this.initScope(); + } + + private clearSelectedVersion = ():void => { + this.$scope.editResourceVersion = { + allVersions: {}, + changeVersion: null + }; + }; + + private versioning:Function = (versionNumber:string):string => { + let version:Array = versionNumber.split('.'); + return '00000000'.slice(version[0].length) + version[0] + '.' + '00000000'.slice(version[1].length) + version[1]; + }; + + private initEditResourceVersion = ():void => { + this.clearSelectedVersion(); + this.$scope.editResourceVersion.allVersions[this.$scope.currentComponent.selectedInstance.componentVersion] = this.$scope.currentComponent.selectedInstance.componentUid; + _.merge(this.$scope.editResourceVersion.allVersions, angular.copy(this.$scope.selectedComponent.allVersions)); + let sorted:any = _.sortBy(_.toPairs(this.$scope.editResourceVersion.allVersions), (item)=> { + return this.versioning(item[0]); + }); + this.clearSelectedVersion(); + _.forEach(sorted, (item)=> { + this.$scope.editResourceVersion.allVersions[item[0]] = item[1]; + }); + + let highestVersion = _.last(Object.keys(this.$scope.selectedComponent.allVersions)); + + if (parseFloat(highestVersion) % 1) { //if highest is minor, make sure it is the latest checked in - + let latestVersionComponent:LeftPaletteComponent = _.find(this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(this.$scope.currentComponent.componentType), (component:LeftPaletteComponent) => { //latest checked in + return (component.systemName === this.$scope.selectedComponent.systemName + || component.uuid === this.$scope.selectedComponent.uuid); + }); + let latestVersion:string = latestVersionComponent ? latestVersionComponent.version : highestVersion; + + if (highestVersion != latestVersion) { //highest is checked out - remove from options + this.$scope.editResourceVersion.allVersions = _.omit(this.$scope.editResourceVersion.allVersions, highestVersion); + } + } + this.$scope.editResourceVersion.changeVersion = this.$scope.currentComponent.selectedInstance.componentVersion; + }; + + private initScope = ():void => { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + this.$scope.expandedSection = ['general', 'tags']; + //this.clearSelectedVersion(); + + this.$scope.$watch('selectedComponent', (component:Component) => { + if (this.$scope.isComponentInstanceSelected()) { + this.initEditResourceVersion(); + } + }); + + this.$scope.changeResourceVersion = ():void => { + this.$scope.isLoading = true; + this.$scope.$parent.isLoading = true; + + let onSuccess = (component:Component)=> { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + this.$scope.onComponentInstanceVersionChange(component); + + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_VERSION_CHANGED, this.$scope.currentComponent); + }; + + let onFailed = (error:any)=> { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + console.log(error); + }; + + let componentUid:string = this.$scope.editResourceVersion.allVersions[this.$scope.editResourceVersion.changeVersion]; + this.$scope.currentComponent.changeComponentInstanceVersion(componentUid).then(onSuccess, onFailed); + }; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html new file mode 100644 index 0000000000..70dc58075a --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html @@ -0,0 +1,136 @@ + + +
+ + + + General Info +
+
+ +
+
+ Type: + +
+
+ Resource Type: + +
+
+ + Version: + + + + +
+
+ Category: + +
+
+ Sub Category: + +
+
+ Creation Date: + +
+
+ Author: + + +
+
+ + +
+
+ Vendor Name: + + +
+
+ Vendor Release: + + +
+
+ + +
+
+ Resource Customization UUID:
+
+
+
+ Description: + + + +
+ +
+
+ +
+ + Additional Information +
+
+ +
+
+ + : + +
+
+
+ + +
+ + Tags +
+
+ +
+
+ + +
+
+
+ + +
+ diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details.less new file mode 100644 index 0000000000..841ab3aa49 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details.less @@ -0,0 +1,72 @@ +.w-sdc-designer-sidebar-tab-content.details { + + .w-sdc-designer-sidebar-section-title + .w-sdc-designer-sidebar-section-content { + padding: 0 10px 0 18px; + } + + .w-sdc-designer-sidebar-section-title.expanded + .w-sdc-designer-sidebar-section-content { + padding: 10px 10px 10px 18px; + } + + .i-sdc-designer-sidebar-section-content-item-label { + font-weight: bold; + &.additional-information{ + max-width:100px; + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + vertical-align: bottom; + } + + } + + + + .i-sdc-designer-sidebar-section-content-item-value { + // .hyphenate; + padding-left: 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + max-width: 160px; + vertical-align:bottom; + font-weight: normal; + &.additional-information{ + max-width:160px; + display: inline-block; + } + &.i-sdc-form-select { + .b_1; + border: 1px solid @border_color_f; + width: 210px; + max-width: 210px; + padding-left: 4px; + + .select-instance-version { + .b_1; + &.minor { + .h_1; + } + } + } + &.minor { + .h_1; + } + } + .i-sdc-designer-sidebar-section-content-description-item-value{ + max-width: none; + font-weight: normal; + } + + .customization-uuid{ + .f-type._12_m; + } + + .w-sdc-designer-sidebar-section.tags { + .i-sdc-designer-sidebar-section-content-item { + white-space: normal; + } + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts new file mode 100644 index 0000000000..84769d7a62 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts @@ -0,0 +1,217 @@ +'use strict'; +import { + AttributeModel, + AttributesGroup, + Component, + ComponentInstance, + PropertyModel, + PropertiesGroup +} from "app/models"; +import {ICompositionViewModelScope} from "../../composition-view-model"; +import {ModalsHandler} from "app/utils"; +import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; + +interface IResourcePropertiesAndAttributesViewModelScope extends ICompositionViewModelScope { + properties:PropertiesGroup; + attributes:AttributesGroup; + propertiesMessage:string; + groupPropertiesByInstance:boolean; + showGroupsOfInstanceProperties:Array; + addProperty():void; + updateProperty(property:PropertyModel):void; + deleteProperty(property:PropertyModel):void; + viewAttribute(attribute:AttributeModel):void; + groupNameByKey(key:string):string; + isPropertyOwner():boolean; + getComponentInstanceNameFromInstanceByKey(key:string):string; +} + +export class ResourcePropertiesViewModel { + + static '$inject' = [ + '$scope', + '$filter', + '$uibModal', + 'ModalsHandler', + 'ComponentServiceNg2' + + ]; + + + constructor(private $scope:IResourcePropertiesAndAttributesViewModelScope, + private $filter:ng.IFilterService, + private $uibModal:ng.ui.bootstrap.IModalService, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2:ComponentServiceNg2) { + + this.getComponentInstancesPropertiesAndAttributes(); + } + + private initComponentProperties = ():void => { + let result:PropertiesGroup = {}; + + if (this.$scope.selectedComponent) { + this.$scope.propertiesMessage = undefined; + this.$scope.groupPropertiesByInstance = false; + if (this.$scope.isComponentInstanceSelected()) { + if (this.$scope.currentComponent.selectedInstance.originType === 'VF') { + this.$scope.groupPropertiesByInstance = true; + } + result[this.$scope.currentComponent.selectedInstance.uniqueId] = this.$scope.currentComponent.componentInstancesProperties[this.$scope.currentComponent.selectedInstance.uniqueId]; + } else if (this.$scope.currentComponent.isService()) { + // Temporally fix to hide properties for service (UI stack when there are many properties) + result = this.$scope.currentComponent.componentInstancesProperties; + this.$scope.propertiesMessage = "Note: properties for service are disabled"; + } else { + let key = this.$scope.selectedComponent.uniqueId; + result[key] = Array(); + let derived = Array(); + _.forEach(this.$scope.selectedComponent.properties, (property:PropertyModel) => { + if (key == property.parentUniqueId) { + result[key].push(property); + } else { + property.readonly = true; + derived.push(property); + } + }); + if (derived.length) { + result['derived'] = derived; + } + } + this.$scope.properties = result; + } + }; + + + private initComponentAttributes = ():void => { + let result:AttributesGroup = {}; + + if (this.$scope.selectedComponent) { + if (this.$scope.isComponentInstanceSelected()) { + result[this.$scope.currentComponent.selectedInstance.uniqueId] = this.$scope.currentComponent.componentInstancesAttributes[this.$scope.currentComponent.selectedInstance.uniqueId]; + } else if (this.$scope.currentComponent.isService()) { + result = this.$scope.currentComponent.componentInstancesAttributes; + } + this.$scope.attributes = result; + } + }; + + /** + * This function is checking if the component is the value owner of the current property + * in order to notify the edit property modal which fields to disable + */ + private isPropertyValueOwner = ():boolean => { + return this.$scope.currentComponent.isService() || !!this.$scope.currentComponent.selectedInstance; + }; + + /** + * The function opens the edit property modal. + * It checks if the property is from the VF or from one of it's resource instances and sends the needed property list. + * For create property reasons an empty array is transferd + * + * @param property the wanted property to edit/create + */ + private openEditPropertyModal = (property:PropertyModel):void => { + this.ModalsHandler.openEditPropertyModal(property, + this.$scope.component, + (this.$scope.isPropertyOwner() ? + this.$scope.properties[property.parentUniqueId] : + this.$scope.properties[property.resourceInstanceUniqueId]) || [], + this.isPropertyValueOwner()).then(() => { + }); + }; + + private openAttributeModal = (atrribute:AttributeModel):void => { + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + template: 'app/view-models/forms/attribute-form/attribute-form-view.html', + controller: 'Sdc.ViewModels.AttributeFormViewModel', + size: 'sdc-md', + backdrop: 'static', + keyboard: false, + resolve: { + attribute: ():AttributeModel => { + return atrribute; + }, + component: ():Component => { + return this.$scope.currentComponent; + } + } + }; + this.$uibModal.open(modalOptions); + }; + + private getComponentInstancesPropertiesAndAttributes = () => { + + this.ComponentServiceNg2.getComponentInstanceAttributesAndProperties(this.$scope.currentComponent).subscribe((genericResponse:ComponentGenericResponse) => { + this.$scope.currentComponent.componentInstancesAttributes = genericResponse.componentInstancesAttributes; + this.$scope.currentComponent.componentInstancesProperties = genericResponse.componentInstancesProperties; + this.initScope(); + }); + }; + + private initScope = ():void => { + + + this.initComponentProperties(); + this.initComponentAttributes(); + + this.$scope.$watchCollection('currentComponent.properties', (newData:any):void => { + this.initComponentProperties(); + }); + + this.$scope.$watch('currentComponent.selectedInstance', (newInstance:ComponentInstance):void => { + if (angular.isDefined(newInstance)) { + this.initComponentProperties(); + this.initComponentAttributes(); + + } + }); + + this.$scope.isPropertyOwner = ():boolean => { + return this.$scope.currentComponent && this.$scope.currentComponent.isResource() && !this.$scope.isComponentInstanceSelected(); + }; + + this.$scope.updateProperty = (property:PropertyModel):void => { + this.openEditPropertyModal(property); + }; + + this.$scope.deleteProperty = (property:PropertyModel):void => { + + let onOk = ():void => { + this.$scope.currentComponent.deleteProperty(property.uniqueId); + }; + + let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + this.$scope.viewAttribute = (attribute:AttributeModel):void => { + this.openAttributeModal(attribute); + }; + + this.$scope.groupNameByKey = (key:string):string => { + switch (key) { + case 'derived': + return "Derived"; + + case this.$scope.currentComponent.uniqueId: + return this.$filter("resourceName")(this.$scope.currentComponent.name); + + default: + return this.$filter("resourceName")((_.find(this.$scope.currentComponent.componentInstances, {uniqueId: key})).name); + } + }; + + this.$scope.getComponentInstanceNameFromInstanceByKey = (key:string):string => { + let instanceName:string = ""; + if (key !== undefined && this.$scope.selectedComponent.uniqueId == this.$scope.currentComponent.selectedInstance.componentUid) { + instanceName = this.$filter("resourceName")((_.find(this.$scope.selectedComponent.componentInstances, {uniqueId: key})).name); + } + return instanceName; + }; + + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html new file mode 100644 index 0000000000..6df8b6a4d6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html @@ -0,0 +1,117 @@ + + +
+ + + + +
+
+ +
+
+ +
+
+
+
+ {{property.name}} +
+
+ {{property.defaultValue}} + {{property.value}} +
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ {{instanceProperty.name}} +
+
+ + {{instanceProperty.value === undefined ? instanceProperty.defaultValue : instanceProperty.value}} +
+
+
+
+
+ + + + + +
+ + + + +
+
+ +
+
+ +
+
+
+
+ {{attribute.name}} +
+
+ {{attribute.defaultValue}} + {{attribute.value}} +
+
+
+ +
+ +
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less new file mode 100644 index 0000000000..41a90bff9d --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less @@ -0,0 +1,38 @@ +.w-sdc-designer-sidebar-tab-content.properties { + .i-sdc-designer-sidebar-section-content-item-property-and-attribute-label{ + font-weight: bold; + } + .i-sdc-designer-sidebar-section-content-item-button.update{ + right: 17px; + } + .i-sdc-designer-sidebar-section-content-item-button.delete{ + right: 35px; + } + + .w-sdc-designer-sidebar-properties-disabled { + .s_14_m; + padding: 20px 20px; + } + + .vfci-properties-group{ + background-color: @func_color_r; + } + + .expand-collapse-title-icon{ + .hand; + .sprite-new; + .expand-collapse-plus-icon; + vertical-align: middle; + margin: 0 6px; + } + + .expanded { + .expand-collapse-title-icon { + .expand-collapse-minus-icon; + } + } + + .w-sdc-designer-sidebar-section-title-text{ + vertical-align: middle; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts new file mode 100644 index 0000000000..325f250ebe --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts @@ -0,0 +1,156 @@ +'use strict'; +import {ICompositionViewModelScope} from "../../composition-view-model"; +import {CapabilitiesGroup, Requirement, RequirementsGroup} from "app/models"; +import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; +import {GRAPH_EVENTS} from "app/utils"; +import {EventListenerService} from "app/services"; +import {ComponentInstance, Capability} from "app/models"; + +interface IRelationsViewModelScope extends ICompositionViewModelScope { + isLoading:boolean; + $parent:ICompositionViewModelScope; + getRelation(requirement:any):any; + capabilities:Array; + requirements:Array; + + //for complex components + capabilitiesInstancesMap:InstanceCapabilitiesMap; + requirementsInstancesMap:InstanceRequirementsMap; +} +export class InstanceCapabilitiesMap { + [key:string]:Array; +} + +export class InstanceRequirementsMap { + [key:string]:Array; +} + +export class RelationsViewModel { + + static '$inject' = [ + '$scope', + '$filter', + 'ComponentServiceNg2', + 'EventListenerService' + ]; + + constructor(private $scope:IRelationsViewModelScope, + private $filter:ng.IFilterService, + private ComponentServiceNg2:ComponentServiceNg2, + private eventListenerService:EventListenerService) { + this.initScope(); + } + + private loadComplexComponentData = () => { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.currentComponent.componentType, this.$scope.currentComponent.uniqueId).subscribe((response:ComponentGenericResponse) => { + this.$scope.currentComponent.capabilities = response.capabilities; + this.$scope.currentComponent.requirements = response.requirements; + this.setScopeCapabilitiesRequirements(this.$scope.currentComponent.capabilities, this.$scope.currentComponent.requirements); + this.initInstancesMap(); + this.$scope.isLoading = false; + }); + } + + + private extractValuesFromMap = (map:CapabilitiesGroup | RequirementsGroup):Array => { + let values = []; + _.forEach(map, (capabilitiesOrRequirements:Array | Array, key) => { + values = values.concat(capabilitiesOrRequirements) + } + ); + return values; + } + + private setScopeCapabilitiesRequirements = (capabilities:CapabilitiesGroup, requirements:RequirementsGroup) => { + this.$scope.capabilities = this.extractValuesFromMap(capabilities); + this.$scope.requirements = this.extractValuesFromMap(requirements); + } + + + private initInstancesMap = ():void => { + + this.$scope.capabilitiesInstancesMap = new InstanceCapabilitiesMap(); + _.forEach(this.$scope.capabilities, (capability:Capability) => { + if (this.$scope.capabilitiesInstancesMap[capability.ownerName]) { + this.$scope.capabilitiesInstancesMap[capability.ownerName] = this.$scope.capabilitiesInstancesMap[capability.ownerName].concat(capability); + } else { + this.$scope.capabilitiesInstancesMap[capability.ownerName] = new Array(capability); + } + }); + + this.$scope.requirementsInstancesMap = new InstanceRequirementsMap(); + _.forEach(this.$scope.requirements, (requirement:Requirement) => { + if (this.$scope.requirementsInstancesMap[requirement.ownerName]) { + this.$scope.requirementsInstancesMap[requirement.ownerName] = this.$scope.requirementsInstancesMap[requirement.ownerName].concat(requirement); + } else { + this.$scope.requirementsInstancesMap[requirement.ownerName] = new Array(requirement); + } + }); + } + + private initRequirementsAndCapabilities = (needUpdate?: boolean) => { + + // if instance selected, we take the requirement and capabilities of the instance - always exist because we load them with the graph + if (this.$scope.isComponentInstanceSelected()) { + this.$scope.isLoading = false; + this.setScopeCapabilitiesRequirements(this.$scope.currentComponent.selectedInstance.capabilities, this.$scope.currentComponent.selectedInstance.requirements); + if (this.$scope.currentComponent.selectedInstance.originType === 'VF') { + this.initInstancesMap(); + } + } else { + // if instance not selected, we take the requirement and capabilities of the VF/SERVICE, if not exist we call api + if (needUpdate || !this.$scope.currentComponent.capabilities || !this.$scope.currentComponent.requirements) { + this.loadComplexComponentData(); + + } else { + this.$scope.isLoading = false; + this.setScopeCapabilitiesRequirements(this.$scope.currentComponent.capabilities, this.$scope.currentComponent.requirements); + this.initInstancesMap(); + } + } + } + + private updateRequirementCapabilities = () => { + if (!this.$scope.isComponentInstanceSelected()) { + this.loadComplexComponentData(); + } + } + + private initEvents = ():void => { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, this.initRequirementsAndCapabilities); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, this.updateRequirementCapabilities); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE, this.updateRequirementCapabilities); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, this.updateRequirementCapabilities); + } + + private initScope = ():void => { + + this.$scope.requirements = []; + this.$scope.capabilities = []; + + this.initEvents(); + this.initRequirementsAndCapabilities(); + + this.$scope.isCurrentDisplayComponentIsComplex = ():boolean => { + if (this.$scope.isComponentInstanceSelected()) { + if (this.$scope.currentComponent.selectedInstance.originType === 'VF') { + return true; + } + return false; + } else { + return this.$scope.currentComponent.isComplex(); + } + } + + this.$scope.$on('$destroy', () => { + + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_NODE_SELECTED, this.initRequirementsAndCapabilities); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, this.updateRequirementCapabilities); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE, this.updateRequirementCapabilities); + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, this.updateRequirementCapabilities); + }); + + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view.html new file mode 100644 index 0000000000..5ecb12cd6f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view.html @@ -0,0 +1,61 @@ + +
+
+ Capabilities +
+
+
+ +
+
+
+ Requirements +
+
+ +
+ +
+
+
+ +
+
+ Capabilities +
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+ +
+ Requirements +
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations.less new file mode 100644 index 0000000000..c3b224d5a6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations.less @@ -0,0 +1,14 @@ +.w-sdc-designer-sidebar-tab-content.relations { + + .w-sdc-designer-sidebar-section-content { + padding: 0; + } + + .w-sdc-designer-sidebar-section-title { + &.expanded { + margin-bottom: 0; + } + } +} + + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.html new file mode 100644 index 0000000000..2070041990 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.html @@ -0,0 +1,13 @@ + + +
+ + Composition +
+
+ +
+ +
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts new file mode 100644 index 0000000000..1c28a46d37 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts @@ -0,0 +1,14 @@ +'use strict'; +import {ICompositionViewModelScope} from "../../composition-view-model"; + +interface IStructureViewModel extends ICompositionViewModelScope { +} + +export class StructureViewModel { + static '$inject' = [ + '$scope' + ]; + + constructor(private $scope:IStructureViewModel) { + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-description-popover.html b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-description-popover.html new file mode 100644 index 0000000000..0d7d5dc8f4 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-description-popover.html @@ -0,0 +1,23 @@ + +
+ +
+ + +
+ + + +
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts new file mode 100644 index 0000000000..2816c312f9 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts @@ -0,0 +1,276 @@ +//@require "./*.html" +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ArtifactModel, ArtifactGroupModel, Resource} from "app/models"; +import {ArtifactsUtils, ModalsHandler, ValidationUtils} from "app/utils"; +import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; + +interface IDeploymentArtifactsViewModelScope extends IWorkspaceViewModelScope { + tableHeadersList:Array; + reverse:boolean; + sortBy:string; + artifacts:Array; + editForm:ng.IFormController; + isLoading:boolean; + artifactDescriptions:any; + selectedArtifactId:string; + popoverTemplate:string; + + addOrUpdate(artifact:ArtifactModel):void; + updateSelectedArtifact():void; + delete(artifact:ArtifactModel):void; + sort(sortBy:string):void; + noArtifactsToShow():boolean; + getValidationPattern(validationType:string, parameterType?:string):RegExp; + validateJson(json:string):boolean; + resetValue(parameter:any):void; + viewModeOrCsarComponent():boolean; + isLicenseArtifact(artifact:ArtifactModel):void; + getEnvArtifact(heatArtifact:ArtifactModel):ArtifactModel; + getEnvArtifactName(artifact:ArtifactModel):string; + openEditEnvParametersModal(artifact:ArtifactModel):void; + openDescriptionPopover(artifactId:string):void; + closeDescriptionPopover():void; +} + +export class DeploymentArtifactsViewModel { + + static '$inject' = [ + '$scope', + '$templateCache', + '$filter', + 'ValidationUtils', + 'ArtifactsUtils', + 'ModalsHandler', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:IDeploymentArtifactsViewModelScope, + private $templateCache:ng.ITemplateCacheService, + private $filter:ng.IFilterService, + private validationUtils:ValidationUtils, + private artifactsUtils:ArtifactsUtils, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2: ComponentServiceNg2) { + this.initScope(); + this.$scope.updateSelectedMenuItem(); + } + + private initDescriptions = ():void => { + this.$scope.artifactDescriptions = {}; + _.forEach(this.$scope.component.deploymentArtifacts, (artifact:ArtifactModel):void => { + this.$scope.artifactDescriptions[artifact.artifactLabel] = artifact.description; + }); + }; + + private setArtifact = (artifact:ArtifactModel):void => { + if (!artifact.description || !this.$scope.getValidationPattern('string').test(artifact.description)) { + artifact.description = this.$scope.artifactDescriptions[artifact.artifactLabel]; + } + }; + + private initScopeArtifacts = ()=> { + this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + _.forEach(this.$scope.artifacts, (artifact:ArtifactModel):void => { + artifact.envArtifact = this.getEnvArtifact(artifact); + }); + }; + + private initArtifacts = (loadFromServer:boolean):void => { + if (loadFromServer) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentDeploymentArtifacts(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.deploymentArtifacts = response.deploymentArtifacts; + this.initScopeArtifacts(); + this.$scope.isLoading = false; + }); + } else { + this.initScopeArtifacts(); + } + + }; + + private getEnvArtifact = (heatArtifact:ArtifactModel):ArtifactModel=> { + return _.find(this.$scope.artifacts, (item:ArtifactModel)=> { + return item.generatedFromId === heatArtifact.uniqueId; + }); + }; + + private getCurrentArtifact = ():ArtifactModel => { + if (!this.$scope.selectedArtifactId) { + return null; + } + let artifact:ArtifactModel = this.$scope.artifacts.filter((art) => { + return art.uniqueId == this.$scope.selectedArtifactId; + })[0]; + return artifact; + } + + private initScope = ():void => { + let self = this; + this.$scope.isLoading = false; + this.$scope.selectedArtifactId = null; + this.initDescriptions(); + if(this.$scope.component.deploymentArtifacts) { + this.initArtifacts(false); + } else { + this.initArtifacts(true); + } + this.$scope.setValidState(true); + + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'artifactDisplayName'}, + {title: 'Type', property: 'artifactType'}, + {title: 'Deployment timeout', property: 'timeout'}, + {title: 'Version', property: 'artifactVersion'}, + {title: 'UUID', property: 'artifactUUID'} + ]; + + this.$templateCache.put("deployment-artifacts-description-popover.html", require('app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-description-popover.html')); + this.$scope.popoverTemplate = "deployment-artifacts-description-popover.html"; + + this.$scope.isLicenseArtifact = (artifact:ArtifactModel):boolean => { + let isLicense:boolean = false; + if (this.$scope.component.isResource() && (this.$scope.component).isCsarComponent()) { + + isLicense = this.artifactsUtils.isLicenseType(artifact.artifactType); + } + + return isLicense; + }; + + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => { + return this.validationUtils.getValidationPattern(validationType, parameterType); + }; + + this.$scope.validateJson = (json:string):boolean => { + if (!json) { + return true; + } + return this.validationUtils.validateJson(json); + }; + + this.$scope.viewModeOrCsarComponent = ():boolean => { + return this.$scope.isViewMode() || (this.$scope.component.isResource() && (this.$scope.component).isCsarComponent()); + }; + + this.$scope.addOrUpdate = (artifact:ArtifactModel):void => { + artifact.artifactGroupType = 'DEPLOYMENT'; + let artifactCopy = new ArtifactModel(artifact); + + let success = (response:any):void => { + this.$scope.artifactDescriptions[artifactCopy.artifactLabel] = artifactCopy.description; + this.initArtifacts(true); + // this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + }; + + let error = (err:any):void => { + console.log(err); + this.initArtifacts(true); + // self.$scope.artifacts = _.values(self.$scope.component.deploymentArtifacts); + }; + + this.ModalsHandler.openArtifactModal(artifactCopy, this.$scope.component).then(success, error); + }; + + this.$scope.noArtifactsToShow = ():boolean => { + return !_.some(this.$scope.artifacts, 'esId'); + }; + + this.$scope.resetValue = (parameter:any):void => { + if (!parameter.currentValue && parameter.defaultValue) { + parameter.currentValue = parameter.defaultValue; + } + else if ('boolean' == parameter.type) { + parameter.currentValue = parameter.currentValue.toUpperCase(); + } + }; + + this.$scope.$watch('editForm.$valid', ():void => { + if (this.$scope.editForm) { + // this.$scope.setValidState(this.$scope.editForm.$valid); + } + }); + + this.$scope.updateSelectedArtifact = ():void => { + if (!this.$scope.isViewMode() && !this.$scope.isLoading) { + let artifact:ArtifactModel = this.getCurrentArtifact(); + this.setArtifact(artifact); //resets artifact description to original value if invalid. + if (artifact && artifact.originalDescription != artifact.description) { + this.$scope.isLoading = true; + let onSuccess = (responseArtifact:ArtifactModel):void => { + this.$scope.artifactDescriptions[responseArtifact.artifactLabel] = responseArtifact.description; + // this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + this.initArtifacts(true); + this.$scope.isLoading = false; + }; + + let onFailed = (error:any):void => { + console.log('Delete artifact returned error:', error); + this.$scope.isLoading = false; + }; + + this.$scope.component.addOrUpdateArtifact(artifact).then(onSuccess, onFailed); + } + } + }; + + this.$scope.delete = (artifact:ArtifactModel):void => { + let onOk = ():void => { + this.$scope.isLoading = true; + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.initArtifacts(true); + //this.$scope.artifacts = _.values(this.$scope.component.deploymentArtifacts); + }; + + let onFailed = (error:any):void => { + this.$scope.isLoading = false; + console.log('Delete artifact returned error:', error); + }; + + this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed); + }; + + let title:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE"); + let message:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + this.$scope.getEnvArtifactName = (artifact:ArtifactModel):string => { + let envArtifact = this.$scope.getEnvArtifact(artifact); + if (envArtifact) { + return envArtifact.artifactDisplayName; + } + }; + + this.$scope.openEditEnvParametersModal = (artifact:ArtifactModel):void => { + this.ModalsHandler.openEditEnvParametersModal(artifact, this.$scope.component).then(()=> { + this.initArtifacts(true); + }, ()=> { + this.initArtifacts(true); + }); + }; + + this.$scope.openDescriptionPopover = (artifactId:string):void => { + if (this.$scope.selectedArtifactId && this.$scope.selectedArtifactId != artifactId) { + this.$scope.updateSelectedArtifact(); + } + this.$scope.selectedArtifactId = artifactId; + + }; + + this.$scope.closeDescriptionPopover = ():void => { + if (this.$scope.selectedArtifactId) { + this.$scope.updateSelectedArtifact(); + this.$scope.selectedArtifactId = null; + } + }; + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html new file mode 100644 index 0000000000..cc914b07a8 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html @@ -0,0 +1,126 @@ +
+ +
Add
+ +
+ +
+ +
+
{{header.title}} + +
+
+
+ +
+ + + + +
+
+
+
+ +
+ {{artifact.artifactDisplayName}} + + +
+ +
+ {{artifact.artifactType}} +
+
+ {{artifact.timeout? artifact.timeout:''}} +
+
+ {{artifact.artifactVersion}} +
+
+ {{artifact.artifactUUID}} +
+ +
+ + + + +
+
+
+ +
+ {{artifact.envArtifact.artifactDisplayName}} +
+ +
+ {{artifact.envArtifact.artifactType}} +
+
+ {{artifact.envArtifact.timeout? artifact.envArtifact.timeout:''}} +
+
+ {{artifact.envArtifact.artifactVersion}} +
+
+ {{artifact.envArtifact.artifactUUID}} +
+ + +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less new file mode 100644 index 0000000000..dd3b16447c --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less @@ -0,0 +1,196 @@ +.workspace-deployment-artifact { + width: 93%; + display: inline-block; + .table-container-flex .table .body .data-row + div.item-opened { + align-items: center; + padding: 10px 40px 10px 30px; + } + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + + .heat-env-connect-container{ + background-color: white; + position: absolute; + height: 70px; + width:20px; + left: 0; + top:0; + } + .heat-env-connect-container-view-mode{ + background-color: @tlv_color_t; + } + .heat-env-connect{ + border-left: 1px #848586 solid; + height: 50px; + margin-left: 10px; + margin-top: 10px; + border-top: 1px #848586 solid; + border-bottom: 1px #848586 solid; + width: 11px; + float: left; + + } + + .artifact-name{ + width:85%; + } + + .table-container-flex .table .body .data-row div .heat-env-connect-container{ + border-right: none; + } + + .i-sdc-designer-sidebar-section-content-item-file-link::before{ + content:""; + background-color: white; + width: 12px; + + } + + + + .table { + height:490px; + margin-bottom: 0; + } + + .parameter-description { + .circle(18px, @color_p); + content: '?'; + line-height: 18px; + vertical-align: middle; + margin-left: 5px; + cursor: default; + display: inline-block; + position: absolute; + top: 16px; + } + + .table-container-flex { + + margin-top: 27px; + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + .hand; + padding-left: 30px; + position: relative; + span.table-arrow { + margin-right: 7px; + } + .description-popover-icon{ + float:right; + margin-top:6px; + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 9; + } + + .flex-item:nth-child(4) { + flex-grow: 3; + } + + .flex-item:nth-child(5) { + flex-grow: 20; + } + + .flex-item:nth-child(6) { + flex-grow: 5; + + &.table-btn-col { + display: flex; + justify-content: space-between; + align-items: center; + + button { + flex: 0 1 auto; + background-color: transparent; + border: 0; + margin: 0; + } + .edit-paramtes-button { + order: -1; + } + } + } + } + .w-sdc-form{ + text-align: left; + + .w-sdc-env-params{ + border-top: 1px solid #cdcdcd; + margin: 25px 0 10px 0; + } + + .i-sdc-form-textarea { + border: 1px solid @color_e; + min-height: 60px; + padding: 10px 13px; + width: 100%; + resize: none; + + } + + .w-sdc-form-item { + &.error { + .i-sdc-form-input, + .i-sdc-form-select, + .i-sdc-form-textarea { + border-color: @color_h; + outline: none; + box-sizing: border-box; + } + } + } + + .i-sdc-env-form-label{ + font-family: @font-omnes-medium; + color: @main_color_m; + overflow: hidden; + max-width: 450px; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + margin-top: 14px; + + &.required::before { + color: #f33; + content: '*'; + margin-right: 4px; + } + } + } +} + +.parameter-description-popover.deployment-artifact-view { + margin-left: -10px; + z-index: 1040; + min-width: 300px; + .input-error { + .q_12_m; + } + .error textarea{ + border-color: @main_color_g; + color: @color_h; + outline: none; + } + .popover-content textarea { + width:100%; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts new file mode 100644 index 0000000000..c94342a51e --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view-model.ts @@ -0,0 +1,127 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentFactory, MenuHandler, ChangeLifecycleStateHandler, ModalsHandler} from "app/utils"; +import {LeftPaletteLoaderService, CacheService, SharingService} from "app/services"; +import {Component, IAppMenu, Tab, ComponentInstance} from "app/models"; +import {GRAPH_EVENTS} from "../../../../utils/constants"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; +import {EventListenerService} from "../../../../services/event-listener-service"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; + +export interface IDeploymentViewModelScope extends IWorkspaceViewModelScope { + + currentComponent:Component; + selectedComponent:Component; + isLoading:boolean; + sharingService:SharingService; + sdcMenu:IAppMenu; + version:string; + isViewOnly:boolean; + tabs:Array; + selectedTab: Tab; + isComponentInstanceSelected():boolean; + updateSelectedComponent():void + openUpdateModal(); + deleteSelectedComponentInstance():void; + onBackgroundClick():void; + setSelectedInstance(componentInstance:ComponentInstance):void; + printScreen():void; + +} + +export class DeploymentViewModel { + + static '$inject' = [ + '$scope', + '$templateCache', + 'sdcMenu', + 'MenuHandler', + '$state', + 'Sdc.Services.SharingService', + '$filter', + 'Sdc.Services.CacheService', + 'ComponentFactory', + 'ChangeLifecycleStateHandler', + 'LeftPaletteLoaderService', + 'ModalsHandler', + 'EventListenerService', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:IDeploymentViewModelScope, + private $templateCache:ng.ITemplateCacheService, + private sdcMenu:IAppMenu, + private MenuHandler:MenuHandler, + private $state:ng.ui.IStateService, + private sharingService:SharingService, + private $filter:ng.IFilterService, + private cacheService:CacheService, + private ComponentFactory:ComponentFactory, + private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, + private LeftPaletteLoaderService:LeftPaletteLoaderService, + private ModalsHandler:ModalsHandler, + private eventListenerService: EventListenerService, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.$scope.setValidState(true); + this.initScope(); + this.initGraphData(); + this.$scope.updateSelectedMenuItem(); + } + + + private initComponent = ():void => { + + this.$scope.currentComponent = this.$scope.component; + this.$scope.selectedComponent = this.$scope.currentComponent; + this.updateUuidMap(); + this.$scope.isViewOnly = this.$scope.isViewMode(); + }; + + + private updateUuidMap = ():void => { + /** + * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined, + * but after loadService or loadResource this.sharingService.currentEntity will be defined. + * Need to update the uuidMap with the new resource or service. + */ + this.sharingService.addUuidValue(this.$scope.currentComponent.uniqueId, this.$scope.currentComponent.uuid); + }; + + private initRightTabs = ()=> { + if (this.$scope.currentComponent.groups) { + this.$templateCache.put("hierarchy-view.html", require('app/view-models/tabs/hierarchy/hierarchy-view.html')); + let hierarchyTab = new Tab("hierarchy-view.html", 'Sdc.ViewModels.HierarchyViewModel', 'hierarchy', this.$scope.isViewMode(), this.$scope.currentComponent, 'hierarchy'); + this.$scope.tabs.push(hierarchyTab) + } + } + + private initGraphData = ():void => { + if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations || !this.$scope.component.groups) { + this.ComponentServiceNg2.getDeploymentGraphData(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.componentInstances = response.componentInstances; + this.$scope.component.componentInstancesRelations = response.componentInstancesRelations; + this.$scope.component.groups = response.groups; + this.$scope.isLoading = false; + this.initComponent(); + this.initRightTabs(); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DEPLOYMENT_GRAPH_DATA_LOADED); + this.$scope.selectedTab = this.$scope.tabs[0]; + }); + } else { + this.$scope.isLoading = false; + this.initRightTabs(); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DEPLOYMENT_GRAPH_DATA_LOADED); + + } + }; + + private initScope = ():void => { + this.$scope.isLoading = true; + this.$scope.sharingService = this.sharingService; + this.$scope.sdcMenu = this.sdcMenu; + this.$scope.version = this.cacheService.get('version'); + this.initComponent(); + this.$scope.tabs = Array(); + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html new file mode 100644 index 0000000000..f8b5f23a25 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment-view.html @@ -0,0 +1,10 @@ +
+ +
+ +
+ +
+ +
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less new file mode 100644 index 0000000000..4c548c7331 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment/deployment.less @@ -0,0 +1,33 @@ +.deployment-view { + + display: inline-block; + text-align: left; + align-items: left; + padding: 0; + width: 100%; + height: 100%; + + .w-sdc-deployment-canvas { + .noselect; + .bg_c; + bottom: 0; + width: 100%; + height: 100%; + + .view-mode{ + background-color: #f8f8f8; + border:0; + } + } + + .w-sdc-deployment-right-bar { + + .noselect; + bottom: 0; + position: absolute; + right: 0px; + transition: right 0.2s; + z-index: 10000; + top: @action_nav_height; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts new file mode 100644 index 0000000000..ce2e0169bf --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts @@ -0,0 +1,83 @@ +'use strict'; +import {Distribution, DistributionComponent, ExportExcel} from "app/models"; + +interface IDistributionStatusModalViewModelScope { + distribution:Distribution; + status:string; + getStatusCount(distributionComponent:Array):any; + getUrlName(url:string):string; + modalDitributionStatus:ng.ui.bootstrap.IModalServiceInstance; + footerButtons:Array; + //exportExcelData:ExportExcel; + close():void; + initDataForExportExcel():ExportExcel; +} + +export class DistributionStatusModalViewModel { + + static '$inject' = ['$scope', '$uibModalInstance', 'data', '$filter']; + + constructor(private $scope:IDistributionStatusModalViewModelScope, + private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance, + private data:any, + private $filter:ng.IFilterService) { + this.initScope(); + } + + private generateMetaDataForExportExcel = ():Array=> { + let metaData = []; + metaData[0] = 'Name:' + this.data.component.name + '| UUID:' + this.data.component.uuid + '| Invariant UUID:' + this.data.component.invariantUUID; + metaData[1] = 'Distribution ID:' + this.$scope.distribution.distributionID + + '| USER ID:' + this.$scope.distribution.userId + + '| Time[UTC]:' + this.$filter('date')(this.$scope.distribution.timestamp, 'MM/dd/yyyy h:mma', 'UTC') + + '| Status:' + this.$scope.distribution.deployementStatus; + return metaData; + }; + + private generateDataObjectForExportExcel = ():any=> { + let correctFormatDataObj = []; + _.each(this.$scope.distribution.distributionComponents, (dComponent:DistributionComponent) => { + if (dComponent.status == this.$scope.status) { + correctFormatDataObj.push({ + 'omfComponentID': dComponent.omfComponentID, + 'artiFactName': this.$scope.getUrlName(dComponent.url), + 'url': dComponent.url, + 'timestamp': this.$filter('date')(dComponent.timestamp, 'MM/dd/yyyy h:mma', 'UTC'), + 'status': dComponent.status + }); + } + }); + return correctFormatDataObj; + }; + + private initScope = ():void => { + this.$scope.distribution = this.data.distribution; + this.$scope.status = this.data.status; + this.$scope.modalDitributionStatus = this.$uibModalInstance; + + + this.$scope.getUrlName = (url:string):string => { + let urlName:string = _.last(url.split('/')); + return urlName; + }; + + this.$scope.initDataForExportExcel = ():ExportExcel => { + let exportExcelData = new ExportExcel(); + exportExcelData.fileName = this.$scope.status; + exportExcelData.groupByField = "omfComponentID"; + exportExcelData.tableHeaders = ["Component ID", "Artifact Name", "URL", "Time(UTC)", "Status"]; + exportExcelData.metaData = this.generateMetaDataForExportExcel(); + exportExcelData.dataObj = this.generateDataObjectForExportExcel(); + return exportExcelData; + }; + + this.$scope.close = ():void => { + this.$uibModalInstance.close(); + }; + + this.$scope.footerButtons = [ + {'name': 'Close', 'css': 'blue', 'callback': this.$scope.close} + ]; + + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html new file mode 100644 index 0000000000..3367193fc7 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html @@ -0,0 +1,130 @@ + + +
+
+ +
+ +
+ + +
+ + +
+
    +
  • +
    + +
    +
    +
    +
    Distribution ID
    +
    +
    +
    +
    +
    +
    +
    +
    Time[UTC]:
    +
    +
    +
    + + +
    +
    +
    +
    Status: {{status}}
    + +
    +
    +
    + +
      +
    • +
      +
      +
      +
      {{omfComponentID}} {{omfComponentList.length}} +
      +
      +
      +
      +
      +
      +
      +
      Component ID
      +
      Artifact Name
      +
      URL
      +
      Time(UTC)
      +
      Status
      +
      + +
      +
      +
      + {{urlList[0].omfComponentID}} +
      +
      + {{getUrlName(urlList[0].url)}} +
      +
      +
      {{urlList[0].url}}
      + +
      +
      +
      +
      {{urlList[0].status}}
      +
      + + +
      +
      +
        +
      • + + {{distributionComponent.status}} + Reason: Component has determined artifact is not needed. + Reason: {{distributionComponent.errorReason}} +
      • +
      +
      +
      +
      +
      +
      +
    • +
    +
  • +
+
+ +
+
+ + +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less new file mode 100644 index 0000000000..d167083a2b --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less @@ -0,0 +1,40 @@ +.w-sdc-classic-top-line-modal { + + .w-sdc-modal-head { + // border-bottom: none; + } + .w-sdc-distribution-view { + .actions-buttons { + height: 29px; + padding: 0 25px 0 0px; + span{ + float: right; + } + } + + .w-sdc-distribution-view-content { + height: 500px; + } + + .w-sdc-distribution-view-content-section { + + .w-sdc-distribute-parent-block { + .w-sdc-distribute-components-block { + + .omf-component-row { + .w-sdc-distribute-status-block { + margin-left: 0; + } + + } + div { + padding-left: 0; + } + } + + } + + } + } +} + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts new file mode 100644 index 0000000000..002b16f63f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts @@ -0,0 +1,111 @@ +'use strict'; +import {Distribution, DistributionComponent, Service} from "app/models"; +import {ModalsHandler, Dictionary} from "app/utils"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; + +interface IDistributionViewModel extends IWorkspaceViewModelScope { + modalDistribution:ng.ui.bootstrap.IModalServiceInstance; + service:Service; + distributions:Array; + showComponents(distribution:Distribution):void; + markAsDeployed(distribution:Distribution):void; + getStatusCount(distributionComponent:Array):any; + initDistributions():void; + getUrlName(url:string):string; + close():void; + openDisributionStatusModal:Function; +} + +export class DistributionViewModel { + + static '$inject' = [ + '$scope', + 'ModalsHandler' + + ]; + + constructor(private $scope:IDistributionViewModel, + private ModalsHandler:ModalsHandler) { + this.initScope(); + this.$scope.setValidState(true); + this.$scope.updateSelectedMenuItem(); + } + + private initScope = ():void => { + this.$scope.service = this.$scope.component; + + + // Open Distribution status modal + this.$scope.openDisributionStatusModal = (distribution:Distribution, status:string):void => { + this.ModalsHandler.openDistributionStatusModal(distribution, status, this.$scope.component).then(()=> { + // OK + }, ()=> { + // ERROR + }); + }; + + + this.$scope.showComponents = (distribution:Distribution):void => { + let onError = (response) => { + console.info('onError showComponents', response); + }; + let onSuccess = (distributionComponents:Array) => { + distribution.distributionComponents = distributionComponents; + distribution.statusCount = this.$scope.getStatusCount(distribution.distributionComponents); + // distribution.components = this.aggregateDistributionComponent(distributionComponents);; + }; + this.$scope.service.getDistributionsComponent(distribution.distributionID).then(onSuccess, onError); + }; + + this.$scope.getStatusCount = (distributionComponent:Array):any => { + return _.countBy(distributionComponent, 'status') + }; + + this.$scope.getUrlName = (url:string):string => { + let urlName:string = _.last(url.split('/')); + return urlName; + }; + + this.$scope.markAsDeployed = (distribution:Distribution):void => { + let onError = (response) => { + console.info('onError markAsDeployed', response); + }; + let onSuccess = (result:any) => { + distribution.deployementStatus = 'Deployed'; + }; + this.$scope.service.markAsDeployed(distribution.distributionID).then(onSuccess, onError); + + }; + + this.$scope.initDistributions = ():void => { + let onError = (response) => { + console.info('onError initDistributions', response); + }; + let onSuccess = (distributions:Array) => { + this.$scope.distributions = distributions; + }; + this.$scope.service.getDistributionsList().then(onSuccess, onError); + }; + + this.$scope.initDistributions(); + + }; + + + private aggregateDistributionComponent = (distributionComponents:Array):any => { + let aggregateDistributions:Dictionary>> = new Dictionary>>(); + let tempAggregateDistributions:any = _.groupBy(distributionComponents, 'omfComponentID'); + let aa = new Dictionary>(); + + let tempAggregate:any; + _.forEach(tempAggregateDistributions, (distributionComponents:Array, omfComponentID:string)=> { + + let urls:any = _.groupBy(distributionComponents, 'url'); + aggregateDistributions.setValue(omfComponentID, urls); + // aggregateDistributions[omfComponentID] = ; + + }); + console.log(aggregateDistributions); + return aggregateDistributions; + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html new file mode 100644 index 0000000000..710336af15 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html @@ -0,0 +1,181 @@ +
+
+
DISTRIBUTION
+
+ +
+
+ + + +
+
    +
  • +
    +
    +
    +
    +
    +
    Distribution ID
    +
    +
    +
    +
    +
    +
    +
    +
    Time[UTC]:
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    Total Artifacts:
    +
    Notified:
    + + + + + +
    Deploy Errors:
    +
    Download Errors:
    +
    +
    +
    + +
      + +
    • +
      +
      +
      +
      {{omfComponentID}} {{(statusCount.NOT_NOTIFIED || 0) + (statusCount.NOTIFIED || 0) }} +
      +
      Notified:
      +
      Downloaded:
      +
      Deployed:
      +
      Not Notified:
      +
      Deploy Errors:
      +
      Download Errors:
      +
      +
      +
      +
      +
      +
      +
      Component ID
      +
      Artifact Name
      +
      URL
      +
      Time(UTC)
      +
      Status
      +
      + +
      +
      +
      + {{urlList[0].omfComponentID}} +
      +
      + {{urlList[0].displayUrl}} +
      +
      +
      {{urlList[0].url}}
      + +
      +
      +
      +
      + {{urlList[0].status}} +
      +
      + + +
      +
      +
        +
      • + + {{distributionComponent.status}} + Reason: Component has determined artifact is not needed. + Reason: {{distributionComponent.errorReason}} +
      • +
      +
      +
      +
      +
      +
      +
    • +
    +
  • +
+
+ +
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution.less b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution.less new file mode 100644 index 0000000000..ee1f7ed2d6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution.less @@ -0,0 +1,362 @@ + +.w-sdc-distribution-view { + text-align: left; + + .g_1; + min-height: 500px; + + .w-sdc-distribution-view-distributed-green-text { + .l_9; + .bold; + } + .w-sdc-distribution-view-distributed-error-red-text { + .h_9; + .bold; + } + + .bg_c; + vertical-align: top; + padding: 30px 10px; + width: 100%; + + .w-sdc-distribution-view-header { + display: flex; + -webkit-justify-content: space-between; + margin: 0 25px 5px 40px; + + .header-spacer { + flex-grow: 5; + } + } + + .top-search { + position: relative; + input { + &.search-text { + height: 26px; + line-height: 26px; + margin: 0 18px 4px 20px; + padding-right: 25px; + } + + } + .magnification { + top: 8px; + right: 25px; + } + } + + .w-sdc-distribution-view-content { + .perfect-scrollbar; + padding: 0 25px 0 0px; + margin-bottom: 25px; + height: 700px; + overflow: hidden; + position: relative; + + } + + .w-sdc-distribution-view-title { + .s_14_r; + + line-height: 30px; + + span { + padding-left: 5px; + } + } + + .blue-font { + .a_14_m; + + } + + .red-font { + .q_14_m; + } + + .w-sdc-distribution-view-block { + div { + display: inline-block; + } + } + + .w-sdc-distribution-view-content-section { + ul { + list-style-type: none; + } + + .distribution-bth { + .hand; + &.disabled { + cursor: none; + } + } + + .copy-link { + padding-right: 19px; + margin-left: 8px; + cursor: pointer; + + } + + .w-sdc-distribute-row-extends { + border-Left: solid 4px transparent; + &.extends { + border-left: solid 4px @main_color_c; + border-bottom: 1px solid @border_color_f; + margin-bottom: 10px; + } + } + .w-sdc-distribute-parent-block { + border: 1px solid @main_color_o;; + width: 100%; + margin-bottom: 6px; + + .status-icon { + vertical-align: middle; + margin-bottom: 4px; + } + + &.extends { + background-color: @tlv_color_t; + } + + :not(.disable-hover):hover { + background-color: @tlv_color_u; + } + + .title-section { + display: inline-block; + margin-right: 10px; + flex-basis: 0; + } + + .title { + .l_12_m; + font-weight: bold; + } + .w-sdc-distribute-content { + display: flex; + align-items: center; + justify-content: space-between; + margin-left: 10px; + } + + .w-sdc-distribution-arrow-btn { + .sprite-new; + .arrow-up-small; + margin: 0 6px; + display: inline-table; + } + .extends.w-sdc-distribution-arrow-btn { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); + } + + .w-sdc-distribute-row { + display: flex; + align-items: center; + justify-content: space-between; + + .w-sdc-distribute-row-content { + margin: 15px 31px 10px 0; + width: 100%; + .w-sdc-distribute-status-block { + border-top: solid 1px @main_color_o; + } + .item-1 { + flex-grow: 2; + } + .item-2 { + flex-grow: 1; + } + .item-3 { + flex-grow: 1; + } + .item-4 { + flex-grow: 1; + } + .item-5 { + flex-grow: 1; + } + } + } + + .w-sdc-distribute-status-block { + display: flex; + align-items: center; + justify-content: space-between; + margin: 10px 5px 0 5px; + padding: 5px 5px 0 5px;; + width: 100%; + div { + border-left: 1px solid @main_color_o; + padding: 0 12px; + } + + .link { + .a_14_m; + cursor: pointer; + &:hover{ + text-decoration: underline; + .b_14_m; + } + } + + span { + padding: 2px; + } + + .deployed { + margin-left: 10px; + .sprite-new; + .success-circle-small; + } + + .error { + .q_14_m; + margin-left: 10px; + .sprite-new; + .error-icon; + } + + .status-item-1 { + border-left: 0; + } + + .status-item-6 { + flex-grow: 1; + border-left: none; + text-align: right; + } + } + + .w-sdc-distribute-components-block { + padding: 0; + padding-bottom: 5px; + list-style-type: none; + + li { + margin: 5px 2px; + } + + .omf-component-row { + border: 1px solid @border_color_f; + padding-left: 3px; + background-color: white; + margin: 0 30px; + &.extends { + padding-left: 0; + border-Left: solid 4px @main_color_c; + + } + + .w-sdc-distribute-status-block { + margin: 5px; + padding: 5px; + } + + .blue-font { + .a_16_m; + + } + + &:hover { + background-color: @tlv_color_u; + } + + } + + } + + .w-sdc-distribute-omfComponent-block { + background-color: white; + margin: 0 30px; + padding: 8px 10px; + border: 1px solid @border_color_f; + + .omfComponent-table-head { + margin-bottom: 5px; + background-color: @tlv_color_u; + .title { + padding: 6px 10px; + border-left: 1px solid @border_color_f; + &:first-child { + border: none; + } + } + } + + .omfComponent-table-row { + border-bottom: 1px solid @border_color_f; + &.row-0 { + border-top: 1px solid @border_color_f; + } + .w-sdc-distribute-cell { + padding: 10px; + border-left: 1px solid @border_color_f; + &:last-child { + border-right: 1px solid @border_color_f; + } + &.item-5 { + .m_14_m; + } + } + } + + .distribution-url { + + } + + .w-sdc-distribute-row.extends { + border-Left: solid 4px @main_color_c; + .item-1 { + border: none; + } + + } + + .item-1 { + width: 20%; + } + .item-2 { + width: 20%; + } + + .item-3 { + width: 24%; + display: flex; + } + + .item-4 { + width: 18%; + } + + .item-5 { + width: 18%; + } + } + + .w-sdc-distribute-url-block { + + padding: 10px 15px; + border: none; + border-right: 1px solid @border_color_f; + border-bottom: 1px solid @border_color_f; + width: 100%; + li { + border: none; + span { + padding-right: 30px; + .m_12_r; + } + } + + } + } + + } +} + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts new file mode 100644 index 0000000000..44953985fc --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts @@ -0,0 +1,349 @@ +'use strict'; +import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON, + ResourceType} from "app/utils"; +import {CacheService, EventListenerService, ProgressService} from "app/services"; +import {IAppConfigurtaion, Product, IValidate, IMainCategory, Resource, ISubCategory,Service} from "app/models"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; + +export class Validation { + componentNameValidationPattern:RegExp; + contactIdValidationPattern:RegExp; + tagValidationPattern:RegExp; + vendorValidationPattern:RegExp; + commentValidationPattern:RegExp; + projectCodeValidationPattern:RegExp; +} + +export class componentCategories {//categories field bind to this obj in order to solve this bug: DE242059 + selectedCategory:string; +} + +export interface IGeneralScope extends IWorkspaceViewModelScope { + validation:Validation; + editForm:ng.IFormController; + categories:Array; + latestCategoryId:string; + latestVendorName:string; + importedFileExtension:any; + isCreate:boolean; + isShowFileBrowse:boolean; + isShowOnboardingSelectionBrowse:boolean; + importedToscaBrowseFileText:string; + importCsarProgressKey:string; + browseFileLabel:string; + componentCategories:componentCategories; + + onToscaFileChange():void + validateField(field:any):boolean; + validateName(isInit:boolean):void; + calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category + onVendorNameChange(oldVendorName:string):void; + convertCategoryStringToOneArray(category:string, subcategory:string):Array; + onCategoryChange():void; + onEcompGeneratedNamingChange():void; + openOnBoardingModal():void; + initCategoreis():void; +} + +export class GeneralViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.CacheService', + 'ComponentNameValidationPattern', + 'ContactIdValidationPattern', + 'TagValidationPattern', + 'VendorValidationPattern', + 'CommentValidationPattern', + 'ValidationUtils', + 'sdcConfig', + 'ProjectCodeValidationPattern', + '$state', + 'ModalsHandler', + 'EventListenerService', + 'Notification', + 'Sdc.Services.ProgressService', + '$interval', + '$filter', + '$timeout' + ]; + + constructor(private $scope:IGeneralScope, + private cacheService:CacheService, + private ComponentNameValidationPattern:RegExp, + private ContactIdValidationPattern:RegExp, + private TagValidationPattern:RegExp, + private VendorValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private ValidationUtils:ValidationUtils, + private sdcConfig:IAppConfigurtaion, + private ProjectCodeValidationPattern:RegExp, + private $state:ng.ui.IStateService, + private ModalsHandler:ModalsHandler, + private EventListenerService:EventListenerService, + private Notification:any, + private progressService:ProgressService, + protected $interval:any, + private $filter:ng.IFilterService, + private $timeout:ng.ITimeoutService) { + + this.initScopeValidation(); + this.initScopeMethods(); + this.initScope(); + this.$scope.updateSelectedMenuItem(); + } + + + + + private initScopeValidation = ():void => { + this.$scope.validation = new Validation(); + this.$scope.validation.componentNameValidationPattern = this.ComponentNameValidationPattern; + this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern; + this.$scope.validation.tagValidationPattern = this.TagValidationPattern; + this.$scope.validation.vendorValidationPattern = this.VendorValidationPattern; + this.$scope.validation.commentValidationPattern = this.CommentValidationPattern; + this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern; + }; + + private initScope = ():void => { + + // Work around to change the csar version + if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { + (this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + } + + this.$scope.importedToscaBrowseFileText = this.$scope.component.name + " (" + (this.$scope.component).csarVersion + ")"; + this.$scope.importCsarProgressKey = "importCsarProgressKey"; + this.$scope.browseFileLabel = this.$scope.component.isResource() && (this.$scope.component).resourceType === ResourceType.VF ? "Upload file" : "Upload VFC"; + this.$scope.progressService = this.progressService; + this.$scope.componentCategories = new componentCategories(); + this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory; + + // Workaround to short vendor name to 25 chars + // Amdocs send 27 chars, and the validation pattern is 25 chars. + if (this.$scope.component.vendorName) { + this.$scope.component.vendorName = this.$scope.component.vendorName.substr(0, 25); + } + + // Init UIModel + this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name); + + // Init categories + this.$scope.initCategoreis(); + + // Init the decision if to show file browse. + this.$scope.isShowFileBrowse = false; + if (this.$scope.component.isResource()) { + let resource:Resource = this.$scope.component; + console.log(resource.name + ": " + resource.csarUUID); + if (resource.importedFile) { // Component has imported file. + this.$scope.isShowFileBrowse = true; + } + if (this.$scope.isEditMode() && resource.resourceType == ResourceType.VF && !resource.csarUUID) { + this.$scope.isShowFileBrowse = true; + } + } + ; + + // Init the decision if to show onboarding + this.$scope.isShowOnboardingSelectionBrowse = false; + if (this.$scope.component.isResource() && + this.$scope.isEditMode() && + (this.$scope.component).resourceType == ResourceType.VF && + (this.$scope.component).csarUUID) { + this.$scope.isShowOnboardingSelectionBrowse = true; + } + + //init file extensions based on the file that was imported. + if (this.$scope.component.isResource() && (this.$scope.component).importedFile) { + let fileName:string = (this.$scope.component).importedFile.filename; + let fileExtension:string = fileName.split(".").pop(); + if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension; + (this.$scope.component).importedFile.filetype = "csar"; + } else if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + (this.$scope.component).importedFile.filetype = "yaml"; + this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension; + } + } else if (this.$scope.isEditMode() && (this.$scope.component).resourceType === ResourceType.VF) { + this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension; + //(this.$scope.component).importedFile.filetype="csar"; + } + + this.$scope.setValidState(true); + + this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => { + let uniqueId:string = mainCategory; + if (subCategory) { + uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category. + } + return uniqueId; + }; + + //TODO remove this after handling contact in UI + if (this.$scope.component.isProduct() && this.$scope.isCreateMode()) { + (this.$scope.component).contacts = []; + (this.$scope.component).contacts.push(this.cacheService.get("user").userId); + } else if (this.$scope.isCreateMode()) { + this.$scope.component.contactId = this.cacheService.get("user").userId; + } + + }; + + // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except) + private convertCategoryStringToOneArray = ():Array => { + let tmp = this.$scope.component.selectedCategory.split("_#_"); + let mainCategory = tmp[0]; + let subCategory = tmp[1]; + + // Find the selected category and add the relevant sub category. + let selectedMainCategory:IMainCategory = _.find(this.$scope.categories, function (item) { + return item["name"] === mainCategory; + + }); + + let mainCategoryClone = angular.copy(selectedMainCategory); + if (subCategory) { + let selectedSubcategory = _.find(selectedMainCategory.subcategories, function (item) { + return item["name"] === subCategory; + }); + mainCategoryClone['subcategories'] = [angular.copy(selectedSubcategory)]; + } + let tmpSelected = mainCategoryClone; + + let result:Array = []; + result.push(tmpSelected); + + return result; + }; + + private updateComponentNameInBreadcrumbs = ():void => { + //update breadcrum after changing name + this.$scope.breadcrumbsModel[1].updateSelectedMenuItemText(this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name); + this.$scope.updateMenuComponentName(this.$scope.component.name); + }; + + private initScopeMethods = ():void => { + + this.$scope.initCategoreis = ():void => { + if (this.$scope.componentType === ComponentType.RESOURCE) { + this.$scope.categories = this.cacheService.get('resourceCategories'); + + } + if (this.$scope.componentType === ComponentType.SERVICE) { + this.$scope.categories = this.cacheService.get('serviceCategories'); + } + }; + + this.$scope.validateField = (field:any):boolean => { + if (field && field.$dirty && field.$invalid) { + return true; + } + return false; + }; + + this.$scope.openOnBoardingModal = ():void => { + let csarUUID = (this.$scope.component).csarUUID; + this.ModalsHandler.openOnboadrdingModal('Update', csarUUID).then(()=> { + // OK + this.$scope.uploadFileChangedInGeneralTab(); + }, ()=> { + // ERROR + }); + }; + + this.$scope.validateName = (isInit:boolean):void => { + if (isInit === undefined) { + isInit = false; + } + + let name = this.$scope.component.name; + if (!name || name === "") { + if (this.$scope.editForm + && this.$scope.editForm["componentName"] + && this.$scope.editForm["componentName"].$error) { + + // Clear the error name already exists + this.$scope.editForm["componentName"].$setValidity('nameExist', true); + } + + return; + } + //????????????????????????? + let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined; + + let onFailed = (response) => { + //console.info('onFaild', response); + //this.$scope.isLoading = false; + }; + + let onSuccess = (validation:IValidate) => { + this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid); + if (validation.isValid) { + //update breadcrumb after changing name + this.updateComponentNameInBreadcrumbs(); + } + }; + + if (isInit) { + // When page is init after update + if (this.$scope.component.name !== this.$scope.originComponent.name) { + if (!(this.$scope.componentType === ComponentType.RESOURCE && (this.$scope.component).csarUUID !== undefined) + ) { + this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed); + } + } + } else { + // Validating on change (has debounce) + if (this.$scope.editForm + && this.$scope.editForm["componentName"] + && this.$scope.editForm["componentName"].$error + && !this.$scope.editForm["componentName"].$error.pattern + && (!this.$scope.originComponent.name || this.$scope.component.name.toUpperCase() !== this.$scope.originComponent.name.toUpperCase()) + ) { + if (!(this.$scope.componentType === ComponentType.RESOURCE && (this.$scope.component).csarUUID !== undefined) + ) { + this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed); + } + } else if (this.$scope.originComponent.name && this.$scope.component.name.toUpperCase() === this.$scope.originComponent.name.toUpperCase()) { + // Clear the error + this.$scope.editForm["componentName"].$setValidity('nameExist', true); + } + } + }; + + this.$scope.$watchCollection('component.name', (newData:any):void => { + this.$scope.validateName(false); + }); + + // Notify the parent if this step valid or not. + this.$scope.$watch("editForm.$valid", (newVal, oldVal) => { + this.$scope.setValidState(newVal); + }); + + this.$scope.$watch("editForm.$dirty", (newVal, oldVal) => { + if (newVal !== oldVal) { + this.$state.current.data.unsavedChanges = newVal && !this.$scope.isCreateMode(); + } + }); + + this.$scope.onCategoryChange = ():void => { + this.$scope.component.selectedCategory = this.$scope.componentCategories.selectedCategory; + this.$scope.component.categories = this.convertCategoryStringToOneArray(); + this.$scope.component.icon = DEFAULT_ICON; + }; + + this.$scope.onEcompGeneratedNamingChange = ():void =>{ + if(!(this.$scope.component).ecompGeneratedNaming){ + (this.$scope.component).namingPolicy = ''; + } + }; + + this.$scope.onVendorNameChange = (oldVendorName:string):void => { + if (this.$scope.component.icon === oldVendorName) { + this.$scope.component.icon = DEFAULT_ICON; + } + }; + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html new file mode 100644 index 0000000000..2ad0cbacd6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html @@ -0,0 +1,354 @@ +
+ +
+ +
+ + +
+ + +
+ + +
+ +
+ {{(fileModel && fileModel.filename) || importedToscaBrowseFileText}} +
+ +
Browse
+
+
+ +
+ + + + + +
+ + +
+ +
+ + +
+ + + +
+ + + + + +
+
+ + + +
+ + + +
+ + + + + +
+
+ + + +
+ + + + +
+ + + +
+
+ + + +
+ + + + +
+ +
+
+ + + +
+ + + +
+ + +
+
+ + + + +
+ + +
+ +
+
+ + + +
+ + + +
+ + +
+
+ + + + +
+ + + +
+ + + +
+ +
+ + + + +
+ + + +
+ + + +
+
+ + + + +
+ +
+ + +
+ + + + +
+ + +
+
+ + + +
+ + + + +
+ + +
+
+ + + +
+
+ +
+ +
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general.less b/catalog-ui/src/app/view-models/workspace/tabs/general/general.less new file mode 100644 index 0000000000..1861d02e98 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general.less @@ -0,0 +1,64 @@ +.sdc-workspace-general-step { + + .w-sdc-form { + padding: 0; + + .i-sdc-form-file-upload{ + input[type="button"] { + cursor: pointer; + display: block; + filter: alpha(opacity=0); + width: 100px; + height: 30px; + opacity: 0; + position: absolute; + right: 0; + text-align: right; + top: 0; + } + + .file-upload-browse-btn { + .noselect; + .bg_n; + padding: 4px 6px; + cursor: pointer; + z-index: 999; + width: 100px; + height: 28px; + text-align: center; + + &.disabled { + cursor: default; + } + } + } + + .w-sdc-form-section-container { + text-align: center; + } + + .i-sdc-form-item { + &.upload { + margin-top: 0; + width: auto; + padding: 10px; + } + } + + .template-desc { + border: 1px dashed @border_color_f; + height: 130px; + overflow: hidden; + padding: 10px 6px 6px 6px; + margin-top: 10px; + } + + .sdc-tag .tag { + max-width: 225px; + } + + } + +} + + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view-model.ts new file mode 100644 index 0000000000..03dad2cc06 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view-model.ts @@ -0,0 +1,111 @@ +/** + * Created by obarda on 4/4/2016. + */ +'use strict'; +import {ComponentFactory} from "app/utils"; +import {AvailableIconsService} from "app/services"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {IMainCategory, ISubCategory} from "app/models"; + +export interface IIconsScope extends IWorkspaceViewModelScope { + icons:Array; + iconSprite:string; + setComponentIcon(iconSrc:string):void; +} + +export class IconsViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.AvailableIconsService', + 'ComponentFactory', + '$state' + ]; + + constructor(private $scope:IIconsScope, + private availableIconsService:AvailableIconsService, + private ComponentFactory:ComponentFactory, + private $state:ng.ui.IStateService) { + + + this.initScope(); + this.initIcons(); + this.$scope.updateSelectedMenuItem(); + this.$scope.iconSprite = this.$scope.component.iconSprite; + + if (this.$scope.component.isResource()) { + this.initVendor(); + } + } + + private initialIcon:string = this.$scope.component.icon; + private initIcons = ():void => { + + // For subcategories that where created by admin, there is no icons + this.$scope.icons = new Array(); + if (this.$scope.component.categories && this.$scope.component.categories.length > 0) { + + _.forEach(this.$scope.component.categories, (category:IMainCategory):void => { + if (category.icons) { + this.$scope.icons = this.$scope.icons.concat(category.icons); + } + if (category.subcategories) { + _.forEach(category.subcategories, (subcategory:ISubCategory):void => { + if (subcategory.icons) { + this.$scope.icons = this.$scope.icons.concat(subcategory.icons); + } + }); + } + }); + } + + if (this.$scope.component.isResource()) { + let resourceType:string = this.$scope.component.getComponentSubType(); + if (resourceType === 'VL') { + this.$scope.icons = ['vl']; + } + if (resourceType === 'CP') { + this.$scope.icons = ['cp']; + } + } + + if (this.$scope.icons.length === 0) { + this.$scope.icons = this.availableIconsService.getIcons(this.$scope.component.componentType); + } + //we always add the defual icon to the list + this.$scope.icons.push('defaulticon'); + }; + + private initVendor = ():void => { + let vendors:Array = this.availableIconsService.getIcons(this.$scope.component.componentType).slice(5, 19); + let vendorName = this.$scope.component.vendorName.toLowerCase(); + if ('at&t' === vendorName) { + vendorName = 'att'; + } + if ('nokia' === vendorName) { + vendorName = 'nokiasiemens'; + } + let vendor:string = _.find(vendors, (vendor:string)=> { + return vendor.replace(/[_]/g, '').toLowerCase() === vendorName; + }); + + if (vendor && this.$scope.icons.indexOf(vendor) === -1) { + this.$scope.icons.push(vendor); + } + }; + + private initScope():void { + this.$scope.icons = []; + this.$scope.setValidState(true); + //if(this.$scope.component.icon === DEFAULT_ICON){ + // //this.$scope.setValidState(false); + //} + + this.$scope.setComponentIcon = (iconSrc:string):void => { + this.$state.current.data.unsavedChanges = !this.$scope.isViewMode() && (iconSrc != this.initialIcon); + this.$scope.component.icon = iconSrc; + // this.$scope.setValidState(true); + }; + + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view.html b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view.html new file mode 100644 index 0000000000..aac14e0e84 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons-view.html @@ -0,0 +1,26 @@ +
+ +
+ +
+
+
+
+
Select one of the icons below for the asset
+
+
+
+
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/icons/icons.less b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons.less new file mode 100644 index 0000000000..65f946f395 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/icons/icons.less @@ -0,0 +1,65 @@ +.workspace-icons { + + width: 89%; + display: inline-block; + text-align: center; + align-items: center; + + .w-sdc-form { + padding-top: 0px; + padding-bottom: 0px; + .selected-icon-container { + text-align: left; + border: 1px solid #cfcfcf; + clear: both; + margin-bottom: 30px; + padding: 2px 0px 5px 5px; + .selected-icon { + margin: 8px 5px 0px 6px; + } + } + + .suggested-icons-container { + text-align: left; + border: 1px solid #cfcfcf; + clear: both; + padding: 2px 0px 5px 5px; + height: 340px; + margin-bottom: 0px; + + .suggested-icon-wrapper { + margin: 8px 5px 0px 6px; + display: inline-block; + + &.selected { + border: 2px solid @main_color_a; + border-radius: 35px; + display: inline-block; + line-height: 0px; + padding: 3px; + } + + } + .suggested-icon { + // margin: 8px 5px 0px 6px; + display: inline-block; + &.disable{ + opacity: 0.4; + } + } + } + + .icons-label { + float: left; + } + + .icons-text { + text-align: left; + line-height: 32px; + padding-left: 10px; + width: 100%; + border: 1px solid #cfcfcf; + border-bottom: none; + } + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts new file mode 100644 index 0000000000..e10a9944d3 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts @@ -0,0 +1,138 @@ +'use strict'; +import {ModalsHandler} from "app/utils"; +import {SharingService} from "app/services"; +import {IAppConfigurtaion, ArtifactModel, IFileDownload} from "app/models"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; +import {ArtifactGroupModel} from "../../../../models/artifacts"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; + +export interface IInformationArtifactsScope extends IWorkspaceViewModelScope { + artifacts:Array; + tableHeadersList:Array; + artifactType:string; + isResourceInstance:boolean; + downloadFile:IFileDownload; + isLoading:boolean; + sortBy:string; + reverse:boolean; + + getTitle():string; + addOrUpdate(artifact:ArtifactModel):void; + delete(artifact:ArtifactModel):void; + download(artifact:ArtifactModel):void; + clickArtifactName(artifact:any):void; + openEditEnvParametersModal(artifactResource:ArtifactModel):void; + sort(sortBy:string):void; + showNoArtifactMessage():boolean; +} + +export class InformationArtifactsViewModel { + + static '$inject' = [ + '$scope', + '$filter', + '$state', + 'sdcConfig', + 'ModalsHandler', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:IInformationArtifactsScope, + private $filter:ng.IFilterService, + private $state:any, + private sdcConfig:IAppConfigurtaion, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2: ComponentServiceNg2) { + this.initInformationalArtifacts(); + this.$scope.updateSelectedMenuItem(); + } + + private initInformationalArtifacts = ():void => { + if(!this.$scope.component.artifacts) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentInformationalArtifacts(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.artifacts = response.artifacts; + this.initScope(); + this.$scope.isLoading = false; + }); + } else { + this.initScope(); + } + } + + private initScope = ():void => { + + this.$scope.isLoading = false; + this.$scope.sortBy = 'artifactDisplayName'; + this.$scope.reverse = false; + this.$scope.setValidState(true); + this.$scope.artifactType = 'informational'; + this.$scope.getTitle = ():string => { + return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts'; + + }; + + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'artifactDisplayName'}, + {title: 'Type', property: 'artifactType'}, + {title: 'Version', property: 'artifactVersion'}, + {title: 'UUID', property: 'artifactUUID'} + ]; + + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + + this.$scope.addOrUpdate = (artifact:ArtifactModel):void => { + artifact.artifactGroupType = 'INFORMATIONAL'; + this.ModalsHandler.openArtifactModal(artifact, this.$scope.component).then(() => { + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + }); + }; + + this.$scope.showNoArtifactMessage = ():boolean => { + let artifacts:any = []; + artifacts = _.filter(this.$scope.artifacts, (artifact:ArtifactModel)=> { + return artifact.esId; + }); + + if (artifacts.length === 0) { + return true; + } + return false; + }; + + this.$scope.delete = (artifact:ArtifactModel):void => { + + let onOk = ():void => { + this.$scope.isLoading = true; + let onSuccess = ():void => { + this.$scope.isLoading = false; + this.$scope.artifacts = _.values(this.$scope.component.artifacts); + }; + + let onFailed = (error:any):void => { + console.log('Delete artifact returned error:', error); + this.$scope.isLoading = false; + }; + + this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed); + }; + + let title:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + + this.$scope.clickArtifactName = (artifact:any) => { + if (!artifact.esId) { + this.$scope.addOrUpdate(artifact); + } + + }; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html new file mode 100644 index 0000000000..7c843e9fe8 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html @@ -0,0 +1,66 @@ +
+
Add
+
+
+
+
{{header.title}} + +
+
+
+
+ +
+ There are no information artifacts to display +
+
+ +
+ + {{artifact.artifactDisplayName}} +
+ +
+ {{artifact.artifactType}} +
+ +
+ {{artifact.artifactVersion}} +
+ +
+ {{artifact.artifactUUID}} +
+ +
+ + + +
+
+
+ + +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts.less b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts.less new file mode 100644 index 0000000000..3ba9cf47d5 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts.less @@ -0,0 +1,57 @@ +.workspace-information-artifact { + width: 93%; + display: inline-block; + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height: 490px; + margin-bottom: 0; + } + + .table-container-flex { + margin-top: 27px; + + .item-opened{ + word-wrap: break-word; + } + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 3; + } + + .flex-item:nth-child(4) { + flex-grow: 20; + } + + .flex-item:nth-child(5) { + flex-grow: 5; + padding-top: 10px; + } + + } + +} + + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/inputs.less b/catalog-ui/src/app/view-models/workspace/tabs/inputs/inputs.less new file mode 100644 index 0000000000..eff5c5395b --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/inputs.less @@ -0,0 +1,225 @@ +.workspace-inputs { + + .sdc-workspace-container .w-sdc-main-right-container .w-sdc-main-container-body-content { + padding: 25px 8% 25px 8%; + } + + .w-sdc-main-container .w-sdc-main-right-container > div:first-child { + /* scroll fix */ + padding-bottom: 0px; + } + + + width: 100%; + display: flex; + + .text { + padding-left: 15px; + .no-overflow; + } + + .title-text { + color: @main_color_m; + .f-type._13_m; + .bold; + text-overflow: ellipsis; + overflow: hidden; + } + + .no-overflow { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .title-blue-text { + color: @main_color_a; + .f-type._13_m; + + &.property-name-text { + padding-right: 13px; + border-right: 1px solid rgba(120, 136, 148, 0.26); + flex-grow: 1; + .no-overflow; + } + } + + .instance-name-text { + flex-grow: 1; + } + + ng-include { + width: 45%; + } + + .w-sdc-inputs-search { + padding: 10px 20px 20px 0; + white-space: nowrap; + position: relative; + width: 60%; + height: 64px; + + .inputs-search-icon { + top: 9px; + right: 11px; + } + + .magnification-white { + .sprite-new; + .search-white-icon; + .hand; + } + + .search-icon-container { + width: 35px; + height: 30px; + background-color: @main_color_a; + white-space: nowrap; + float: right; + position: relative; + bottom: 31px; + right: 1px; + border-radius: 0px 4px 4px 0px; + .hand + } + } + + .total-inputs-count { + width: 100%; + font-weight: bold; + text-align: left; + } + + .new-input-button { + margin: 9px 0 0 0; + } + + .w-sdc-inputs-search-input { + border: 1px solid @color_e; + .border-radius(4px); + height: 32px; + margin: 0; + padding: 0px 28px 3px 10px; + vertical-align: 4px; + width: 100%; + outline: none; + font-style: italic; + } + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .prop-to-input-button { + position: absolute; + top: 50%; + margin-right: -20px; + margin-bottom: -10px; + + } + + .table-container-flex { + + .flex-item { + line-height: 22px; + } + .expand-collapse-table-row { + + .data-row { + background: @tlv_color_u; + .hand; + align-items: center; + padding: 0 12px; + margin: 1px 0px 1px 0px; + min-height: 40px; + } + + .data-row:hover { + .bg_j; + } + } + } + + .table { + height: 640px; + margin-bottom: 0; + clear: both; + + .empty-row { + padding: 3px; + } + + .flex-item { + line-height: 22px; + } + + .table-header { + + line-height: 14px; + background-color: @main_color_a; + color: @main_color_p; + text-align: left; + padding: 7px 5px 7px 10px; + .f-type._14_m; + } + .head { + background-color: #e6e6e6; + } + + .body { + .scrollbar-container { + .perfect-scrollbar; + max-height: 610px; + } + + .expand-collapse-inputs-table-icon { + .hand; + .sprite-new; + .arrow-up; + transition: .3s all; + position: relative; + left: 8px; + border: none !important; + padding: 0px 10px 0px 10px; + } + + .table-col-text { + margin-left: 14px; + } + } + } + + .inputs-header { + width: 100%; + position: relative; + bottom: 31px; + } + + .inputs-tables-container { + width: 100%; + min-width: 100%; + display: flex; + } + + .inputs-button-container { + width: 8%; + min-width: 8%; + display: flex; + + .right-arrow-btn { + .sprite-new; + .blue-right-arrow-circle; + margin: auto; + cursor: pointer; + } + } + + .table-container-flex { + margin-top: 27px; + width: 46%; + min-width: 46%; + display: inline-block; + float: left; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts new file mode 100644 index 0000000000..49fedd6e21 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts @@ -0,0 +1,117 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentInstance, InstancesInputsOrPropertiesMapData, Resource, PropertyModel, InputModel} from "app/models"; +import {ModalsHandler} from "app/utils"; + +export interface IInputsViewModelScope extends IWorkspaceViewModelScope { + InstanceInputsProperties:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used + vfInstancesList:Array; + component:Resource; + + onArrowPressed():void; + getInputPropertiesForInstance(instanceId:string, instance:ComponentInstance):ng.IPromise ; + loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise ; + openEditValueModal(input:InputModel):void; + openEditPropertyModal(property:PropertyModel):void; +} + +export class ResourceInputsViewModel { + + static '$inject' = [ + '$scope', + '$q', + 'ModalsHandler' + ]; + + constructor(private $scope:IInputsViewModelScope, private $q:ng.IQService, private ModalsHandler:ModalsHandler) { + this.initScope(); + this.$scope.updateSelectedMenuItem(); + } + + private initScope = ():void => { + + this.$scope.InstanceInputsProperties = new InstancesInputsOrPropertiesMapData(); + this.$scope.vfInstancesList = this.$scope.component.componentInstances; + + // Need to cast all inputs to InputModel for the search to work + let tmpInputs:Array = new Array(); + _.each(this.$scope.component.inputs, (input):void => { + tmpInputs.push(new InputModel(input)); + }); + this.$scope.component.inputs = tmpInputs; + // This function is not supported for resource + //this.$scope.component.getComponentInputs(); + + /* + * When clicking on instance input in the left or right table, this function will load all properties of the selected input + */ + this.$scope.getInputPropertiesForInstance = (instanceId:string, instance:ComponentInstance):ng.IPromise => { + let deferred = this.$q.defer(); + instance.properties = this.$scope.component.componentInstancesProperties[instanceId]; + deferred.resolve(true); + return deferred.promise; + }; + + /* + * When clicking on instance input in the left or right table, this function will load all properties of the selected input + */ + this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:InputModel):ng.IPromise => { + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array) => { + input.properties = properties; + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false) + }; + + if (!input.properties) { + this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When pressing the arrow, we create service inputs from the inputs selected + */ + this.$scope.onArrowPressed = ():void => { + let onSuccess = (inputsCreated:Array) => { + + //disabled all the inputs in the left table + _.forEach(this.$scope.InstanceInputsProperties, (properties:Array) => { + _.forEach(properties, (property:PropertyModel) => { + property.isAlreadySelected = true; + }); + }); + + // Adding color to the new inputs (right table) + _.forEach(inputsCreated, (input) => { + input.isNew = true; + }); + + // Removing color to the new inputs (right table) + setTimeout(() => { + _.forEach(inputsCreated, (input) => { + input.isNew = false; + }); + this.$scope.$apply(); + }, 3000); + }; + + this.$scope.component.createInputsFormInstances(this.$scope.InstanceInputsProperties).then(onSuccess); + }; + + this.$scope.openEditValueModal = (input:InputModel) => { + this.ModalsHandler.openEditInputValueModal(input); + }; + + this.$scope.openEditPropertyModal = (property:PropertyModel):void => { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.componentInstancesProperties[property.resourceInstanceUniqueId], false).then(() => { + }); + } + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html new file mode 100644 index 0000000000..a0715973ab --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html @@ -0,0 +1,86 @@ +
+
+ +
+
VFC instances inputs
+
+
+ + + +
+
+
+ {{instance.name}} +
+
+ +
+ +
+
No properties to display
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+
+
VF inputs
+ + +
+ + + +
+
+ +
+
No properties to display
+
+ +
+
+ +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less new file mode 100644 index 0000000000..ebb32fbdb2 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less @@ -0,0 +1,9 @@ +.workspace-inputs { + + .property-row { + .input-check-box { + text-align: center; + } + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts new file mode 100644 index 0000000000..f3b2de0943 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts @@ -0,0 +1,378 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentInstance, InstancesInputsOrPropertiesMapData, Service, IAppMenu, InputModel, PropertyModel, InputPropertyBase} from "app/models"; +import {DataTypesService} from "app/services"; +import {ModalsHandler, ResourceType} from "app/utils"; + + +interface IServiceInputsViewModelScope extends IWorkspaceViewModelScope { + + vfInstancesList:Array; + instanceInputsMap:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used + instancePropertiesMap:InstancesInputsOrPropertiesMapData; + component:Service; + sdcMenu:IAppMenu; + isViewOnly:boolean; + isArrowDisabled:boolean; + onArrowPressed():void; + checkArrowState():void; + loadComponentInputs():void; + loadInstanceInputs(instance:ComponentInstance):ng.IPromise ; + loadInstanceProperties(instance:ComponentInstance):ng.IPromise ; + loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise ; + loadInputInputs(input:InputModel):ng.IPromise; + deleteInput(input:InputModel):void + openEditValueModal(input:InputModel):void; + openSelectPropertyDataTypeViewModel(instanceId:string, property:PropertyModel):void; + openEditPropertyDataTypeViewModel(property:PropertyModel):void; + dataTypesService:DataTypesService; +} + +export class ServiceInputsViewModel { + + static '$inject' = [ + '$scope', + '$q', + 'ModalsHandler', + 'Sdc.Services.DataTypesService' + ]; + + constructor(private $scope:IServiceInputsViewModelScope, + private $q:ng.IQService, + private ModalsHandler:ModalsHandler, + private DataTypesService:DataTypesService) { + this.initScope(); + this.$scope.updateSelectedMenuItem(); + this.$scope.isViewOnly = this.$scope.isViewMode(); + } + + + private getInputsOrPropertiesAlreadySelected = (instanceNormalizeName:string, arrayToFilter:Array):Array => { + let alreadySelectedInput = []; + _.forEach(arrayToFilter, (inputOrProperty:InputPropertyBase) => { + let expectedServiceInputName = instanceNormalizeName + '_' + inputOrProperty.name; + let inputAlreadyInService = _.find(this.$scope.component.inputs, (serviceInput:InputModel) => { + //Checking if the input prefix is the instance name + '_' + property/input name (prefix because we don't need to check full name for complex dataType) + return serviceInput.name.substring(0, expectedServiceInputName.length) === expectedServiceInputName; + }); + if (inputAlreadyInService) { + inputOrProperty.isAlreadySelected = true; + alreadySelectedInput.push(inputOrProperty); + } else { + inputOrProperty.isAlreadySelected = false; + } + }); + return alreadySelectedInput; + }; + + + /* + * When loading the screen again, we need to disabled the inputs that already created on the service, + * we do that by comparing the service input name, to the instance name + '_' + the resource instance input name. + */ + private disableEnableSelectedInputsOrPropertiesOnInit = (instance:ComponentInstance):void => { + + if (instance.originType === ResourceType.VF) { + this.$scope.instanceInputsMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.inputs); + } else { + this.$scope.instancePropertiesMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.properties); + } + }; + + /* + * Enable Input/Property after delete + */ + private enableInputsAfterDelete = (propertiesOrInputsDeletes:Array):void => { + + _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted + + let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => { + return inputOrProperty.uniqueId === deletedInputInput.uniqueId; + }); + inputOrPropertyDeleted.isAlreadySelected = false; + delete _.remove(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0]; + }); + }; + + /* + * Enable Input/Property after delete + */ + private enablePropertiesAfterDelete = (propertiesOrInputsDeletes:Array):void => { + + _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted + let componentInstance = _.find(this.$scope.vfInstancesList, (instance:ComponentInstance) => { + return instance.uniqueId === deletedInputInput.componentInstanceId; + }); + let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => { + return inputOrProperty.uniqueId === deletedInputInput.uniqueId; + }); + + let expectedName = componentInstance.normalizedName + '_' + inputOrPropertyDeleted.name; + let isAnotherInputExist = _.find(this.$scope.component.inputs, (input:InputModel) => { + return input.name.substring(0, expectedName.length) === expectedName; + }); + if (!isAnotherInputExist) { + inputOrPropertyDeleted.isAlreadySelected = false; + delete _.remove(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0]; + } + }); + }; + + private initScope = ():void => { + + this.$scope.instanceInputsMap = new InstancesInputsOrPropertiesMapData(); + this.$scope.instancePropertiesMap = new InstancesInputsOrPropertiesMapData(); + this.$scope.isLoading = true; + this.$scope.isArrowDisabled = true; + // Why do we need this? we call this later. + //this.$scope.component.getComponentInputs(); + + let onSuccess = (componentInstances:Array) => { + console.log("component instances loaded: ", componentInstances); + this.$scope.vfInstancesList = componentInstances; + this.$scope.isLoading = false; + }; + + //This function will get al component instance for the left table - in + // future the instances will be filter according to search text + this.$scope.component.getComponentInstancesFilteredByInputsAndProperties().then(onSuccess); + + // This function will get the service inputs for the right table + this.$scope.component.getComponentInputs(); + + /* + * When clicking on instance in the left table, this function will load all instance inputs + */ + this.$scope.loadInstanceInputs = (instance:ComponentInstance):ng.IPromise => { + let deferred = this.$q.defer(); + + let onSuccess = (inputs:Array) => { + instance.inputs = inputs; + this.disableEnableSelectedInputsOrPropertiesOnInit(instance); + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false); + }; + + if (!instance.inputs) { + this.$scope.component.getComponentInstanceInputs(instance.uniqueId, instance.componentUid).then(onSuccess, onError); + //this.disableEnableSelectedInputs(instance); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + + this.$scope.loadInstanceProperties = (instance:ComponentInstance):ng.IPromise => { + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array) => { + instance.properties = properties; + this.disableEnableSelectedInputsOrPropertiesOnInit(instance); + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false); + }; + + if (!instance.properties) { + this.$scope.component.getComponentInstanceProperties(instance.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When clicking on instance input in the left or right table, this function will load all properties of the selected input + */ + this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:InputModel):ng.IPromise => { + let deferred = this.$q.defer(); + + let onSuccess = (properties:Array) => { + input.properties = properties; + deferred.resolve(true); + }; + + let onError = () => { + deferred.resolve(false) + }; + + if (!input.properties) { + this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When clicking on input in the right table, this function will load all inputs of the selected input + */ + this.$scope.loadInputInputs = (input:InputModel):ng.IPromise => { + let deferred = this.$q.defer(); + + let onSuccess = () => { + deferred.resolve(true); + }; + let onError = () => { + deferred.resolve(false); + }; + + if (!input.inputs) { // Caching, if exists do not get it. + this.$scope.component.getServiceInputInputsAndProperties(input.uniqueId).then(onSuccess, onError); + } else { + deferred.resolve(true); + } + return deferred.promise; + }; + + /* + * When pressing the arrow, we create service inputs from the inputs selected + */ + this.$scope.onArrowPressed = ():void => { + let onSuccess = (inputsCreated:Array) => { + + //disabled all the inputs in the left table + _.forEach(this.$scope.instanceInputsMap, (inputs:Array, instanceId:string) => { + _.forEach(inputs, (input:InputModel) => { + input.isAlreadySelected = true; + }); + }); + _.forEach(this.$scope.instancePropertiesMap, (properties:Array, instanceId:string) => { + _.forEach(properties, (property:PropertyModel) => { + property.isAlreadySelected = true; + }); + }); + this.addColorToItems(inputsCreated); + }; + + let onFailed = (error:any) => { + this.$scope.isArrowDisabled = false; + console.log("Error declaring input/property"); + }; + + this.$scope.isArrowDisabled = true; + this.$scope.component.createInputsFormInstances(this.$scope.instanceInputsMap, this.$scope.instancePropertiesMap).then(onSuccess, onFailed); + }; + + + /* Iterates through array of selected inputs and properties and returns true if there is at least one new selection on left */ + this.$scope.checkArrowState = ()=> { + + let newInputSelected:boolean = _.some(this.$scope.instanceInputsMap, (inputs:Array) => { + return _.some(inputs, (input:InputModel)=> { + return input.isAlreadySelected === false; + }); + }); + + let newPropSelected:boolean = _.some(this.$scope.instancePropertiesMap, (properties:Array) => { + return _.some(properties, (property:PropertyModel) => { + return property.isAlreadySelected === false; + }); + }); + + this.$scope.isArrowDisabled = !(newInputSelected || newPropSelected); + + }; + + this.$scope.deleteInput = (inputToDelete:InputModel):void => { + + let onDelete = ():void => { + + let onSuccess = (deletedInput:InputModel):void => { + if (deletedInput.inputs && deletedInput.inputs.length > 0) { // Enable input declared from input + this.enableInputsAfterDelete(deletedInput.inputs); + } + + if (deletedInput.properties && deletedInput.properties.length > 0) { // Enable properties + this.enablePropertiesAfterDelete(deletedInput.properties); + } + deletedInput.isDeleteDisabled = false; + this.$scope.checkArrowState(); + + }; + + let onFailed = (error:any):void => { + console.log("Error deleting input"); + inputToDelete.isDeleteDisabled = false; + }; + + inputToDelete.isDeleteDisabled = true; + this.addColorToItems([inputToDelete]); + this.$scope.component.deleteServiceInput(inputToDelete.uniqueId).then((deletedInput:InputModel):void => { + onSuccess(deletedInput); + }, onFailed); + }; + + // Get confirmation modal text from menu.json + let state = "deleteInput"; + let title:string = this.$scope.sdcMenu.alertMessages[state].title; + let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([inputToDelete.name]); + + // Open confirmation modal + this.ModalsHandler.openAlertModal(title, message).then(onDelete); + }; + + this.$scope.openEditValueModal = (input:InputModel) => { + this.ModalsHandler.openEditInputValueModal(input); + }; + + this.$scope.openSelectPropertyDataTypeViewModel = (instanceId:string, property:PropertyModel) => { + //to open the select data type modal + let selectedInstance = _.find(this.$scope.vfInstancesList, {uniqueId: instanceId}); + this.DataTypesService.selectedInstance = selectedInstance; //set the selected instance on the service for compering the input name on the service & the complex property + this.DataTypesService.selectedComponentInputs = this.$scope.component.inputs; // set all the service inputs on the data type service + let filteredPropertiesMap = _.filter(this.$scope.instancePropertiesMap[instanceId], (instanceProperty)=> { + return instanceProperty.name == property.name; + });//get all properties under the specific property + this.DataTypesService.selectedPropertiesName = property.propertiesName; + + this.ModalsHandler.openSelectDataTypeModal(property, this.$scope.component, this.$scope.component.properties, filteredPropertiesMap).then((selectedProperty:PropertyModel)=> { + if (selectedProperty && selectedProperty.propertiesName) { + let propertyToUpdate:PropertyModel = _.find(selectedInstance.properties, {uniqueId: selectedProperty.uniqueId}); + let existingProperty:PropertyModel = (_.find(this.$scope.instancePropertiesMap[instanceId], {uniqueId: propertyToUpdate.uniqueId})); + + if (existingProperty) { + existingProperty.propertiesName = selectedProperty.propertiesName; + existingProperty.input = selectedProperty.input; + existingProperty.isAlreadySelected = false; + } else { + propertyToUpdate.propertiesName = selectedProperty.propertiesName; + propertyToUpdate.input = selectedProperty.input; + this.$scope.instancePropertiesMap[instanceId].push(propertyToUpdate); + + } + this.$scope.checkArrowState(); + + } + }); + }; + + + this.$scope.openEditPropertyDataTypeViewModel = (property:PropertyModel)=> { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.properties, false).then(() => { + }); + } + }; + + private addColorToItems = (inputsCreated:Array):void => { + + // Adding color to the new inputs (right table) + _.forEach(inputsCreated, (input) => { + input.isNew = true; + }); + + // Removing color to the new inputs (right table) + setTimeout(() => { + _.forEach(inputsCreated, (input) => { + input.isNew = false; + }); + this.$scope.$apply(); + }, 3000); + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html new file mode 100644 index 0000000000..cb4d853f58 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html @@ -0,0 +1,134 @@ +
+
+ +
+
Resource instance inputs
+
+
+ + + +
+
+
+ {{instance.name}} +
+
+
+ +
+
+ + + + + +
+
No properties to display +
+
+ +
+
+
+
+
No + properties to display +
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
Service inputs
+ + + + + +
+
+ + + + +
+
No properties to display +
+
+ +
+
+
+
+
No properties to display
+
+ +
+
+
+ + +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less new file mode 100644 index 0000000000..f783d0b6d6 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs.less @@ -0,0 +1,47 @@ +.workspace-inputs { + + .service-inputs-view { + + .table-container-flex { + width:100% !important; + } + + .table-loader { + position: relative; + top:215px; + } + + } + + .infinite-scroll { + + overflow-y: scroll; + overflow-x: hidden; + max-height: 400px; + } + + .class_with_css_props_leading_to_a_scroll { + height: 100%; + overflow-y: auto; + } + + .table-container-flex { + .expand-collapse-table-row { + .service-input-row { + padding-left: 15px; + border: none; + border-bottom: rgba(120, 136, 148, 0.26) solid 1px; + + &.service-input-row { + background: @tlv_color_u; + &.new-input { + background: @tlv_color_v; + } + } + } + + + } + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts new file mode 100644 index 0000000000..b7428e990b --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts @@ -0,0 +1,127 @@ + 'use strict'; + import {ArtifactType} from "app/utils"; + import {ArtifactGroupModel} from "app/models"; + import {participant} from "app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model"; + import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; + import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; + import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; + + export interface IManagementWorkflowViewModelScope extends IWorkspaceViewModelScope { + vendorModel:VendorModel; + } + + export class VendorModel { + artifacts: ArtifactGroupModel; + serviceID: string; + readonly: boolean; + sessionID: string; + requestID: string; + diagramType: string; + participants:Array; + + constructor(artifacts: ArtifactGroupModel, serviceID:string, readonly:boolean, sessionID:string, + requestID:string, diagramType:string, participants:Array){ + this.artifacts = artifacts; + this.serviceID = serviceID; + this.readonly = readonly; + this.sessionID = sessionID; + this.requestID = requestID; + this.diagramType = diagramType; + this.participants = participants; + } + } + + export class ManagementWorkflowViewModel { + + static '$inject' = [ + '$scope', + 'uuid4', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:IManagementWorkflowViewModelScope, + private uuid4:any, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.initInformationalArtifacts(); + this.$scope.updateSelectedMenuItem(); + } + + + private initInformationalArtifacts = ():void => { + if(!this.$scope.component.artifacts) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentInformationalArtifacts(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.artifacts = response.artifacts; + this.initScope(); + this.$scope.isLoading = false; + }); + } else { + this.initScope(); + } + } + + private static getParticipants():Array { + return [ + { + "id": "1", + "name": "Customer"}, + { + "id": "2", + "name": "CCD" + }, + { + "id": "3", + "name": "Infrastructure" + }, + { + "id": "4", + "name": "MSO" + }, + { + "id": "5", + "name": "SDN-C" + }, + { + "id": "6", + "name": "A&AI" + }, + { + "id": "7", + "name": "APP-C" + }, + { + "id": "8", + "name": "Cloud" + }, + { + "id": "9", + "name": "DCAE" + }, + { + "id": "10", + "name": "ALTS" + }, + { + "id": "11", + "name": "VF" + } + ] + } + + + private initScope():void { + this.$scope.vendorModel = new VendorModel( + this.$scope.component.artifacts.filteredByType(ArtifactType.THIRD_PARTY_RESERVED_TYPES.WORKFLOW), + this.$scope.component.uniqueId, + this.$scope.isViewMode(), + this.$scope.user.userId, + this.uuid4.generate(), + ArtifactType.THIRD_PARTY_RESERVED_TYPES.WORKFLOW, + ManagementWorkflowViewModel.getParticipants() + ); + + this.$scope.thirdParty = true; + this.$scope.setValidState(true); + } + } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view.html b/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view.html new file mode 100644 index 0000000000..bd196daec8 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/management-workflow/management-workflow-view.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts new file mode 100644 index 0000000000..511c17eb60 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts @@ -0,0 +1,79 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {VendorModel} from "app/view-models/workspace/tabs/management-workflow/management-workflow-view-model"; +import {ResourceType, ArtifactType} from "app/utils"; +import {ComponentInstance} from "app/models"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; + +export interface INetworkCallFlowViewModelScope extends IWorkspaceViewModelScope { + vendorMessageModel:VendorModel; +} + +export class participant { + name:string; + id:string; + + constructor(instance:ComponentInstance) { + this.name = instance.name; + this.id = instance.uniqueId; + } +} + + +export class NetworkCallFlowViewModel { + + static '$inject' = [ + '$scope', + 'uuid4', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:INetworkCallFlowViewModelScope, + private uuid4:any, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.initComponentInstancesAndInformationalArtifacts(); + this.$scope.updateSelectedMenuItem(); + } + + private getVFParticipantsFromInstances(instances:Array):Array { + let participants = []; + _.forEach(instances, (instance)=> { + if (ResourceType.VF == instance.originType) { + participants.push(new participant(instance)); + } + }); + return participants; + } + + private initComponentInstancesAndInformationalArtifacts = ():void => { + if(!this.$scope.component.artifacts || !this.$scope.component.componentInstances) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentInformationalArtifactsAndInstances(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.artifacts = response.artifacts; + this.$scope.component.componentInstances = response.componentInstances; + this.initScope(); + this.$scope.isLoading = false; + }); + } else { + this.initScope(); + } + } + + private initScope():void { + this.$scope.vendorMessageModel = new VendorModel( + this.$scope.component.artifacts.filteredByType(ArtifactType.THIRD_PARTY_RESERVED_TYPES.NETWORK_CALL_FLOW), + this.$scope.component.uniqueId, + this.$scope.isViewMode(), + this.$scope.user.userId, + this.uuid4.generate(), + ArtifactType.THIRD_PARTY_RESERVED_TYPES.NETWORK_CALL_FLOW, + this.getVFParticipantsFromInstances(this.$scope.component.componentInstances) + ); + + this.$scope.thirdParty = true; + this.$scope.setValidState(true); + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html new file mode 100644 index 0000000000..6ce3e8e2b7 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts new file mode 100644 index 0000000000..233b12952b --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts @@ -0,0 +1,109 @@ +'use strict'; +import {ComponentFactory} from "app/utils"; +import {Product, IGroup, ISubCategory, IMainCategory} from "app/models"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {CacheService} from "app/services"; + +export interface IProductHierarchyScope extends IWorkspaceViewModelScope { + + categoriesOptions:Array; + product:Product; + isLoading:boolean; + showDropDown:boolean; + + onInputTextClicked():void; + onGroupSelected(category:IMainCategory, subcategory:ISubCategory, group:IGroup):void; + clickOutside():void; + deleteGroup(uniqueId:string):void; +} + +export class ProductHierarchyViewModel { + + static '$inject' = [ + '$scope', + 'Sdc.Services.CacheService', + 'ComponentFactory', + '$state' + ]; + + constructor(private $scope:IProductHierarchyScope, + private cacheService:CacheService, + private ComponentFactory:ComponentFactory, + private $state:ng.ui.IStateService) { + + + this.$scope.product = this.$scope.getComponent(); + this.$scope.setValidState(true); + this.initScope(); + this.$scope.updateSelectedMenuItem(); + } + + private initCategories = () => { + this.$scope.categoriesOptions = angular.copy(this.cacheService.get('productCategories')); + let selectedGroup:Array = []; + _.forEach(this.$scope.product.categories, (category:IMainCategory) => { + _.forEach(category.subcategories, (subcategory:ISubCategory) => { + selectedGroup = selectedGroup.concat(subcategory.groupings); + }); + }); + _.forEach(this.$scope.categoriesOptions, (category:IMainCategory) => { + _.forEach(category.subcategories, (subcategory:ISubCategory) => { + _.forEach(subcategory.groupings, (group:ISubCategory) => { + let componentGroup:IGroup = _.find(selectedGroup, (componentGroupObj) => { + return componentGroupObj.uniqueId == group.uniqueId; + }); + if (componentGroup) { + group.isDisabled = true; + } + }); + }); + }); + }; + + private setFormValidation = ():void => { + + this.$scope.setValidState(true); + + }; + + private initScope = ():void => { + this.$scope.isLoading = false; + this.$scope.showDropDown = false; + this.initCategories(); + this.setFormValidation(); + + this.$scope.onGroupSelected = (category:IMainCategory, subcategory:ISubCategory, group:IGroup):void => { + this.$scope.product.addGroup(category, subcategory, group); + this.$state.current.data.unsavedChanges = !this.$scope.isViewMode(); + group.isDisabled = true; + this.$scope.showDropDown = false; + this.setFormValidation(); + }; + + this.$scope.onInputTextClicked = ():void => {//just edit the component in place, no pop up nor server update ? + this.$scope.showDropDown = !this.$scope.showDropDown; + }; + + this.$scope.clickOutside = ():any => { + this.$scope.showDropDown = false; + }; + + this.$scope.deleteGroup = (uniqueId:string):void => { + //delete group from component + this.$scope.product.deleteGroup(uniqueId); + this.$state.current.data.unsavedChanges = !this.$scope.isViewMode(); + this.setFormValidation(); + //enabled group + _.forEach(this.$scope.categoriesOptions, (category:IMainCategory) => { + _.forEach(category.subcategories, (subcategory:ISubCategory) => { + let groupObj:IGroup = _.find(subcategory.groupings, (group) => { + return group.uniqueId === uniqueId; + }); + if (groupObj) { + groupObj.isDisabled = false; + } + }); + }); + } + }; +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html new file mode 100644 index 0000000000..2335ad7c74 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html @@ -0,0 +1,40 @@ +
+ +
+ +
+
+
+
+ +
+
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less new file mode 100644 index 0000000000..c992558ed2 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less @@ -0,0 +1,130 @@ +.workspace-hierarchy { + display: inline-block; + width: 93%; + + .scrollbar-container{ + max-height:400px; + .perfect-scrollbar; + } + + .dropdown-container { + position: relative; + display: inline-block; + width: 100%; + + &:after{ + top: 47%; + right: 1%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(0, 0, 0, 0); + border-top-color: black; + border-width: 4px; + margin-left: -4px; + } + + .dropdown-input-text { + width: 100%; + padding: 4px 10px; + } + + .dropdown-content { + .perfect-scrollbar; + border: 1px solid #d8d8d8; + display: none; + position: absolute; + overflow: hidden; + width: 100%; + .bg_c; + max-height: 400px; + z-index: 999999; + + .dropdown-option { + border-bottom: 1px solid #d8d8d8; + display: inline-block; + width: 100%; + } + + .category-container{ + width: 250px; + float: left; + padding-left: 5px; + + .category { + .bold; + padding: 3px 3px 2px 3px; + &:after{ + .sprite; + .arrow-left; + content: ''; + margin-left: 5px; + transform: rotate(180deg); + } + } + .subcategory { + padding-left: 3px; + } + } + + .groupings-container{ + display: inline-block; + width: 424px; + border-left: 1px solid #d8d8d8; + min-height: 55px; + .group{ + padding: 3px 3px 3px 10px; + &:hover{ + .hand; + .bg_n; + } + &.disabled-group { + opacity: 0.5; + &:hover{ + cursor: auto; + .bg_c; + } + } + } + } + + .seperator { + height: 1px; + width: 100%; + .bg_j; + margin: 5px 0px; + } + } + .show { + display: block; + } + } + + .hierarchy-groups-container{ + .b_9; + width: 100%; + border: 1px solid #d8d8d8; + height: 425px; + padding: 15px; + text-align: center; + + .scrollbar-container { + z-index: 0; + } + + .no-group-text{ + text-align: center; + margin-top:25px; + a { + cursor: pointer; + } + } + .group-tag{ + display: inline-block; + float: left; + } + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts new file mode 100644 index 0000000000..923cc58a7e --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view-model.ts @@ -0,0 +1,92 @@ +'use strict'; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {PropertyModel} from "app/models"; +import {ModalsHandler} from "app/utils"; +import {COMPONENT_FIELDS} from "../../../../utils/constants"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; + +interface IPropertiesViewModelScope extends IWorkspaceViewModelScope { + tableHeadersList:Array; + reverse:boolean; + sortBy:string; + filteredProperties:Array; + + addOrUpdateProperty(property?:PropertyModel):void; + delete(property:PropertyModel):void; + sort(sortBy:string):void; +} + +export class PropertiesViewModel { + + static '$inject' = [ + '$scope', + '$filter', + 'ModalsHandler', + 'ComponentServiceNg2' + ]; + + + constructor(private $scope:IPropertiesViewModelScope, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2:ComponentServiceNg2) { + this.initComponentProperties(); + this.$scope.updateSelectedMenuItem(); + } + + private initComponentProperties = ():void => { + + if(!this.$scope.component.properties) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentProperties(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.properties = response.properties; + this.initScope(); + this.$scope.isLoading = false; + }, () => { + this.$scope.isLoading = false; + }); + } else { + this.initScope(); + } + } + + private openEditPropertyModal = (property:PropertyModel):void => { + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties, false).then(() => { + }); + }; + + private initScope = ():void => { + + //let self = this; + this.$scope.filteredProperties = this.$scope.component.properties; + this.$scope.sortBy = 'name'; + this.$scope.reverse = false; + this.$scope.setValidState(true); + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Type', property: 'type'}, + {title: 'Schema', property: 'schema.property.type'}, + {title: 'Description', property: 'description'}, + ]; + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + + this.$scope.addOrUpdateProperty = (property?:PropertyModel):void => { + this.openEditPropertyModal(property ? property : new PropertyModel()); + }; + + this.$scope.delete = (property:PropertyModel):void => { + + let onOk = ():void => { + this.$scope.component.deleteProperty(property.uniqueId); + }; + let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE"); + let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}"); + this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk); + }; + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view.html b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view.html new file mode 100644 index 0000000000..d1e0582386 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties-view.html @@ -0,0 +1,59 @@ +
+
+ Total Properties: {{component.properties.length}} + + +
+
Add Property
+
+
+
+
{{header.title}} + +
+
+ +
+ +
+ +
+ There are no properties to display
+ click here to add one + +
+
+ + + +
+ {{property.type.replace("org.openecomp.datatypes.heat.","")}} +
+
+ {{property.schema.property.type.replace("org.openecomp.datatypes.heat.","")}} +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/properties/properties.less b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties.less new file mode 100644 index 0000000000..3e8d6c3fbd --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/properties/properties.less @@ -0,0 +1,115 @@ +.workspace-properties { + + width: 93%; + display: inline-block; + + #left-top-bar{ + float: left; + width: 155px; + ::-webkit-input-placeholder { + font-style: italic; + } + :-moz-placeholder { + font-style: italic; + } + ::-moz-placeholder { + font-style: italic; + } + :-ms-input-placeholder { + font-style: italic; + } + + #properties-count{ + font-weight: bold; + float: left; + } + + #search-by-name{ + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + width: 245px; + height: 32px; + line-height: 32px; + border: 1px solid @main_color_o; + text-indent: 10px; + } + + .search-button{ + .hand; + cursor: pointer; + float: right; + position: relative; + top: -22px; + right: -80px; + } + } + + .add-btn { + margin: 36px 0 0 0; + } + + .delete-col-header{ + .sprite; + .sprite.e-sdc-small-icon-delete; + } + + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .table{ + height:490px; + margin-bottom: 0; + } + + .data-row{ + .table-delete-btn{ + display: none !important; + } + &:hover{ + .table-delete-btn{ + display: inline-block !important; + } + } + } + + .table-container-flex { + margin-top: 27px; + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + a{ + .hand + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 6; + } + + .flex-item:nth-child(4) { + flex-grow: 20; + + } + .flex-item:nth-child(5) { + flex-grow: 3; + padding-top: 10px; + } + + + } + +} 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 new file mode 100644 index 0000000000..4ac99edf8a --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts @@ -0,0 +1,147 @@ +/** + * Created by rcohen on 9/22/2016. + */ +'use strict'; +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"; + +export class SortTableDefined { + reverse:boolean; + sortByField:string; +} + +interface IReqAndCapabilitiesViewModelScope extends IWorkspaceViewModelScope { + requirementsTableHeadersList:Array; + capabilitiesTableHeadersList:Array; + capabilityPropertiesTableHeadersList:Array; + requirementsSortTableDefined:SortTableDefined; + capabilitiesSortTableDefined:SortTableDefined; + propertiesSortTableDefined:SortTableDefined; + requirements:Array; + capabilities:Array; + mode:string; + filteredProperties:Array>; + searchText:string; + + sort(sortBy:string, sortByTableDefined:SortTableDefined):void; + updateProperty(property:PropertyModel, indexInFilteredProperties:number):void; + allCapabilitiesSelected(selected:boolean):void; +} + +export class ReqAndCapabilitiesViewModel { + + static '$inject' = [ + '$scope', + '$filter', + 'ModalsHandler', + 'ComponentServiceNg2' + ]; + + + constructor(private $scope:IReqAndCapabilitiesViewModelScope, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private ComponentServiceNg2: ComponentServiceNg2) { + + this.initCapabilitiesAndRequirements(); + this.$scope.updateSelectedMenuItem(); + } + + 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.initScope(); + } + + } + + private openEditPropertyModal = (property:PropertyModel, indexInFilteredProperties:number):void => { + //...because there is not be api + _.forEach(this.$scope.filteredProperties[indexInFilteredProperties], (prop:PropertyModel)=> { + prop.readonly = true; + }); + this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties[indexInFilteredProperties], false).then(() => { + + }); + }; + + private initScope = ():void => { + + this.$scope.requirementsSortTableDefined = { + reverse: false, + sortByField: 'name' + }; + this.$scope.capabilitiesSortTableDefined = { + reverse: false, + sortByField: 'name' + }; + this.$scope.propertiesSortTableDefined = { + reverse: false, + sortByField: 'name' + }; + + this.$scope.setValidState(true); + this.$scope.requirementsTableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Capability', property: 'capability'}, + {title: 'Node', property: 'node'}, + {title: 'Relationship', property: 'relationship'}, + {title: 'Connected To', property: ''}, + {title: 'Occurrences', property: ''} + ]; + this.$scope.capabilitiesTableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Type', property: 'type'}, + {title: 'Description', property: ''}, + {title: 'Valid Source', property: ''}, + {title: 'Occurrences', property: ''} + ]; + this.$scope.capabilityPropertiesTableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Type', property: 'type'}, + {title: 'Schema', property: 'schema.property.type'}, + {title: 'Description', property: 'description'}, + ]; + this.$scope.filteredProperties = []; + + this.$scope.mode = 'requirements'; + this.$scope.requirements = []; + _.forEach(this.$scope.component.requirements, (req:Array, capName)=> { + this.$scope.requirements = this.$scope.requirements.concat(req); + }); + + this.$scope.capabilities = []; + _.forEach(this.$scope.component.capabilities, (cap:Array, capName)=> { + this.$scope.capabilities = this.$scope.capabilities.concat(cap); + }); + + this.$scope.sort = (sortBy:string, sortByTableDefined:SortTableDefined):void => { + sortByTableDefined.reverse = (sortByTableDefined.sortByField === sortBy) ? !sortByTableDefined.reverse : false; + sortByTableDefined.sortByField = sortBy; + }; + + this.$scope.updateProperty = (property:PropertyModel, indexInFilteredProperties:number):void => { + this.openEditPropertyModal(property, indexInFilteredProperties); + }; + + this.$scope.allCapabilitiesSelected = (selected:boolean):void => { + _.forEach(this.$scope.capabilities, (cap:Capability)=> { + cap.selected = selected; + }); + }; + } +} + diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html new file mode 100644 index 0000000000..3d6ccb9d00 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html @@ -0,0 +1,143 @@ +
+
+ + +
+
+ + +
+ +
+
+
+
{{header.title}} + +
+
+ +
+ +
+ There are no requirements to display + +
+
+ +
+ {{req.name}} +
+
+ {{req.capability.substring("tosca.capabilities.".length)}} +
+
+ {{req.node.substring("tosca.nodes.".length)}} +
+
+ {{req.relationship.substring("tosca.relationships.".length)}} +
+
+
+ {{req.minOccurrences}},{{req.maxOccurrences}} +
+
+
+
+ +
+
+
+
+
+
{{header.title}} + +
+
+ +
+ +
+ There are no capabilities to display + +
+
+ +
+ + {{capability.name}} +
+
+ {{capability.type.replace("tosca.capabilities.","")}} +
+ +
+ {{capability.description}} +
+ +
+ {{capability.validSourceTypes.join(',')}} +
+ +
+ {{capability.minOccurrences}},{{capability.maxOccurrences}} +
+
+
+

Properties

+
+
+
+
{{header.title}} + +
+
+ +
+
+ There are no properties to display +
+
+ + + +
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+ 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 new file mode 100644 index 0000000000..9b52fad411 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less @@ -0,0 +1,196 @@ +.workspace-req-and-cap { + + width: 93%; + display: inline-block; + + .tabs{ + float: left; + position: relative; + top: 6px; + button{ + float: left; + width: 233px; + height: 38px; + background-color: @tlv_color_t; + border: 1px solid @main_color_o; + color: black; + &:nth-child(1){ + border-radius: 10px 0 0 0; + } + &:nth-child(2){ + border-radius: 0 10px 0 0; + } + &.selected{ + background-color: @main_color_a; + border: 1px solid @main_color_a; + color: white; + } + } + } + .search{ + margin-bottom: 12px; + float: right; + ::-webkit-input-placeholder { + font-style: italic; + } + :-moz-placeholder { + font-style: italic; + } + ::-moz-placeholder { + font-style: italic; + } + :-ms-input-placeholder { + font-style: italic; + } + #search-box{ + -webkit-border-radius: 2px 0 0 2px; + -moz-border-radius: 2px 0 0 2px; + border-radius: 2px 0 0 2px; + width: 213px; + height: 32px; + line-height: 32px; + border: 1px solid @main_color_o; + text-indent: 10px; + float: left; + } + .search-icon-container{ + background-color: @main_color_a; + height: 32px; + width: 32px; + border-radius: 0 2px 2px 0; + float: left; + .search-icon{ + position: relative; + top: 9px; + } + } + } + .expand-collapse-buttons{ + float: right; + width: 44px; + margin-left: 11px; + margin-top: 10px; + span{ + vertical-align: bottom; + .hand; + } + } + + + + .table{ + height:490px; + margin-bottom: 0; + } + + .arrow-up-small{ + &.opened{ + .arrow-up-small-hover; + } + } + + .item-opened{ + background-color: @tlv_color_t; + } + + .properties-title{ + margin:0; + font-weight: bold; + } + + .table-container-flex { + margin-top: 10px; + + .text{ + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + &.requirements-table{ + border-top: 4px solid @main_color_a; + .flex-item:nth-child(1) { + flex-grow: 20; + } + + .flex-item:nth-child(2) { + flex-grow: 20; + } + + .flex-item:nth-child(3) { + flex-grow: 20; + } + + .flex-item:nth-child(4) { + flex-grow: 20; + } + + .flex-item:nth-child(5) { + flex-grow: 20; + } + + .flex-item:nth-child(6) { + flex-grow: 20; + } + } + + &.capabilities-table{ + border-top: 4px solid @main_color_a; + .selected{ + .flex-item:nth-child(1) { + border-left: 4px solid @main_color_a; + padding-right: 11px; + } + } + .flex-item:nth-child(1) { + flex-grow: 10; + } + + .flex-item:nth-child(2) { + flex-grow: 10; + } + + .flex-item:nth-child(3) { + flex-grow: 10; + } + + .flex-item:nth-child(4) { + flex-grow: 10; + } + + .flex-item:nth-child(5) { + flex-grow: 10; + } + + } + + &.properties-table{ + .table{ + height: auto; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + a{ + .hand + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 6; + } + + .flex-item:nth-child(4) { + flex-grow: 20; + + } + } + + } + +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts new file mode 100644 index 0000000000..06022786ee --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts @@ -0,0 +1,84 @@ +'use strict'; +import {ArtifactModel, IFileDownload} from "app/models"; +import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; +import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; +export interface IToscaArtifactsScope extends IWorkspaceViewModelScope { + artifacts:Array; + tableHeadersList:Array; + artifactType:string; + downloadFile:IFileDownload; + isLoading:boolean; + sortBy:string; + reverse:boolean; + + getTitle():string; + download(artifact:ArtifactModel):void; + sort(sortBy:string):void; + showNoArtifactMessage():boolean; +} + +export class ToscaArtifactsViewModel { + + static '$inject' = [ + '$scope', + '$filter', + 'ComponentServiceNg2' + ]; + + constructor(private $scope:IToscaArtifactsScope, + private $filter:ng.IFilterService, + private ComponentServiceNg2:ComponentServiceNg2) { + this.initToscaArtifacts(); + this.$scope.updateSelectedMenuItem(); + } + + private initToscaArtifacts = (): void => { + + if(!this.$scope.component.toscaArtifacts) { + this.$scope.isLoading = true; + this.ComponentServiceNg2.getComponentToscaArtifacts(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + this.$scope.component.toscaArtifacts = response.toscaArtifacts; + this.initScope(); + this.$scope.isLoading = false; + }, () => { + this.$scope.isLoading = false; + }); + } else { + this.initScope(); + } + } + + private initScope = ():void => { + this.$scope.isLoading = false; + this.$scope.sortBy = 'artifactDisplayName'; + this.$scope.reverse = false; + this.$scope.setValidState(true); + this.$scope.artifactType = 'informational'; + this.$scope.getTitle = ():string => { + return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts'; + + }; + + this.$scope.tableHeadersList = [ + {title: 'Name', property: 'artifactDisplayName'}, + {title: 'Type', property: 'artifactType'}, + {title: 'Version', property: 'artifactVersion'} + ]; + + this.$scope.artifacts = _.values(this.$scope.component.toscaArtifacts); + this.$scope.sort = (sortBy:string):void => { + this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false; + this.$scope.sortBy = sortBy; + }; + + + this.$scope.showNoArtifactMessage = ():boolean => { + if (this.$scope.artifacts.length === 0) { + return true; + } + return false; + }; + + } +} diff --git a/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html new file mode 100644 index 0000000000..5df6b5c11f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html @@ -0,0 +1,49 @@ +
+
+
+
+
{{header.title}} + +
+
+
+
+ +
+ There are no TOSCA artifacts to display +
+
+ +
+ + {{artifact.artifactDisplayName}} +
+ +
+ {{artifact.artifactType}} +
+ +
+ {{artifact.artifactVersion}} +
+ +
+ +
+
+
+
Label: {{artifact.artifactLabel}}
+
UUID: {{artifact.uniqueId}}
+
Description: {{artifact.description}}
+ + +
+ +
+
+
+
+
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less new file mode 100644 index 0000000000..6dfec2980f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less @@ -0,0 +1,78 @@ +.workspace-tosca-artifact { + width: 93%; + display: inline-block; + .w-sdc-classic-btn { + float: right; + margin-bottom: 10px; + } + + .details-title{ + font-weight: bold; + margin-right: 5px; + } + + .table{ + height: 490px; + margin-bottom: 0; + } + + + .table-container-flex { + margin-top: 27px; + + .item-opened{ + word-wrap: break-word; + } + + + .flex-item:nth-child(1) { + flex-grow: 15; + .hand; + span.table-arrow { + margin-right: 7px; + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 1; + } + + .flex-item:nth-child(4) { + flex-grow: 1; + } + + + + + .table-download-btn{ + &.tosca{ + margin-left: 0; + margin-top: 8px; + } + } + + .download-icon-container{ + position: relative; + + .loader{ + left: 60%; + top: 45px; + border: none; + background-color: transparent; + height: 0px; + width: 63px; + outline: none; + + } + } + + + } + +} + + 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 new file mode 100644 index 0000000000..ff4636e002 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts @@ -0,0 +1,718 @@ +/** + * Created by obarda on 3/30/2016. + */ +'use strict'; +import {IUserProperties, IAppMenu, Resource, Component} from "app/models"; +import { + WorkspaceMode, ComponentFactory, ChangeLifecycleStateHandler, Role, ComponentState, MenuItemGroup, MenuHandler, + MenuItem, ModalsHandler, States, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ResourceType +} from "app/utils"; +import { + EventListenerService, + EntityService, + ProgressService, + CacheService, + LeftPaletteLoaderService +} from "app/services"; +import {FileUploadModel} from "../../directives/file-upload/file-upload"; + + +export interface IWorkspaceViewModelScope extends ng.IScope { + + isLoading:boolean; + isCreateProgress:boolean; + component:Component; + originComponent:Component; + componentType:string; + importFile:any; + leftBarTabs:MenuItemGroup; + isNew:boolean; + isFromImport:boolean; + isValidForm:boolean; + mode:WorkspaceMode; + breadcrumbsModel:Array; + sdcMenu:IAppMenu; + changeLifecycleStateButtons:any; + version:string; + versionsList:Array; + changeVersion:any; + isComposition:boolean; + isDeployment:boolean; + $state:ng.ui.IStateService; + user:IUserProperties; + thirdParty:boolean; + disabledButtons:boolean; + menuComponentTitle:string; + progressService:ProgressService; + progressMessage:string; + // leftPanelComponents:Array; //this is in order to load the left panel once, and not wait long time when moving to composition + + showChangeStateButton():boolean; + getComponent():Component; + setComponent(component:Component):void; + onMenuItemPressed(state:string):ng.IPromise; + save():ng.IPromise; + setValidState(isValid:boolean):void; + revert():void; + changeLifecycleState(state:string):void; + enabledTabs():void + isDesigner():boolean; + isProductManager():boolean; + isViewMode():boolean; + isEditMode():boolean; + isCreateMode():boolean; + isDisableMode():boolean; + showFullIcons():boolean; + goToBreadcrumbHome():void; + onVersionChanged(selectedId:string):void; + getLatestVersion():void; + getStatus():string; + showLifecycleIcon():boolean; + updateSelectedMenuItem():void; + uploadFileChangedInGeneralTab():void; + updateMenuComponentName(ComponentName:string):void; +} + +export class WorkspaceViewModel { + + static '$inject' = [ + '$scope', + 'injectComponent', + 'ComponentFactory', + '$state', + 'sdcMenu', + '$q', + 'MenuHandler', + 'Sdc.Services.CacheService', + 'ChangeLifecycleStateHandler', + 'ModalsHandler', + 'LeftPaletteLoaderService', + '$filter', + 'EventListenerService', + 'Sdc.Services.EntityService', + 'Notification', + '$stateParams', + 'Sdc.Services.ProgressService' + ]; + + constructor(private $scope:IWorkspaceViewModelScope, + private injectComponent:Component, + private ComponentFactory:ComponentFactory, + private $state:ng.ui.IStateService, + private sdcMenu:IAppMenu, + private $q:ng.IQService, + private MenuHandler:MenuHandler, + private cacheService:CacheService, + private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler, + private ModalsHandler:ModalsHandler, + private LeftPaletteLoaderService:LeftPaletteLoaderService, + private $filter:ng.IFilterService, + private EventListenerService:EventListenerService, + private EntityService:EntityService, + private Notification:any, + private $stateParams:any, + private progressService:ProgressService) { + + this.initScope(); + this.initAfterScope(); + } + + private role:string; + private components:Array; + + private initViewMode = ():WorkspaceMode => { + let mode = WorkspaceMode.VIEW; + + if (!this.$state.params['id']) { //&& !this.$state.params['vspComponent'] + mode = WorkspaceMode.CREATE; + } else { + if (this.$scope.component.lifecycleState === ComponentState.NOT_CERTIFIED_CHECKOUT && + this.$scope.component.lastUpdaterUserId === this.cacheService.get("user").userId) { + if (this.$scope.component.isProduct() && this.role == Role.PRODUCT_MANAGER) { + mode = WorkspaceMode.EDIT; + } + if ((this.$scope.component.isService() || this.$scope.component.isResource()) && this.role == Role.DESIGNER) { + mode = WorkspaceMode.EDIT; + } + } + } + return mode; + }; + + private initChangeLifecycleStateButtons = ():void => { + let state = this.$scope.component.isService() && (Role.OPS == this.role || Role.GOVERNOR == this.role) ? this.$scope.component.distributionStatus : this.$scope.component.lifecycleState; + this.$scope.changeLifecycleStateButtons = this.sdcMenu.roles[this.role].changeLifecycleStateButtons[state]; + }; + + private isNeedSave = ():boolean => { + return this.$scope.isEditMode() && + this.$state.current.data && this.$state.current.data.unsavedChanges; + }; + + private initLeftPalette = ():void => { + this.LeftPaletteLoaderService.loadLeftPanel(this.$scope.component.componentType); + }; + + private initScope = ():void => { + + this.$scope.component = this.injectComponent; + this.initLeftPalette(); + this.$scope.menuComponentTitle = this.$scope.component.name; + this.$scope.disabledButtons = false; + this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); + this.$scope.componentType = this.$scope.component.componentType; + this.$scope.version = this.cacheService.get('version'); + this.$scope.user = this.cacheService.get("user"); + this.role = this.$scope.user.role; + this.$scope.mode = this.initViewMode(); + this.$scope.isValidForm = true; + this.initChangeLifecycleStateButtons(); + this.initVersionObject(); + this.$scope.$state = this.$state; + this.$scope.isLoading = false; + this.$scope.isComposition = (this.$state.current.name.indexOf(States.WORKSPACE_COMPOSITION) > -1); + this.$scope.isDeployment = (this.$state.current.name.indexOf(States.WORKSPACE_DEPLOYMENT) > -1); + this.$scope.progressService = this.progressService; + + this.$scope.getComponent = ():Component => { + return this.$scope.component; + }; + + this.$scope.updateMenuComponentName = (ComponentName:string):void => { + this.$scope.menuComponentTitle = ComponentName; + }; + + this.$scope.sdcMenu = this.sdcMenu; + // Will be called from each step after save to update the resource. + this.$scope.setComponent = (component:Component):void => { + this.$scope.component = component; + }; + + this.$scope.uploadFileChangedInGeneralTab = ():void => { + // In case user select browse file, and in update mode, need to disable submit for testing and checkin buttons. + if (this.$scope.isEditMode() && this.$scope.component.isResource() && (this.$scope.component).resourceType == ResourceType.VF) { + this.$scope.disabledButtons = true; + } + }; + + this.$scope.onMenuItemPressed = (state:string):ng.IPromise => { + let deferred = this.$q.defer(); + let goToState = ():void => { + this.$state.go(state, { + id: this.$scope.component.uniqueId, + type: this.$scope.component.componentType.toLowerCase(), + components: this.components + }); + deferred.resolve(true); + }; + if (this.isNeedSave()) { + if (this.$scope.isValidForm) { + this.$scope.save().then(goToState); + } else { + console.log('form is not valid'); + deferred.reject(false); + } + } else if (this.$scope.isEditMode() && //this is a workaround for amdocs - we need to get the artifact in order to avoid saving the vf when moving from their tabs + (this.$state.current.name === States.WORKSPACE_MANAGEMENT_WORKFLOW || this.$state.current.name === States.WORKSPACE_NETWORK_CALL_FLOW)) { + let onGetSuccess = (component:Component) => { + this.$scope.isLoading = false; + // Update the components + this.$scope.component = component; + goToState(); + }; + let onFailed = () => { + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_ERROR); + this.$scope.isLoading = false; // stop the progress. + deferred.reject(false); + }; + this.$scope.component.getComponent().then(onGetSuccess, onFailed); + } else { + goToState(); + } + return deferred.promise; + }; + + this.$scope.setValidState = (isValid:boolean):void => { + this.$scope.isValidForm = isValid; + }; + + this.$scope.onVersionChanged = (selectedId:string):void => { + if (this.$state.current.data && this.$state.current.data.unsavedChanges) { + this.$scope.changeVersion.selectedVersion = _.find(this.$scope.versionsList, {versionId: this.$scope.component.uniqueId}); + } + this.$scope.isLoading = true; + this.$state.go(this.$state.current.name, { + id: selectedId, + type: this.$scope.componentType.toLowerCase(), + mode: WorkspaceMode.VIEW, + components: this.$state.params['components'] + }); + + }; + + this.$scope.getLatestVersion = ():void => { + this.$scope.onVersionChanged(_.first(this.$scope.versionsList).versionId); + }; + + this.$scope.save = (state?:string):ng.IPromise => { + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_CLICK); + + this.progressService.initCreateComponentProgress(this.$scope.component.uniqueId); + + let deferred = this.$q.defer(); + let modalInstance:ng.ui.bootstrap.IModalServiceInstance; + + let onFailed = () => { + _.first(this.$scope.leftBarTabs.menuItems).isDisabled = false;//enabled click on general tab (DE246274) + this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_SAVE_BUTTON_ERROR); + this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + modalInstance && modalInstance.close(); // Close the modal in case it is opened. + this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);// for fix DE246217 + this.$scope.isCreateProgress = false; + this.$scope.isLoading = false; // stop the progress. + + this.$scope.setValidState(true); // Set the form valid (if sent form is valid, the error from server). + if (!this.$scope.isCreateMode()) { + this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); // Set the component back to the original. + this.enableMenuItems(); // Enable the menu items (left tabs), so user can press on them. + this.$scope.disabledButtons = false; // Enable "submit for testing" & checking buttons. + } + + deferred.reject(false); + }; + + let onSuccessCreate = (component:Component) => { + + this.showSuccessNotificationMessage(); + this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + //update components for breadcrumbs + this.components.unshift(component); + this.$state.go(States.WORKSPACE_GENERAL, { + id: component.uniqueId, + type: component.componentType.toLowerCase(), + components: this.components + }); + + deferred.resolve(true); + }; + + let onSuccessUpdate = (component:Component) => { + this.$scope.isCreateProgress = false; + this.$scope.disabledButtons = false; + this.showSuccessNotificationMessage(); + this.progressService.deleteProgressValue(this.$scope.component.uniqueId); + + // Stop the circle loader. + this.$scope.isLoading = false; + + component.tags = _.reject(component.tags, (item)=> { + return item === component.name + }); + + // Update the components + this.$scope.component = component; + this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); + + //update components for breadcrumbs + this.components.unshift(component); + + // Enable left tags + this.$scope.enabledTabs(); + + + if (this.$state.current.data) { + this.$state.current.data.unsavedChanges = false; + } + + deferred.resolve(true); + }; + + if (this.$scope.isCreateMode()) { + this.$scope.progressMessage = "Creating Asset..."; + // CREATE MODE + this.$scope.isCreateProgress = true; + + _.first(this.$scope.leftBarTabs.menuItems).isDisabled = true;//disabled click on general tab (DE246274) + + // Start creating the component + this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccessCreate, onFailed); + + // In case we import CSAR. Notify user that import VF will take long time (the create is performed in the background). + if (this.$scope.component.isResource() && (this.$scope.component).csarUUID) { + this.Notification.info({ + message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION"), + title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE") + }); + } + } else { + // UPDATE MODE + this.$scope.isCreateProgress = true; + this.$scope.progressMessage = "Updating Asset..."; + this.disableMenuItems(); + + + // Work around to change the csar version + if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { + (this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + this.cacheService.remove(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + } + + this.$scope.component.updateComponent().then(onSuccessUpdate, onFailed); + } + return deferred.promise; + }; + + this.$scope.revert = ():void => { + //in state of import file leave the file in place + if (this.$scope.component.isResource() && (this.$scope.component).importedFile) { + let tempFile:FileUploadModel = (this.$scope.component).importedFile; + this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); + (this.$scope.component).importedFile = tempFile; + } else { + this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent); + } + + }; + + this.$scope.changeLifecycleState = (state:string):void => { + if (this.isNeedSave() && state !== 'deleteVersion') { + this.$scope.save().then(() => { + changeLifecycleState(state); + }) + } else { + changeLifecycleState(state); + } + }; + + let defaultActionAfterChangeLifecycleState = ():void => { + if (this.$state.current.data && this.$state.current.data.unsavedChanges) { + this.$state.current.data.unsavedChanges = false; + } + this.$state.go('dashboard'); + }; + + let changeLifecycleState = (state:string) => { + if ('monitor' === state) { + this.$state.go('workspace.distribution'); + return; + } + + let data = this.$scope.changeLifecycleStateButtons[state]; + let onSuccess = (component:Component, url:string):void => { + //Updating the component from server response + + //the server returns only metaData (small component) except checkout (Full component) ,so we update only the statuses of distribution & lifecycle + this.$scope.component.lifecycleState = component.lifecycleState; + this.$scope.component.distributionStatus = component.distributionStatus; + + switch (url) { + case 'lifecycleState/CHECKOUT': + // only checkOut get the full component from server + this.$scope.component = component; + // Work around to change the csar version + if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { + (this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + } + + //when checking out a minor version uuid remains + let bcComponent:Component = _.find(this.components, (item) => { + return item.uuid === component.uuid; + }); + if (bcComponent) { + this.components[this.components.indexOf(bcComponent)] = component; + } else { + //when checking out a major(certified) version + this.components.unshift(component); + } + + this.$state.go(this.$state.current.name, { + id: component.uniqueId, + type: component.componentType.toLowerCase(), + components: this.components + }); + this.Notification.success({ + message: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'lifecycleState/CHECKIN': + defaultActionAfterChangeLifecycleState(); + this.Notification.success({ + message: this.$filter('translate')("CHECKIN_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("CHECKIN_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'lifecycleState/UNDOCHECKOUT': + defaultActionAfterChangeLifecycleState(); + this.Notification.success({ + message: this.$filter('translate')("DELETE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("DELETE_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'lifecycleState/certificationRequest': + defaultActionAfterChangeLifecycleState(); + this.Notification.success({ + message: this.$filter('translate')("SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TITLE") + }); + break; + //Tester Role + case 'lifecycleState/failCertification': + defaultActionAfterChangeLifecycleState(); + this.Notification.success({ + message: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'lifecycleState/certify': + defaultActionAfterChangeLifecycleState(); + this.Notification.success({ + message: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TITLE") + }); + break; + //DE203504 Bug Fix Start + case 'lifecycleState/startCertification': + this.initChangeLifecycleStateButtons(); + this.Notification.success({ + message: this.$filter('translate')("START_TESTING_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("START_TESTING_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'lifecycleState/cancelCertification': + this.initChangeLifecycleStateButtons(); + this.Notification.success({ + message: this.$filter('translate')("CANCEL_TESTING_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("CANCEL_TESTING_SUCCESS_MESSAGE_TITLE") + }); + break; + //Ops Role + case 'distribution/PROD/activate': + this.initChangeLifecycleStateButtons(); + this.Notification.success({ + message: this.$filter('translate')("DISTRIBUTE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("DISTRIBUTE_SUCCESS_MESSAGE_TITLE") + }); + break; + //Governor Role + case 'distribution-state/reject': + this.initChangeLifecycleStateButtons(); + this.Notification.success({ + message: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TITLE") + }); + break; + case 'distribution-state/approve': + this.initChangeLifecycleStateButtons(); + this.$state.go('catalog'); + this.Notification.success({ + message: this.$filter('translate')("APPROVE_SUCCESS_MESSAGE_TEXT"), + title: this.$filter('translate')("APPROVE_SUCCESS_MESSAGE_TITLE") + }); + break; + //DE203504 Bug Fix End + + default : + defaultActionAfterChangeLifecycleState(); + + } + if (data.url != 'lifecycleState/CHECKOUT') { + this.$scope.isLoading = false; + } + }; + //this.$scope.isLoading = true; + this.ChangeLifecycleStateHandler.changeLifecycleState(this.$scope.component, data, this.$scope, onSuccess); + }; + + this.$scope.enabledTabs = ():void => { + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.isDisabled = false; + }); + }; + + this.$scope.isViewMode = ():boolean => { + return this.$scope.mode === WorkspaceMode.VIEW; + }; + + this.$scope.isDesigner = ():boolean => { + return this.role == Role.DESIGNER; + }; + + this.$scope.isProductManager = ():boolean => { + return this.role == Role.PRODUCT_MANAGER; + }; + + this.$scope.isDisableMode = ():boolean => { + return this.$scope.mode === WorkspaceMode.VIEW && this.$scope.component.lifecycleState === ComponentState.NOT_CERTIFIED_CHECKIN; + }; + + this.$scope.showFullIcons = ():boolean => { + //we show revert and save icons only in general\icon view + return this.$state.current.name === States.WORKSPACE_GENERAL || + this.$state.current.name === States.WORKSPACE_ICONS; + }; + + this.$scope.isCreateMode = ():boolean => { + return this.$scope.mode === WorkspaceMode.CREATE; + }; + + this.$scope.isEditMode = ():boolean => { + return this.$scope.mode === WorkspaceMode.EDIT; + }; + + this.$scope.goToBreadcrumbHome = ():void => { + let bcHome:MenuItemGroup = this.$scope.breadcrumbsModel[0]; + this.$state.go(bcHome.menuItems[bcHome.selectedIndex].state); + }; + + this.$scope.showLifecycleIcon = ():boolean => { + return this.role == Role.DESIGNER || + this.role == Role.PRODUCT_MANAGER; + }; + + this.$scope.getStatus = ():string => { + if (this.$scope.isCreateMode()) { + return 'IN DESIGN'; + } + + return this.$scope.component.getStatus(this.sdcMenu); + }; + + this.initMenuItems(); + + this.$scope.showChangeStateButton = ():boolean => { + let result:boolean = true; + if (!this.$scope.component.isLatestVersion() && Role.OPS != this.role && Role.GOVERNOR != this.role) { + result = false; + } + if (this.role === Role.PRODUCT_MANAGER && !this.$scope.component.isProduct()) { + result = false; + } + if ((this.role === Role.DESIGNER || this.role === Role.TESTER) + && this.$scope.component.isProduct()) { + result = false; + } + if (ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState && this.$scope.isViewMode()) { + result = false; + } + if (ComponentState.CERTIFIED != this.$scope.component.lifecycleState && + (Role.OPS == this.role || Role.GOVERNOR == this.role)) { + result = false; + } + return result; + }; + + this.$scope.updateSelectedMenuItem = ():void => { + let selectedItem:MenuItem = _.find(this.$scope.leftBarTabs.menuItems, (item:MenuItem) => { + return item.state === this.$state.current.name; + }); + this.$scope.leftBarTabs.selectedIndex = selectedItem ? this.$scope.leftBarTabs.menuItems.indexOf(selectedItem) : 0; + }; + + this.$scope.$watch('$state.current.name', (newVal:string):void => { + if (newVal) { + this.$scope.isComposition = (newVal.indexOf(States.WORKSPACE_COMPOSITION) > -1); + this.$scope.isDeployment = (newVal.indexOf(States.WORKSPACE_DEPLOYMENT) > -1); + } + }); + }; + + private initAfterScope = ():void => { + // In case user select csar from the onboarding modal, need to disable checkout and submit for testing. + if (this.$state.params['disableButtons'] === true) { + this.$scope.uploadFileChangedInGeneralTab(); + } + }; + + private initVersionObject = ():void => { + this.$scope.versionsList = (this.$scope.component.getAllVersionsAsSortedArray()).reverse(); + this.$scope.changeVersion = {selectedVersion: _.find(this.$scope.versionsList, {versionId: this.$scope.component.uniqueId})}; + }; + + private getNewComponentBreadcrumbItem = ():MenuItem => { + let text = ""; + if (this.$scope.component.isResource() && (this.$scope.component).isCsarComponent()) { + text = this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name; + } else { + text = 'Create new ' + this.$state.params['type']; + } + return new MenuItem(text, null, States.WORKSPACE_GENERAL, 'goToState', [this.$state.params]); + }; + + private updateMenuItemByRole = (menuItems:Array, role:string) => { + let tempMenuItems:Array = new Array(); + menuItems.forEach((item:MenuItem) => { + //remove item if role is disabled + if (!(item.disabledRoles && item.disabledRoles.indexOf(role) > -1)) { + tempMenuItems.push(item); + } + }); + return tempMenuItems; + }; + + private initBreadcrumbs = () => { + this.components = this.cacheService.get('breadcrumbsComponents'); + let breadcrumbsComponentsLvl = this.MenuHandler.generateBreadcrumbsModelFromComponents(this.components, this.$scope.component); + + if (this.$scope.isCreateMode()) { + let createItem = this.getNewComponentBreadcrumbItem(); + if (!breadcrumbsComponentsLvl.menuItems) { + breadcrumbsComponentsLvl.menuItems = []; + } + breadcrumbsComponentsLvl.menuItems.unshift(createItem); + breadcrumbsComponentsLvl.selectedIndex = 0; + } + + this.$scope.breadcrumbsModel = [breadcrumbsComponentsLvl, this.$scope.leftBarTabs]; + }; + + private initMenuItems() { + + let inCreateMode = this.$scope.isCreateMode(); + this.$scope.leftBarTabs = new MenuItemGroup(); + this.$scope.leftBarTabs.menuItems = this.updateMenuItemByRole(this.sdcMenu.component_workspace_menu_option[this.$scope.component.getComponentSubType()], this.role); + + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.params = [item.state]; + item.callback = this.$scope.onMenuItemPressed; + item.isDisabled = (inCreateMode && States.WORKSPACE_GENERAL != item.state) || + (States.WORKSPACE_DEPLOYMENT === item.state && this.$scope.component.groups && this.$scope.component.groups.length === 0 && this.$scope.component.isResource()); + }); + + if (this.cacheService.get('breadcrumbsComponents')) { + this.initBreadcrumbs(); + } else { + let onSuccess = (components:Array) => { + this.cacheService.set('breadcrumbsComponents', components); + this.initBreadcrumbs(); + }; + this.EntityService.getCatalog().then(onSuccess); //getAllComponents() doesnt return components from catalog + } + } + + private disableMenuItems() { + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.params = [item.state]; + item.callback = this.$scope.onMenuItemPressed; + item.isDisabled = (States.WORKSPACE_GENERAL != item.state); + }); + } + + private enableMenuItems() { + this.$scope.leftBarTabs.menuItems.forEach((item:MenuItem) => { + item.params = [item.state]; + item.callback = this.$scope.onMenuItemPressed; + item.isDisabled = false; + }); + } + + private showSuccessNotificationMessage = ():void => { + this.Notification.success({ + message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_FINISHED_DESCRIPTION"), + title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_FINISHED_TITLE") + }); + }; + +} + + diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view.html b/catalog-ui/src/app/view-models/workspace/workspace-view.html new file mode 100644 index 0000000000..dbb7fa6d63 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/workspace-view.html @@ -0,0 +1,78 @@ +
+ +
+ +
+
+ +
+
+ +
+ + +
+
+ + + +
+ +
+
+ {{getStatus()}} +
+ + +
+ +
+ +
+ + Switch to the latest version + + + + + + + + Delete + + Revert + + + + Close + +
+
+ +
Created: {{component.creationDate | date:'MM/dd/yyyy'}}, {{component.creatorFullName}} | Modifed: {{component.lastUpdateDate | date:'MM/dd/yyyy'}} | UUID: {{component.uuid}} Invariant UUID: {{component.invariantUUID}}
+ +
+
+
+ + +
diff --git a/catalog-ui/src/app/view-models/workspace/workspace.less b/catalog-ui/src/app/view-models/workspace/workspace.less new file mode 100644 index 0000000000..6cf024e335 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/workspace.less @@ -0,0 +1,150 @@ +.sdc-workspace-container { + .bg_p; + + .add-btn { + .f-color.a; + .f-type._12_m; + .hand; + float: right; + margin-bottom: 15px; + + &:before { + .sprite-new; + .plus-icon; + margin-right: 5px; + content: ""; + + } + &:hover { + .f-color.b; + &:before { + .sprite-new; + .plus-icon-hover; + } + } + + } + .w-sdc-left-sidebar { + padding: 3px 3px 0px 0px; + background-color: @main_color_p; + box-shadow: 7px -3px 6px -8px @main_color_n; + z-index: 2; + } + + .sdc-asset-creation-info { + .n_12_r; + float: right; + margin: 8px 20px 0 0; + } + + .w-sdc-main-right-container { + + padding: 0px 0px 0px 0px; + background-color: @main_color_p; + z-index: 1; + + .sdc-workspace-top-bar { + height: @action_nav_height; + padding: 12px 10px 0px 50px; + border-bottom: 1px solid @main_color_o; + display: flex; + justify-content: space-between; + + .version-container { + + } + + .progress-container { + flex-grow: 4; + z-index: 10000000; + + .general-view-top-progress { + width: 30%; + margin: 0 auto; + min-width: 200px; + } + } + + .not-latest { + position: absolute; + left: 24px; + top: 20px; + .sprite-new; + .asdc-warning; + } + + .sdc-workspace-top-bar-buttons { + + > button, > span:not(.delimiter) { + margin-right: 10px; + vertical-align: middle; + .hand; + + &.sprite-new { + text-indent: 100%; + } + &.disabled, &:hover.disabled { + pointer-events: none; + } + } + .delimiter { + height: 32px; + width: 1px; + background-color: #959595; + display: inline-block; + vertical-align: middle; + margin-right: 20px; + } + + } + + .lifecycle-state { + padding: 7px 0 0 10px; + margin: 2px 0 7px 10px; + border-left: 1px solid @main_color_o; + line-height: 15px; + font-family: @font-omnes-medium; + color: @main_color_m; + + .lifecycle-state-icon { + .sprite-new; + } + .lifecycle-state-text { + + font-weight: bold; + text-transform: uppercase; + vertical-align: top; + padding: 3px; + } + } + + .version-selector { + // float:left; + background-color: transparent; + border: none; + margin-top: 6px; + } + } + .w-sdc-main-container-body-content { + height:calc(~'100% - @{action_nav_height}'); + + text-align: center; + align-items: center; + padding: 40px 14% 20px 14%; + &.third-party { + text-align: left; + padding: 0; + position: absolute; + top: @action_nav_height; + left: 0; + right: 0; + bottom: 0; + } + } + } +} + + +.properties-assignment .sdc-workspace-container .w-sdc-main-right-container .w-sdc-main-container-body-content{ + padding: 80px 2% 40px 2%; +} \ No newline at end of file -- cgit 1.2.3-korg