Add new code new version
Change-Id: Ic02a76313503b526f17c3df29eb387a29fe6a42a
Signed-off-by: Michael Lando <ml636r@att.com>
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js
new file mode 100644
index 0000000..d39b2af
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingActionHelper.js
@@ -0,0 +1,188 @@
+/*-
+ * ============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=========================================================
+ */
+
+import LicenseModelActionHelper from './licenseModel/LicenseModelActionHelper.js';
+import LicenseAgreementActionHelper from './licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
+import FeatureGroupsActionHelper from './licenseModel/featureGroups/FeatureGroupsActionHelper.js';
+import LicenseKeyGroupsActionHelper from './licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js';
+import EntitlementPoolsActionHelper from './licenseModel/entitlementPools/EntitlementPoolsActionHelper.js';
+import SoftwareProductActionHelper from './softwareProduct/SoftwareProductActionHelper.js';
+import SoftwareProductProcessesActionHelper from './softwareProduct/processes/SoftwareProductProcessesActionHelper.js';
+import SoftwareProductNetworksActionHelper from './softwareProduct/networks/SoftwareProductNetworksActionHelper.js';
+import SoftwareProductComponentsActionHelper from './softwareProduct/components/SoftwareProductComponentsActionHelper.js';
+import SoftwareProductComponentProcessesActionHelper from './softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js';
+import SoftwareProductComponentsNetworkActionHelper from './softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js';
+import SoftwareProductComponentsMonitoringAction from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js';
+import {actionTypes, enums} from './OnboardingConstants.js';
+import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+import store from 'sdc-app/AppStore.js';
+
+function setCurrentScreen(dispatch, screen, props = {}) {
+ dispatch({
+ type: actionTypes.SET_CURRENT_SCREEN,
+ currentScreen: {
+ screen,
+ props
+ }
+ });
+}
+
+function getCurrentLicenseModelVersion(licenseModelId) {
+ return store.getState().licenseModelList.find(({id}) => id === licenseModelId).version;
+}
+
+export default {
+
+ navigateToOnboardingCatalog(dispatch) {
+ LicenseModelActionHelper.fetchLicenseModels(dispatch);
+ SoftwareProductActionHelper.fetchSoftwareProductList(dispatch);
+ setCurrentScreen(dispatch, enums.SCREEN.ONBOARDING_CATALOG);
+ },
+
+ navigateToLicenseAgreements(dispatch, {licenseModelId, version}) {
+ if(!version) {
+ version = getCurrentLicenseModelVersion(licenseModelId);
+ }
+ LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version});
+ LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => {
+ setCurrentScreen(dispatch, enums.SCREEN.LICENSE_AGREEMENTS, {licenseModelId});
+ });
+ },
+
+ navigateToFeatureGroups(dispatch, {licenseModelId, version}) {
+ if(!version) {
+ version = getCurrentLicenseModelVersion(licenseModelId);
+ }
+ FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version});
+ setCurrentScreen(dispatch, enums.SCREEN.FEATURE_GROUPS, {licenseModelId});
+ },
+
+ navigateToEntitlementPools(dispatch, {licenseModelId, version}) {
+ if(!version) {
+ version = getCurrentLicenseModelVersion(licenseModelId);
+ }
+ EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId, version});
+ setCurrentScreen(dispatch, enums.SCREEN.ENTITLEMENT_POOLS, {licenseModelId});
+ },
+
+ navigateToLicenseKeyGroups(dispatch, {licenseModelId, version}) {
+ if(!version) {
+ version = getCurrentLicenseModelVersion(licenseModelId);
+ }
+ LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version});
+ setCurrentScreen(dispatch, enums.SCREEN.LICENSE_KEY_GROUPS, {licenseModelId});
+ },
+
+ navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version, licensingVersion}) {
+ SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version}).then(response => {
+ if(!licensingVersion) {
+ licensingVersion = response[0].licensingVersion;
+ }
+ if (!licenseModelId) {
+ licenseModelId = response[0].vendorId;
+ }
+
+ SoftwareProductActionHelper.loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion});
+ SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version});
+
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE, {softwareProductId, licenseModelId, version});
+ });
+ },
+
+ navigateToSoftwareProductDetails(dispatch, {softwareProductId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_DETAILS, {softwareProductId});
+ },
+
+ navigateToSoftwareProductAttachments(dispatch, {softwareProductId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS, {softwareProductId});
+ },
+
+ navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version}) {
+ if (softwareProductId) {
+ SoftwareProductProcessesActionHelper.fetchProcessesList(dispatch, {softwareProductId, version});
+ }
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES, {softwareProductId});
+ },
+
+ navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version}) {
+ if (softwareProductId) {
+ SoftwareProductNetworksActionHelper.fetchNetworksList(dispatch, {softwareProductId, version});
+ }
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS, {softwareProductId});
+ },
+
+ navigateToSoftwareProductComponents(dispatch, {softwareProductId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS, {softwareProductId});
+ },
+
+ navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId, version}) {
+ if (componentId && softwareProductId) {
+ SoftwareProductComponentProcessesActionHelper.fetchProcessesList(dispatch, {componentId, softwareProductId, version});
+ }
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES, {softwareProductId, componentId});
+ },
+
+ navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId}){
+ if (componentId && softwareProductId) {
+ SoftwareProductComponentsMonitoringAction.fetchExistingFiles(dispatch, {componentId, softwareProductId});
+ }
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING, {softwareProductId, componentId});
+ },
+
+ navigateToComponentStorage(dispatch, {softwareProductId, componentId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE, {softwareProductId, componentId});
+ },
+
+ navigateToComponentCompute(dispatch, {softwareProductId, componentId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE, {softwareProductId, componentId});
+ },
+
+ navigateToComponentNetwork(dispatch, {softwareProductId, componentId, version}) {
+ SoftwareProductComponentsNetworkActionHelper.fetchNICsList(dispatch, {softwareProductId, componentId, version});
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK, {softwareProductId, componentId});
+ },
+
+ navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version}) {
+ if (componentId && softwareProductId) {
+ SoftwareProductComponentsActionHelper.fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, version});
+ SoftwareProductComponentsActionHelper.fetchSoftwareProductComponentQuestionnaire(dispatch, {
+ softwareProductId,
+ vspComponentId: componentId,
+ version
+ });
+ }
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL, {softwareProductId, componentId});
+ },
+
+ navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version}) {
+ this.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version});
+ dispatch({
+ type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM,
+ mapOfExpandedIds: {
+ [SoftwareProductNavigationItems.COMPONENTS]: true,
+ [SoftwareProductNavigationItems.COMPONENTS + '|' + componentId]: true
+ }
+ });
+ },
+
+ navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId}) {
+ setCurrentScreen(dispatch, enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING, {softwareProductId, componentId});
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js
new file mode 100644
index 0000000..4772c8d
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalog.js
@@ -0,0 +1,59 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import {default as OnboardingCatalogView, catalogItemTypes} from './OnboardingCatalogView.jsx';
+import OnboardingActionHelper from './OnboardingActionHelper.js';
+import LicenseModelCreationActionHelper from './licenseModel/creation/LicenseModelCreationActionHelper.js';
+import SoftwareProductCreationActionHelper from './softwareProduct/creation/SoftwareProductCreationActionHelper.js';
+
+
+const mapStateToProps = ({licenseModelList, softwareProductList, softwareProduct: {softwareProductCreation}, licenseModel: {licenseModelCreation}}) => {
+
+ let modalToShow;
+
+ if(licenseModelCreation.data) {
+ modalToShow = catalogItemTypes.LICENSE_MODEL;
+ } else if(softwareProductCreation.showModal && softwareProductCreation.data) {
+ modalToShow = catalogItemTypes.SOFTWARE_PRODUCT;
+ }
+
+ return {
+ licenseModelList,
+ softwareProductList,
+ modalToShow
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onSelectLicenseModel({id: licenseModelId}) {
+ OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId});
+ },
+ onSelectSoftwareProduct(softwareProduct) {
+ let {id: softwareProductId, vendorId: licenseModelId, licensingVersion} = softwareProduct;
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion});
+ },
+ onAddSoftwareProductClick: () => SoftwareProductCreationActionHelper.open(dispatch),
+ onAddLicenseModelClick: () => LicenseModelCreationActionHelper.open(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(OnboardingCatalogView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx
new file mode 100644
index 0000000..f2a9db1
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingCatalogView.jsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+import objectValues from 'lodash/values.js';
+import LicenseModelCreation from './licenseModel/creation/LicenseModelCreation.js';
+import SoftwareProductCreation from './softwareProduct/creation/SoftwareProductCreation.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import classnames from 'classnames';
+import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx';
+
+export const catalogItemTypes = Object.freeze({
+ LICENSE_MODEL: 'license-model',
+ SOFTWARE_PRODUCT: 'software-product'
+});
+
+const catalogItemTypeClasses = {
+ LICENSE_MODEL: 'license-model-type',
+ SOFTWARE_PRODUCT: 'software-product-type'
+};
+
+class OnboardingCatalogView extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {searchValue: ''};
+ this.handleSearch = this.handleSearch.bind(this);
+ }
+
+ handleSearch(event){
+ this.setState({searchValue: event.target.value});
+ }
+
+ static propTypes = {
+ licenseModelList: React.PropTypes.array,
+ softwareProductList: React.PropTypes.array,
+ modalToShow: React.PropTypes.oneOf(objectValues(catalogItemTypes)),
+ onSelectLicenseModel: React.PropTypes.func.isRequired,
+ onSelectSoftwareProduct: React.PropTypes.func.isRequired,
+ onAddLicenseModelClick: React.PropTypes.func.isRequired,
+ onAddSoftwareProductClick: React.PropTypes.func.isRequired
+ };
+
+ getModalDetails() {
+ const {modalToShow} = this.props;
+ switch (modalToShow) {
+ case catalogItemTypes.LICENSE_MODEL:
+ return {
+ title: i18n('New License Model'),
+ element: <LicenseModelCreation/>
+ };
+ case catalogItemTypes.SOFTWARE_PRODUCT:
+ return {
+ title: i18n('New Software Product'),
+ element: <SoftwareProductCreation/>
+ };
+ }
+ }
+
+ render() {
+ const modalDetails = this.getModalDetails();
+ const {licenseModelList, softwareProductList, onSelectLicenseModel, onSelectSoftwareProduct, onAddLicenseModelClick, onAddSoftwareProductClick, modalToShow} = this.props;
+
+ return (
+ <div className='catalog-view'>
+ <div className='catalog-header'>
+ <div className='catalog-header-title'>{i18n('Onboarding Catalog')}</div>
+ <ExpandableInput
+ onChange={this.handleSearch}
+ iconType='search'/>
+ </div>
+ <div className='catalog-list'>
+
+ <div className='create-catalog-item tile'>
+ <div className='plus-section'>
+ <div className='plus-icon-button'/>
+ <span>{i18n('ADD')}</span>
+ </div>
+ <div className='primary-btn new-license-model'>
+ <span
+ className='primary-btn-text'
+ onClick={() => onAddLicenseModelClick()}>{i18n('New License Model')}</span></div>
+ <div className='primary-btn'>
+ <span
+ className='primary-btn-text'
+ onClick={() => onAddSoftwareProductClick()}>{i18n('New Vendor Software Product')}</span>
+ </div>
+ </div>
+ {licenseModelList.filter(vlm => vlm.vendorName.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(licenseModel => this.renderTile(
+ {
+ ...licenseModel,
+ name: licenseModel.vendorName
+ },
+ catalogItemTypeClasses.LICENSE_MODEL,
+ () => onSelectLicenseModel(licenseModel))
+ )}
+ {softwareProductList.filter(vsp => vsp.name.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1).map(softwareProduct => this.renderTile(softwareProduct,
+ catalogItemTypeClasses.SOFTWARE_PRODUCT,
+ () => onSelectSoftwareProduct(softwareProduct))
+ )}
+ </div>
+ <Modal
+ show={Boolean(modalDetails)}
+ className={`${this.getCatalogItemTypeClassByItemType(modalToShow)}-modal`}>
+ <Modal.Header>
+ <Modal.Title>{modalDetails && modalDetails.title}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {
+ modalDetails && modalDetails.element
+ }
+ </Modal.Body>
+ </Modal>
+ </div>
+ );
+
+ }
+
+ getCatalogItemTypeClassByItemType(catalogItemType) {
+ switch (catalogItemType) {
+ case catalogItemTypes.LICENSE_MODEL:
+ return catalogItemTypeClasses.LICENSE_MODEL;
+ case catalogItemTypes.SOFTWARE_PRODUCT:
+ return catalogItemTypeClasses.SOFTWARE_PRODUCT;
+ }
+ }
+
+ renderTile(catalogItemData, catalogItemTypeClass, onSelect) {
+ let {status: itemStatus} = VersionControllerUtils.getCheckOutStatusKindByUserID(catalogItemData.status, catalogItemData.lockingUser);
+ return (
+ <div className='catalog-tile tile' key={catalogItemData.id} onClick={() => onSelect()}>
+ <div className={`catalog-tile-type ${catalogItemTypeClass}`}/>
+ <div className='catalog-tile-icon'>
+ <div className={`icon ${catalogItemTypeClass}-icon`}></div>
+ </div>
+ <div className='catalog-tile-content'>
+ <div className='catalog-tile-item-details'>
+ <div className='catalog-tile-item-name'>{catalogItemData.name}</div>
+ <div className='catalog-tile-item-version'>V {catalogItemData.version}</div>
+ </div>
+ <div className={classnames('catalog-tile-check-in-status', {'sprite-new checkout-editable-status-icon': itemStatus === 'Locked'})}>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+export default OnboardingCatalogView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js
new file mode 100644
index 0000000..d9c177f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingConstants.js
@@ -0,0 +1,72 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ SET_CURRENT_SCREEN: null,
+ SET_CURRENT_LICENSE_MODEL: null
+});
+
+export const enums = keyMirror({
+
+ BREADCRUMS: {
+ LICENSE_MODEL: 'LICENSE_MODEL',
+ LICENSE_AGREEMENTS: 'LICENSE_AGREEMENTS',
+ FEATURE_GROUPS: 'FEATURE_GROUPS',
+ ENTITLEMENT_POOLS: 'ENTITLEMENT_POOLS',
+ LICENSE_KEY_GROUPS: 'LICENSE_KEY_GROUPS',
+
+ SOFTWARE_PRODUCT: 'SOFTWARE_PRODUCT',
+ SOFTWARE_PRODUCT_DETAILS: 'SOFTWARE_PRODUCT_DETAILS',
+ SOFTWARE_PRODUCT_ATTACHMENTS: 'SOFTWARE_PRODUCT_ATTACHMENTS',
+ SOFTWARE_PRODUCT_PROCESSES: 'SOFTWARE_PRODUCT_PROCESSES',
+ SOFTWARE_PRODUCT_NETWORKS: 'SOFTWARE_PRODUCT_NETWORKS',
+ SOFTWARE_PRODUCT_COMPONENTS: 'SOFTWARE_PRODUCT_COMPONENTS',
+ SOFTWARE_PRODUCT_COMPONENT_PROCESSES: 'SOFTWARE_PRODUCT_COMPONENT_PROCESSES',
+ SOFTWARE_PRODUCT_COMPONENT_STORAGE: 'SOFTWARE_PRODUCT_COMPONENT_STORAGE',
+ SOFTWARE_PRODUCT_COMPONENT_GENERAL: 'SOFTWARE_PRODUCT_COMPONENT_GENERAL',
+ SOFTWARE_PRODUCT_COMPONENT_COMPUTE: 'SOFTWARE_PRODUCT_COMPONENT_COMPUTE',
+ SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: 'SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING',
+ SOFTWARE_PRODUCT_COMPONENT_MONITORING: 'SOFTWARE_PRODUCT_COMPONENT_MONITORING'
+ },
+
+ SCREEN: {
+ ONBOARDING_CATALOG: null,
+ LICENSE_AGREEMENTS: null,
+ FEATURE_GROUPS: null,
+ ENTITLEMENT_POOLS: null,
+ LICENSE_KEY_GROUPS: null,
+
+ SOFTWARE_PRODUCT_LANDING_PAGE: null,
+ SOFTWARE_PRODUCT_DETAILS: null,
+ SOFTWARE_PRODUCT_ATTACHMENTS: null,
+ SOFTWARE_PRODUCT_PROCESSES: null,
+ SOFTWARE_PRODUCT_NETWORKS: null,
+ SOFTWARE_PRODUCT_COMPONENTS: null,
+ SOFTWARE_PRODUCT_COMPONENT_PROCESSES: null,
+ SOFTWARE_PRODUCT_COMPONENT_COMPUTE: null,
+ SOFTWARE_PRODUCT_COMPONENT_STORAGE: null,
+ SOFTWARE_PRODUCT_COMPONENT_NETWORK: null,
+ SOFTWARE_PRODUCT_COMPONENT_GENERAL: null,
+ SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING: null,
+ SOFTWARE_PRODUCT_COMPONENT_MONITORING: null
+ }
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx
new file mode 100644
index 0000000..c4627b1
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingPunchOut.jsx
@@ -0,0 +1,501 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {connect} from 'react-redux';
+import isEqual from 'lodash/isEqual.js';
+import objectValues from 'lodash/values.js';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Application from 'sdc-app/Application.jsx';
+import store from 'sdc-app/AppStore.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+import OnboardingCatalog from './OnboardingCatalog.js';
+import LicenseModel from './licenseModel/LicenseModel.js';
+import LicenseAgreementListEditor from './licenseModel/licenseAgreement/LicenseAgreementListEditor.js';
+import FeatureGroupListEditor from './licenseModel/featureGroups/FeatureGroupListEditor.js';
+import LicenseKeyGroupsListEditor from './licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js';
+import EntitlementPoolsListEditor from './licenseModel/entitlementPools/EntitlementPoolsListEditor.js';
+import SoftwareProduct from './softwareProduct/SoftwareProduct.js';
+import SoftwareProductLandingPage from './softwareProduct/landingPage/SoftwareProductLandingPage.js';
+import SoftwareProductDetails from './softwareProduct/details/SoftwareProductDetails.js';
+import SoftwareProductAttachments from './softwareProduct/attachments/SoftwareProductAttachments.js';
+import SoftwareProductProcesses from './softwareProduct/processes/SoftwareProductProcesses.js';
+import SoftwareProductNetworks from './softwareProduct/networks/SoftwareProductNetworks.js';
+import SoftwareProductComponentsList from './softwareProduct/components/SoftwareProductComponentsList.js';
+import SoftwareProductComponentProcessesList from './softwareProduct/components/processes/SoftwareProductComponentProcessesList.js';
+import SoftwareProductComponentStorage from './softwareProduct/components/storage/SoftwareProductComponentStorage.js';
+import SoftwareProductComponentsNetworkList from './softwareProduct/components/network/SoftwareProductComponentsNetworkList.js';
+import SoftwareProductComponentsGeneral from './softwareProduct/components/general/SoftwareProductComponentsGeneral.js';
+import SoftwareProductComponentsCompute from './softwareProduct/components/compute/SoftwareProductComponentCompute.js';
+import SoftwareProductComponentLoadBalancing from './softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js';
+import SoftwareProductComponentsMonitoring from './softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js';
+import {navigationItems as SoftwareProductNavigationItems, actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+
+import {enums} from './OnboardingConstants.js';
+import OnboardingActionHelper from './OnboardingActionHelper.js';
+
+
+class OnboardingView extends React.Component {
+ static propTypes = {
+ currentScreen: React.PropTypes.shape({
+ screen: React.PropTypes.oneOf(objectValues(enums.SCREEN)).isRequired,
+ props: React.PropTypes.object.isRequired
+ }).isRequired
+ };
+
+ componentDidMount() {
+ let element = ReactDOM.findDOMNode(this);
+ element.addEventListener('click', event => {
+ if (event.target.tagName === 'A') {
+ event.preventDefault();
+ }
+ });
+ ['wheel', 'mousewheel', 'DOMMouseScroll'].forEach(eventType =>
+ element.addEventListener(eventType, event => event.stopPropagation())
+ );
+ }
+
+ render() {
+ let {currentScreen} = this.props;
+ let {screen, props} = currentScreen;
+
+ return (
+ <div className='dox-ui dox-ui-punch-out dox-ui-punch-out-full-page'>
+ {(() => {
+ switch (screen) {
+ case enums.SCREEN.ONBOARDING_CATALOG:
+ return <OnboardingCatalog {...props}/>;
+
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ case enums.SCREEN.FEATURE_GROUPS:
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ return (
+ <LicenseModel currentScreen={currentScreen}>
+ {
+ (()=>{
+ switch(screen) {
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ return <LicenseAgreementListEditor {...props}/>;
+ case enums.SCREEN.FEATURE_GROUPS:
+ return <FeatureGroupListEditor {...props}/>;
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ return <EntitlementPoolsListEditor {...props}/>;
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ return <LicenseKeyGroupsListEditor {...props}/>;
+ }
+ })()
+ }
+ </LicenseModel>
+ );
+
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ return (
+ <SoftwareProduct currentScreen={currentScreen}>
+ {
+ (()=>{
+ switch(screen) {
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ return <SoftwareProductLandingPage {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ return <SoftwareProductDetails {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS:
+ return <SoftwareProductAttachments className='no-padding-content-area' {...props} />;
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ return <SoftwareProductProcesses {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ return <SoftwareProductNetworks {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ return <SoftwareProductComponentsList {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ return <SoftwareProductComponentProcessesList {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ return <SoftwareProductComponentStorage {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ return <SoftwareProductComponentsNetworkList {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ return <SoftwareProductComponentsGeneral{...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ return <SoftwareProductComponentsCompute {...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ return <SoftwareProductComponentLoadBalancing{...props}/>;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ return <SoftwareProductComponentsMonitoring {...props}/>;
+ }
+ })()
+ }
+ </SoftwareProduct>
+ );
+ }
+ })()}
+ </div>
+ );
+ }
+}
+const mapStateToProps = ({currentScreen}) => ({currentScreen});
+let Onboarding = connect(mapStateToProps, null)(OnboardingView);
+
+export default class OnboardingPunchOut {
+
+ render({options: {data, apiRoot, apiHeaders}, onEvent}, element) {
+ if (!this.unsubscribeFromStore) {
+ this.unsubscribeFromStore = store.subscribe(() => this.handleStoreChange());
+ }
+
+ if (!this.isConfigSet) {
+ Configuration.setATTApiRoot(apiRoot);
+ Configuration.setATTApiHeaders(apiHeaders);
+ this.isConfigSet = true;
+ }
+
+ this.onEvent = (...args) => onEvent(...args);
+ this.handleData(data);
+
+ if (!this.rendered) {
+ ReactDOM.render(
+ <Application>
+ <Onboarding/>
+ </Application>,
+ element
+ );
+ this.rendered = true;
+ }
+ }
+
+ unmount(element) {
+ ReactDOM.unmountComponentAtNode(element);
+ this.rendered = false;
+ this.unsubscribeFromStore();
+ this.unsubscribeFromStore = null;
+ }
+
+ handleData(data) {
+ let {breadcrumbs: {selectedKeys = []} = {}} = data;
+ let dispatch = action => store.dispatch(action);
+ let {softwareProductList, softwareProduct: {softwareProductEditor: {data: {id: currentSoftwareProductId, version: currentSoftwareProductVersion} = {}}},
+ licenseModelList, licenseModel: {licenseModelEditor: {data: {id: currentLicenseModelId, version: currentLicenseModelVersion} = {}}}} = store.getState();
+
+ if (this.programmaticBreadcrumbsUpdate) {
+ this.prevSelectedKeys = selectedKeys;
+ this.programmaticBreadcrumbsUpdate = false;
+ return;
+ }
+
+ if (!isEqual(selectedKeys, this.prevSelectedKeys)) {
+ this.breadcrumbsPrefixSelected = isEqual(selectedKeys, this.prevSelectedKeys && this.prevSelectedKeys.slice(0, selectedKeys.length));
+ this.prevSelectedKeys = selectedKeys;
+
+ if (selectedKeys.length === 0) {
+ OnboardingActionHelper.navigateToOnboardingCatalog(dispatch);
+ } else if (selectedKeys.length === 1 || selectedKeys[1] === enums.BREADCRUMS.LICENSE_MODEL) {
+ let [licenseModelId, , licenseModelScreen] = selectedKeys;
+ if (!licenseModelScreen) {
+ licenseModelScreen = enums.BREADCRUMS.LICENSE_AGREEMENTS;
+ }
+ if(currentLicenseModelId !== licenseModelId) {
+ currentLicenseModelVersion = licenseModelList.find(lm => lm.id === licenseModelId).version;
+ }
+ switch (licenseModelScreen) {
+ case enums.BREADCRUMS.LICENSE_AGREEMENTS:
+ OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version: currentLicenseModelVersion});
+ break;
+ case enums.BREADCRUMS.FEATURE_GROUPS:
+ OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion});
+ break;
+ case enums.BREADCRUMS.ENTITLEMENT_POOLS:
+ OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version: currentLicenseModelVersion});
+ break;
+ case enums.BREADCRUMS.LICENSE_KEY_GROUPS:
+ OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version: currentLicenseModelVersion});
+ break;
+ }
+ } else if (selectedKeys.length <= 4 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT) {
+ let [licenseModelId, , softwareProductId, softwareProductScreen] = selectedKeys;
+ let softwareProduct = softwareProductId ?
+ softwareProductList.find(({id}) => id === softwareProductId) :
+ softwareProductList.find(({vendorId}) => vendorId === licenseModelId);
+ if (!softwareProductId) {
+ softwareProductId = softwareProduct.id;
+ }
+ if(currentSoftwareProductId !== softwareProductId) {
+ currentSoftwareProductVersion = softwareProduct.version;
+ }
+ switch (softwareProductScreen) {
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS:
+ OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS:
+ OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES:
+ OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: currentSoftwareProductVersion});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS:
+ OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: currentSoftwareProductVersion});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS:
+ OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId, version: currentSoftwareProductVersion});
+ dispatch({
+ type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM,
+ mapOfExpandedIds: {
+ [SoftwareProductNavigationItems.COMPONENTS]: true
+ }
+ });
+ break;
+ default:
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: currentSoftwareProductVersion});
+ break;
+ }
+ }else if (selectedKeys.length === 5 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) {
+ let [licenseModelId, , softwareProductId, , componentId] = selectedKeys;
+ let softwareProduct = softwareProductId ?
+ softwareProductList.find(({id}) => id === softwareProductId) :
+ softwareProductList.find(({vendorId}) => vendorId === licenseModelId);
+ if (!softwareProductId) {
+ softwareProductId = softwareProduct.id;
+ }
+ if(currentSoftwareProductId !== softwareProductId) {
+ currentSoftwareProductVersion = softwareProduct.version;
+ }
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion});
+ }else if (selectedKeys.length === 6 && selectedKeys[1] === enums.BREADCRUMS.SOFTWARE_PRODUCT && selectedKeys[3] === enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS) {
+ let [licenseModelId, , softwareProductId, , componentId, componentScreen] = selectedKeys;
+ let softwareProduct = softwareProductId ?
+ softwareProductList.find(({id}) => id === softwareProductId) :
+ softwareProductList.find(({vendorId}) => vendorId === licenseModelId);
+ if (!softwareProductId) {
+ softwareProductId = softwareProduct.id;
+ }
+ if(currentSoftwareProductId !== softwareProductId) {
+ currentSoftwareProductVersion = softwareProduct.version;
+ }
+ switch (componentScreen) {
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId, version: currentSoftwareProductVersion});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId});
+ break;
+ case enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId});
+ break;
+ }
+ } else {
+ console.error('Unknown breadcrumbs path: ', selectedKeys);
+ }
+ }
+ }
+
+ handleStoreChange() {
+ let {currentScreen, licenseModelList, softwareProductList} = store.getState();
+ let breadcrumbsData = {currentScreen, licenseModelList, softwareProductList};
+
+ if (!isEqual(breadcrumbsData, this.prevBreadcrumbsData) || this.breadcrumbsPrefixSelected) {
+ this.prevBreadcrumbsData = breadcrumbsData;
+ this.breadcrumbsPrefixSelected = false;
+ this.programmaticBreadcrumbsUpdate = true;
+
+ let breadcrumbs = this.buildBreadcrumbs(breadcrumbsData);
+ this.onEvent('breadcrumbsupdated', breadcrumbs);
+ }
+ }
+
+ buildBreadcrumbs({currentScreen: {screen, props}, licenseModelList, softwareProductList}) {
+ let componentsList;
+ if(props.componentId) {
+ componentsList = store.getState().softwareProduct.softwareProductComponents.componentsList;
+ }
+ let screenToBreadcrumb;
+ switch (screen) {
+ case enums.SCREEN.ONBOARDING_CATALOG:
+ return [];
+
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ case enums.SCREEN.FEATURE_GROUPS:
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ screenToBreadcrumb = {
+ [enums.SCREEN.LICENSE_AGREEMENTS]: enums.BREADCRUMS.LICENSE_AGREEMENTS,
+ [enums.SCREEN.FEATURE_GROUPS]: enums.BREADCRUMS.FEATURE_GROUPS,
+ [enums.SCREEN.ENTITLEMENT_POOLS]: enums.BREADCRUMS.ENTITLEMENT_POOLS,
+ [enums.SCREEN.LICENSE_KEY_GROUPS]: enums.BREADCRUMS.LICENSE_KEY_GROUPS
+ };
+ return [
+ {
+ selectedKey: props.licenseModelId,
+ menuItems: licenseModelList.map(({id, vendorName}) => ({
+ key: id,
+ displayText: vendorName
+ }))
+ },
+ {
+ selectedKey: enums.BREADCRUMS.LICENSE_MODEL,
+ menuItems: [{
+ key: enums.BREADCRUMS.LICENSE_MODEL,
+ displayText: i18n('License Model')
+ },
+ ...(softwareProductList.findIndex(({vendorId}) => vendorId === props.licenseModelId) === -1 ? [] : [{
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT,
+ displayText: i18n('Software Products')
+ }])]
+ }, {
+ selectedKey: screenToBreadcrumb[screen],
+ menuItems: [{
+ key: enums.BREADCRUMS.LICENSE_AGREEMENTS,
+ displayText: i18n('License Agreements')
+ }, {
+ key: enums.BREADCRUMS.FEATURE_GROUPS,
+ displayText: i18n('Feature Groups')
+ }, {
+ key: enums.BREADCRUMS.ENTITLEMENT_POOLS,
+ displayText: i18n('Entitlement Pools')
+ }, {
+ key: enums.BREADCRUMS.LICENSE_KEY_GROUPS,
+ displayText: i18n('License Key Groups')
+ }]
+ }
+ ];
+
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ screenToBreadcrumb = {
+ [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES,
+ [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS
+ };
+ let componentScreenToBreadcrumb = {
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING
+ };
+ let licenseModelId = softwareProductList.find(({id}) => id === props.softwareProductId).vendorId;
+ let returnedBreadcrumb = [
+ {
+ selectedKey: licenseModelId,
+ menuItems: licenseModelList.map(({id, vendorName}) => ({
+ key: id,
+ displayText: vendorName
+ }))
+ },
+ {
+ selectedKey: enums.BREADCRUMS.SOFTWARE_PRODUCT,
+ menuItems: [{
+ key: enums.BREADCRUMS.LICENSE_MODEL,
+ displayText: i18n('License Model')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT,
+ displayText: i18n('Software Products')
+ }]
+ },
+ {
+ selectedKey: props.softwareProductId,
+ menuItems: softwareProductList
+ .filter(({vendorId}) => vendorId === licenseModelId)
+ .map(({id, name}) => ({
+ key: id,
+ displayText: name
+ }))
+ },
+ ...(screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE ? [] : [{
+ selectedKey: screenToBreadcrumb[screen] || enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS,
+ menuItems: [{
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_DETAILS,
+ displayText: i18n('General')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_ATTACHMENTS,
+ displayText: i18n('Attachments')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_PROCESSES,
+ displayText: i18n('Process Details')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_NETWORKS,
+ displayText: i18n('Networks')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENTS,
+ displayText: i18n('Components')
+ }]
+ }])
+ ];
+ if(props.componentId) {
+ returnedBreadcrumb = [
+ ...returnedBreadcrumb, {
+ selectedKey: props.componentId,
+ menuItems: componentsList
+ .map(({id, displayName}) => ({
+ key: id,
+ displayText: displayName
+ }))
+ },
+ ...[{
+ selectedKey: componentScreenToBreadcrumb[screen],
+ menuItems: [{
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_GENERAL,
+ displayText: i18n('General')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_COMPUTE,
+ displayText: i18n('Compute')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING,
+ displayText: i18n('High Availability & Load Balancing')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_NETWORK,
+ displayText: i18n('Networks')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_STORAGE,
+ displayText: i18n('Storage')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_PROCESSES,
+ displayText: i18n('Process Details')
+ }, {
+ key: enums.BREADCRUMS.SOFTWARE_PRODUCT_COMPONENT_MONITORING,
+ displayText: i18n('Monitoring')
+ }]
+ }]
+ ];
+ }
+ return returnedBreadcrumb;
+ }
+ }
+}
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js
new file mode 100644
index 0000000..9af2427
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducers.js
@@ -0,0 +1,29 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes, enums} from './OnboardingConstants.js';
+
+export const currentScreenReducer = (state = {screen: enums.SCREEN.ONBOARDING_CATALOG, props: {}}, action) => {
+ if (action.type === actionTypes.SET_CURRENT_SCREEN) {
+ return action.currentScreen;
+ }
+ return state;
+};
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js
new file mode 100644
index 0000000..92d53a3
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/OnboardingReducersMap.js
@@ -0,0 +1,36 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {currentScreenReducer} from './OnboardingReducers.js';
+import licenseModelListReducer from './licenseModel/LicenseModelListReducer.js';
+import finalizedLicenseModelListReducer from './licenseModel/FinalizedLicenseModelListReducer.js';
+import licenseModelReducer from './licenseModel/LicenseModelReducer.js';
+import softwareProductReducer from './softwareProduct/SoftwareProductReducer.js';
+import softwareProductListReducer from './softwareProduct/SoftwareProductListReducer.js';
+
+
+export default {
+ currentScreen: currentScreenReducer,
+ licenseModelList: licenseModelListReducer,
+ finalizedLicenseModelList: finalizedLicenseModelListReducer,
+ licenseModel: licenseModelReducer,
+ softwareProduct: softwareProductReducer,
+ softwareProductList: softwareProductListReducer
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js
new file mode 100644
index 0000000..a851e77
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/FinalizedLicenseModelListReducer.js
@@ -0,0 +1,30 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './LicenseModelConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.FINALIZED_LICENSE_MODELS_LIST_LOADED:
+ return [...action.response.results];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js
new file mode 100644
index 0000000..ad91a0d
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModel.js
@@ -0,0 +1,147 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx';
+
+import {enums} from 'sdc-app/onboarding/OnboardingConstants.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+
+import {navigationItems} from './LicenseModelConstants.js';
+import LicenseModelActionHelper from './LicenseModelActionHelper.js';
+import LicenseAgreementActionHelper from './licenseAgreement/LicenseAgreementActionHelper.js';
+import FeatureGroupsActionHelper from './featureGroups/FeatureGroupsActionHelper.js';
+import EntitlementPoolsActionHelper from './entitlementPools/EntitlementPoolsActionHelper.js';
+import LicenseKeyGroupsActionHelper from './licenseKeyGroups/LicenseKeyGroupsActionHelper.js';
+
+
+const buildNavigationBarProps = (licenseModel, screen) => {
+ const {id, vendorName, version} = licenseModel;
+ const meta = {version};
+
+ const groups = [{
+ id,
+ name: vendorName,
+ items: [
+ {
+ id: navigationItems.LICENSE_AGREEMENTS,
+ name: i18n('License Agreements'),
+ meta
+ },
+ {
+ id: navigationItems.FEATURE_GROUPS,
+ name: i18n('Feature Groups'),
+ meta
+ },
+ {
+ id: navigationItems.ENTITLEMENT_POOLS,
+ name: i18n('Entitlement Pools'),
+ meta
+ },
+ {
+ id: navigationItems.LICENSE_KEY_GROUPS,
+ name: i18n('License Key Groups'),
+ meta
+ }
+ ]
+ }];
+
+ const activeItemId = ({
+ [enums.SCREEN.LICENSE_AGREEMENTS]: navigationItems.LICENSE_AGREEMENTS,
+ [enums.SCREEN.FEATURE_GROUPS]: navigationItems.FEATURE_GROUPS,
+ [enums.SCREEN.ENTITLEMENT_POOLS]: navigationItems.ENTITLEMENT_POOLS,
+ [enums.SCREEN.LICENSE_KEY_GROUPS]: navigationItems.LICENSE_KEY_GROUPS
+ })[screen];
+
+ return {
+ activeItemId, groups
+ };
+};
+
+
+const buildVersionControllerProps = (licenseModel) => {
+ let {version, viewableVersions, status: currentStatus, lockingUser} = licenseModel;
+ let {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ?
+ VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) :
+ {status: currentStatus, isCheckedOut: false};
+
+ return {
+ version,
+ viewableVersions,
+ status,
+ isCheckedOut
+ };
+};
+
+
+const mapStateToProps = ({licenseModel: {licenseModelEditor}}, {currentScreen: {screen}}) => {
+ return {
+ versionControllerProps: buildVersionControllerProps(licenseModelEditor.data),
+ navigationBarProps: buildNavigationBarProps(licenseModelEditor.data, screen)
+ };
+};
+
+
+const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {licenseModelId}}}) => {
+ return {
+ onVersionControllerAction: action =>
+ LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => {
+ switch(screen) {
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId});
+ break;
+ case enums.SCREEN.FEATURE_GROUPS:
+ FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId});
+ break;
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId});
+ break;
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId});
+ break;
+ }
+ }),
+ onVersionSwitching: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}),
+ onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch),
+
+ onNavigate: ({id, meta: {version}}) => {
+ switch(id) {
+ case navigationItems.LICENSE_AGREEMENTS:
+ OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version});
+ break;
+ case navigationItems.FEATURE_GROUPS:
+ OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version});
+ break;
+ case navigationItems.ENTITLEMENT_POOLS:
+ OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version});
+ break;
+ case navigationItems.LICENSE_KEY_GROUPS:
+ OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version});
+ break;
+ }
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(TabulatedEditor);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js
new file mode 100644
index 0000000..a379a2c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js
@@ -0,0 +1,101 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes} from './LicenseModelConstants.js';
+import {actionsEnum as vcActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js';
+
+function baseUrl() {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/`;
+}
+
+function fetchLicenseModels() {
+ return RestAPIUtil.fetch(baseUrl());
+}
+
+function fetchFinalizedLicenseModels() {
+ return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
+}
+
+function fetchLicenseModelById(licenseModelId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl()}${licenseModelId}${versionQuery}`);
+}
+
+function putLicenseModelAction(id, action) {
+ return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action});
+}
+
+const LicenseModelActionHelper = {
+
+ fetchLicenseModels(dispatch) {
+ return fetchLicenseModels().then(response => {
+ dispatch({
+ type: actionTypes.LICENSE_MODELS_LIST_LOADED,
+ response
+ });
+ });
+ },
+
+ fetchFinalizedLicenseModels(dispatch) {
+ return fetchFinalizedLicenseModels().then(response => dispatch({
+ type: actionTypes.FINALIZED_LICENSE_MODELS_LIST_LOADED,
+ response
+ }));
+
+ },
+
+ fetchLicenseModelById(dispatch, {licenseModelId, version}) {
+ return fetchLicenseModelById(licenseModelId, version).then(response => {
+ if(version) {
+ response.version = version;
+ }
+ dispatch({
+ type: actionTypes.LICENSE_MODEL_LOADED,
+ response
+ });
+ });
+ },
+
+ addLicenseModel(dispatch, {licenseModel}){
+ dispatch({
+ type: actionTypes.ADD_LICENSE_MODEL,
+ licenseModel
+ });
+ },
+
+ performVCAction(dispatch, {licenseModelId, action}) {
+ return putLicenseModelAction(licenseModelId, action).then(() => {
+ if(action === vcActionsEnum.SUBMIT){
+ dispatch({
+ type: NotificationConstants.NOTIFY_SUCCESS,
+ data: {title: i18n('Submit Succeeded'), msg: i18n('This license model successfully submitted'), timeout: 2000}
+ });
+ }
+ return LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId});
+ });
+ }
+};
+
+export default LicenseModelActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js
new file mode 100644
index 0000000..13fa9f5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelConstants.js
@@ -0,0 +1,36 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ LICENSE_MODEL_LOADED: null,
+ LICENSE_MODELS_LIST_LOADED: null,
+ FINALIZED_LICENSE_MODELS_LIST_LOADED: null,
+ ADD_LICENSE_MODEL: null,
+ EDIT_LICENSE_MODEL: null
+});
+
+export const navigationItems = keyMirror({
+ LICENSE_AGREEMENTS: 'License Agreements',
+ FEATURE_GROUPS: 'Feature Groups',
+ ENTITLEMENT_POOLS: 'Entitlement Pools',
+ LICENSE_KEY_GROUPS: 'License Key Groups'
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js
new file mode 100644
index 0000000..e92e32a
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelEditorReducer.js
@@ -0,0 +1,33 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './LicenseModelConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.LICENSE_MODEL_LOADED:
+ return {
+ ...state,
+ data: action.response
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js
new file mode 100644
index 0000000..8874c4c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelListReducer.js
@@ -0,0 +1,32 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './LicenseModelConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.LICENSE_MODELS_LIST_LOADED:
+ return [...action.response.results];
+ case actionTypes.ADD_LICENSE_MODEL:
+ return [...state, action.licenseModel];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js
new file mode 100644
index 0000000..5982b9f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/LicenseModelReducer.js
@@ -0,0 +1,66 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {combineReducers} from 'redux';
+
+import licenseModelCreationReducer from './creation/LicenseModelCreationReducer.js';
+import licenseModelEditorReducer from './LicenseModelEditorReducer.js';
+
+import licenseAgreementListReducer from './licenseAgreement/LicenseAgreementListReducer.js';
+import licenseAgreementEditorReducer from './licenseAgreement/LicenseAgreementEditorReducer.js';
+import {actionTypes as licenseAgreementActionTypes} from './licenseAgreement/LicenseAgreementConstants.js';
+
+import featureGroupsEditorReducer from './featureGroups/FeatureGroupsEditorReducer.js';
+import featureGroupsListReducer from './featureGroups/FeatureGroupsListReducer.js';
+import {actionTypes as featureGroupsActionConstants} from './featureGroups/FeatureGroupsConstants';
+
+import entitlementPoolsListReducer from './entitlementPools/EntitlementPoolsListReducer.js';
+import entitlementPoolsEditorReducer from './entitlementPools/EntitlementPoolsEditorReducer.js';
+import {actionTypes as entitlementPoolsConstants} from './entitlementPools/EntitlementPoolsConstants';
+
+import licenseKeyGroupsEditorReducer from './licenseKeyGroups/LicenseKeyGroupsEditorReducer.js';
+import licenseKeyGroupsListReducer from './licenseKeyGroups/LicenseKeyGroupsListReducer.js';
+import {actionTypes as licenseKeyGroupsConstants} from './licenseKeyGroups/LicenseKeyGroupsConstants.js';
+
+export default combineReducers({
+ licenseModelCreation: licenseModelCreationReducer,
+ licenseModelEditor: licenseModelEditorReducer,
+
+ licenseAgreement: combineReducers({
+ licenseAgreementEditor: licenseAgreementEditorReducer,
+ licenseAgreementList: licenseAgreementListReducer,
+ licenseAgreementToDelete: (state = false, action) => action.type === licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM ? action.licenseAgreementToDelete : state
+ }),
+ featureGroup: combineReducers({
+ featureGroupEditor: featureGroupsEditorReducer,
+ featureGroupsList: featureGroupsListReducer,
+ featureGroupToDelete: (state = false, action) => action.type === featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM ? action.featureGroupToDelete : state
+ }),
+ entitlementPool: combineReducers({
+ entitlementPoolEditor: entitlementPoolsEditorReducer,
+ entitlementPoolsList: entitlementPoolsListReducer,
+ entitlementPoolToDelete: (state = false, action) => action.type === entitlementPoolsConstants.ENTITLEMENT_POOLS_DELETE_CONFIRM ? action.entitlementPoolToDelete : state
+ }),
+ licenseKeyGroup: combineReducers({
+ licenseKeyGroupsEditor: licenseKeyGroupsEditorReducer,
+ licenseKeyGroupsList: licenseKeyGroupsListReducer,
+ licenseKeyGroupToDelete: (state = false, action) => action.type === licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM ? action.licenseKeyGroupToDelete : state
+ }),
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js
new file mode 100644
index 0000000..63d0f27
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js
@@ -0,0 +1,41 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import LicenseModelCreationActionHelper from './LicenseModelCreationActionHelper.js';
+import LicenseModelCreationView from './LicenseModelCreationView.jsx';
+
+const mapStateToProps = ({licenseModel: {licenseModelCreation}}) => licenseModelCreation;
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onDataChanged: deltaData => LicenseModelCreationActionHelper.dataChanged(dispatch, {deltaData}),
+ onCancel: () => LicenseModelCreationActionHelper.close(dispatch),
+ onSubmit: (licenseModel) => {
+ LicenseModelCreationActionHelper.close(dispatch);
+ LicenseModelCreationActionHelper.createLicenseModel(dispatch, {licenseModel}).then(licenseModelId => {
+ OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId});
+ });
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(LicenseModelCreationView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js
new file mode 100644
index 0000000..c2a0409
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationActionHelper.js
@@ -0,0 +1,72 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+import {actionTypes} from './LicenseModelCreationConstants.js';
+
+function baseUrl() {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/`;
+}
+
+function createLicenseModel(licenseModel) {
+ return RestAPIUtil.create(baseUrl(), {
+ vendorName: licenseModel.vendorName,
+ description: licenseModel.description,
+ iconRef: 'icon'
+ });
+}
+
+
+export default {
+
+ open(dispatch) {
+ dispatch({
+ type: actionTypes.OPEN
+ });
+ },
+
+ close(dispatch){
+ dispatch({
+ type: actionTypes.CLOSE
+ });
+ },
+
+ dataChanged(dispatch, {deltaData}){
+ dispatch({
+ type: actionTypes.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ createLicenseModel(dispatch, {licenseModel}){
+ return createLicenseModel(licenseModel).then(response => {
+ LicenseModelActionHelper.addLicenseModel(dispatch, {
+ licenseModel: {
+ ...licenseModel,
+ id: response.value
+ }
+ });
+ return response.value;
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js
new file mode 100644
index 0000000..603d177
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationConstants.js
@@ -0,0 +1,27 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js
new file mode 100644
index 0000000..a54d1b3
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationReducer.js
@@ -0,0 +1,43 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './LicenseModelCreationConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.OPEN:
+ return {
+ ...state,
+ data: {}
+ };
+ case actionTypes.CLOSE:
+ return {};
+ case actionTypes.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx
new file mode 100644
index 0000000..4dccc9e
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/creation/LicenseModelCreationView.jsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+
+const LicenseModelPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ vendorName: React.PropTypes.string,
+ description: React.PropTypes.string
+});
+
+class LicenseModelCreationView extends React.Component {
+
+ static propTypes = {
+ data: LicenseModelPropType,
+ onDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancel: React.PropTypes.func.isRequired
+ };
+
+ render() {
+ let {data = {}, onDataChanged} = this.props;
+ let {vendorName, description} = data;
+ return (
+ <div>
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => this.props.onCancel() }
+ labledButtons={true}>
+ <ValidationInput
+ value={vendorName}
+ label={i18n('Vendor Name')}
+ ref='vendor-name'
+ onChange={vendorName => onDataChanged({vendorName})}
+ validations={{maxLength: 25, required: true}}
+ type='text'
+ className='field-section'/>
+ <ValidationInput
+ value={description}
+ label={i18n('Description')}
+ ref='description'
+ onChange={description => onDataChanged({description})}
+ validations={{maxLength: 1000, required: true}}
+ type='textarea'
+ className='field-section'/>
+ </ValidationForm>
+ </div>
+ );
+ }
+
+
+ submit() {
+ const {data:licenseModel} = this.props;
+ this.props.onSubmit(licenseModel);
+ }
+}
+
+export default LicenseModelCreationView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js
new file mode 100644
index 0000000..631597a
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes as entitlementPoolsActionTypes } from './EntitlementPoolsConstants.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+
+function baseUrl(licenseModelId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/entitlement-pools`;
+}
+
+function fetchEntitlementPoolsList(licenseModelId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`);
+}
+
+function postEntitlementPool(licenseModelId, entitlementPool) {
+ return RestAPIUtil.create(baseUrl(licenseModelId), {
+ name: entitlementPool.name,
+ description: entitlementPool.description,
+ thresholdValue: entitlementPool.thresholdValue,
+ thresholdUnits: entitlementPool.thresholdUnits,
+ entitlementMetric: entitlementPool.entitlementMetric,
+ increments: entitlementPool.increments,
+ aggregationFunction: entitlementPool.aggregationFunction,
+ operationalScope: entitlementPool.operationalScope,
+ time: entitlementPool.time,
+ manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber
+ });
+}
+
+
+function putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool) {
+ return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${entitlementPool.id}`, {
+ name: entitlementPool.name,
+ description: entitlementPool.description,
+ thresholdValue: entitlementPool.thresholdValue,
+ thresholdUnits: entitlementPool.thresholdUnits,
+ entitlementMetric: entitlementPool.entitlementMetric,
+ increments: entitlementPool.increments,
+ aggregationFunction: entitlementPool.aggregationFunction,
+ operationalScope: entitlementPool.operationalScope,
+ time: entitlementPool.time,
+ manufacturerReferenceNumber: entitlementPool.manufacturerReferenceNumber
+ });
+}
+
+function deleteEntitlementPool(licenseModelId, entitlementPoolId) {
+ return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${entitlementPoolId}`);
+}
+
+
+export default {
+ fetchEntitlementPoolsList(dispatch, {licenseModelId, version}) {
+ return fetchEntitlementPoolsList(licenseModelId, version).then(response => dispatch({
+ type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_LIST_LOADED,
+ response
+ }));
+ },
+
+ openEntitlementPoolsEditor(dispatch, {entitlementPool} = {}) {
+ dispatch({
+ type: entitlementPoolsActionTypes.entitlementPoolsEditor.OPEN,
+ entitlementPool
+ });
+ },
+
+ deleteEntitlementPool(dispatch, {licenseModelId, entitlementPoolId}) {
+ return deleteEntitlementPool(licenseModelId, entitlementPoolId).then(() => {
+ dispatch({
+ type: entitlementPoolsActionTypes.DELETE_ENTITLEMENT_POOL,
+ entitlementPoolId
+ });
+ });
+ },
+
+ entitlementPoolsEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: entitlementPoolsActionTypes.entitlementPoolsEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ closeEntitlementPoolsEditor(dispatch) {
+ dispatch({
+ type: entitlementPoolsActionTypes.entitlementPoolsEditor.CLOSE
+ });
+ },
+
+ saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool}) {
+ if (previousEntitlementPool) {
+ return putEntitlementPool(licenseModelId, previousEntitlementPool, entitlementPool).then(() => {
+ dispatch({
+ type: entitlementPoolsActionTypes.EDIT_ENTITLEMENT_POOL,
+ entitlementPool
+ });
+ });
+ }
+ else {
+ return postEntitlementPool(licenseModelId, entitlementPool).then(response => {
+ dispatch({
+ type: entitlementPoolsActionTypes.ADD_ENTITLEMENT_POOL,
+ entitlementPool: {
+ ...entitlementPool,
+ id: response.value
+ }
+ });
+ });
+ }
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_DELETE_CONFIRM,
+ entitlementPoolToDelete: false
+ });
+ },
+ openDeleteEntitlementPoolConfirm(dispatch, {entitlementPool}) {
+ dispatch({
+ type: entitlementPoolsActionTypes.ENTITLEMENT_POOLS_DELETE_CONFIRM,
+ entitlementPoolToDelete: entitlementPool
+ });
+ },
+
+ switchVersion(dispatch, {licenseModelId, version}) {
+ LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => {
+ this.fetchEntitlementPoolsList(dispatch, {licenseModelId, version});
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx
new file mode 100644
index 0000000..04f038f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConfirmationModal.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function renderMsg(entitlementPoolToDelete) {
+ let poolName = entitlementPoolToDelete ? entitlementPoolToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{poolName}"?', {poolName});
+ let subMsg = entitlementPoolToDelete
+ && entitlementPoolToDelete.referencingFeatureGroups
+ && entitlementPoolToDelete.referencingFeatureGroups.length > 0 ?
+ i18n('This entitlement pool is associated with one or more feature groups') :
+ '';
+ return (
+ <div>
+ <p>{msg}</p>
+ <p>{subMsg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({licenseModel: {entitlementPool}}, {licenseModelId}) => {
+ let {entitlementPoolToDelete} = entitlementPool;
+ const show = entitlementPoolToDelete !== false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: renderMsg(entitlementPoolToDelete),
+ confirmationDetails: {entitlementPoolToDelete, licenseModelId}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({entitlementPoolToDelete, licenseModelId}) => {
+ EntitlementPoolsActionHelper.deleteEntitlementPool(dispatch, {
+ licenseModelId,
+ entitlementPoolId: entitlementPoolToDelete.id
+ });
+ EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ EntitlementPoolsActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js
new file mode 100644
index 0000000..8a85507
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js
@@ -0,0 +1,112 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export const actionTypes = keyMirror({
+
+ ENTITLEMENT_POOLS_LIST_LOADED: null,
+ ADD_ENTITLEMENT_POOL: null,
+ EDIT_ENTITLEMENT_POOL: null,
+ DELETE_ENTITLEMENT_POOL: null,
+ ENTITLEMENT_POOLS_DELETE_CONFIRM: null,
+
+ entitlementPoolsEditor: {
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null,
+ }
+
+});
+
+export const enums = keyMirror({
+ SELECTED_FEATURE_GROUP_TAB: {
+ GENERAL: 1,
+ ENTITLEMENT_POOLS: 2,
+ LICENCE_KEY_GROUPS: 3
+ },
+ SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: {
+ ASSOCIATED_ENTITLEMENT_POOLS: 1,
+ AVAILABLE_ENTITLEMENT_POOLS: 2
+ }
+});
+
+export const defaultState = {
+ ENTITLEMENT_POOLS_EDITOR_DATA: {
+ entitlementMetric: {choice: '', other: ''},
+ aggregationFunction: {choice: '', other: ''},
+ operationalScope: {choices: [], other: ''},
+ time: {choice: '', other: ''}
+ }
+};
+
+export const thresholdUnitType = {
+ ABSOLUTE: 'Absolute',
+ PERCENTAGE: 'Percentage'
+};
+
+export const optionsInputValues = {
+ OPERATIONAL_SCOPE: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Network_Wide', title: 'Network Wide'},
+ {enum: 'Availability_Zone', title: 'Availability Zone'},
+ {enum: 'Data_Center', title: 'Data Center'},
+ {enum: 'Tenant', title: 'Tenant'},
+ {enum: 'VM', title: 'VM'},
+ {enum: 'CPU', title: 'CPU'},
+ {enum: 'Core', title: 'Core'}
+ ],
+ TIME: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Hour', title: 'Hour'},
+ {enum: 'Day', title: 'Day'},
+ {enum: 'Month', title: 'Month'}
+ ],
+ AGGREGATE_FUNCTION: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Peak', title: 'Peak'},
+ {enum: 'Average', title: 'Average'}
+ ],
+ ENTITLEMENT_METRIC: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Software_Instances_Count', title: 'Software Instances'},
+ {enum: 'Core', title: 'Core'},
+ {enum: 'CPU', title: 'CPU'},
+ {enum: 'Trunks', title: 'Trunks'},
+ {enum: 'User', title: 'User'},
+ {enum: 'Subscribers', title: 'Subscribers'},
+ {enum: 'Tenants', title: 'Tenants'},
+ {enum: 'Tokens', title: 'Tokens'},
+ {enum: 'Seats', title: 'Seats'},
+ {enum: 'Units_TB', title: 'Units-TB'},
+ {enum: 'Units_GB', title: 'Units-GB'},
+ {enum: 'Units_MB', title: 'Units-MB'}
+ ],
+ THRESHOLD_UNITS: [
+ {enum: '', title: i18n('please select…')},
+ {enum: thresholdUnitType.ABSOLUTE, title: 'Absolute'},
+ {enum: thresholdUnitType.PERCENTAGE, title: '%'}
+ ]
+};
+
+
+
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js
new file mode 100644
index 0000000..d5bd07e
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditor.js
@@ -0,0 +1,53 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js';
+import EntitlementPoolsEditorView from './EntitlementPoolsEditorView.jsx';
+
+const mapStateToProps = ({licenseModel: {entitlementPool}}) => {
+
+
+ let {data} = entitlementPool.entitlementPoolEditor;
+
+ let previousData;
+ const entitlementPoolId = data ? data.id : null;
+ if(entitlementPoolId) {
+ previousData = entitlementPool.entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId);
+ }
+
+ return {
+ data,
+ previousData
+ };
+};
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onDataChanged: deltaData => EntitlementPoolsActionHelper.entitlementPoolsEditorDataChanged(dispatch, {deltaData}),
+ onCancel: () => EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch),
+ onSubmit: ({previousEntitlementPool, entitlementPool}) => {
+ EntitlementPoolsActionHelper.closeEntitlementPoolsEditor(dispatch);
+ EntitlementPoolsActionHelper.saveEntitlementPool(dispatch, {licenseModelId, previousEntitlementPool, entitlementPool});
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(EntitlementPoolsEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js
new file mode 100644
index 0000000..86e97ec
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorReducer.js
@@ -0,0 +1,44 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes, defaultState} from './EntitlementPoolsConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.entitlementPoolsEditor.OPEN:
+ return {
+ ...state,
+ data: action.entitlementPool ? {...action.entitlementPool} : defaultState.ENTITLEMENT_POOLS_EDITOR_DATA
+ };
+ case actionTypes.entitlementPoolsEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ case actionTypes.entitlementPoolsEditor.CLOSE:
+ return {};
+ default:
+ return state;
+ }
+
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx
new file mode 100644
index 0000000..77c5a12
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsEditorView.jsx
@@ -0,0 +1,167 @@
+import React from 'react';
+
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import {optionsInputValues as EntitlementPoolsOptionsInputValues, thresholdUnitType} from './EntitlementPoolsConstants.js';
+import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+
+
+const EntitlementPoolPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ manufacturerReferenceNumber: React.PropTypes.string,
+ operationalScope: React.PropTypes.shape({
+ choices: React.PropTypes.array,
+ other: React.PropTypes.string
+ }),
+ aggregationFunction: React.PropTypes.shape({
+ choice: React.PropTypes.string,
+ other: React.PropTypes.string
+ }),
+ increments: React.PropTypes.string,
+ time: React.PropTypes.shape({
+ choice: React.PropTypes.string,
+ other: React.PropTypes.string
+ }),
+ entitlementMetric: React.PropTypes.shape({
+ choice: React.PropTypes.string,
+ other: React.PropTypes.string
+ })
+});
+
+class EntitlementPoolsEditorView extends React.Component {
+
+ static propTypes = {
+ data: EntitlementPoolPropType,
+ previousData: EntitlementPoolPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ onDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancel: React.PropTypes.func.isRequired
+ };
+
+ static defaultProps = {
+ data: {}
+ };
+
+ render() {
+ let {data = {}, onDataChanged, isReadOnlyMode} = this.props;
+ let {
+ name, description, manufacturerReferenceNumber, operationalScope, aggregationFunction, thresholdUnits, thresholdValue,
+ increments, time, entitlementMetric} = data;
+ let thresholdValueValidation = thresholdUnits === thresholdUnitType.PERCENTAGE ? {numeric: true, required: true, maxValue: 100} : {numeric: true, required: true};
+ let timeValidation = time && time.choice === optionInputOther.OTHER ? {numeric: true, required: true} : {required: true};
+
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => this.props.onCancel() }
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ className='entitlement-pools-form'>
+ <div className='entitlement-pools-form-row'>
+ <ValidationInput
+ onChange={name => onDataChanged({name})}
+ label={i18n('Name')}
+ value={name}
+ validations={{maxLength: 120, required: true}}
+ type='text'/>
+
+ <ValidationInput
+ isMultiSelect={true}
+ onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}})}
+ onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})}
+ multiSelectedEnum={operationalScope && operationalScope.choices}
+ label={i18n('Operational Scope')}
+ otherValue={operationalScope && operationalScope.other}
+ validations={{required: true}}
+ values={EntitlementPoolsOptionsInputValues.OPERATIONAL_SCOPE}/>
+
+ </div>
+ <div className='entitlement-pools-form-row'>
+ <ValidationInput
+ onChange={description => onDataChanged({description})}
+ label={i18n('Description')}
+ value={description}
+ validations={{maxLength: 1000, required: true}}
+ type='textarea'/>
+ <div className='entitlement-pools-form-row-group'>
+ <div className='entitlement-pools-form-row'>
+ <ValidationInput
+ onEnumChange={thresholdUnits => onDataChanged({thresholdUnits})}
+ selectedEnum={thresholdUnits}
+ label={i18n('Threshold Value')}
+ type='select'
+ values={EntitlementPoolsOptionsInputValues.THRESHOLD_UNITS}
+ validations={{required: true}}/>
+ <ValidationInput
+ className='entitlement-pools-form-row-threshold-value'
+ onChange={thresholdValue => onDataChanged({thresholdValue})}
+ value={thresholdValue}
+ validations={thresholdValueValidation}
+ type='text'/>
+ </div>
+
+ <ValidationInput
+ onEnumChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: entitlementMetric, other: ''}})}
+ onOtherChange={entitlementMetric => onDataChanged({entitlementMetric:{choice: optionInputOther.OTHER, other: entitlementMetric}})}
+ selectedEnum={entitlementMetric && entitlementMetric.choice}
+ otherValue={entitlementMetric && entitlementMetric.other}
+ label={i18n('Entitlement Metric')}
+ validations={{required: true}}
+ values={EntitlementPoolsOptionsInputValues.ENTITLEMENT_METRIC}/>
+ <ValidationInput
+ onEnumChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: aggregationFunction, other: ''}})}
+ onOtherChange={aggregationFunction => onDataChanged({aggregationFunction:{choice: optionInputOther.OTHER, other: aggregationFunction}})}
+ selectedEnum={aggregationFunction && aggregationFunction.choice}
+ otherValue={aggregationFunction && aggregationFunction.other}
+ validations={{required: true}}
+ label={i18n('Aggregate Function')}
+ values={EntitlementPoolsOptionsInputValues.AGGREGATE_FUNCTION}/>
+
+ </div>
+ </div>
+ <div className='entitlement-pools-form-row'>
+
+ <ValidationInput
+ onChange={manufacturerReferenceNumber => onDataChanged({manufacturerReferenceNumber})}
+ label={i18n('Manufacturer Reference Number')}
+ value={manufacturerReferenceNumber}
+ validations={{maxLength: 100, required: true}}
+ type='text'/>
+
+ <ValidationInput
+ onEnumChange={time => onDataChanged({time:{choice: time, other: ''}})}
+ onOtherChange={time => onDataChanged({time:{choice: optionInputOther.OTHER, other: time}})}
+ selectedEnum={time && time.choice}
+ otherValue={time && time.other}
+ validations={timeValidation}
+ label={i18n('Time')}
+ values={EntitlementPoolsOptionsInputValues.TIME}/>
+ </div>
+ <div className='entitlement-pools-form-row'>
+ <ValidationInput
+ onChange={increments => onDataChanged({increments})}
+ label={i18n('Increments')}
+ value={increments}
+ validations={{maxLength: 120}}
+ type='text'/>
+
+ </div>
+ </ValidationForm>
+ );
+ }
+
+ submit() {
+ const {data: entitlementPool, previousData: previousEntitlementPool} = this.props;
+ this.props.onSubmit({entitlementPool, previousEntitlementPool});
+ }
+}
+
+export default EntitlementPoolsEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js
new file mode 100644
index 0000000..4b21a2f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditor.js
@@ -0,0 +1,53 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import EntitlementPoolsActionHelper from './EntitlementPoolsActionHelper.js';
+import EntitlementPoolsListEditorView from './EntitlementPoolsListEditorView.jsx';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+const mapStateToProps = ({licenseModel: {entitlementPool, licenseModelEditor}}) => {
+ let {entitlementPoolsList} = entitlementPool;
+ let {data} = entitlementPool.entitlementPoolEditor;
+
+ let {vendorName} = licenseModelEditor.data;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data);
+
+ return {
+ vendorName,
+ entitlementPoolsList,
+ isReadOnlyMode,
+ isDisplayModal: Boolean(data),
+ isModalInEditMode: Boolean(data && data.id),
+ };
+};
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onAddEntitlementPoolClick: () => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch),
+ onEditEntitlementPoolClick: entitlementPool => EntitlementPoolsActionHelper.openEntitlementPoolsEditor(dispatch, {entitlementPool}),
+ onDeleteEntitlementPool: entitlementPool => EntitlementPoolsActionHelper.openDeleteEntitlementPoolConfirm(dispatch, {
+ licenseModelId,
+ entitlementPool
+ })
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(EntitlementPoolsListEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx
new file mode 100644
index 0000000..52df102
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListEditorView.jsx
@@ -0,0 +1,132 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import EntitlementPoolsEditor from './EntitlementPoolsEditor.js';
+import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+import {optionsInputValues} from './EntitlementPoolsConstants';
+import EntitlementPoolsConfirmationModal from './EntitlementPoolsConfirmationModal.jsx';
+
+
+class EntitlementPoolsListEditorView extends React.Component {
+ static propTypes = {
+ vendorName: React.PropTypes.string,
+ licenseModelId: React.PropTypes.string.isRequired,
+ entitlementPoolsList: React.PropTypes.array,
+ isReadOnlyMode: React.PropTypes.bool.isRequired,
+ isDisplayModal: React.PropTypes.bool,
+ isModalInEditMode: React.PropTypes.bool,
+ onAddEntitlementPoolClick: React.PropTypes.func,
+ onEditEntitlementPoolClick: React.PropTypes.func,
+ onDeleteEntitlementPool: React.PropTypes.func,
+ };
+
+ static defaultProps = {
+ entitlementPoolsList: []
+ };
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props;
+ let {onAddEntitlementPoolClick} = this.props;
+ const {localFilter} = this.state;
+
+ return (
+ <div className='entitlement-pools-list-editor'>
+ <ListEditorView
+ title={i18n('Entitlement Pools for {vendorName} License Model', {vendorName})}
+ plusButtonTitle={i18n('Add Entitlement Pool')}
+ onAdd={onAddEntitlementPoolClick}
+ filterValue={localFilter}
+ onFilter={filter => this.setState({localFilter: filter})}
+ isReadOnlyMode={isReadOnlyMode}>
+ {this.filterList().map(entitlementPool => this.renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode))}
+ </ListEditorView>
+ <Modal show={isDisplayModal} bsSize='large' animation={true} className='entitlement-pools-modal'>
+ <Modal.Header>
+ <Modal.Title>{`${isModalInEditMode ? i18n('Edit Entitlement Pool') : i18n('Create New Entitlement Pool')}`}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {
+ isDisplayModal && (
+ <EntitlementPoolsEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/>
+ )
+ }
+ </Modal.Body>
+ </Modal>
+
+ <EntitlementPoolsConfirmationModal licenseModelId={licenseModelId}/>
+ </div>
+ );
+ }
+
+ filterList() {
+ let {entitlementPoolsList} = this.props;
+ let {localFilter} = this.state;
+ if(localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return entitlementPoolsList.filter(({name = '', description = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return entitlementPoolsList;
+ }
+ }
+
+ renderEntitlementPoolListItem(entitlementPool, isReadOnlyMode) {
+ let {id, name, description, thresholdValue, thresholdUnits, entitlementMetric, aggregationFunction,
+ manufacturerReferenceNumber, time} = entitlementPool;
+ let {onEditEntitlementPoolClick, onDeleteEntitlementPool} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ onSelect={() => onEditEntitlementPoolClick(entitlementPool)}
+ onDelete={() => onDeleteEntitlementPool(entitlementPool)}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='text name'>{name}</div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Entitlement')}</div>
+ <div className='entitlement-parameters'>{`${this.extractValue(aggregationFunction)} ${this.extractValue(entitlementMetric)} per ${this.extractValue(time)}`}</div>
+ <div className='entitlement-pools-count'>{`${thresholdValue ? thresholdValue : ''} ${this.extractUnits(thresholdUnits)}`}</div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Manufacturer Reference Number')}</div>
+ <div className='text contract-number'>{manufacturerReferenceNumber}</div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='text description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+
+
+ extractUnits(units) {
+ if (units === undefined) {return '';} //TODO fix it later
+ return units === 'Absolute' ? '' : '%';
+ }
+
+ extractValue(item) {
+ if (item === undefined) {return '';} //TODO fix it later
+
+ return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : '';
+ }
+}
+
+export default EntitlementPoolsListEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js
new file mode 100644
index 0000000..63e351f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsListReducer.js
@@ -0,0 +1,36 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './EntitlementPoolsConstants';
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.ENTITLEMENT_POOLS_LIST_LOADED:
+ return [...action.response.results];
+ case actionTypes.ADD_ENTITLEMENT_POOL:
+ return [...state, action.entitlementPool];
+ case actionTypes.EDIT_ENTITLEMENT_POOL:
+ const indexForEdit = state.findIndex(entitlementPool => entitlementPool.id === action.entitlementPool.id);
+ return [...state.slice(0, indexForEdit), action.entitlementPool, ...state.slice(indexForEdit + 1)];
+ case actionTypes.DELETE_ENTITLEMENT_POOL:
+ return state.filter(entitlementPool => entitlementPool.id !== action.entitlementPoolId);
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js
new file mode 100644
index 0000000..c2b269b
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditor.js
@@ -0,0 +1,66 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js';
+import FeatureGroupEditorView from './FeatureGroupEditorView.jsx';
+
+const mapStateToProps = ({licenseModel: {featureGroup, entitlementPool, licenseKeyGroup}}) => {
+
+ const {featureGroupEditor} = featureGroup;
+
+ let {data, selectedTab, selectedEntitlementPoolsButtonTab, selectedLicenseKeyGroupsButtonTab} = featureGroupEditor;
+
+ let previousData;
+ const featureGroupId = data ? data.id : null;
+ if (featureGroupId) {
+ previousData = featureGroup.featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId);
+ }
+ let {entitlementPoolsList = []} = entitlementPool;
+ let {licenseKeyGroupsList = []} = licenseKeyGroup;
+
+ return {
+ data,
+ previousData,
+ selectedTab,
+ selectedEntitlementPoolsButtonTab,
+ selectedLicenseKeyGroupsButtonTab,
+ entitlementPoolsList,
+ licenseKeyGroupsList
+ };
+};
+
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onTabSelect: tab => FeatureGroupsActionHelper.selectEntitlementPoolsEditorTab(dispatch, {tab}),
+ onEntitlementPoolsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}),
+ onLicenseKeyGroupsButtonTabSelect: buttonTab => FeatureGroupsActionHelper.selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}),
+ onDataChanged: deltaData => FeatureGroupsActionHelper.featureGroupsEditorDataChanged(dispatch, {deltaData}),
+ onSubmit: (previousFeatureGroup, featureGroup) => {
+ FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch);
+ FeatureGroupsActionHelper.saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup});
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupEditorView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx
new file mode 100644
index 0000000..6fecd16
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupEditorView.jsx
@@ -0,0 +1,339 @@
+import React from 'react';
+import ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx';
+import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx';
+import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js';
+import Button from 'react-bootstrap/lib/Button.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import {state as FeatureGroupStateConstants} from './FeatureGroupsConstants.js';
+
+const FeatureGroupsPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ partNumber: React.PropTypes.string,
+ entitlementPoolsIds: React.PropTypes.array(React.PropTypes.string),
+ licenseKeyGroupsIds: React.PropTypes.array(React.PropTypes.string)
+});
+
+class FeatureGroupEditorView extends React.Component {
+
+
+ static propTypes = {
+ data: FeatureGroupsPropType,
+ previousData: FeatureGroupsPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+
+ onSubmit: React.PropTypes.func,
+ onCancel: React.PropTypes.func,
+
+ selectedTab: React.PropTypes.number,
+ onTabSelect: React.PropTypes.func,
+
+ selectedEntitlementPoolsButtonTab: React.PropTypes.number,
+ selectedLicenseKeyGroupsButtonTab: React.PropTypes.number,
+ onEntitlementPoolsButtonTabSelect: React.PropTypes.func,
+ onLicenseKeyGroupsButtonTabSelect: React.PropTypes.func,
+
+ entitlementPoolsList: DualListboxView.propTypes.availableList,
+ licenseKeyGroupsList: DualListboxView.propTypes.availableList
+ };
+
+
+ static defaultProps = {
+ data: {},
+ selectedTab: FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL,
+ selectedEntitlementPoolsButtonTab: FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS,
+ selectedLicenseKeyGroupsButtonTab: FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS
+ };
+
+ state = {
+ localEntitlementPoolsListFilter: '',
+ localLicenseKeyGroupsListFilter: ''
+ };
+
+
+ render() {
+ let {selectedTab, onTabSelect, isReadOnlyMode} = this.props;
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => this.props.onCancel() }
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ name='feature-group-validation-form'
+ className='feature-group-form'>
+ <ValidationTabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect}>
+ {this.renderGeneralTab()}
+ {this.renderEntitlementPoolsTab()}
+ {this.renderLicenseKeyGroupsTab()}
+ </ValidationTabs>
+
+ </ValidationForm>
+ );
+ }
+
+ submit() {
+ const {data: featureGroup, previousData: previousFeatureGroup} = this.props;
+ this.props.onSubmit(previousFeatureGroup, featureGroup);
+ }
+
+ renderGeneralTab() {
+ let {data = {}, onDataChanged} = this.props;
+ let {name, description, partNumber} = data;
+ return (
+ <ValidationTab eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.GENERAL} title={i18n('General')}>
+ <div>
+ <ValidationInput
+ groupClassName='field-section'
+ onChange={name => onDataChanged({name})}
+ ref='name'
+ label={i18n('Name')}
+ value={name}
+ name='feature-group-name'
+ validations={{maxLength: 120, required: true}}
+ type='text'/>
+ <ValidationInput
+ groupClassName='field-section'
+ className='description-field'
+ onChange={description => onDataChanged({description})}
+ ref='description'
+ label={i18n('Description')}
+ value={description}
+ name='feature-group-description'
+ validations={{maxLength: 1000, required: true}}
+ type='textarea'/>
+ <ValidationInput
+ groupClassName='field-section'
+ onChange={partNumber => onDataChanged({partNumber})}
+ label={i18n('Part Number')}
+ value={partNumber}
+ validations={{required: true}}
+ type='text'/>
+ </div>
+ </ValidationTab>
+ );
+ }
+
+ renderEntitlementPoolsTab() {
+ let {selectedEntitlementPoolsButtonTab, onEntitlementPoolsButtonTabSelect, entitlementPoolsList} = this.props;
+ if (entitlementPoolsList.length > 0) {
+ return (
+ <ValidationTab
+ eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS}
+ title={i18n('Entitlement Pools')}>
+ <ButtonGroup>
+ {
+ this.renderButtonsTab(
+ FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS,
+ selectedEntitlementPoolsButtonTab,
+ i18n('Associated Entitlement Pools'),
+ onEntitlementPoolsButtonTabSelect
+ )
+ }
+ {
+ this.renderButtonsTab(
+ FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS,
+ selectedEntitlementPoolsButtonTab,
+ i18n('Available Entitlement Pools'),
+ onEntitlementPoolsButtonTabSelect
+ )
+ }
+ </ButtonGroup>
+ {this.renderEntitlementPoolsButtonTabContent(selectedEntitlementPoolsButtonTab)}
+ </ValidationTab>
+ );
+ } else {
+ return (
+ <ValidationTab
+ eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS}
+ title={i18n('Entitlement Pools')}>
+ <p>{i18n('There is no available entitlement pools.')}</p>
+ </ValidationTab>
+ );
+ }
+ }
+
+ renderLicenseKeyGroupsTab() {
+ let {selectedLicenseKeyGroupsButtonTab, onLicenseKeyGroupsButtonTabSelect, licenseKeyGroupsList} = this.props;
+ if (licenseKeyGroupsList.length > 0) {
+ return (
+ <ValidationTab
+ eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS}
+ title={i18n('License Key Groups')}>
+ <ButtonGroup>
+ {
+ this.renderButtonsTab(
+ FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS,
+ selectedLicenseKeyGroupsButtonTab,
+ i18n('Associated License Key Groups'),
+ onLicenseKeyGroupsButtonTabSelect
+ )
+ }
+ {
+ this.renderButtonsTab(
+ FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS,
+ selectedLicenseKeyGroupsButtonTab,
+ i18n('Available License Key Groups'),
+ onLicenseKeyGroupsButtonTabSelect
+ )
+ }
+ </ButtonGroup>
+ {this.renderLicenseKeyGroupsTabContent(selectedLicenseKeyGroupsButtonTab)}
+ </ValidationTab>
+ );
+ } else {
+ return (
+ <ValidationTab
+ eventKey={FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS}
+ title={i18n('License Key Groups')}>
+ <p>{i18n('There is no available license key groups')}</p>
+ </ValidationTab>);
+ }
+ }
+
+ renderButtonsTab(buttonTab, selectedButtonTab, title, onClick) {
+ const isSelected = buttonTab === selectedButtonTab;
+ return (
+ <Button
+ className='button-tab'
+ active={isSelected}
+ onClick={() => !isSelected && onClick(buttonTab)}>
+ { title }
+ </Button>
+ );
+ }
+
+ renderEntitlementPoolsButtonTabContent(selectedFeatureGroupsButtonTab) {
+ const {entitlementPoolsList = [], data: {entitlementPoolsIds = []}} = this.props;
+ let dualBoxTitle = {
+ left: i18n('Available Entitlement Pools'),
+ right: i18n('Selected Entitlement Pools')
+ };
+
+ if (entitlementPoolsList.length) {
+ const {localEntitlementPoolsListFilter} = this.state;
+ let selectedEntitlementPools = entitlementPoolsIds.map(entitlementPoolId => entitlementPoolsList.find(entitlementPool => entitlementPool.id === entitlementPoolId));
+
+ switch (selectedFeatureGroupsButtonTab) {
+ case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.ASSOCIATED_ENTITLEMENT_POOLS:
+ if (selectedEntitlementPools.length) {
+ return (
+ <ListEditorView
+ className='thinner-list'
+ filterValue={localEntitlementPoolsListFilter}
+ onFilter={localEntitlementPoolsListFilter => this.setState({localEntitlementPoolsListFilter})}>
+ {this.filterAssociatedItems(selectedEntitlementPools, localEntitlementPoolsListFilter)
+ .map(entitlementPool => this.renderAssociatedListItem(entitlementPool
+ , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.ENTITLEMENT_POOLS))}
+ </ListEditorView>
+ );
+ }
+ else {
+ return (
+ <div>
+ <br/>{i18n('There are currently no entitlement pools associated with this feature group. Click "Available Entitlement Pools" to associate.')}
+ </div>
+ );
+ }
+ case FeatureGroupStateConstants.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB.AVAILABLE_ENTITLEMENT_POOLS:
+ return (
+ <DualListboxView
+ filterTitle={dualBoxTitle}
+ selectedValuesList={entitlementPoolsIds}
+ availableList={entitlementPoolsList}
+ onChange={ selectedValuesList => this.props.onDataChanged( { entitlementPoolsIds: selectedValuesList } )}/>
+ );
+ }
+ }
+ }
+
+ renderLicenseKeyGroupsTabContent(selectedFeatureGroupsButtonTab) {
+ const {licenseKeyGroupsList = [], data: {licenseKeyGroupsIds = []}} = this.props;
+ let dualBoxFilterTitle = {
+ left: i18n('Available License Key Groups'),
+ right: i18n('Selected License Key Groups')
+ };
+
+ if (licenseKeyGroupsList.length) {
+ const {localLicenseKeyGroupsListFilter} = this.state;
+ let selectedLicenseKeyGroups = licenseKeyGroupsIds.map(licenseKeyGroupId => licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId));
+
+ switch (selectedFeatureGroupsButtonTab) {
+ case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.ASSOCIATED_LICENSE_KEY_GROUPS:
+ if (selectedLicenseKeyGroups.length) {
+ return (
+ <ListEditorView
+ className='thinner-list'
+ filterValue={localLicenseKeyGroupsListFilter}
+ onFilter={localLicenseKeyGroupsListFilter => this.setState({localLicenseKeyGroupsListFilter})}>
+ {this.filterAssociatedItems(selectedLicenseKeyGroups, localLicenseKeyGroupsListFilter)
+ .map(licenseKeyGroup => this.renderAssociatedListItem(licenseKeyGroup
+ , FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS))}
+ </ListEditorView>
+ );
+ } else {
+ return (
+ <div className='no-items-msg'>
+ {i18n('There are currently no license key groups associated with this feature group. Click "Available License Key Groups" to associate.')}
+ </div>
+ );
+ }
+ case FeatureGroupStateConstants.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB.AVAILABLE_LICENSE_KEY_GROUPS:
+ return (
+ <DualListboxView
+ filterTitle={dualBoxFilterTitle}
+ selectedValuesList={this.props.data.licenseKeyGroupsIds}
+ availableList={this.props.licenseKeyGroupsList}
+ onChange={ selectedValuesList => this.props.onDataChanged( { licenseKeyGroupsIds: selectedValuesList } )}/>
+ );
+ }
+ }
+ }
+
+
+ renderAssociatedListItem(listItem, itemType) {
+ let {isReadOnlyMode} = this.props;
+ return (
+ <ListEditorViewItem
+ key={listItem.id}
+ onDelete={() => this.deleteAssociatedItem(listItem.id, itemType)}
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='name'>{listItem.name}</div>
+ <div className='description'>{listItem.description}</div>
+ </ListEditorViewItem>
+ );
+ }
+
+ filterAssociatedItems(list, localList) {
+ if (localList) {
+ const filter = new RegExp(escape(localList), 'i');
+ return list.filter(({name = '', description = ''}) => name.match(filter) || description.match(filter));
+ }
+ else {
+ return list;
+ }
+ }
+
+ deleteAssociatedItem(id, type) {
+ const {data: {licenseKeyGroupsIds = [], entitlementPoolsIds = []}} = this.props;
+ if (type === FeatureGroupStateConstants.SELECTED_FEATURE_GROUP_TAB.LICENCE_KEY_GROUPS) {
+ this.props.onDataChanged({licenseKeyGroupsIds: licenseKeyGroupsIds.filter(listId => listId !== id)});
+ } else {
+ this.props.onDataChanged({entitlementPoolsIds: entitlementPoolsIds.filter(listId => listId !== id)});
+ }
+
+ }
+}
+
+
+export default FeatureGroupEditorView;
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js
new file mode 100644
index 0000000..9ea5a31
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditor.js
@@ -0,0 +1,56 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js';
+import FeatureGroupListEditorView from './FeatureGroupListEditorView.jsx';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+const mapStateToProps = ({licenseModel: {featureGroup, licenseModelEditor}}) => {
+ const {featureGroupEditor: {data}, featureGroupsList} = featureGroup;
+ let {vendorName} = licenseModelEditor.data;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data);
+
+ return {
+ vendorName,
+ featureGroupsModal: {
+ show: Boolean(data),
+ editMode: Boolean(data && data.id)
+ },
+ featureGroupsList,
+ isReadOnlyMode
+ };
+};
+
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onDeleteFeatureGroupClick: (featureGroup) => FeatureGroupsActionHelper.openDeleteFeatureGroupConfirm(dispatch, {licenseModelId, featureGroup}),
+ onCancelFeatureGroupsEditor: () => FeatureGroupsActionHelper.closeFeatureGroupsEditor(dispatch),
+
+ onAddFeatureGroupClick: () => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {licenseModelId}),
+ onEditFeatureGroupClick: featureGroup => FeatureGroupsActionHelper.openFeatureGroupsEditor(dispatch, {
+ featureGroup,
+ licenseModelId
+ })
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(FeatureGroupListEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx
new file mode 100644
index 0000000..d998f92
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupListEditorView.jsx
@@ -0,0 +1,136 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import FeatureGroupEditor from './FeatureGroupEditor.js';
+import FeatureGroupsConfirmationModal from './FeatureGroupsConfirmationModal.jsx';
+
+class FeatureGroupListEditorView extends React.Component {
+ static propTypes = {
+ vendorName: React.PropTypes.string,
+ licenseModelId: React.PropTypes.string.isRequired,
+ featureGroupsModal: React.PropTypes.shape({
+ show: React.PropTypes.bool,
+ editMode: React.PropTypes.bool
+ }),
+ isReadOnlyMode: React.PropTypes.bool.isRequired,
+ onAddFeatureGroupClick: React.PropTypes.func,
+ onEditFeatureGroupClick: React.PropTypes.func,
+ onDeleteFeatureGroupClick: React.PropTypes.func,
+ onCancelFeatureGroupsEditor: React.PropTypes.func,
+ featureGroupsList: React.PropTypes.array
+ };
+
+ static defaultProps = {
+ featureGroupsList: [],
+ featureGroupsModal: {
+ show: false,
+ editMode: false
+ }
+ };
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ let {vendorName, licenseModelId, featureGroupsModal, isReadOnlyMode, onAddFeatureGroupClick} = this.props;
+ const {localFilter} = this.state;
+
+ return (
+ <div className='feature-groups-list-editor'>
+ <ListEditorView
+ title={i18n('Feature Groups for {vendorName} License Model', {vendorName})}
+ plusButtonTitle={i18n('Add Feature Group')}
+ filterValue={localFilter}
+ onFilter={filter => this.setState({localFilter: filter})}
+ onAdd={() => onAddFeatureGroupClick()}
+ isReadOnlyMode={isReadOnlyMode}>
+ {this.filterList().map(listItem => this.renderFeatureGroupListItem(listItem, isReadOnlyMode))}
+ </ListEditorView>
+ <Modal show={featureGroupsModal.show} bsSize='large' animation={true} className='feature-group-modal'>
+ <Modal.Header>
+ <Modal.Title>{`${featureGroupsModal.editMode ? i18n('Edit Feature Group') : i18n('Create New Feature Group')}`}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <FeatureGroupEditor
+ onCancel={() => this.closeFeatureGroupsEditor()}
+ licenseModelId={licenseModelId}
+ isReadOnlyMode={isReadOnlyMode}/>
+ </Modal.Body>
+ </Modal>
+
+ <FeatureGroupsConfirmationModal licenseModelId={licenseModelId}/>
+
+ </div>
+ );
+ }
+
+
+ renderFeatureGroupListItem(listItem, isReadOnlyMode) {
+ let {name, description, entitlementPoolsIds = [], licenseKeyGroupsIds = []} = listItem;
+ return (
+ <ListEditorItemView
+ key={listItem.id}
+ onDelete={() => this.deleteFeatureGroupItem(listItem)}
+ onSelect={() => this.editFeatureGroupItem(listItem)}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='text name'>{name}</div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='feature-groups-count-field'>
+ <div className='title'>{i18n('Entitlement')}</div>
+ <div className='title'>{i18n('Pools')}</div>
+ <div className='feature-groups-count-ep'>{entitlementPoolsIds.length || 0}</div>
+ </div>
+ <div className='feature-groups-count-field'>
+ <div className='title'>{i18n('License key')}</div>
+ <div className='title'>{i18n('Groups')}</div>
+ <div className='feature-groups-count-lk'>{licenseKeyGroupsIds.length || 0}</div>
+ </div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='text description'>{description}</div>
+ </div>
+
+ </ListEditorItemView>
+ );
+ }
+
+ filterList() {
+ let {featureGroupsList} = this.props;
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return featureGroupsList.filter(({name = '', description = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return featureGroupsList;
+ }
+ }
+
+ closeFeatureGroupsEditor() {
+ this.props.onCancelFeatureGroupsEditor();
+ }
+
+ editFeatureGroupItem(featureGroup) {
+ this.props.onEditFeatureGroupClick(featureGroup);
+ }
+
+ deleteFeatureGroupItem(featureGroup) {
+ this.props.onDeleteFeatureGroupClick(featureGroup);
+ }
+}
+
+export default FeatureGroupListEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js
new file mode 100644
index 0000000..3776c01
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js
@@ -0,0 +1,165 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes as featureGroupsActionConstants} from './FeatureGroupsConstants.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+import EntitlementPoolsActionHelper from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsActionHelper.js';
+import LicenseKeyGroupsActionHelper from 'sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js';
+
+function baseUrl(licenseModelId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/feature-groups`;
+}
+
+function fetchFeatureGroupsList(licenseModelId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`);
+}
+
+function deleteFeatureGroup(licenseModelId, featureGroupId) {
+ return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${featureGroupId}`);
+}
+
+function addFeatureGroup(licenseModelId, featureGroup) {
+ return RestAPIUtil.create(baseUrl(licenseModelId), {
+ name: featureGroup.name,
+ description: featureGroup.description,
+ partNumber: featureGroup.partNumber,
+ addedLicenseKeyGroupsIds: featureGroup.licenseKeyGroupsIds,
+ addedEntitlementPoolsIds: featureGroup.entitlementPoolsIds
+ });
+}
+
+function updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup) {
+
+ const {licenseKeyGroupsIds = []} = featureGroup;
+ const {licenseKeyGroupsIds: prevLicenseKeyGroupsIds = []} = previousFeatureGroup;
+ const {entitlementPoolsIds = []} = featureGroup;
+ const {entitlementPoolsIds: prevEntitlementPoolsIds = []} = previousFeatureGroup;
+ return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${featureGroup.id}`, {
+ name: featureGroup.name,
+ description: featureGroup.description,
+ partNumber: featureGroup.partNumber,
+ addedLicenseKeyGroupsIds: licenseKeyGroupsIds.filter(licenseKeyGroupId => prevLicenseKeyGroupsIds.indexOf(licenseKeyGroupId) === -1),
+ removedLicenseKeyGroupsIds: prevLicenseKeyGroupsIds.filter(prevLicenseKeyGroupId => licenseKeyGroupsIds.indexOf(prevLicenseKeyGroupId) === -1),
+ addedEntitlementPoolsIds: entitlementPoolsIds.filter(entitlementPoolId => prevEntitlementPoolsIds.indexOf(entitlementPoolId) === -1),
+ removedEntitlementPoolsIds: prevEntitlementPoolsIds.filter(prevEntitlementPoolId => entitlementPoolsIds.indexOf(prevEntitlementPoolId) === -1)
+
+ });
+}
+
+export default {
+ fetchFeatureGroupsList(dispatch, {licenseModelId, version}) {
+ return fetchFeatureGroupsList(licenseModelId, version).then(response => dispatch({
+ type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
+ response
+ }));
+ },
+
+ deleteFeatureGroup(dispatch, {licenseModelId, featureGroupId}) {
+ return deleteFeatureGroup(licenseModelId, featureGroupId).then(() => dispatch({
+ type: featureGroupsActionConstants.DELETE_FEATURE_GROUPS,
+ featureGroupId
+ }));
+ },
+
+ saveFeatureGroup(dispatch, {licenseModelId, previousFeatureGroup, featureGroup}) {
+ if (previousFeatureGroup) {
+ return updateFeatureGroup(licenseModelId, previousFeatureGroup, featureGroup).then(() => dispatch({
+ type: featureGroupsActionConstants.EDIT_FEATURE_GROUPS,
+ featureGroup
+ }));
+ }
+ else {
+ return addFeatureGroup(licenseModelId, featureGroup).then(response => dispatch({
+ type: featureGroupsActionConstants.ADD_FEATURE_GROUPS,
+ featureGroup: {
+ ...featureGroup,
+ id: response.value
+ }
+ }));
+ }
+ },
+
+ selectEntitlementPoolsEditorTab(dispatch, {tab}) {
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.SELECT_TAB,
+ tab
+ });
+ },
+
+ selectFeatureGroupsEditorEntitlementPoolsButtonTab(dispatch, {buttonTab}) {
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB,
+ buttonTab
+ });
+ },
+
+ selectFeatureGroupsEditorLicenseKeyGroupsButtonTab(dispatch, {buttonTab}) {
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB,
+ buttonTab
+ });
+ },
+
+ openFeatureGroupsEditor(dispatch, {featureGroup, licenseModelId}) {
+ EntitlementPoolsActionHelper.fetchEntitlementPoolsList(dispatch, {licenseModelId});
+ LicenseKeyGroupsActionHelper.fetchLicenseKeyGroupsList(dispatch, {licenseModelId});
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.OPEN,
+ featureGroup
+ });
+ },
+
+ closeFeatureGroupsEditor(dispatch) {
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.CLOSE
+ });
+ },
+
+ featureGroupsEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: featureGroupsActionConstants.featureGroupsEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM,
+ featureGroupToDelete: false
+ });
+ },
+
+ openDeleteFeatureGroupConfirm(dispatch, {featureGroup}) {
+ dispatch({
+ type: featureGroupsActionConstants.FEATURE_GROUPS_DELETE_CONFIRM,
+ featureGroupToDelete: featureGroup
+ });
+ },
+
+ switchVersion(dispatch, {licenseModelId, version}) {
+ LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => {
+ this.fetchFeatureGroupsList(dispatch, {licenseModelId, version});
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx
new file mode 100644
index 0000000..142ec3c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConfirmationModal.jsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import FeatureGroupsActionHelper from './FeatureGroupsActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function renderMsg(featureGroupToDelete) {
+ let name = featureGroupToDelete ? featureGroupToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{name}"?', {name});
+ let subMsg = featureGroupToDelete
+ && featureGroupToDelete.referencingLicenseAgreements
+ && featureGroupToDelete.referencingLicenseAgreements.length > 0 ?
+ i18n('This feature group is associated with one ore more license agreements') :
+ '';
+ return (
+ <div>
+ <p>{msg}</p>
+ <p>{subMsg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({licenseModel: {featureGroup}}, {licenseModelId}) => {
+ let {featureGroupToDelete} = featureGroup;
+ const show = featureGroupToDelete !== false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: renderMsg(featureGroupToDelete),
+ confirmationDetails: {featureGroupToDelete, licenseModelId}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({featureGroupToDelete, licenseModelId}) => {
+ FeatureGroupsActionHelper.deleteFeatureGroup(dispatch, {featureGroupId: featureGroupToDelete.id, licenseModelId});
+ FeatureGroupsActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ FeatureGroupsActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js
new file mode 100644
index 0000000..e02c545
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js
@@ -0,0 +1,60 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ FEATURE_GROUPS_LIST_LOADED: null,
+ ADD_FEATURE_GROUPS: null,
+ EDIT_FEATURE_GROUPS: null,
+ DELETE_FEATURE_GROUPS: null,
+ FEATURE_GROUPS_DELETE_CONFIRM: null,
+
+
+ ENTITLEMENT_POOLS_LIST_LOADED: null,
+
+ featureGroupsEditor: {
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null,
+ SELECT_TAB: null,
+ SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: null,
+ SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: null
+ }
+});
+
+export const state = keyMirror({
+ SELECTED_FEATURE_GROUP_TAB: {
+ GENERAL: 1,
+ ENTITLEMENT_POOLS: 2,
+ LICENCE_KEY_GROUPS: 3
+ },
+ SELECTED_ENTITLEMENT_POOLS_BUTTONTAB: {
+ ASSOCIATED_ENTITLEMENT_POOLS: 1,
+ AVAILABLE_ENTITLEMENT_POOLS: 2
+ },
+ SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB: {
+ ASSOCIATED_LICENSE_KEY_GROUPS: 1,
+ AVAILABLE_LICENSE_KEY_GROUPS: 2
+ },
+});
+
+
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js
new file mode 100644
index 0000000..576a535
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsEditorReducer.js
@@ -0,0 +1,62 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './FeatureGroupsConstants.js';
+
+
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.featureGroupsEditor.OPEN:
+ return {
+ ...state,
+ data: action.featureGroup || {}
+ };
+ case actionTypes.featureGroupsEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ case actionTypes.featureGroupsEditor.CLOSE:
+ return {};
+ case actionTypes.featureGroupsEditor.SELECT_TAB:
+ return {
+ ...state,
+ selectedTab: action.tab
+ };
+
+ case actionTypes.featureGroupsEditor.SELECTED_ENTITLEMENT_POOLS_BUTTONTAB:
+ return {
+ ...state,
+ selectedEntitlementPoolsButtonTab: action.buttonTab
+ };
+ case actionTypes.featureGroupsEditor.SELECTED_LICENSE_KEY_GROUPS_BUTTONTAB:
+ return {
+ ...state,
+ selectedLicenseKeyGroupsButtonTab: action.buttonTab
+ };
+ default:
+ return state;
+ }
+
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js
new file mode 100644
index 0000000..5cf3248
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsListReducer.js
@@ -0,0 +1,36 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './FeatureGroupsConstants.js';
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.FEATURE_GROUPS_LIST_LOADED:
+ return [...action.response.results];
+ case actionTypes.ADD_FEATURE_GROUPS:
+ return [...state, action.featureGroup];
+ case actionTypes.EDIT_FEATURE_GROUPS:
+ const indexForEdit = state.findIndex(featureGroup => featureGroup.id === action.featureGroup.id);
+ return [...state.slice(0, indexForEdit), action.featureGroup, ...state.slice(indexForEdit + 1)];
+ case actionTypes.DELETE_FEATURE_GROUPS:
+ return state.filter(featureGroup => featureGroup.id !== action.featureGroupId);
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js
new file mode 100644
index 0000000..9616b60
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js
@@ -0,0 +1,160 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants.js';
+import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+
+function baseUrl(licenseModelId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-agreements`;
+}
+
+function fetchLicenseAgreementList(licenseModelId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`);
+}
+
+function postLicenseAgreement(licenseModelId, licenseAgreement) {
+ return RestAPIUtil.create(baseUrl(licenseModelId), {
+ name: licenseAgreement.name,
+ description: licenseAgreement.description,
+ licenseTerm: licenseAgreement.licenseTerm,
+ requirementsAndConstrains: licenseAgreement.requirementsAndConstrains,
+ addedFeatureGroupsIds: licenseAgreement.featureGroupsIds
+ });
+}
+
+function putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement) {
+ const {featureGroupsIds = []} = licenseAgreement;
+ const {featureGroupsIds: prevFeatureGroupsIds = []} = previousLicenseAgreement;
+ return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseAgreement.id}`, {
+ name: licenseAgreement.name,
+ description: licenseAgreement.description,
+ licenseTerm: licenseAgreement.licenseTerm,
+ requirementsAndConstrains: licenseAgreement.requirementsAndConstrains,
+ addedFeatureGroupsIds: featureGroupsIds.filter(featureGroupId => prevFeatureGroupsIds.indexOf(featureGroupId) === -1),
+ removedFeatureGroupsIds: prevFeatureGroupsIds.filter(prevFeatureGroupsId => featureGroupsIds.indexOf(prevFeatureGroupsId) === -1)
+ });
+}
+
+function deleteLicenseAgreement(licenseModelId, licenseAgreementId) {
+ return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseAgreementId}`);
+}
+
+export default {
+
+ fetchLicenseAgreementList(dispatch, {licenseModelId, version}) {
+ return fetchLicenseAgreementList(licenseModelId, version).then(response => dispatch({
+ type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
+ response
+ }));
+ },
+
+ openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}) {
+ FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId});
+ dispatch({
+ type: licenseAgreementActionTypes.licenseAgreementEditor.OPEN,
+ licenseAgreement
+ });
+ },
+
+ licenseAgreementEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: licenseAgreementActionTypes.licenseAgreementEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ closeLicenseAgreementEditor(dispatch) {
+ dispatch({
+ type: licenseAgreementActionTypes.licenseAgreementEditor.CLOSE
+ });
+ },
+
+
+ saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement}) {
+ if (previousLicenseAgreement) {
+ return putLicenseAgreement(licenseModelId, previousLicenseAgreement, licenseAgreement).then(() => {
+ dispatch({
+ type: licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT,
+ licenseAgreement
+ });
+ });
+ }
+ else {
+ return postLicenseAgreement(licenseModelId, licenseAgreement).then(response => {
+ dispatch({
+ type: licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT,
+ licenseAgreement: {
+ ...licenseAgreement,
+ id: response.value
+ }
+ });
+ });
+ }
+ },
+
+ deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId}) {
+ return deleteLicenseAgreement(licenseModelId, licenseAgreementId).then(() => {
+ dispatch({
+ type: licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT,
+ licenseAgreementId
+ });
+ });
+ },
+
+ selectLicenseAgreementEditorTab(dispatch, {tab}) {
+ dispatch({
+ type: licenseAgreementActionTypes.licenseAgreementEditor.SELECT_TAB,
+ tab
+ });
+ },
+
+ selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}) {
+ dispatch({
+ type: licenseAgreementActionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB,
+ buttonTab
+ });
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM,
+ licenseAgreementToDelete: false
+ });
+ },
+
+ openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement} ) {
+ dispatch({
+ type: licenseAgreementActionTypes.LICENSE_AGREEMENT_DELETE_CONFIRM,
+ licenseAgreementToDelete: licenseAgreement
+ });
+ },
+
+ switchVersion(dispatch, {licenseModelId, version}) {
+ LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => {
+ this.fetchLicenseAgreementList(dispatch, {licenseModelId, version});
+ });
+ }
+};
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx
new file mode 100644
index 0000000..42f2407
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConfirmationModal.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function renderMsg(licenseAgreementToDelete) {
+ let name = licenseAgreementToDelete ? licenseAgreementToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{name}"?', {name});
+ return(
+ <div>
+ <p>{msg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({licenseModel: {licenseAgreement}}, {licenseModelId}) => {
+ let {licenseAgreementToDelete} = licenseAgreement;
+ const show = licenseAgreementToDelete !== false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: renderMsg(licenseAgreementToDelete),
+ confirmationDetails: {licenseAgreementToDelete, licenseModelId}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({licenseAgreementToDelete, licenseModelId}) => {
+
+ LicenseAgreementActionHelper.deleteLicenseAgreement(dispatch, {licenseModelId, licenseAgreementId: licenseAgreementToDelete.id});
+ LicenseAgreementActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ LicenseAgreementActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js
new file mode 100644
index 0000000..af5c454
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js
@@ -0,0 +1,66 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export const actionTypes = keyMirror({
+ LICENSE_AGREEMENT_LIST_LOADED: null,
+ ADD_LICENSE_AGREEMENT: null,
+ EDIT_LICENSE_AGREEMENT: null,
+ DELETE_LICENSE_AGREEMENT: null,
+ LICENSE_AGREEMENT_DELETE_CONFIRM: null,
+
+ licenseAgreementEditor: {
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null,
+ SELECT_TAB: null,
+ SELECT_FEATURE_GROUPS_BUTTONTAB: null,
+ }
+
+});
+
+export const enums = keyMirror({
+ SELECTED_LICENSE_AGREEMENT_TAB: {
+ GENERAL: 1,
+ FEATURE_GROUPS: 2
+ },
+
+ SELECTED_FEATURE_GROUPS_BUTTONTAB: {
+ ASSOCIATED_FEATURE_GROUPS: 1,
+ AVAILABLE_FEATURE_GROUPS: 2
+ }
+});
+
+export const defaultState = {
+ LICENSE_AGREEMENT_EDITOR_DATA: {
+ licenseTerm: {choice: '', other: ''}
+ }
+};
+
+export const optionsInputValues = {
+ LICENSE_MODEL_TYPE: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Fixed_Term', title: 'Fixed Term'},
+ {enum: 'Perpetual', title: 'Perpetual'},
+ {enum: 'Unlimited', title: 'Unlimited'}
+ ]
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js
new file mode 100644
index 0000000..6a3e4db
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditor.js
@@ -0,0 +1,60 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js';
+import LicenseAgreementEditorView from './LicenseAgreementEditorView.jsx';
+
+export const mapStateToProps = ({licenseModel: {licenseAgreement, featureGroup}}) => {
+
+
+ let {data, selectedTab, selectedFeatureGroupsButtonTab} = licenseAgreement.licenseAgreementEditor;
+
+ let previousData;
+ const licenseAgreementId = data ? data.id : null;
+ if(licenseAgreementId) {
+ previousData = licenseAgreement.licenseAgreementList.find(licenseAgreement => licenseAgreement.id === licenseAgreementId);
+ }
+
+ const {featureGroupsList = []} = featureGroup;
+
+ return {
+ data,
+ previousData,
+ selectedTab,
+ selectedFeatureGroupsButtonTab,
+ featureGroupsList
+ };
+};
+
+export const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onDataChanged: deltaData => LicenseAgreementActionHelper.licenseAgreementEditorDataChanged(dispatch, {deltaData}),
+ onTabSelect: tab => LicenseAgreementActionHelper.selectLicenseAgreementEditorTab(dispatch, {tab}),
+ onFeatureGroupsButtonTabSelect: buttonTab => LicenseAgreementActionHelper.selectLicenseAgreementEditorFeatureGroupsButtonTab(dispatch, {buttonTab}),
+ onCancel: () => LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch),
+ onSubmit: ({previousLicenseAgreement, licenseAgreement}) => {
+ LicenseAgreementActionHelper.closeLicenseAgreementEditor(dispatch);
+ LicenseAgreementActionHelper.saveLicenseAgreement(dispatch, {licenseModelId, previousLicenseAgreement, licenseAgreement});
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(LicenseAgreementEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js
new file mode 100644
index 0000000..74e2f6e
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js
@@ -0,0 +1,54 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes, defaultState} from './LicenseAgreementConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.licenseAgreementEditor.OPEN:
+ return {
+ ...state,
+ data: action.licenseAgreement ? { ...action.licenseAgreement } : defaultState.LICENSE_AGREEMENT_EDITOR_DATA
+ };
+ case actionTypes.licenseAgreementEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ case actionTypes.licenseAgreementEditor.CLOSE:
+ return {};
+ case actionTypes.licenseAgreementEditor.SELECT_TAB:
+ return {
+ ...state,
+ selectedTab: action.tab
+ };
+ case actionTypes.licenseAgreementEditor.SELECT_FEATURE_GROUPS_BUTTONTAB:
+ return {
+ ...state,
+ selectedFeatureGroupsButtonTab: action.buttonTab
+ };
+ default:
+ return state;
+ }
+
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx
new file mode 100644
index 0000000..b21f943
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx
@@ -0,0 +1,247 @@
+import React from 'react';
+import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js';
+import Button from 'react-bootstrap/lib/Button.js';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationTabs from 'nfvo-components/input/validation/ValidationTabs.jsx';
+import ValidationTab from 'nfvo-components/input/validation/ValidationTab.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorViewItem from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues} from './LicenseAgreementConstants.js';
+
+
+const LicenseAgreementPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ requirementsAndConstrains: React.PropTypes.string,
+ licenseTerm: React.PropTypes.object,
+ featureGroupsIds: React.PropTypes.arrayOf(React.PropTypes.string)
+});
+
+class LicenseAgreementEditorView extends React.Component {
+
+ static propTypes = {
+ data: LicenseAgreementPropType,
+ previousData: LicenseAgreementPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ onDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancel: React.PropTypes.func.isRequired,
+
+ selectedTab: React.PropTypes.number,
+ onTabSelect: React.PropTypes.func,
+
+ selectedFeatureGroupsButtonTab: React.PropTypes.number,
+ onFeatureGroupsButtonTabSelect: React.PropTypes.func,
+ featureGroupsList: DualListboxView.propTypes.availableList
+ };
+
+ static defaultProps = {
+ selectedTab: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL,
+ selectedFeatureGroupsButtonTab: LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS,
+ data: {}
+ };
+
+ state = {
+ localFeatureGroupsListFilter: ''
+ };
+
+ render() {
+ let {selectedTab, onTabSelect, isReadOnlyMode} = this.props;
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => this.props.onCancel() }
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ className='license-agreement-form'>
+ <ValidationTabs activeKey={onTabSelect ? selectedTab : undefined} onSelect={onTabSelect}>
+ {this.renderGeneralTab()}
+ {this.renderFeatureGroupsTab()}
+ </ValidationTabs>
+ </ValidationForm>
+ );
+ }
+
+ submit() {
+ const {data: licenseAgreement, previousData: previousLicenseAgreement} = this.props;
+ this.props.onSubmit({licenseAgreement, previousLicenseAgreement});
+ }
+
+ renderGeneralTab() {
+ let {data = {}, onDataChanged} = this.props;
+ let {name, description, requirementsAndConstrains, licenseTerm} = data;
+ return (
+ <ValidationTab
+ eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL}
+ title={i18n('General')}>
+ <div className='license-agreement-form-row'>
+ <div className='license-agreement-form-col'>
+ <ValidationInput
+ onChange={name => onDataChanged({name})}
+ label={i18n('Name')}
+ value={name}
+ name='license-agreement-name'
+ validations={{maxLength: 25, required: true}}
+ type='text'/>
+ <ValidationInput
+ onChange={requirementsAndConstrains => onDataChanged({requirementsAndConstrains})}
+ label={i18n('Requirements and Constraints')}
+ value={requirementsAndConstrains}
+ name='license-agreement-requirements-and-constraints'
+ validations={{maxLength: 1000}}
+ type='textarea'/>
+ </div>
+ <ValidationInput
+ onChange={description => onDataChanged({description})}
+ label={i18n('Description')}
+ value={description}
+ name='license-agreement-description'
+ validations={{maxLength: 1000, required: true}}
+ type='textarea'/>
+ </div>
+ <div className='license-agreement-form-row'>
+ <ValidationInput
+ onEnumChange={licenseTerm => onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}})}
+ selectedEnum={licenseTerm && licenseTerm.choice}
+ validations={{required: true}}
+ type='select'
+ label={i18n('License Term')}
+ values={LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE}/>
+ </div>
+ </ValidationTab>
+ );
+ }
+
+ renderFeatureGroupsTab() {
+ let {onFeatureGroupsButtonTabSelect, selectedFeatureGroupsButtonTab, featureGroupsList} = this.props;
+ if (featureGroupsList.length > 0) {
+ return (
+ <ValidationTab
+ eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS}
+ title={i18n('Feature Groups')}>
+ <ButtonGroup>
+ {
+ this.renderFeatureGroupsButtonTab(
+ LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS,
+ selectedFeatureGroupsButtonTab,
+ i18n('Associated Feature Groups'),
+ onFeatureGroupsButtonTabSelect
+ )
+ }
+ {
+ this.renderFeatureGroupsButtonTab(
+ LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS,
+ selectedFeatureGroupsButtonTab,
+ i18n('Available Feature Groups'),
+ onFeatureGroupsButtonTabSelect
+ )
+ }
+ </ButtonGroup>
+ {this.renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab)}
+ </ValidationTab>
+ );
+ } else {
+ return (
+ <ValidationTab
+ eventKey={LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.FEATURE_GROUPS}
+ title={i18n('Feature Groups')}>
+ <p>{i18n('There is no available feature groups')}</p>
+ </ValidationTab>
+ );
+ }
+ }
+
+ renderFeatureGroupsButtonTabContent(selectedFeatureGroupsButtonTab) {
+ const {featureGroupsList = [], data: {featureGroupsIds = []}} = this.props;
+ const {localFeatureGroupsListFilter} = this.state;
+ let selectedFeatureGroups = featureGroupsIds.map(featureGroupId => featureGroupsList.find(featureGroup => featureGroup.id === featureGroupId));
+
+ const dualBoxFilterTitle = {
+ left: i18n('Available Feature Groups'),
+ right: i18n('Selected Feature Groups')
+ };
+
+ switch (selectedFeatureGroupsButtonTab) {
+ case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.ASSOCIATED_FEATURE_GROUPS:
+ if (!selectedFeatureGroups.length) {
+ return (
+ <div className='no-items-msg'>
+ {i18n('There are currently no feature groups associated with this license agreement. Click "Available Feature Groups" to associate.')}
+ </div>
+ );
+ }
+ if (featureGroupsList.length) {
+ return (
+ <ListEditorView
+ className='thinner-list'
+ filterValue={localFeatureGroupsListFilter}
+ onFilter={localFeatureGroupsListFilter => this.setState({localFeatureGroupsListFilter})}>
+ {this.filterAssociatedFeatureGroupsList(selectedFeatureGroups).map(featureGroup => this.renderAssociatedFeatureGroupListItem(featureGroup))}
+ </ListEditorView>
+ );
+ }
+ return;
+ case LicenseAgreementEnums.SELECTED_FEATURE_GROUPS_BUTTONTAB.AVAILABLE_FEATURE_GROUPS:
+ return (
+ <DualListboxView
+ filterTitle={dualBoxFilterTitle}
+ selectedValuesList={this.props.data.featureGroupsIds}
+ availableList={this.props.featureGroupsList}
+ onChange={ selectedValuesList => this.props.onDataChanged( { featureGroupsIds: selectedValuesList } )}/>
+ );
+ }
+ }
+
+ renderFeatureGroupsButtonTab(buttonTab, selectedButtonTab, title, onClick) {
+ const isSelected = buttonTab === selectedButtonTab;
+ return (
+ <Button
+ className='button-tab'
+ active={isSelected}
+ onClick={() => !isSelected && onClick(buttonTab)}>
+ { title }
+ </Button>
+ );
+ }
+
+ renderAssociatedFeatureGroupListItem({id, name, entitlementPoolsIds = [], licenseKeyGroupsIds = []}) {
+ const {onDataChanged, data: {featureGroupsIds}, isReadOnlyMode} = this.props;
+ return (
+ <ListEditorViewItem
+ key={id}
+ onDelete={() => onDataChanged({featureGroupsIds: featureGroupsIds.filter(featureGroupId => featureGroupId !== id)})}
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='name'>{name}</div>
+ <div className='inner-objects-count'>{
+ i18n(
+ 'Entitlement Pools({entitlementPoolsCounter}), License Key Groups({licenseKeyGroupsCount})',
+ {
+ entitlementPoolsCounter: entitlementPoolsIds.length,
+ licenseKeyGroupsCount: licenseKeyGroupsIds.length
+ }
+ )
+ }</div>
+ </ListEditorViewItem>
+ );
+ }
+
+ filterAssociatedFeatureGroupsList(featureGroupsList) {
+ let {localFeatureGroupsListFilter} = this.state;
+ if (localFeatureGroupsListFilter) {
+ const filter = new RegExp(escape(localFeatureGroupsListFilter), 'i');
+ return featureGroupsList.filter(({name}) => name.match(filter));
+ }
+ else {
+ return featureGroupsList;
+ }
+ }
+}
+
+export default LicenseAgreementEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js
new file mode 100644
index 0000000..ca18bfa
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditor.js
@@ -0,0 +1,59 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+import LicenseAgreementActionHelper from './LicenseAgreementActionHelper.js';
+import LicenseAgreementListEditorView from './LicenseAgreementListEditorView.jsx';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+
+const mapStateToProps = ({licenseModel: {licenseAgreement, licenseModelEditor}}) => {
+ let {licenseAgreementList} = licenseAgreement;
+ let {data} = licenseAgreement.licenseAgreementEditor;
+ let {vendorName} = licenseModelEditor.data;
+
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data);
+
+ return {
+ vendorName,
+ licenseAgreementList,
+ isReadOnlyMode,
+ isDisplayModal: Boolean(data),
+ isModalInEditMode: Boolean(data && data.id)
+ };
+};
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onAddLicenseAgreementClick: () => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId}),
+ onEditLicenseAgreementClick: licenseAgreement => LicenseAgreementActionHelper.openLicenseAgreementEditor(dispatch, {licenseModelId, licenseAgreement}),
+ onDeleteLicenseAgreement: licenseAgreement => LicenseAgreementActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseAgreement}),
+ onCallVCAction: action => {
+ LicenseModelActionHelper.performVCAction(dispatch, {licenseModelId, action}).then(() => {
+ LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId});
+ });
+ },
+ switchLicenseModelVersion: version => LicenseAgreementActionHelper.switchVersion(dispatch, {licenseModelId, version}),
+ onClose: () => OnboardingActionHelper.navigateToOnboardingCatalog(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(LicenseAgreementListEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx
new file mode 100644
index 0000000..4d7e704
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListEditorView.jsx
@@ -0,0 +1,126 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+import LicenseAgreementEditor from './LicenseAgreementEditor.js';
+import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+import {optionsInputValues} from './LicenseAgreementConstants';
+import LicenseAgreementConfirmationModal from './LicenseAgreementConfirmationModal.jsx';
+
+
+class LicenseAgreementListEditorView extends React.Component {
+ static propTypes = {
+ vendorName: React.PropTypes.string,
+ licenseModelId: React.PropTypes.string.isRequired,
+ licenseAgreementList: React.PropTypes.array,
+ isReadOnlyMode: React.PropTypes.bool.isRequired,
+ isDisplayModal: React.PropTypes.bool,
+ isModalInEditMode: React.PropTypes.bool,
+ onAddLicenseAgreementClick: React.PropTypes.func,
+ onEditLicenseAgreementClick: React.PropTypes.func,
+ onDeleteLicenseAgreement: React.PropTypes.func,
+ onCallVCAction: React.PropTypes.func
+ };
+
+ static defaultProps = {
+ licenseAgreementList: []
+ };
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ const {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props;
+ const {onAddLicenseAgreementClick} = this.props;
+ const {localFilter} = this.state;
+
+ return (
+ <div className='license-agreement-list-editor'>
+ <ListEditorView
+ title={i18n('License Agreements for {vendorName} License Model', {vendorName})}
+ plusButtonTitle={i18n('Add License Agreement')}
+ onAdd={onAddLicenseAgreementClick}
+ filterValue={localFilter}
+ onFilter={filter => this.setState({localFilter: filter})}
+ isReadOnlyMode={isReadOnlyMode}>
+ {this.filterList().map(licenseAgreement => this.renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode))}
+ </ListEditorView>
+ <Modal show={isDisplayModal} bsSize='large' animation={true} className='license-agreement-modal'>
+ <Modal.Header>
+ <Modal.Title>{`${isModalInEditMode ? i18n('Edit License Agreement') : i18n('Create New License Agreement')}`}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {
+ isDisplayModal && (
+ <LicenseAgreementEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode} />
+ )
+ }
+ </Modal.Body>
+ </Modal>
+ <LicenseAgreementConfirmationModal licenseModelId={licenseModelId}/>
+
+ </div>
+ );
+ }
+
+ filterList() {
+ let {licenseAgreementList} = this.props;
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return licenseAgreementList.filter(({name = '', description = '', licenseTerm = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter) || escape(this.extractValue(licenseTerm)).match(filter);
+ });
+ }
+ else {
+ return licenseAgreementList;
+ }
+ }
+
+ renderLicenseAgreementListItem(licenseAgreement, isReadOnlyMode) {
+ let {id, name, description, licenseTerm, featureGroupsIds = []} = licenseAgreement;
+ let {onEditLicenseAgreementClick, onDeleteLicenseAgreement} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ onSelect={() => onEditLicenseAgreementClick(licenseAgreement)}
+ onDelete={() => onDeleteLicenseAgreement(licenseAgreement)}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='text name'>{name}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='list-editor-item-view-field-tight'>
+ <div className='title'>{i18n('Type')}</div>
+ <div className='text type'>{this.extractValue(licenseTerm)}</div>
+ </div>
+ <div className='list-editor-item-view-field-tight'>
+ <div className='title'>{i18n('Feature')}</div>
+ <div className='title'>{i18n('Groups')}</div>
+ <div className='feature-groups-count'>{featureGroupsIds.length}</div>
+ </div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='text description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ extractValue(item) {
+ if (item === undefined) {
+ return '';
+ } //TODO fix it later
+
+ return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : '';
+ }
+}
+
+export default LicenseAgreementListEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js
new file mode 100644
index 0000000..5b5fa00
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementListReducer.js
@@ -0,0 +1,37 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes as licenseAgreementActionTypes} from './LicenseAgreementConstants';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED:
+ return [...action.response.results];
+ case licenseAgreementActionTypes.ADD_LICENSE_AGREEMENT:
+ return [...state, action.licenseAgreement];
+ case licenseAgreementActionTypes.EDIT_LICENSE_AGREEMENT:
+ const indexForEdit = state.findIndex(licenseAgreement => licenseAgreement.id === action.licenseAgreement.id);
+ return [...state.slice(0, indexForEdit), action.licenseAgreement, ...state.slice(indexForEdit + 1)];
+ case licenseAgreementActionTypes.DELETE_LICENSE_AGREEMENT:
+ return state.filter(licenseAgreement => licenseAgreement.id !== action.licenseAgreementId);
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js
new file mode 100644
index 0000000..50ac2c8
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsActionHelper.js
@@ -0,0 +1,139 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes as licenseKeyGroupsConstants} from './LicenseKeyGroupsConstants.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+
+function baseUrl(licenseModelId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-license-models/${licenseModelId}/license-key-groups`;
+}
+
+function fetchLicenseKeyGroupsList(licenseModelId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(licenseModelId)}${versionQuery}`);
+}
+
+function deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId) {
+ return RestAPIUtil.destroy(`${baseUrl(licenseModelId)}/${licenseKeyGroupId}`);
+}
+
+function postLicenseKeyGroup(licenseModelId, licenseKeyGroup) {
+ return RestAPIUtil.create(baseUrl(licenseModelId), {
+ name: licenseKeyGroup.name,
+ description: licenseKeyGroup.description,
+ operationalScope: licenseKeyGroup.operationalScope,
+ type: licenseKeyGroup.type
+ });
+}
+
+function putLicenseKeyGroup(licenseModelId, licenseKeyGroup) {
+ return RestAPIUtil.save(`${baseUrl(licenseModelId)}/${licenseKeyGroup.id}`, {
+ name: licenseKeyGroup.name,
+ description: licenseKeyGroup.description,
+ operationalScope: licenseKeyGroup.operationalScope,
+ type: licenseKeyGroup.type
+ });
+}
+
+
+export default {
+ fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version}) {
+ return fetchLicenseKeyGroupsList(licenseModelId, version).then(response => dispatch({
+ type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_LIST_LOADED,
+ response
+ }));
+ },
+
+ openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup} = {}) {
+ dispatch({
+ type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.OPEN,
+ licenseKeyGroup
+ });
+ },
+
+ closeLicenseKeyGroupEditor(dispatch){
+ dispatch({
+ type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.CLOSE
+ });
+ },
+
+ saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup}) {
+ if (previousLicenseKeyGroup) {
+ return putLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(() => {
+ dispatch({
+ type: licenseKeyGroupsConstants.EDIT_LICENSE_KEY_GROUP,
+ licenseKeyGroup
+ });
+ });
+ }
+ else {
+ return postLicenseKeyGroup(licenseModelId, licenseKeyGroup).then(response => {
+ dispatch({
+ type: licenseKeyGroupsConstants.ADD_LICENSE_KEY_GROUP,
+ licenseKeyGroup: {
+ ...licenseKeyGroup,
+ id: response.value
+ }
+ });
+ });
+ }
+
+
+ },
+
+ deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId}){
+ return deleteLicenseKeyGroup(licenseModelId, licenseKeyGroupId).then(()=> {
+ dispatch({
+ type: licenseKeyGroupsConstants.DELETE_LICENSE_KEY_GROUP,
+ licenseKeyGroupId
+ });
+ });
+ },
+
+ licenseKeyGroupEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: licenseKeyGroupsConstants.licenseKeyGroupsEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM,
+ licenseKeyGroupToDelete: false
+ });
+ },
+
+ openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup}) {
+ dispatch({
+ type: licenseKeyGroupsConstants.LICENSE_KEY_GROUPS_DELETE_CONFIRM,
+ licenseKeyGroupToDelete: licenseKeyGroup
+ });
+ },
+
+ switchVersion(dispatch, {licenseModelId, version}) {
+ LicenseModelActionHelper.fetchLicenseModelById(dispatch, {licenseModelId, version}).then(() => {
+ this.fetchLicenseKeyGroupsList(dispatch, {licenseModelId, version});
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx
new file mode 100644
index 0000000..2413db5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConfirmationModal.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function renderMsg(licenseKeyGroupToDelete) {
+ let name = licenseKeyGroupToDelete ? licenseKeyGroupToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{name}"?', {name});
+ let subMsg = licenseKeyGroupToDelete
+ && licenseKeyGroupToDelete.referencingFeatureGroups
+ && licenseKeyGroupToDelete.referencingFeatureGroups.length > 0 ?
+ i18n('This license key group is associated with one or more feature groups') :
+ '';
+ return(
+ <div>
+ <p>{msg}</p>
+ <p>{subMsg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({licenseModel: {licenseKeyGroup}}, {licenseModelId}) => {
+ let {licenseKeyGroupToDelete} = licenseKeyGroup;
+ const show = licenseKeyGroupToDelete !== false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: renderMsg(licenseKeyGroupToDelete),
+ confirmationDetails: {licenseKeyGroupToDelete, licenseModelId}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({licenseKeyGroupToDelete, licenseModelId}) => {
+
+ LicenseKeyGroupsActionHelper.deleteLicenseKeyGroup(dispatch, {licenseModelId, licenseKeyGroupId:licenseKeyGroupToDelete.id});
+ LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ LicenseKeyGroupsActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js
new file mode 100644
index 0000000..d32bc52
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsConstants.js
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export const actionTypes = keyMirror({
+
+ LICENSE_KEY_GROUPS_LIST_LOADED: null,
+ DELETE_LICENSE_KEY_GROUP: null,
+ EDIT_LICENSE_KEY_GROUP: null,
+ ADD_LICENSE_KEY_GROUP: null,
+ LICENSE_KEY_GROUPS_DELETE_CONFIRM: null,
+ licenseKeyGroupsEditor: {
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null,
+ }
+});
+
+export const defaultState = {
+ licenseKeyGroupsEditor: {
+ type: '',
+ operationalScope: {choices: [], other: ''}
+ }
+};
+
+export const optionsInputValues = {
+ OPERATIONAL_SCOPE: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Network_Wide', title: 'Network Wide'},
+ {enum: 'Availability_Zone', title: 'Availability Zone'},
+ {enum: 'Data_Center', title: 'Data Center'},
+ {enum: 'Tenant', title: 'Tenant'},
+ {enum: 'VM', title: 'VM'},
+ {enum: 'CPU', title: 'CPU'},
+ {enum: 'Core', title: 'Core'}
+ ],
+ TYPE: [
+ {enum: '', title: i18n('please select…')},
+ {enum: 'Universal', title: 'Universal'},
+ {enum: 'Unique', title: 'Unique'},
+ {enum: 'One_Time', title: 'One Time'}
+ ]
+};
+
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js
new file mode 100644
index 0000000..3940ec5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditor.js
@@ -0,0 +1,53 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js';
+import LicenseKeyGroupsEditorView from './LicenseKeyGroupsEditorView.jsx';
+
+const mapStateToProps = ({licenseModel: {licenseKeyGroup}}) => {
+
+
+ let {data} = licenseKeyGroup.licenseKeyGroupsEditor;
+
+ let previousData;
+ const licenseKeyGroupId = data ? data.id : null;
+ if(licenseKeyGroupId) {
+ previousData = licenseKeyGroup.licenseKeyGroupsList.find(licenseKeyGroup => licenseKeyGroup.id === licenseKeyGroupId);
+ }
+
+ return {
+ data,
+ previousData
+ };
+};
+
+const mapActionsToProps = (dispatch, {licenseModelId}) => {
+ return {
+ onDataChanged: deltaData => LicenseKeyGroupsActionHelper.licenseKeyGroupEditorDataChanged(dispatch, {deltaData}),
+ onCancel: () => LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch),
+ onSubmit: ({previousLicenseKeyGroup, licenseKeyGroup}) => {
+ LicenseKeyGroupsActionHelper.closeLicenseKeyGroupEditor(dispatch);
+ LicenseKeyGroupsActionHelper.saveLicenseKeyGroup(dispatch, {licenseModelId, previousLicenseKeyGroup, licenseKeyGroup});
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(LicenseKeyGroupsEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js
new file mode 100644
index 0000000..a744982
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorReducer.js
@@ -0,0 +1,43 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes, defaultState} from './LicenseKeyGroupsConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.licenseKeyGroupsEditor.OPEN:
+ return {
+ ...state,
+ data: action.licenseKeyGroup ? {...action.licenseKeyGroup} : defaultState.licenseKeyGroupsEditor
+ };
+ case actionTypes.licenseKeyGroupsEditor.CLOSE:
+ return {};
+ case actionTypes.licenseKeyGroupsEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx
new file mode 100644
index 0000000..102e713
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsEditorView.jsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import {optionsInputValues as licenseKeyGroupOptionsInputValues} from './LicenseKeyGroupsConstants.js';
+import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+
+const LicenseKeyGroupPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ operationalScope: React.PropTypes.shape({
+ choices: React.PropTypes.array,
+ other: React.PropTypes.string
+ }),
+ type: React.PropTypes.string
+});
+
+class LicenseKeyGroupsEditorView extends React.Component {
+ static propTypes = {
+ data: LicenseKeyGroupPropType,
+ previousData: LicenseKeyGroupPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ onDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancel: React.PropTypes.func.isRequired
+ };
+
+ static defaultProps = {
+ data: {}
+ };
+
+ render() {
+ let {data = {}, onDataChanged, isReadOnlyMode} = this.props;
+ let {name, description, operationalScope, type} = data;
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => this.props.onCancel() }
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ className='license-key-groups-form'>
+ <div className='license-key-groups-form-row'>
+ <ValidationInput
+ onChange={name => onDataChanged({name})}
+ ref='name'
+ label={i18n('Name')}
+ value={name}
+ validations={{maxLength: 120, required: true}}
+ type='text'/>
+ <ValidationInput
+ isMultiSelect={true}
+ isRequired={true}
+ onEnumChange={operationalScope => onDataChanged({operationalScope:{choices: operationalScope, other: ''}})}
+ onOtherChange={operationalScope => onDataChanged({operationalScope:{choices: [optionInputOther.OTHER], other: operationalScope}})}
+ label={i18n('Operational Scope')}
+ validations={{required: true}}
+ multiSelectedEnum={operationalScope && operationalScope.choices}
+ otherValue={operationalScope && operationalScope.other}
+ values={licenseKeyGroupOptionsInputValues.OPERATIONAL_SCOPE}/>
+ </div>
+ <div className='license-key-groups-form-row'>
+ <ValidationInput
+ onChange={description => onDataChanged({description})}
+ ref='description'
+ label={i18n('Description')}
+ value={description}
+ validations={{maxLength: 1000, required: true}}
+ type='textarea'/>
+ <ValidationInput
+ isRequired={true}
+ onEnumChange={type => onDataChanged({type})}
+ selectedEnum={type}
+ label={i18n('Type')}
+ type='select'
+ validations={{required: true}}
+ values={licenseKeyGroupOptionsInputValues.TYPE}/>
+ </div>
+ </ValidationForm>
+ );
+ }
+
+ submit() {
+ const {data: licenseKeyGroup, previousData: previousLicenseKeyGroup} = this.props;
+ this.props.onSubmit({licenseKeyGroup, previousLicenseKeyGroup});
+ }
+}
+
+export default LicenseKeyGroupsEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js
new file mode 100644
index 0000000..e1b610f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditor.js
@@ -0,0 +1,50 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import LicenseKeyGroupsActionHelper from './LicenseKeyGroupsActionHelper.js';
+import LicenseKeyGroupsListEditorView from './LicenseKeyGroupsListEditorView.jsx';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+const mapStateToProps = ({licenseModel: {licenseKeyGroup, licenseModelEditor}}) => {
+ let {licenseKeyGroupsList} = licenseKeyGroup;
+ let {data} = licenseKeyGroup.licenseKeyGroupsEditor;
+ let {vendorName} = licenseModelEditor.data;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(licenseModelEditor.data);
+
+ return {
+ vendorName,
+ licenseKeyGroupsList,
+ isReadOnlyMode,
+ isDisplayModal: Boolean(data),
+ isModalInEditMode: Boolean(data && data.id)
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onAddLicenseKeyGroupClick: () => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch),
+ onEditLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openLicenseKeyGroupsEditor(dispatch, {licenseKeyGroup}),
+ onDeleteLicenseKeyGroupClick: licenseKeyGroup => LicenseKeyGroupsActionHelper.openDeleteLicenseAgreementConfirm(dispatch, {licenseKeyGroup})
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(LicenseKeyGroupsListEditorView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx
new file mode 100644
index 0000000..1ed1d20
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListEditorView.jsx
@@ -0,0 +1,138 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import LicenseKeyGroupsEditor from './LicenseKeyGroupsEditor.js';
+import InputOptions, {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+import {optionsInputValues} from './LicenseKeyGroupsConstants';
+import LicenseKeyGroupsConfirmationModal from './LicenseKeyGroupsConfirmationModal.jsx';
+
+
+class LicenseKeyGroupsListEditorView extends React.Component {
+ static propTypes = {
+ vendorName: React.PropTypes.string,
+ licenseModelId: React.PropTypes.string.isRequired,
+ licenseKeyGroupsList: React.PropTypes.array,
+ isReadOnlyMode: React.PropTypes.bool.isRequired,
+ isDisplayModal: React.PropTypes.bool,
+ isModalInEditMode: React.PropTypes.bool,
+ onAddLicenseKeyGroupClick: React.PropTypes.func,
+ onEditLicenseKeyGroupClick: React.PropTypes.func,
+ onDeleteLicenseKeyGroupClick: React.PropTypes.func
+ };
+
+ static defaultProps = {
+ licenseKeyGroupsList: []
+ };
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ let {licenseModelId, vendorName, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props;
+ let {onAddLicenseKeyGroupClick} = this.props;
+ const {localFilter} = this.state;
+
+ return (
+ <div className='license-key-groups-list-editor'>
+ <ListEditorView
+ title={i18n('License Key Groups for {vendorName} License Model', {vendorName})}
+ plusButtonTitle={i18n('Add License Key Group')}
+ onAdd={onAddLicenseKeyGroupClick}
+ filterValue={localFilter}
+ onFilter={filter => this.setState({localFilter: filter})}
+ isReadOnlyMode={isReadOnlyMode}>
+ {this.filterList().map(licenseKeyGroup => (this.renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode)))}
+ </ListEditorView>
+ <Modal show={isDisplayModal} bsSize='large' animation={true} className='license-key-groups-modal'>
+ <Modal.Header>
+ <Modal.Title>{`${isModalInEditMode ? i18n('Edit License Key Group') : i18n('Create New License Key Group')}`}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {
+ isDisplayModal && (
+ <LicenseKeyGroupsEditor licenseModelId={licenseModelId} isReadOnlyMode={isReadOnlyMode}/>
+ )
+ }
+ </Modal.Body>
+ </Modal>
+ <LicenseKeyGroupsConfirmationModal licenseModelId={licenseModelId}/>
+
+ </div>
+ );
+ }
+
+ filterList() {
+ let {licenseKeyGroupsList} = this.props;
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return licenseKeyGroupsList.filter(({name = '', description = '', operationalScope = '', type = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter) || escape(this.extractValue(operationalScope)).match(filter) || escape(type).match(filter);
+ });
+ }
+ else {
+ return licenseKeyGroupsList;
+ }
+ }
+
+ renderLicenseKeyGroupListItem(licenseKeyGroup, isReadOnlyMode) {
+ let {id, name, description, operationalScope, type} = licenseKeyGroup;
+ let {onEditLicenseKeyGroupClick, onDeleteLicenseKeyGroupClick} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ onSelect={() => onEditLicenseKeyGroupClick(licenseKeyGroup)}
+ onDelete={() => onDeleteLicenseKeyGroupClick(licenseKeyGroup)}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='text name'>{name}</div>
+ </div>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Operational Scope')}</div>
+ <div className='text operational-scope'>{operationalScope && this.getOperationalScopes(operationalScope)}</div>
+
+ <div className='title'>{i18n('Type')}</div>
+ <div className='text type'>{InputOptions.getTitleByName(optionsInputValues, type)}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='text description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ getOperationalScopes(operationalScope) {
+ if(operationalScope.choices.toString() === i18n(optionInputOther.OTHER) && operationalScope.other !== '') {
+ return operationalScope.other;
+ }
+ else {
+ let allOpScopes = '';
+ for (let opScope of operationalScope.choices) {
+ allOpScopes += allOpScopes === '' ? InputOptions.getTitleByName(optionsInputValues, opScope) : `, ${InputOptions.getTitleByName(optionsInputValues, opScope)}`;
+ }
+ return allOpScopes;
+ }
+ }
+
+ extractValue(item) {
+ if (item === undefined) {
+ return '';
+ } //TODO fix it later
+
+ return item ? item.choice === optionInputOther.OTHER ? item.other : InputOptions.getTitleByName(optionsInputValues, item.choice) : '';
+ }
+}
+
+export default LicenseKeyGroupsListEditorView;
+
+
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js
new file mode 100644
index 0000000..54ce4e3
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseKeyGroups/LicenseKeyGroupsListReducer.js
@@ -0,0 +1,37 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './LicenseKeyGroupsConstants.js';
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.LICENSE_KEY_GROUPS_LIST_LOADED:
+ return [...action.response.results];
+ case actionTypes.DELETE_LICENSE_KEY_GROUP:
+ return state.filter(licenseKeyGroup => licenseKeyGroup.id !== action.licenseKeyGroupId);
+ case actionTypes.ADD_LICENSE_KEY_GROUP:
+ return [...state, action.licenseKeyGroup];
+ case actionTypes.EDIT_LICENSE_KEY_GROUP:
+ const indexForEdit = state.findIndex(licenseKeyGroup => licenseKeyGroup.id === action.licenseKeyGroup.id);
+ return [...state.slice(0, indexForEdit), action.licenseKeyGroup, ...state.slice(indexForEdit + 1)];
+ default:
+ return state;
+ }
+};
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js
new file mode 100644
index 0000000..2dbef6b
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProduct.js
@@ -0,0 +1,321 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {statusEnum as versionStatusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import TabulatedEditor from 'src/nfvo-components/editor/TabulatedEditor.jsx';
+
+import {enums} from 'sdc-app/onboarding/OnboardingConstants.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+
+import {navigationItems} from './SoftwareProductConstants.js';
+import SoftwareProductActionHelper from './SoftwareProductActionHelper.js';
+import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js';
+
+const buildComponentNavigationBarGroups = ({componentId, meta}) => {
+ const groups = ([
+ {
+ id: navigationItems.GENERAL + '|' + componentId,
+ name: i18n('General'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.COMPUTE + '|' + componentId,
+ name: i18n('Compute'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.LOAD_BALANCING + '|' + componentId,
+ name: i18n('High Availability & Load Balancing'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.NETWORKS + '|' + componentId,
+ name: i18n('Networks'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.STORAGE + '|' + componentId,
+ name: i18n('Storage'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.PROCESS_DETAILS + '|' + componentId,
+ name: i18n('Process Details'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.MONITORING + '|' + componentId,
+ name: i18n('Monitoring'),
+ disabled: false,
+ meta
+ }
+ ]);
+
+ return groups;
+};
+
+const buildNavigationBarProps = ({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds}) => {
+ const {softwareProductEditor: {data: currentSoftwareProduct = {}}} = softwareProduct;
+ const {id, name} = currentSoftwareProduct;
+ const groups = [{
+ id: id,
+ name: name,
+ items: [
+ {
+ id: navigationItems.VENDOR_SOFTWARE_PRODUCT,
+ name: i18n('Overview'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.GENERAL,
+ name: i18n('General'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.PROCESS_DETAILS,
+ name: i18n('Process Details'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.NETWORKS,
+ name: i18n('Networks'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.ATTACHMENTS,
+ name: i18n('Attachments'),
+ disabled: false,
+ meta
+ }, {
+ id: navigationItems.COMPONENTS,
+ name: i18n('Components'),
+ hidden: componentsList.length <= 0,
+ meta,
+ expanded: mapOfExpandedIds[navigationItems.COMPONENTS] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE,
+ items: [
+ ...componentsList.map(({id, displayName}) => ({
+ id: navigationItems.COMPONENTS + '|' + id,
+ name: displayName,
+ meta,
+ expanded: mapOfExpandedIds[navigationItems.COMPONENTS + '|' + id] === true && screen !== enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE,
+ items: buildComponentNavigationBarGroups({componentId: id, meta})
+ }))
+ ]
+ }
+ ]
+ }];
+ let activeItemId = ({
+ [enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE]: navigationItems.VENDOR_SOFTWARE_PRODUCT,
+ [enums.SCREEN.SOFTWARE_PRODUCT_DETAILS]: navigationItems.GENERAL,
+ [enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS]: navigationItems.ATTACHMENTS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES]: navigationItems.PROCESS_DETAILS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS]: navigationItems.NETWORKS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: navigationItems.COMPONENTS
+ })[screen];
+
+ if(componentId) {
+ activeItemId =
+ Object.keys(mapOfExpandedIds).length === 1 && mapOfExpandedIds[navigationItems.COMPONENTS] === true ?
+ navigationItems.COMPONENTS : ({
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL]: navigationItems.GENERAL,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE]: navigationItems.COMPUTE,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING]: navigationItems.LOAD_BALANCING,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK]: navigationItems.NETWORKS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE]: navigationItems.STORAGE,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES]: navigationItems.PROCESS_DETAILS,
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING]: navigationItems.MONITORING
+ })[screen] + '|' + componentId;
+ }
+
+ return {
+ activeItemId, groups
+ };
+};
+
+const buildVersionControllerProps = (softwareProduct) => {
+ const {softwareProductEditor} = softwareProduct;
+ const {data: currentSoftwareProduct = {}, isValidityData = true} = softwareProductEditor;
+
+ const {version, viewableVersions, status: currentStatus, lockingUser} = currentSoftwareProduct;
+ const {status, isCheckedOut} = (currentStatus === versionStatusEnum.CHECK_OUT_STATUS) ?
+ VersionControllerUtils.getCheckOutStatusKindByUserID(currentStatus, lockingUser) :
+ {status: currentStatus, isCheckedOut: false};
+
+ return {
+ status, isCheckedOut, version, viewableVersions,
+ isFormDataValid: isValidityData
+ };
+};
+
+const mapStateToProps = ({softwareProduct}, {currentScreen: {screen, props: {componentId}}}) => {
+ const {softwareProductEditor, softwareProductComponents, softwareProductQuestionnaire} = softwareProduct;
+ const {data: currentSoftwareProduct = {}, mapOfExpandedIds = []} = softwareProductEditor;
+ const {version} = currentSoftwareProduct;
+ const {componentsList = []} = softwareProductComponents;
+ const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+ const {qdata} = softwareProductQuestionnaire;
+ let currentComponentMeta = {};
+ if(componentId) {
+ const {componentEditor: {data: componentData = {} , qdata: componentQdata}} = softwareProductComponents;
+ currentComponentMeta = {componentData, componentQdata};
+ }
+ const meta = {softwareProduct: currentSoftwareProduct, qdata, version, isReadOnlyMode, currentComponentMeta};
+ return {
+ versionControllerProps: buildVersionControllerProps(softwareProduct),
+ navigationBarProps: buildNavigationBarProps({softwareProduct, meta, screen, componentId, componentsList, mapOfExpandedIds})
+ };
+};
+
+const autoSaveBeforeNavigate = ({dispatch, screen, softwareProductId, componentId, meta: {isReadOnlyMode, softwareProduct, qdata, currentComponentMeta: {componentData, componentQdata}}}) => {
+ let promise;
+ if (isReadOnlyMode) {
+ promise = Promise.resolve();
+ } else {
+ switch(screen) {
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ promise = SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId: componentId, componentData, qdata: componentQdata});
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ promise = SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata: componentQdata});
+ break;
+ default:
+ promise = Promise.resolve();
+ break;
+ }
+ }
+ return promise;
+};
+
+
+const onComponentNavigate = (dispatch, {id, softwareProductId, version, currentComponentId}) => {
+ const [nextScreen, nextComponentId] = id.split('|');
+ switch(nextScreen) {
+ case navigationItems.COMPONENTS:
+ if(nextComponentId === currentComponentId) {
+ OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId});
+ } else {
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version});
+ }
+ break;
+ case navigationItems.GENERAL:
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {softwareProductId, componentId: nextComponentId, version});
+ break;
+ case navigationItems.COMPUTE:
+ OnboardingActionHelper.navigateToComponentCompute(dispatch, {softwareProductId, componentId: nextComponentId});
+ break;
+ case navigationItems.LOAD_BALANCING:
+ OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {softwareProductId, componentId: nextComponentId});
+ break;
+ case navigationItems.NETWORKS:
+ OnboardingActionHelper.navigateToComponentNetwork(dispatch, {softwareProductId, componentId: nextComponentId, version});
+ break;
+ case navigationItems.STORAGE:
+ OnboardingActionHelper.navigateToComponentStorage(dispatch, {softwareProductId, componentId: nextComponentId});
+ break;
+ case navigationItems.PROCESS_DETAILS:
+ OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {softwareProductId, componentId: nextComponentId});
+ break;
+ case navigationItems.MONITORING:
+ OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {softwareProductId, componentId: nextComponentId});
+ break;
+ }
+};
+
+const mapActionsToProps = (dispatch, {currentScreen: {screen, props: {softwareProductId, componentId: currentComponentId}}}) => {
+
+ const props = {
+ onClose: ({version}) => {
+ if (screen === enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE) {
+ OnboardingActionHelper.navigateToOnboardingCatalog(dispatch);
+ } else {
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version});
+ }
+ },
+ onVersionSwitching: (version) => {
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version});
+ },
+ onToggle: (groups, itemIdToExpand) => groups.map(({items}) => SoftwareProductActionHelper.toggleNavigationItems(dispatch, {items, itemIdToExpand})),
+ onNavigate: ({id, meta}) => {
+ let preNavigate = autoSaveBeforeNavigate({dispatch, screen, meta, softwareProductId, componentId: currentComponentId});
+ preNavigate.then(() => {
+ switch(id) {
+ case navigationItems.VENDOR_SOFTWARE_PRODUCT:
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, version: meta.version});
+ break;
+ case navigationItems.GENERAL:
+ OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {softwareProductId});
+ break;
+ case navigationItems.PROCESS_DETAILS:
+ OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {softwareProductId, version: meta.version});
+ break;
+ case navigationItems.NETWORKS:
+ OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {softwareProductId, version: meta.version});
+ break;
+ case navigationItems.ATTACHMENTS:
+ OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId});
+ break;
+ case navigationItems.COMPONENTS:
+ OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {softwareProductId});
+ break;
+ default:
+ onComponentNavigate(dispatch, {id, softwareProductId, version: meta.version, screen, currentComponentId});
+ break;
+ }
+ });
+ }
+ };
+
+ switch (screen) {
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ props.onSave = () => {
+ return Promise.resolve();
+ };
+ break;
+ default:
+ props.onSave = ({softwareProduct, qdata}) => SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});
+ break;
+ }
+
+
+ props.onVersionControllerAction = (action) =>
+ SoftwareProductActionHelper.performVCAction(dispatch, {softwareProductId, action}).then(() => {
+ SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId});
+ });
+
+ return props;
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(TabulatedEditor);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
new file mode 100644
index 0000000..d9ed8af
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
@@ -0,0 +1,333 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
+import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
+import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
+
+import {actionTypes} from './SoftwareProductConstants.js';
+import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js';
+import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
+
+function baseUrl() {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/`;
+}
+function softwareProductCategoriesUrl() {
+ const restATTPrefix = Configuration.get('restATTPrefix');
+ return `${restATTPrefix}/v1/categories/resources/`;
+}
+
+function uploadFile(vspId, formData) {
+
+ return RestAPIUtil.create(`${baseUrl()}${vspId}/upload`, formData);
+
+}
+
+function putSoftwareProduct(softwareData) {
+ return RestAPIUtil.save(`${baseUrl()}${softwareData.id}`, {
+ name: softwareData.name,
+ description: softwareData.description,
+ category: softwareData.category,
+ subCategory: softwareData.subCategory,
+ vendorId: softwareData.vendorId,
+ vendorName: softwareData.vendorName,
+ licensingVersion: softwareData.licensingVersion,
+ icon: softwareData.icon,
+ licensingData: softwareData.licensingData
+ });
+}
+
+function putSoftwareProductQuestionnaire(vspId, qdata) {
+ return RestAPIUtil.save(`${baseUrl()}${vspId}/questionnaire`, qdata);
+}
+
+function putSoftwareProductAction(id, action) {
+ return RestAPIUtil.save(`${baseUrl()}${id}/actions`, {action: action});
+}
+
+function fetchSoftwareProductList() {
+ return RestAPIUtil.fetch(baseUrl());
+}
+
+function fetchSoftwareProduct(vspId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl()}${vspId}${versionQuery}`);
+}
+
+function fetchSoftwareProductQuestionnaire(vspId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl()}${vspId}/questionnaire${versionQuery}`);
+}
+
+function objToString(obj) {
+ let str = '';
+ if (obj instanceof Array) {
+ obj.forEach((item) => {
+ str += objToString(item) + '\n';
+ });
+ } else {
+ for (let p in obj) {
+ if (obj.hasOwnProperty(p)) {
+ str += obj[p] + '\n';
+ }
+ }
+ }
+ return str;
+}
+
+function parseUploadErrorMsg(error) {
+ let message = '';
+ for (let key in error) {
+ if (error.hasOwnProperty(key)) {
+ message += objToString(error[key]) + '\n';
+ }
+ }
+ return message;
+}
+
+function fetchSoftwareProductCategories(dispatch) {
+ let handleResponse = response => dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
+ softwareProductCategories: response
+ });
+ return RestAPIUtil.fetch(softwareProductCategoriesUrl())
+ .then(handleResponse)
+ .fail(() => handleResponse(null));
+}
+
+function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
+ LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion});
+ FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion});
+}
+
+function getExpandedItemsId(items, itemIdToToggle) {
+ for(let i = 0; i < items.length; i++) {
+ if(items[i].id === itemIdToToggle) {
+ if (items[i].expanded) {
+ return {};
+ } else {
+ return {[itemIdToToggle]: true};
+ }
+ }
+ else if(items[i].items && items[i].items.length > 0) {
+ let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle);
+ if (mapOfExpandedIds !== false) {
+ mapOfExpandedIds[items[i].id] = true;
+ return mapOfExpandedIds;
+ }
+ }
+ }
+ return false;
+}
+
+const SoftwareProductActionHelper = {
+
+ loadSoftwareProductAssociatedData(dispatch) {
+ fetchSoftwareProductCategories(dispatch);
+ LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
+ },
+
+ loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
+ SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
+ loadLicensingData(dispatch, {licenseModelId, licensingVersion});
+ },
+
+ fetchSoftwareProductList(dispatch) {
+ return fetchSoftwareProductList().then(response => dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
+ response
+ }));
+ },
+
+ uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle}) {
+ Promise.resolve()
+ .then(() => uploadFile(softwareProductId, formData))
+ .then(response => {
+ if (response.status !== 'Success') {
+ throw new Error(parseUploadErrorMsg(response.errors));
+ }
+ })
+ .then(() => {
+ SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId});
+ OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId});
+ SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId});
+ })
+ .catch(error => {
+ dispatch({
+ type: NotificationConstants.NOTIFY_ERROR,
+ data: {title: failedNotificationTitle, msg: error.message}
+ });
+ });
+ },
+
+ uploadConfirmation(dispatch, {softwareProductId, formData, failedNotificationTitle}) {
+ dispatch({
+ type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION,
+ uploadData: {
+ softwareProductId,
+ formData,
+ failedNotificationTitle
+ }
+ });
+ },
+ hideUploadConfirm (dispatch) {
+ dispatch({
+ type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
+ });
+ },
+ updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
+ return Promise.all([
+ SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
+ () => dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
+ payload: {softwareProduct}
+ })
+ ),
+ SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
+ softwareProductId: softwareProduct.id,
+ qdata
+ })
+ ]);
+ },
+
+ updateSoftwareProductData(dispatch, {softwareProduct}) {
+ return putSoftwareProduct(softwareProduct);
+ },
+
+ updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata}) {
+ return putSoftwareProductQuestionnaire(softwareProductId, qdata);
+ },
+
+ softwareProductEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.softwareProductEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ softwareProductQuestionnaireUpdate(dispatch, {data}) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
+ payload: {qdata: data}
+ });
+ },
+
+ softwareProductEditorVendorChanged(dispatch, {deltaData}) {
+ LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion});
+ FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId: deltaData.vendorId, version: deltaData.licensingVersion});
+ SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData});
+ },
+
+ setIsValidityData(dispatch, {isValidityData}) {
+ dispatch({
+ type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
+ isValidityData
+ });
+ },
+
+ addSoftwareProduct(dispatch, {softwareProduct}) {
+ dispatch({
+ type: actionTypes.ADD_SOFTWARE_PRODUCT,
+ softwareProduct
+ });
+ },
+
+ fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
+ return Promise.all([
+ fetchSoftwareProduct(softwareProductId, version).then(response => {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_LOADED,
+ response
+ });
+ return response;
+ }),
+ fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
+ payload: {
+ qdata: response.data ? JSON.parse(response.data) : {},
+ qschema: JSON.parse(response.schema)
+ }
+ });
+ })
+ ]);
+ },
+
+ performVCAction(dispatch, {softwareProductId, action}) {
+ if (action === VersionControllerActionsEnum.SUBMIT) {
+ return putSoftwareProductAction(softwareProductId, action).then(() => {
+ return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE).then(() => {
+ dispatch({
+ type: NotificationConstants.NOTIFY_SUCCESS,
+ data: {
+ title: i18n('Submit Succeeded'),
+ msg: i18n('This software product successfully submitted'),
+ timeout: 2000
+ }
+ });
+ fetchSoftwareProduct(softwareProductId).then(response => {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_LOADED,
+ response
+ });
+ });
+ });
+ }, error => dispatch({
+ type: NotificationConstants.NOTIFY_ERROR,
+ data: {title: i18n('Submit Failed'), validationResponse: error.responseJSON}
+ }));
+ }
+ else {
+ return putSoftwareProductAction(softwareProductId, action).then(() => {
+ fetchSoftwareProduct(softwareProductId).then(response => {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_LOADED,
+ response
+ });
+ });
+ });
+ }
+ },
+
+ switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, version});
+ },
+
+ toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
+ let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
+ dispatch({
+ type: actionTypes.TOGGLE_NAVIGATION_ITEM,
+ mapOfExpandedIds
+ });
+ },
+
+ /** for the next verision */
+ addComponent(dispatch) {
+ return dispatch;
+ }
+};
+
+export default SoftwareProductActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js
new file mode 100644
index 0000000..812afe5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js
@@ -0,0 +1,35 @@
+/*-
+ * ============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=========================================================
+ */
+
+export default {
+
+ getCurrentCategoryOfSubCategory(selectedSubCategory, softwareProductCategories) {
+ let category, subCategory;
+ for (var i = 0; i < softwareProductCategories.length; i++) {
+ let {subcategories = []} = softwareProductCategories[i];
+ subCategory = subcategories.find(sub => sub.uniqueId === selectedSubCategory);
+ if (subCategory) {
+ category = softwareProductCategories[i].uniqueId;
+ break;
+ }
+ }
+ return category;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js
new file mode 100644
index 0000000..5f10c27
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js
@@ -0,0 +1,53 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ SOFTWARE_PRODUCT_LOADED: null,
+ SOFTWARE_PRODUCT_LIST_LOADED: null,
+ SOFTWARE_PRODUCT_LIST_EDIT: null,
+ SOFTWARE_PRODUCT_CATEGORIES_LOADED: null,
+ SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE: null,
+ ADD_SOFTWARE_PRODUCT: null,
+ TOGGLE_NAVIGATION_ITEM: null,
+
+ softwareProductEditor: {
+ OPEN: null,
+ CLOSE: null,
+ DATA_CHANGED: null,
+ IS_VALIDITY_DATA_CHANGED: null,
+ UPLOAD_CONFIRMATION: null
+ }
+});
+
+export const navigationItems = keyMirror({
+ VENDOR_SOFTWARE_PRODUCT: 'Vendor Software Product',
+ GENERAL: 'General',
+ PROCESS_DETAILS: 'Process Details',
+ NETWORKS: 'Networks',
+ ATTACHMENTS: 'Attachments',
+ COMPONENTS: 'Components',
+
+ COMPUTE: 'Compute',
+ LOAD_BALANCING: 'Load Balancing',
+ STORAGE: 'Storage',
+ MONITORING: 'Monitoring'
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js
new file mode 100644
index 0000000..6d1db16
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductListReducer.js
@@ -0,0 +1,35 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.SOFTWARE_PRODUCT_LIST_LOADED:
+ return [...action.response.results];
+ case actionTypes.SOFTWARE_PRODUCT_LIST_EDIT:
+ const indexForEdit = state.findIndex(vsp => vsp.id === action.payload.softwareProduct.id);
+ return [...state.slice(0, indexForEdit), action.payload.softwareProduct, ...state.slice(indexForEdit + 1)];
+ case actionTypes.ADD_SOFTWARE_PRODUCT:
+ return [...state, action.softwareProduct];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js
new file mode 100644
index 0000000..784ac9d
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js
@@ -0,0 +1,80 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {combineReducers} from 'redux';
+import {actionTypes} from './SoftwareProductConstants.js';
+import SoftwareProductAttachmentsReducer from './attachments/SoftwareProductAttachmentsReducer.js';
+import SoftwareProductCreationReducer from './creation/SoftwareProductCreationReducer.js';
+import SoftwareProductDetailsReducer from './details/SoftwareProductDetailsReducer.js';
+import SoftwareProductProcessesListReducer from './processes/SoftwareProductProcessesListReducer.js';
+import SoftwareProductProcessesEditorReducer from './processes/SoftwareProductProcessesEditorReducer.js';
+import SoftwareProductNetworksListReducer from './networks/SoftwareProductNetworksListReducer.js';
+import SoftwareProductComponentsListReducer from './components/SoftwareProductComponentsListReducer.js';
+import SoftwareProductComponentEditorReducer from './components/SoftwareProductComponentEditorReducer.js';
+import {actionTypes as processesActionTypes} from './processes/SoftwareProductProcessesConstants.js';
+import SoftwareProductComponentProcessesListReducer from './components/processes/SoftwareProductComponentProcessesListReducer.js';
+import SoftwareProductComponentProcessesEditorReducer from './components/processes/SoftwareProductComponentProcessesEditorReducer.js';
+import {actionTypes as componentProcessesActionTypes} from './components/processes/SoftwareProductComponentProcessesConstants.js';
+import SoftwareProductComponentsNICListReducer from './components/network/SoftwareProductComponentsNICListReducer.js';
+import SoftwareProductComponentsNICEditorReducer from './components/network/SoftwareProductComponentsNICEditorReducer.js';
+import SoftwareProductComponentsMonitoringReducer from './components/monitoring/SoftwareProductComponentsMonitoringReducer.js';
+
+export default combineReducers({
+ softwareProductAttachments: SoftwareProductAttachmentsReducer,
+ softwareProductCreation: SoftwareProductCreationReducer,
+ softwareProductEditor: SoftwareProductDetailsReducer,
+ softwareProductProcesses: combineReducers({
+ processesList: SoftwareProductProcessesListReducer,
+ processesEditor: SoftwareProductProcessesEditorReducer,
+ processToDelete: (state = false, action) => action.type === processesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM ? action.processToDelete : state
+ }),
+ softwareProductNetworks: combineReducers({
+ networksList: SoftwareProductNetworksListReducer
+ }),
+ softwareProductComponents: combineReducers({
+ componentsList: SoftwareProductComponentsListReducer,
+ componentEditor: SoftwareProductComponentEditorReducer,
+ componentProcesses: combineReducers({
+ processesList: SoftwareProductComponentProcessesListReducer,
+ processesEditor: SoftwareProductComponentProcessesEditorReducer,
+ processToDelete: (state = false, action) => action.type === componentProcessesActionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM ? action.processToDelete : state,
+ }),
+ network: combineReducers({
+ nicList: SoftwareProductComponentsNICListReducer,
+ nicEditor: SoftwareProductComponentsNICEditorReducer
+ }),
+ monitoring: SoftwareProductComponentsMonitoringReducer
+ }),
+ softwareProductCategories: (state = [], action) => {
+ if (action.type === actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED) {
+ return action.softwareProductCategories;
+ }
+ return state;
+ },
+ softwareProductQuestionnaire: (state = {}, action) => {
+ if (action.type === actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE) {
+ return {
+ ...state,
+ ...action.payload
+ };
+ }
+ return state;
+ }
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js
new file mode 100644
index 0000000..a4b95a4
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachments.js
@@ -0,0 +1,43 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductAttachmentsView from './SoftwareProductAttachmentsView.jsx';
+import SoftwareProductAttachmentsActionHelper from './SoftwareProductAttachmentsActionHelper.js';
+
+export const mapStateToProps = ({softwareProduct: {softwareProductAttachments}}) => {
+ let {attachmentsTree, hoveredNode, selectedNode, errorList} = softwareProductAttachments;
+ return {
+ attachmentsTree,
+ hoveredNode,
+ selectedNode,
+ errorList
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ toggleExpanded: (path) => SoftwareProductAttachmentsActionHelper.toggleExpanded(dispatch, {path}),
+ onSelectNode: (nodeName) => SoftwareProductAttachmentsActionHelper.onSelectNode(dispatch, {nodeName}),
+ onUnselectNode: () => SoftwareProductAttachmentsActionHelper.onUnselectNode(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductAttachmentsView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js
new file mode 100644
index 0000000..a7f7a51
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsActionHelper.js
@@ -0,0 +1,44 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductAttachmentsConstants.js';
+
+export default {
+
+ toggleExpanded(dispatch, {path}) {
+ dispatch({
+ type: actionTypes.TOGGLE_EXPANDED,
+ path
+ });
+ },
+
+ onSelectNode(dispatch, {nodeName}) {
+ dispatch({
+ type: actionTypes.SELECTED_NODE,
+ nodeName
+ });
+ },
+
+ onUnselectNode(dispatch) {
+ dispatch({
+ type: actionTypes.UNSELECTED_NODE
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js
new file mode 100644
index 0000000..33af476
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsConstants.js
@@ -0,0 +1,55 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export const actionTypes = keyMirror({
+ TOGGLE_EXPANDED: null,
+ SELECTED_NODE: null,
+ UNSELECTED_NODE: null
+});
+
+export const errorTypes = keyMirror({
+ MISSING_FILE_IN_ZIP: i18n('missing file in zip'),
+ MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'),
+ MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'),
+ FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'),
+ FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'),
+ ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'),
+ ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'),
+ MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'),
+ MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'),
+ ARTIFACT_NOT_IN_USE: i18n('artifact not in use')
+});
+
+export const nodeTypes = keyMirror({
+ heat: i18n('Heat'),
+ volume: i18n('Volume'),
+ network: i18n('Network'),
+ artifact: i18n('Artifact'),
+ env: i18n('Environment'),
+ other: i18n('')
+});
+
+export const mouseActions = keyMirror({
+ MOUSE_BUTTON_CLICK: 0
+});
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js
new file mode 100644
index 0000000..5c5567b
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsReducer.js
@@ -0,0 +1,199 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+import {actionTypes} from './SoftwareProductAttachmentsConstants.js';
+
+const mapVolumeData = ({fileName, env, errors}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'volume',
+ children: env && [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }],
+ errors
+});
+
+const mapNetworkData = ({fileName, env, errors}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'network',
+ children: env && [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }],
+ errors
+});
+
+const mapArtifactsData = ({fileName, errors}) => ({
+ name: fileName,
+ type: 'artifact',
+ errors
+});
+
+const mapOtherData = ({fileName, errors}) => ({
+ name: fileName,
+ type: 'other',
+ errors
+});
+
+
+const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'heat',
+ errors,
+ children: [
+ ...(volume ? volume.map(mapVolumeData) : []),
+ ...(network ? network.map(mapNetworkData) : []),
+ ...(env ? [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }] : []),
+ ...(artifacts ? artifacts.map(mapArtifactsData) : []),
+ ...(other ? other.map(mapOtherData) : []),
+ ...(nested ? nested.map(mapHeatData) : [])
+ ]
+});
+
+function createErrorList(node, parent, deep = 0, errorList = []) {
+ if (node.errors) {
+ errorList.push(...node.errors.map((error) => ({
+ errorLevel: error.level,
+ errorMessage: error.message,
+ name: node.name,
+ hasParent: deep > 2,
+ parentName: parent.name,
+ type: node.type,
+ })));
+ }
+ if (node.children && node.children.length) {
+ node.children.map((child) => createErrorList(child, node, deep + 1, errorList));
+ }
+ return errorList;
+}
+
+const mapValidationDataToTree = validationData => {
+ let {HEAT, volume, network, artifacts, other} = validationData.importStructure || {};
+ return {
+ children: [
+ {
+ name: 'HEAT',
+ expanded: true,
+ type: 'heat',
+ children: (HEAT ? HEAT.map(mapHeatData) : [])
+ },
+ ...(artifacts ? [{
+ name: 'artifacts',
+ expanded: true,
+ type: 'artifact',
+ children: (artifacts ? artifacts.map(mapArtifactsData) : [])
+ }] : []),
+ ...(network ? [{
+ name: 'networks',
+ expanded: true,
+ type: 'network',
+ children: (network ? network.map(mapNetworkData) : []),
+ }] : []),
+ ...(volume ? [{
+ name: 'volume',
+ expanded: true,
+ type: 'volume',
+ children: (volume ? volume.map(mapVolumeData) : []),
+ }] : []),
+ ...(other ? [{
+ name: 'other',
+ expanded: true,
+ type: 'other',
+ children: (other ? other.map(mapOtherData) : []),
+ }] : [])
+ ]
+ };
+};
+
+const toggleExpanded = (node, path) => {
+ let newNode = {...node};
+ if (path.length === 0) {
+ newNode.expanded = !node.expanded;
+ } else {
+ let index = path[0];
+ newNode.children = [
+ ...node.children.slice(0, index),
+ toggleExpanded(node.children[index], path.slice(1)),
+ ...node.children.slice(index + 1)
+ ];
+ }
+ return newNode;
+};
+
+const expandSelected = (node, selectedNode) => {
+ let shouldExpand = node.name === selectedNode;
+ let children = node.children && node.children.map(child => {
+ let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode);
+ shouldExpand = shouldExpand || shouldExpandChild;
+ return newChild;
+ });
+
+ return {
+ node: {
+ ...node,
+ expanded: node.expanded || shouldExpand,
+ children
+ },
+ shouldExpand
+ };
+};
+
+export default (state = {attachmentsTree: {}}, action) => {
+ switch (action.type) {
+ case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED:
+ let currentSoftwareProduct = action.response;
+ let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {};
+ let errorList = createErrorList(attachmentsTree);
+ return {
+ ...state,
+ attachmentsTree,
+ errorList
+ };
+ case actionTypes.TOGGLE_EXPANDED:
+ return {
+ ...state,
+ attachmentsTree: toggleExpanded(state.attachmentsTree, action.path)
+ };
+ case actionTypes.SELECTED_NODE:
+ let selectedNode = action.nodeName;
+ return {
+ ...state,
+ attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node,
+ selectedNode
+ };
+ case actionTypes.UNSELECTED_NODE:
+ return {
+ ...state,
+ selectedNode: undefined
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx
new file mode 100644
index 0000000..c52999c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/SoftwareProductAttachmentsView.jsx
@@ -0,0 +1,182 @@
+import React from 'react';
+import FontAwesome from 'react-fontawesome';
+import classNames from 'classnames';
+import Collapse from 'react-bootstrap/lib/Collapse.js';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {nodeTypes, mouseActions} from './SoftwareProductAttachmentsConstants';
+
+const typeToIcon = Object.freeze({
+ heat: 'building-o',
+ volume: 'database',
+ network: 'cloud',
+ artifact: 'gear',
+ env: 'server',
+ other: 'cube'
+});
+
+const leftPanelWidth = 250;
+
+class SoftwareProductAttachmentsView extends React.Component {
+
+ static propTypes = {
+ attachmentsTree: React.PropTypes.object.isRequired
+ };
+ state = {
+ treeWidth: '400'
+ };
+
+ render() {
+ let {attachmentsTree, errorList} = this.props;
+ let {treeWidth} = this.state;
+ return (
+ <div className='software-product-attachments'>
+ <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}>
+ <div className='tree-wrapper'>
+ {
+ attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))
+ }
+ </div>
+ </div>
+ <div onMouseDown={(e) => this.onChangeTreeWidth(e)} className='software-product-attachments-separator'/>
+
+ <div className='software-product-attachments-error-list'>
+ {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ?
+ i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>}
+ </div>
+ </div>
+ );
+ }
+
+ renderNode(node, path) {
+ let isFolder = node.children && node.children.length > 0;
+ let {onSelectNode} = this.props;
+ return (
+ <div key={node.name} className='tree-block-inside'>
+ {
+ <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}>
+ {
+ isFolder &&
+ <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}>
+ <FontAwesome name='caret-down'/>
+ </div>
+ }
+ {
+
+ <span className='tree-node-icon'>
+ <FontAwesome name={typeToIcon[node.type]}/>
+ </span>
+ }
+ {
+
+ <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}>
+ {node.name}
+ </span>
+ }
+ </div>
+ }
+ {
+ isFolder &&
+ <Collapse in={node.expanded}>
+ <div className='tree-node-children'>
+ {
+ node.children.map((child, ind) => this.renderNode(child, [...path, ind]))
+ }
+ </div>
+ </Collapse>
+ }
+ </div>
+ );
+ }
+
+ createErrorList(errorList, node, parent) {
+ if (node.errors) {
+ node.errors.forEach(error => errorList.push({
+ error,
+ name: node.name,
+ parentName: parent.name,
+ type: node.type
+ }));
+ }
+ if (node.children && node.children.length) {
+ node.children.map((child) => this.createErrorList(errorList, child, node));
+ }
+ }
+
+ renderErrorList(errors) {
+ let prevError = {};
+ let {selectedNode} = this.props;
+ return errors.map(error => {
+ let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName;
+ prevError = error;
+
+ return (
+ <div
+ key={error.name + error.errorMessage + error.parentName}
+
+ onClick={() => this.selectNode(error.name)}
+ className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}>
+ <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}>
+ {
+ error.hasParent ?
+ i18n('{type} {name} in {parentName}: ', {
+ type: nodeTypes[error.type],
+ name: error.name,
+ parentName: error.parentName
+ }) :
+ i18n('{type} {name}: ', {
+ type: nodeTypes[error.type],
+ name: error.name
+ })
+ }
+ </span>
+ <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span>
+ </div>
+ );
+ });
+ }
+
+ selectNode(currentSelectedNode) {
+ let {onUnselectNode, onSelectNode, selectedNode} = this.props;
+ if (currentSelectedNode !== selectedNode) {
+ onSelectNode(currentSelectedNode);
+ }else{
+ onUnselectNode();
+ }
+
+ }
+
+ getTreeRowClassName(name) {
+ let {hoveredNode, selectedNode} = this.props;
+ return classNames({
+ 'tree-node-row': true,
+ 'tree-node-selected': name === hoveredNode,
+ 'tree-node-clicked': name === selectedNode
+ });
+ }
+
+ getTreeTextClassName(node) {
+ let {selectedNode} = this.props;
+ return classNames({
+ 'tree-element-text': true,
+ 'error-status': node.errors,
+ 'error-status-selected': node.name === selectedNode
+ });
+ }
+
+ onChangeTreeWidth(e) {
+ if (e.button === mouseActions.MOUSE_BUTTON_CLICK) {
+ let onMouseMove = (e) => {
+ this.setState({treeWidth: e.clientX - leftPanelWidth});
+ };
+ let onMouseUp = () => {
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ };
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+ }
+ }
+}
+
+export default SoftwareProductAttachmentsView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js
new file mode 100644
index 0000000..3b8bc4f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentEditorReducer.js
@@ -0,0 +1,47 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentsConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.COMPONENT_UPDATE:
+ return {
+ ...state,
+ data: action.component
+ };
+ case actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE:
+ return {
+ ...state,
+ qdata: action.payload.qdata || state.qdata,
+ qschema: action.payload.qschema || state.qschema
+ };
+ case actionTypes.COMPONENT_DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js
new file mode 100644
index 0000000..e53b2ec
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js
@@ -0,0 +1,129 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+import {actionTypes} from './SoftwareProductComponentsConstants.js';
+
+function baseUrl(softwareProductId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components`;
+}
+
+function fetchSoftwareProductComponents(softwareProductId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`);
+}
+
+function putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, vspComponent) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire`, vspComponent);
+}
+
+function fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version){
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}/questionnaire${versionQuery}`);
+}
+
+function fetchSoftwareProductComponent(softwareProductId, vspComponentId, version){
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}/${vspComponentId}${versionQuery}`);
+}
+
+function putSoftwareProductComponent(softwareProductId, vspComponentId, vspComponent) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${vspComponentId}`, {
+ name: vspComponent.name,
+ displayName: vspComponent.displayName,
+ description: vspComponent.description
+ });
+}
+
+const SoftwareProductComponentsActionHelper = {
+ fetchSoftwareProductComponents(dispatch, {softwareProductId, version}) {
+ return fetchSoftwareProductComponents(softwareProductId, version).then(response => {
+ dispatch({
+ type: actionTypes.COMPONENTS_LIST_UPDATE,
+ componentsList: response.results
+ });
+ });
+ },
+
+ componentDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.COMPONENT_DATA_CHANGED,
+ deltaData
+ });
+ },
+
+
+ updateSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, componentData, qdata}) {
+ return Promise.all([
+ SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}),
+ SoftwareProductComponentsActionHelper.updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData})
+ ]);
+ },
+
+ updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, qdata}) {
+ return putSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, qdata);
+ },
+
+ updateSoftwareProductComponentData(dispatch, {softwareProductId, vspComponentId, componentData}) {
+ return putSoftwareProductComponent(softwareProductId, vspComponentId, componentData).then(() => dispatch({
+ type: actionTypes.COMPONENTS_LIST_EDIT,
+ component: {
+ id: vspComponentId,
+ ...componentData
+ }
+ }));
+ },
+
+
+ fetchSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId, version}) {
+ return fetchSoftwareProductComponentQuestionnaire(softwareProductId, vspComponentId, version).then(response => {
+ dispatch({
+ type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE,
+ payload: {
+ qdata: response.data ? JSON.parse(response.data) : {},
+ qschema: JSON.parse(response.schema)
+ }
+ });
+ });
+ },
+
+ fetchSoftwareProductComponent(dispatch, {softwareProductId, vspComponentId, version}) {
+ return fetchSoftwareProductComponent(softwareProductId, vspComponentId, version).then(response => {
+ dispatch({
+ type: actionTypes.COMPONENT_UPDATE,
+ component: response.data
+ });
+ });
+ },
+
+ componentQuestionnaireUpdated(dispatch, {data}) {
+ dispatch({
+ type: actionTypes.COMPONENT_QUESTIONNAIRE_UPDATE,
+ payload: {
+ qdata: data
+ }
+ });
+ },
+};
+
+export default SoftwareProductComponentsActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js
new file mode 100644
index 0000000..dee517e
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsConstants.js
@@ -0,0 +1,46 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ COMPONENTS_LIST_UPDATE: null,
+ COMPONENTS_LIST_EDIT: null,
+ COMPONENT_UPDATE: null,
+ COMPONENT_DATA_CHANGED: null,
+ COMPONENT_QUESTIONNAIRE_UPDATE: null
+});
+
+export const storageConstants = keyMirror({
+ backupType: {
+ ON_SITE: 'OnSite',
+ OFF_SITE: 'OffSite'
+ }
+});
+
+export const navigationItems = keyMirror({
+ STORAGE: 'Storage',
+ PROCESS_DETAILS: 'Process Details',
+ MONITORING: 'Monitoring',
+ NETWORK: 'Network',
+ COMPUTE: 'Compute',
+ NETWORK: 'Network',
+ LOAD_BALANCING: 'High Availability & Load Balancing'
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js
new file mode 100644
index 0000000..f1c1ed1
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsList.js
@@ -0,0 +1,48 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentsListView from './SoftwareProductComponentsListView.jsx';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductComponents} = softwareProduct;
+ let {componentsList} = softwareProductComponents;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+
+ return {
+ currentSoftwareProduct,
+ isReadOnlyMode,
+ componentsList
+ };
+};
+
+
+const mapActionToProps = (dispatch, {version}) => {
+ return {
+ onComponentSelect: ({id: softwareProductId, componentId}) => {
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version });
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(SoftwareProductComponentsListView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js
new file mode 100644
index 0000000..b91362a
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListReducer.js
@@ -0,0 +1,33 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentsConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.COMPONENTS_LIST_UPDATE:
+ return [...action.componentsList];
+ case actionTypes.COMPONENTS_LIST_EDIT:
+ const indexForEdit = state.findIndex(component => component.id === action.component.id);
+ return [...state.slice(0, indexForEdit), action.component, ...state.slice(indexForEdit + 1)];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx
new file mode 100644
index 0000000..0c0ba0f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsListView.jsx
@@ -0,0 +1,89 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+const ComponentPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ displayName: React.PropTypes.string,
+ description: React.PropTypes.string
+});
+
+class SoftwareProductComponentsListView extends React.Component {
+
+ state = {
+ localFilter: ''
+ };
+
+ static propTypes = {
+ isReadOnlyMode: React.PropTypes.bool,
+ componentsList: React.PropTypes.arrayOf(ComponentPropType),
+ onComponentSelect: React.PropTypes.func
+ };
+
+ render() {
+ let {componentsList = []} = this.props;
+ return (
+ <div className=''>
+ {
+ componentsList.length > 0 && this.renderComponents()
+ }
+ </div>
+ );
+ }
+
+ renderComponents() {
+ const {localFilter} = this.state;
+ let {isReadOnlyMode} = this.props;
+
+ return (
+ <ListEditorView
+ title={i18n('Virtual Function Components')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter Components')}
+ onFilter={filter => this.setState({localFilter: filter})}
+ isReadOnlyMode={isReadOnlyMode}>
+ {this.filterList().map(component => this.renderComponentsListItem(component))}
+ </ListEditorView>
+ );
+ }
+
+ renderComponentsListItem(component) {
+ let {id: componentId, name, displayName, description = ''} = component;
+ let {currentSoftwareProduct: {id}, onComponentSelect} = this.props;
+ return (
+ <ListEditorItemView
+ key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()}
+ className='list-editor-item-view'
+ onSelect={() => onComponentSelect({id, componentId})}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Component')}</div>
+ <div className='name'>{displayName}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ filterList() {
+ let {componentsList = []} = this.props;
+
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return componentsList.filter(({displayName = '', description = ''}) => {
+ return escape(displayName).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return componentsList;
+ }
+ }
+}
+
+export default SoftwareProductComponentsListView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js
new file mode 100644
index 0000000..7ac1c70
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentCompute.js
@@ -0,0 +1,52 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentComputeView from './SoftwareProductComponentComputeView.jsx';
+import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct;
+ let {componentEditor: {qdata, qschema}} = softwareProductComponents;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP);
+
+ let minNumberOfVMsSelectedByUser = 0;
+ if(qdata && qdata.compute && qdata.compute.numOfVMs){
+ minNumberOfVMsSelectedByUser = qdata.compute.numOfVMs.minimum || 0;
+ }
+
+ return {
+ qdata,
+ qschema,
+ isReadOnlyMode,
+ minNumberOfVMsSelectedByUser
+ };
+};
+
+const mapActionToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}),
+ onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});}
+ };
+};
+
+export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentComputeView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx
new file mode 100644
index 0000000..3bad147
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/compute/SoftwareProductComponentComputeView.jsx
@@ -0,0 +1,129 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx';
+
+
+class SoftwareProductComponentComputeView extends React.Component {
+
+ static propTypes = {
+ qdata: React.PropTypes.object,
+ qschema: React.PropTypes.object,
+ isReadOnlyMode: React.PropTypes.bool,
+ minNumberOfVMsSelectedByUser: React.PropTypes.number,
+ onQDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired
+ };
+
+ render() {
+ let {qdata, qschema, isReadOnlyMode, minNumberOfVMsSelectedByUser, onQDataChanged, onSubmit} = this.props;
+
+ return (
+ <div className='vsp-component-questionnaire-view'>
+ <ValidationForm
+ ref='computeValidationForm'
+ hasButtons={false}
+ onSubmit={() => onSubmit({qdata})}
+ className='component-questionnaire-validation-form'
+ isReadOnlyMode={isReadOnlyMode}
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ schema={qschema}>
+
+ <div className='section-title'>{i18n('VM Sizing')}</div>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Number of CPUs')}
+ pointer={'/compute/vmSizing/numOfCPUs'}/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('File System Size (GB)')}
+ pointer={'/compute/vmSizing/fileSystemSizeGB'}/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Persistent Storage/Volume Size (GB)')}
+ pointer={'/compute/vmSizing/persistentStorageVolumeSize'}/>
+ </div>
+ <ValidationInput
+ type='text'
+ label={i18n('I/O Operations (per second)')}
+ pointer={'/compute/vmSizing/IOOperationsPerSec'}/>
+ </div>
+ </div>
+ <div className='section-title'>{i18n('Number of VMs')}</div>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Minimum')}
+ pointer={'/compute/numOfVMs/minimum'}/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Maximum')}
+ pointer={'/compute/numOfVMs/maximum'}
+ validations={{minValue: minNumberOfVMsSelectedByUser}}/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='select'
+ label={i18n('CPU Oversubscription Ratio')}
+ pointer={'/compute/numOfVMs/CpuOverSubscriptionRatio'}/>
+ </div>
+ <ValidationInput
+ type='select'
+ label={i18n('Memory - RAM')}
+ pointer={'/compute/numOfVMs/MemoryRAM'}/>
+ </div>
+ </div>
+
+ <div className='section-title'>{i18n('Guest OS')}</div>
+ <div className='rows-section'>
+ <div className='section-field row-flex-components input-row'>
+ <div className='two-col'>
+ <ValidationInput
+ label={i18n('Guest OS')}
+ type='text'
+ pointer={'/compute/guestOS/name'}/>
+ </div>
+ <div className='empty-two-col'/>
+ </div>
+ <div className='vertical-flex input-row'>
+ <label key='label' className='control-label'>{i18n('OS Bit Size')}</label>
+ <div className='radio-options-content-row input-row'>
+ <ValidationInput
+ type='radiogroup'
+ pointer={'/compute/guestOS/bitSize'}
+ className='radio-field'/>
+ </div>
+ </div>
+ <div className='section-field row-flex-components input-row'>
+ <div className='two-col'>
+ <ValidationInput
+ type='textarea'
+ label={i18n('Guest OS Tools:')}
+ pointer={'/compute/guestOS/tools'}/>
+ </div>
+ <div className='empty-two-col'/>
+ </div>
+ </div>
+ </ValidationForm>
+ </div>
+ );
+ }
+
+ save(){
+ return this.refs.computeValidationForm.handleFormSubmit(new Event('dummy'));
+ }
+}
+
+export default SoftwareProductComponentComputeView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js
new file mode 100644
index 0000000..e4c330b
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneral.js
@@ -0,0 +1,52 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentsGeneralView from './SoftwareProductComponentsGeneralView.jsx';
+import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+export const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct;
+ let {componentEditor: {data: componentData = {} , qdata, qschema}} = softwareProductComponents;
+
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP);
+
+ return {
+ componentData,
+ qdata,
+ qschema,
+ isReadOnlyMode
+ };
+};
+
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onDataChanged: deltaData => SoftwareProductComponentsActionHelper.componentDataChanged(dispatch, {deltaData}),
+ onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}),
+ onSubmit: ({componentData, qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponent(dispatch,
+ {softwareProductId, vspComponentId: componentId, componentData, qdata});
+ }
+ };
+
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsGeneralView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx
new file mode 100644
index 0000000..5d11e42
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/general/SoftwareProductComponentsGeneralView.jsx
@@ -0,0 +1,186 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx';
+
+
+class SoftwareProductComponentsGeneralView extends React.Component {
+
+ render() {
+ let {qdata, qschema, onQDataChanged, onDataChanged, componentData: {displayName, description}, isReadOnlyMode} = this.props;
+ return(
+ <div className='vsp-components-general'>
+ <div className='general-data'>
+ <ValidationForm
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={false}>
+ <div className=''>
+ <h3 className='section-title'>{i18n('General')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ {/** disabled until backend will be ready to implement it
+ <div className='validation-input-wrapper'>
+ <div className='form-group'>
+ <label className='control-label'>{i18n('Name')}</label>
+ <div>{name}</div>
+ </div>
+ </div>
+
+ */}
+ <div className='single-col'>
+ <ValidationInput label={i18n('Name')} value={displayName} disabled={true} type='text'/>
+ </div>
+ <div className='two-col'>
+ <ValidationInput
+ label={i18n('Description')}
+ onChange={description => onDataChanged({description})}
+ disabled={isReadOnlyMode}
+ value={description}
+ type='textarea'/>
+ </div>
+ <div className='empty-col' />
+ </div>
+ </div>
+ </div>
+ </ValidationForm>
+ {
+ qschema &&
+ <ValidationForm
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ schema={qschema}
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={false}>
+ <h3 className='section-title additional-validation-form'>{i18n('Hypervisor')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('Supported Hypervisors')}
+ type='select'
+ pointer='/general/hypervisor/hypervisor'/>
+ </div>
+ <div className='two-col'>
+ <ValidationInput
+ label={i18n('Hypervisor Drivers')}
+ type='text'
+ pointer='/general/hypervisor/drivers'/>
+ </div>
+ <div className='empty-col' />
+ </div>
+ <div className='row-flex-components input-row'>
+ <div className='three-col'>
+ <ValidationInput
+ label={i18n('Describe Container Features')}
+ type='textarea'
+ pointer='/general/hypervisor/containerFeaturesDescription'/>
+ </div>
+ <div className='empty-col' />
+ </div>
+ </div>
+ <h3 className='section-title'>{i18n('Image')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('Image format')}
+ type='select'
+ pointer='/general/image/format'/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('Image provided by')}
+ type='select'
+ pointer='/general/image/providedBy'/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('Size of boot disk per VM (GB)')}
+ type='text'
+ pointer='/general/image/bootDiskSizePerVM'/>
+ </div>
+ <ValidationInput
+ label={i18n('Size of ephemeral disk per VM (GB)')}
+ type='text'
+ pointer='/general/image/ephemeralDiskSizePerVM'/>
+ </div>
+ </div>
+ <h3 className='section-title'>{i18n('Recovery')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('VM Recovery Point Objective (Minutes)')}
+ type='text'
+ pointer='/general/recovery/pointObjective'/>
+ </div>
+ <ValidationInput
+ label={i18n('VM Recovery Time Objective (Minutes)')}
+ type='text'
+ pointer='/general/recovery/timeObjective'/>
+ <div className='empty-two-col' />
+ </div>
+
+
+ <div className='row-flex-components input-row'>
+ <div className='two-col'>
+ <ValidationInput
+ className='textarea'
+ label={i18n('How are in VM process failures handled?')}
+ type='textarea'
+ pointer='/general/recovery/vmProcessFailuresHandling'/>
+ </div>
+ <div className='empty-two-col' />
+ {
+ /** disabled until backend will be ready to implement it
+ <div className='row'>
+ <div className='col-md-3'>
+ <ValidationInput
+ label={i18n('VM Recovery Document')}
+ type='text'
+ pointer='/general/recovery/VMRecoveryDocument'/>
+ </div>
+ </div>
+ */
+ }
+ </div>
+ </div>
+ <h3 className='section-title'>{i18n('DNS Configuration')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='two-col'>
+ <ValidationInput
+ label={i18n('Do you have a need for DNS as a Service? Please describe.')}
+ type='textarea'
+ pointer='/general/dnsConfiguration'/>
+ </div>
+ <div className='empty-two-col' />
+ </div>
+ </div>
+ <h3 className='section-title'>{i18n('Clone')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='two-col'>
+ <ValidationInput
+ label={i18n('Describe VM Clone Use')}
+ type='textarea'
+ pointer='/general/vmCloneUsage'/>
+ </div>
+ <div className='empty-two-col' />
+ </div>
+ </div>
+ </ValidationForm>
+ }
+ </div>
+ </div>
+ );
+ }
+
+ save() {
+ let {onSubmit, componentData, qdata} = this.props;
+ return onSubmit({componentData, qdata});
+ }
+}
+
+export default SoftwareProductComponentsGeneralView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js
new file mode 100644
index 0000000..4d4034d
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancing.js
@@ -0,0 +1,47 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentLoadBalancingView from './SoftwareProductComponentLoadBalancingRefView.jsx';
+import SoftwareProductComponentsActionHelper from '../SoftwareProductComponentsActionHelper.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+export const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct;
+ let {componentEditor: {qdata, qschema}} = softwareProductComponents;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP);
+
+ return {
+ qdata,
+ qschema,
+ isReadOnlyMode
+ };
+};
+
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}),
+ onSubmit: ({qdata}) =>{ return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});}
+ };
+
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentLoadBalancingView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx
new file mode 100644
index 0000000..1aa2bab
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/loadBalancing/SoftwareProductComponentLoadBalancingRefView.jsx
@@ -0,0 +1,103 @@
+import React from 'react';
+import FontAwesome from 'react-fontawesome';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx';
+
+const prefix = '/highAvailabilityAndLoadBalancing/';
+
+const pointers = [
+ {
+ key: 'failureLoadDistribution',
+ description: 'How is load distributed across live vms in the event of a vm/host failure? please describe'
+ },
+ {
+ key: 'nkModelImplementation',
+ description: 'Does each VM implement the N+K model for redundancy and failure protection? Please describe.'
+ },
+ {
+ key: 'architectureChoice',
+ description: 'What architecture is being implemented: ACTIVE-ACTIVE and/or ACTIVE-PASSIVE. ',
+ added: 'Will the arrangement be 1-1 or N-M? Please describe.'
+ },
+ {key: 'slaRequirements', description: 'Specify application SLA requirements on Cloud platform.'},
+ {
+ key: 'horizontalScaling',
+ description: 'Is horizontal scaling the preferred solution for HA and resiliency? Please describe.'
+ },
+ {
+ key: 'loadDistributionMechanism',
+ description: 'Can load be distributed across VMs? If so, are special mechanisms needed to re-balance data across VMs?',
+ added: 'Please describe.'
+ }
+];
+
+class SoftwareProductComponentLoadBalancingView extends React.Component {
+ static propTypes = {
+ componentId: React.PropTypes.string.isRequired,
+ softwareProductId: React.PropTypes.string.isRequired,
+ qdata: React.PropTypes.object,
+ qschema: React.PropTypes.object,
+ currentSoftwareProduct: React.PropTypes.object
+ };
+
+ state = {
+ expanded: {}
+ };
+
+ renderTextAreaItem(item) {
+ return (
+ <div className='question'>
+ <div className={this.state.expanded[item.key] ? 'title' : 'title add-padding'}
+ onClick={() => this.toggle(item.key)}>
+ <FontAwesome name={this.state.expanded[item.key] ? 'chevron-up' : 'chevron-down'}/>
+ {i18n(item.description)}
+ {item.added && <div className='new-line'>{i18n(item.added)}</div>}
+ </div>
+ <div className={this.state.expanded[item.key] ? 'collapse in' : 'collapse'}>
+ <div className='row'>
+ <div className='col-md-9'>
+ <ValidationInput
+ type='textarea'
+ pointer={`${prefix}${item.key}`}
+ maxLength='1000' />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ render() {
+ let {qdata, qschema, onQDataChanged, isReadOnlyMode} = this.props;
+ return (
+ <div className='vsp-components-load-balancing'>
+ <div className='halb-data'>
+ <div className='load-balancing-page-title'>{i18n('High Availability & Load Balancing')}</div>
+ <ValidationForm
+ onDataChanged={onQDataChanged}
+ data={qdata} schema={qschema}
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={false}>
+ {pointers.map(pointer => this.renderTextAreaItem(pointer))}
+ </ValidationForm>
+ </div>
+ </div>
+ );
+ }
+
+ toggle(name) {
+ let st = this.state.expanded[name] ? true : false;
+ let newState = {...this.state};
+ newState.expanded[name] = !st;
+ this.setState(newState);
+ }
+
+ save() {
+ let {onSubmit, qdata} = this.props;
+ return onSubmit({qdata});
+ }
+}
+
+export default SoftwareProductComponentLoadBalancingView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js
new file mode 100644
index 0000000..ed7c5a1
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoring.js
@@ -0,0 +1,59 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import SoftwareProductComponentsMonitoringView from './SoftwareProductComponentsMonitoringView.jsx';
+import SoftwareProductComponentsMonitoringAction from './SoftwareProductComponentsMonitoringActionHelper.js';
+
+
+export const mapStateToProps = ({softwareProduct}) => {
+
+ let {softwareProductEditor: {data:currentVSP = {}}, softwareProductComponents: {monitoring}} = softwareProduct;
+ let {trapFilename, pollFilename} = monitoring;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP);
+
+ return {
+ isReadOnlyMode,
+ trapFilename,
+ pollFilename
+ };
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onDropMibFileToUpload: (formData, type) =>
+ SoftwareProductComponentsMonitoringAction.uploadSnmpFile(dispatch, {
+ softwareProductId,
+ componentId,
+ formData,
+ type
+ }),
+
+ onDeleteSnmpFile: type => SoftwareProductComponentsMonitoringAction.deleteSnmpFile(dispatch, {
+ softwareProductId,
+ componentId,
+ type
+ })
+
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsMonitoringView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js
new file mode 100644
index 0000000..3faf571
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringActionHelper.js
@@ -0,0 +1,110 @@
+/*-
+ * ============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=========================================================
+ */
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import SoftwareProductComponentsMonitoringConstants, {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js';
+
+const UPLOAD = true;
+
+function baseUrl(vspId, componentId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${vspId}/components/${componentId}/monitors`;
+}
+
+function snmpTrapUrl(vspId, componentId, isUpload) {
+ return `${baseUrl(vspId, componentId)}/snmp-trap${isUpload ? '/upload' : ''}`;
+}
+
+function snmpPollUrl(vspId, componentId, isUpload) {
+ return `${baseUrl(vspId, componentId)}/snmp${isUpload ? '/upload' : ''}`;
+}
+
+let onInvalidFileSizeUpload = (dispatch) => dispatch({
+ type: NotificationConstants.NOTIFY_ERROR,
+ data: {
+ title: i18n('Upload Failed'),
+ msg: i18n('no zip file was uploaded or zip file doesn\'t exist')
+ }
+});
+
+let uploadSnmpTrapFile = (dispatch, {softwareProductId, componentId, formData}) => {
+ RestAPIUtil.create(snmpTrapUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({
+ type: actionTypes.SNMP_TRAP_UPLOADED, data: {filename: formData.get('upload').name}
+ }));
+};
+
+let uploadSnmpPollFile = (dispatch, {softwareProductId, componentId, formData}) => {
+ RestAPIUtil.create(snmpPollUrl(softwareProductId, componentId, UPLOAD), formData).then(()=> dispatch({
+ type: actionTypes.SNMP_POLL_UPLOADED, data: {filename: formData.get('upload').name}
+ }));
+};
+
+let deleteSnmpTrapFile = (dispatch, {softwareProductId, componentId}) => {
+ RestAPIUtil.destroy(snmpTrapUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({
+ type: actionTypes.SNMP_TRAP_DELETED
+ }));
+};
+
+let deleteSnmpPollFile = (dispatch, {softwareProductId, componentId}) => {
+ RestAPIUtil.destroy(snmpPollUrl(softwareProductId, componentId, !UPLOAD)).then(()=> dispatch({
+ type: actionTypes.SNMP_POLL_DELETED
+ }));
+};
+
+const SoftwareProductComponentsMonitoringAction = {
+
+ fetchExistingFiles(dispatch, {softwareProductId, componentId}){
+ RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/snmp`).then(response =>
+ dispatch({
+ type: actionTypes.SNMP_FILES_DATA_CHANGE,
+ data: {trapFilename: response.snmpTrap, pollFilename: response.snmpPoll}
+ })
+ );
+ },
+
+ uploadSnmpFile(dispatch, {softwareProductId, componentId, formData, type}){
+ if (formData.get('upload').size) {
+ if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) {
+ uploadSnmpTrapFile(dispatch, {softwareProductId, componentId, formData});
+ }
+ else {
+ uploadSnmpPollFile(dispatch, {softwareProductId, componentId, formData});
+ }
+ }
+ else {
+ onInvalidFileSizeUpload(dispatch);
+ }
+ },
+
+ deleteSnmpFile(dispatch, {softwareProductId, componentId, type}){
+ if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) {
+ deleteSnmpTrapFile(dispatch, {softwareProductId, componentId});
+ }
+ else {
+ deleteSnmpPollFile(dispatch, {softwareProductId, componentId});
+ }
+ }
+
+};
+
+export default SoftwareProductComponentsMonitoringAction;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js
new file mode 100644
index 0000000..eeececb
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringConstants.js
@@ -0,0 +1,38 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+
+ SNMP_FILES_DATA_CHANGE: null,
+
+ SNMP_TRAP_UPLOADED: null,
+ SNMP_POLL_UPLOADED: null,
+
+ SNMP_TRAP_DELETED: null,
+ SNMP_POLL_DELETED: null
+});
+
+export default keyMirror({
+ SNMP_TRAP: null,
+ SNMP_POLL: null
+});
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js
new file mode 100644
index 0000000..72e0a85
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringReducer.js
@@ -0,0 +1,54 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentsMonitoringConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.SNMP_FILES_DATA_CHANGE:
+ return {
+ ...state,
+ trapFilename: action.data.trapFilename,
+ pollFilename: action.data.pollFilename
+ };
+ case actionTypes.SNMP_TRAP_UPLOADED:
+ return {
+ ...state,
+ trapFilename: action.data.filename
+ };
+ case actionTypes.SNMP_POLL_UPLOADED:
+ return {
+ ...state,
+ pollFilename: action.data.filename
+ };
+ case actionTypes.SNMP_TRAP_DELETED:
+ return {
+ ...state,
+ trapFilename: undefined
+ };
+ case actionTypes.SNMP_POLL_DELETED:
+ return {
+ ...state,
+ pollFilename: undefined
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx
new file mode 100644
index 0000000..ca090c5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/monitoring/SoftwareProductComponentsMonitoringView.jsx
@@ -0,0 +1,117 @@
+import React, {Component, PropTypes} from 'react';
+import Dropzone from 'react-dropzone';
+import ButtonGroup from 'react-bootstrap/lib/ButtonGroup.js';
+import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar.js';
+import Button from 'react-bootstrap/lib/Button.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import SoftwareProductComponentsMonitoringConstants from './SoftwareProductComponentsMonitoringConstants.js';
+
+class SoftwareProductComponentsMonitoringView extends Component {
+
+ static propTypes = {
+ isReadOnlyMode: PropTypes.bool,
+ trapFilename: PropTypes.string,
+ pollFilename: PropTypes.string,
+ softwareProductId: PropTypes.string,
+
+ onDropMibFileToUpload: PropTypes.func,
+ onDeleteSnmpFile: PropTypes.func
+ };
+
+ state = {
+ dragging: false
+ };
+
+
+ render() {
+ return (
+ <div className='vsp-component-monitoring'>
+ {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_TRAP)}
+ {this.renderDropzoneWithType(SoftwareProductComponentsMonitoringConstants.SNMP_POLL)}
+ </div>
+ );
+ }
+
+ renderDropzoneWithType(type) {
+ let {isReadOnlyMode, trapFilename, pollFilename} = this.props;
+ let fileName;
+ if (type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP) {
+ fileName = trapFilename;
+ }
+ else {
+ fileName = pollFilename;
+ }
+ let refAndName = `fileInput${type.toString()}`;
+ let typeDisplayName = this.getFileTypeDisplayName(type);
+ return (
+ <Dropzone
+ className={`snmp-dropzone ${this.state.dragging ? 'active-dragging' : ''}`}
+ onDrop={files => this.handleImport(files, {isReadOnlyMode, type, refAndName})}
+ onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)}
+ onDragLeave={() => this.setState({dragging:false})}
+ multiple={false}
+ disableClick={true}
+ ref={refAndName}
+ name={refAndName}
+ accept='.zip'
+ disabled>
+ <div className='draggable-wrapper'>
+ <div className='section-title'>{typeDisplayName}</div>
+ {fileName ? this.renderUploadedFileName(fileName, type) : this.renderUploadButton(refAndName)}
+ </div>
+ </Dropzone>
+ );
+ }
+
+ renderUploadButton(refAndName) {
+ let {isReadOnlyMode} = this.props;
+ return (
+ <div
+ className={`software-product-landing-view-top-block-col-upl${isReadOnlyMode ? ' disabled' : ''}`}>
+ <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
+ <div className='or-text'>{i18n('or')}</div>
+ <div className='upload-btn primary-btn' onClick={() => this.refs[refAndName].open()}>
+ <span className='primary-btn-text'>{i18n('Select file')}</span>
+ </div>
+ </div>
+ );
+ }
+
+ renderUploadedFileName(filename, type) {
+ return (
+ <ButtonToolbar>
+ <ButtonGroup>
+ <Button disabled>{filename}</Button>
+ <Button className='delete-button' onClick={()=>this.props.onDeleteSnmpFile(type)}>X</Button>
+ </ButtonGroup>
+ </ButtonToolbar>
+ );
+ }
+
+
+ handleOnDragEnter(isReadOnlyMode) {
+ if (!isReadOnlyMode) {
+ this.setState({dragging: true});
+ }
+ }
+
+ handleImport(files, {isReadOnlyMode, type, refAndName}) {
+ if (isReadOnlyMode) {
+ return;
+ }
+
+ this.setState({dragging: false});
+ let file = files[0];
+ let formData = new FormData();
+ formData.append('upload', file);
+ this.refs[refAndName].value = '';
+ this.props.onDropMibFileToUpload(formData, type);
+ }
+
+ getFileTypeDisplayName(type) {
+ return type === SoftwareProductComponentsMonitoringConstants.SNMP_TRAP ? 'SNMP Trap' : 'SNMP Poll';
+ }
+
+}
+
+export default SoftwareProductComponentsMonitoringView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js
new file mode 100644
index 0000000..a412456
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js
@@ -0,0 +1,54 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js';
+import SoftwareProductComponentsNICEditorView from './SoftwareProductComponentsNICEditorView.jsx';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+export const mapStateToProps = ({softwareProduct}) => {
+
+ let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct;
+
+ let {network: {nicEditor = {}}} = softwareProductComponents;
+ let {data, qdata, qschema} = nicEditor;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+
+ return {
+ currentSoftwareProduct,
+ isValidityData,
+ data,
+ qdata,
+ qschema,
+ isReadOnlyMode
+ };
+
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onDataChanged: deltaData => SoftwareProductComponentsNetworkActionHelper.updateNICData(dispatch, {deltaData}),
+ onSubmit: ({data, qdata}) => SoftwareProductComponentsNetworkActionHelper.saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}),
+ onCancel: () => SoftwareProductComponentsNetworkActionHelper.closeNICEditor(dispatch),
+ onQDataChanged: ({data}) => SoftwareProductComponentsNetworkActionHelper.updateNICQuestionnaire(dispatch, {data})
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentsNICEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js
new file mode 100644
index 0000000..d49f9cc
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorReducer.js
@@ -0,0 +1,49 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.NICEditor.OPEN:
+ return {
+ ...state,
+ data: action.nic
+ };
+ case actionTypes.NICEditor.CLOSE:
+ return {};
+ case actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE:
+ return {
+ ...state,
+ qdata: action.payload.qdata || state.qdata,
+ qschema: action.payload.qschema || state.qschema
+ };
+ case actionTypes.NICEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx
new file mode 100644
index 0000000..7393a83
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditorView.jsx
@@ -0,0 +1,322 @@
+import React from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+
+class SoftwareProductComponentsNetworkEditorView extends React.Component {
+
+ render() {
+ let {onCancel, isReadOnlyMode} = this.props;
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => onCancel() }
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ className='vsp-components-network-editor'>
+ {this.renderEditorFields()}
+ </ValidationForm>
+ );
+ }
+
+ renderEditorFields() {
+ let {data = {}, qdata = {}, qschema = {}, onQDataChanged, onDataChanged, isReadOnlyMode} = this.props;
+ let {name, description, networkName} = data;
+ let netWorkValues = [{
+ enum: networkName,
+ title: networkName
+ }];
+ return(
+ <div className='editor-data'>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Name')}
+ value={name}
+ disabled={true}
+ type='text'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Purpose of NIC')}
+ value={description}
+ onChange={description => onDataChanged({description})}
+ disabled={isReadOnlyMode}
+ type='textarea'/>
+ </div>
+ </div>
+ <ValidationForm
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ schema={qschema}
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={false}>
+ <div className='row'>
+ <div className='part-title'>{i18n('Protocols')}</div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Protocols')}
+ type='select'
+ pointer='/protocols/protocols'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Protocol with Highest Traffic Profile')}
+ type='select'
+ pointer='/protocols/protocolWithHighestTrafficProfile'/>
+ </div>
+ </div>
+ <div className='row'>
+ <div className='part-title'>{i18n('IP Configuration')}</div>
+ <div className='col-md-3'>
+ <ValidationInput
+ label={i18n('IPv4 Required')}
+ type='checkbox'
+ pointer='/ipConfiguration/ipv4Required'/>
+ </div>
+ <div className='col-md-9'>
+ <ValidationInput
+ label={i18n('IPv6 Required')}
+ type='checkbox'
+ pointer='/ipConfiguration/ipv6Required'/>
+ </div>
+ </div>
+ </ValidationForm>
+ <div className='row'>
+ <div className='part-title'>{i18n('Network')}</div>
+ <div className='col-md-2'>
+ <ValidationInput
+ label={i18n('Internal')}
+ disabled
+ checked={true}
+ className='network-radio disabled'
+ type='radio'/>
+ </div>
+ <div className='col-md-4'>
+ <ValidationInput
+ label={i18n('External')}
+ disabled
+ checked={false}
+ className='network-radio disabled'
+ type='radio'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Network')}
+ type='select'
+ disabled={true}
+ values={netWorkValues}/>
+ </div>
+ </div>
+ <ValidationForm
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ schema={qschema}
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={false}>
+ <div className='row'>
+ <div className='part-title'>{i18n('Sizing')}</div>
+ <div className='col-md-12'>
+ <ValidationInput
+ label={i18n('Describe Quality of Service')}
+ type='textarea'
+ pointer='/sizing/describeQualityOfService'/>
+ </div>
+ </div>
+
+ <div className='row'>
+ <div className='part-title'>{i18n('Inflow Traffic per second')}</div>
+ </div>
+
+ <div className='row'>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Packets')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/inflowTrafficPerSecond/packets/peak'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/inflowTrafficPerSecond/packets/avg'/>
+ </div>
+ </div>
+ </div>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Bytes')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/inflowTrafficPerSecond/bytes/peak'/>
+
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/inflowTrafficPerSecond/bytes/avg'/>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='row'>
+ <div className='part-title'>{i18n('Outflow Traffic per second')}</div>
+ </div>
+
+ <div className='row'>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Packets')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/outflowTrafficPerSecond/packets/peak'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/outflowTrafficPerSecond/packets/avg'/>
+
+ </div>
+ </div>
+ </div>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Bytes')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/outflowTrafficPerSecond/bytes/peak'/>
+
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/outflowTrafficPerSecond/bytes/avg'/>
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='row'>
+ <div className='part-title'>{i18n('Flow Length')}</div>
+ </div>
+
+ <div className='row'>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Packets')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/flowLength/packets/peak'/>
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/flowLength/packets/avg'/>
+ </div>
+ </div>
+ </div>
+ <div className='col-md-6'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Bytes')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Peak')}
+ type='text'
+ pointer='/sizing/flowLength/bytes/peak'/>
+
+ </div>
+ <div className='col-md-6'>
+ <ValidationInput
+ label={i18n('Avg')}
+ type='text'
+ pointer='/sizing/flowLength/bytes/avg'/>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div className='row'>
+ <div className='col-md-9'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Acceptable Jitter')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-4'>
+ <ValidationInput
+ label={i18n('Min')}
+ type='text'
+ pointer='/sizing/acceptableJitter/mean'/>
+ </div>
+ <div className='col-md-4'>
+ <ValidationInput
+ label={i18n('Max')}
+ type='text'
+ pointer='/sizing/acceptableJitter/max'/>
+ </div>
+ <div className='col-md-4'>
+ <ValidationInput
+ label={i18n('Var')}
+ type='text'
+ pointer='/sizing/acceptableJitter/variable'/>
+ </div>
+ </div>
+ </div>
+ <div className='col-md-3'>
+ <div className='row'>
+ <div className='part-title-small'>{i18n('Acceptable Packet Loss %')}</div>
+ </div>
+ <div className='row'>
+ <div className='col-md-12'>
+ <ValidationInput
+ label={i18n('In Percent')}
+ type='text'
+ pointer='/sizing/acceptablePacketLoss'/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ValidationForm>
+ </div>
+
+ );
+ }
+ submit() {
+ let {data, qdata, onSubmit} = this.props;
+ onSubmit({data, qdata});
+ }
+}
+
+export default SoftwareProductComponentsNetworkEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js
new file mode 100644
index 0000000..bc53e1a
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICListReducer.js
@@ -0,0 +1,33 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.NIC_LIST_UPDATE:
+ return [...action.response];
+ case actionTypes.NIC_LIST_EDIT:
+ const indexForEdit = state.findIndex(nic => nic.id === action.nic.id);
+ return [...state.slice(0, indexForEdit), action.nic, ...state.slice(indexForEdit + 1)];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js
new file mode 100644
index 0000000..8ff6b50
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkActionHelper.js
@@ -0,0 +1,129 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+import {actionTypes} from './SoftwareProductComponentsNetworkConstants.js';
+
+function baseUrl(softwareProductId, componentId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/nics`;
+}
+
+
+function fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire${versionQuery}`);
+}
+
+function fetchNIC({softwareProductId, componentId, nicId, version}) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}/${nicId}${versionQuery}`);
+}
+
+function fetchNICsList({softwareProductId, componentId, version}) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`);
+}
+
+function saveNIC({softwareProductId, componentId, nic: {id, name, description, networkId}}) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${id}`,{
+ name,
+ description,
+ networkId
+ });
+}
+
+function saveNICQuestionnaire({softwareProductId, componentId, nicId, qdata}) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${nicId}/questionnaire`, qdata);
+}
+
+const SoftwareProductComponentNetworkActionHelper = {
+
+ fetchNICsList(dispatch, {softwareProductId, componentId, version}) {
+ return fetchNICsList({softwareProductId, componentId, version}).then((response) => {
+ dispatch({
+ type: actionTypes.NIC_LIST_UPDATE,
+ response: response.results
+ });
+ });
+ },
+
+ openNICEditor(dispatch, {nic = {}, data = {}}) {
+ dispatch({
+ type: actionTypes.NICEditor.OPEN,
+ nic: {...data, id: nic.id}
+ });
+ },
+
+ closeNICEditor(dispatch) {
+ dispatch({
+ type: actionTypes.NICEditor.CLOSE
+ });
+ },
+
+ loadNICData({softwareProductId, componentId, nicId, version}) {
+ return fetchNIC({softwareProductId, componentId, nicId, version});
+ },
+
+ loadNICQuestionnaire(dispatch, {softwareProductId, componentId, nicId, version}) {
+ return fetchNICQuestionnaire({softwareProductId, componentId, nicId, version}).then((response) => {
+ dispatch({
+ type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE,
+ payload: {
+ qdata: response.data ? JSON.parse(response.data) : {},
+ qschema: JSON.parse(response.schema)
+ }
+ });
+ });
+ },
+
+ updateNICData(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.NICEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ updateNICQuestionnaire(dispatch, {data}) {
+ dispatch({
+ type: actionTypes.NICEditor.NIC_QUESTIONNAIRE_UPDATE,
+ payload: {
+ qdata: data
+ }
+ });
+ },
+
+ saveNICDataAndQuestionnaire(dispatch, {softwareProductId, componentId, data, qdata}) {
+ SoftwareProductComponentNetworkActionHelper.closeNICEditor(dispatch);
+ return Promise.all([
+ saveNICQuestionnaire({softwareProductId, componentId, nicId: data.id, qdata}),
+ saveNIC({softwareProductId, componentId, nic: data}).then(() => {
+ dispatch({
+ type: actionTypes.NIC_LIST_EDIT,
+ nic: data
+ });
+ })
+ ]);
+ }
+};
+
+export default SoftwareProductComponentNetworkActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js
new file mode 100644
index 0000000..193f4b2
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js
@@ -0,0 +1,33 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ NIC_LIST_EDIT: null,
+ NIC_LIST_UPDATE: null,
+
+ NICEditor: {
+ OPEN: null,
+ CLOSE: null,
+ NIC_QUESTIONNAIRE_UPDATE: null,
+ DATA_CHANGED: null
+ }
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js
new file mode 100644
index 0000000..9172dc6
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkList.js
@@ -0,0 +1,86 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js';
+import SoftwareProductComponentsNetworkListView from './SoftwareProductComponentsNetworkListView.jsx';
+import SoftwareProductComponentsNetworkActionHelper from './SoftwareProductComponentsNetworkActionHelper.js';
+
+
+export const mapStateToProps = ({softwareProduct}) => {
+
+ let {softwareProductEditor: {data: currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents} = softwareProduct;
+ let {network: {nicEditor = {}, nicList = []}, componentEditor: {data: componentData, qdata, qschema}} = softwareProductComponents;
+ let {data} = nicEditor;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+ let {version} = currentSoftwareProduct;
+ let manualMode = nicList.length <= 0;
+ let isModalInEditMode = true;
+
+ return {
+ version,
+ componentData,
+ qdata,
+ qschema,
+ isValidityData,
+ nicList,
+ isDisplayModal: Boolean(data),
+ isModalInEditMode,
+ manualMode,
+ isReadOnlyMode
+ };
+
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}),
+ onAddNIC: () => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch),
+ onEditNicClick: (nic, version) => {
+ Promise.all([
+ SoftwareProductComponentsNetworkActionHelper.loadNICData({
+ softwareProductId,
+ componentId,
+ nicId: nic.id,
+ version
+ }),
+ SoftwareProductComponentsNetworkActionHelper.loadNICQuestionnaire(dispatch, {
+ softwareProductId,
+ componentId,
+ nicId: nic.id,
+ version
+ })
+ ]).then(
+ ([{data}]) => SoftwareProductComponentsNetworkActionHelper.openNICEditor(dispatch, {nic, data})
+ );
+ },
+ onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch,
+ {softwareProductId,
+ vspComponentId: componentId,
+ qdata});
+ }
+
+
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsNetworkListView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx
new file mode 100644
index 0000000..b3e17ff
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkListView.jsx
@@ -0,0 +1,136 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+
+import SoftwareProductComponentsNICEditor from './SoftwareProductComponentsNICEditor.js';
+
+class SoftwareProductComponentsNetworkView extends React.Component {
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ let {qdata, qschema, onQDataChanged, isModalInEditMode, isDisplayModal, softwareProductId, componentId, isReadOnlyMode} = this.props;
+
+ return(
+ <div className='vsp-components-network'>
+ <div className='network-data'>
+ <div>
+ <ValidationForm
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ isReadOnlyMode={isReadOnlyMode}
+ schema={qschema}
+ hasButtons={false}>
+ <h3 className='section-title'>{i18n('Network Capacity')}</h3>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ label={i18n('Protocol with Highest Traffic Profile across all NICs')}
+ type='select'
+ pointer='/network/networkCapacity/protocolWithHighestTrafficProfileAcrossAllNICs'/>
+ </div>
+ <div className='single-col add-line-break'>
+ <ValidationInput
+ label={i18n('Network Transactions per Second')}
+ type='text'
+ pointer='/network/networkCapacity/networkTransactionsPerSecond'/>
+ </div>
+ <div className='empty-two-col' />
+ </div>
+ </div>
+
+ </ValidationForm>
+ </div>
+ {this.renderNicList()}
+ </div>
+ <Modal show={isDisplayModal} bsSize='large' animation={true} className='network-nic-modal'>
+ <Modal.Header>
+ <Modal.Title>{isModalInEditMode ? i18n('Edit NIC') : i18n('Create New NIC')}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {
+ <SoftwareProductComponentsNICEditor
+ softwareProductId={softwareProductId}
+ componentId={componentId}
+ isReadOnlyMode={isReadOnlyMode}/>
+ }
+ </Modal.Body>
+ </Modal>
+ </div>
+ );
+ }
+
+ renderNicList() {
+ const {localFilter} = this.state;
+ let {onAddNIC, manualMode, isReadOnlyMode} = this.props;
+ let onAdd = manualMode ? onAddNIC : false;
+ return (
+ <ListEditorView
+ title={i18n('Interfaces')}
+ plusButtonTitle={i18n('Add NIC')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter NICs by Name')}
+ onAdd={onAdd}
+ isReadOnlyMode={isReadOnlyMode}
+ onFilter={filter => this.setState({localFilter: filter})}>
+ {!manualMode && this.filterList().map(nic => this.renderNicListItem(nic, isReadOnlyMode))}
+ </ListEditorView>
+ );
+ }
+
+ renderNicListItem(nic, isReadOnlyMode) {
+ let {id, name, description, networkName = ''} = nic;
+ let {onEditNicClick, version} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}
+ onSelect={() => onEditNicClick(nic, version)}>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='name'>{name}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Purpose of NIC')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Network')}</div>
+ <div className='artifact-name'>{networkName}</div>
+ </div>
+
+ </ListEditorItemView>
+ );
+ }
+
+ filterList() {
+ let {nicList} = this.props;
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return nicList.filter(({name = '', description = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return nicList;
+ }
+ }
+
+ save() {
+ let {onSubmit, qdata} = this.props;
+ return onSubmit({qdata});
+ }
+}
+
+export default SoftwareProductComponentsNetworkView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js
new file mode 100644
index 0000000..d535a34
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesActionHelper.js
@@ -0,0 +1,145 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {actionTypes} from './SoftwareProductComponentProcessesConstants.js';
+
+function baseUrl(softwareProductId, componentId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${softwareProductId}/components/${componentId}/processes`;
+}
+
+function fetchProcessesList({softwareProductId, componentId, version}) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId, componentId)}${versionQuery}`);
+}
+
+function deleteProcess({softwareProductId, componentId, processId}) {
+ return RestAPIUtil.destroy(`${baseUrl(softwareProductId, componentId)}/${processId}`);
+}
+
+function putProcess({softwareProductId, componentId, process}) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId, componentId)}/${process.id}`, {
+ name: process.name,
+ description: process.description
+ });
+}
+
+function postProcess({softwareProductId,componentId, process}) {
+ return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}`, {
+ name: process.name,
+ description: process.description
+ });
+}
+
+function uploadFileToProcess({softwareProductId, processId, componentId, formData}) {
+ return RestAPIUtil.create(`${baseUrl(softwareProductId, componentId)}/${processId}/upload`, formData);
+}
+
+
+
+const SoftwareProductComponentProcessesActionHelper = {
+ fetchProcessesList(dispatch, {softwareProductId, componentId, version}) {
+ dispatch({
+ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES,
+ processesList: []
+ });
+
+ return fetchProcessesList({softwareProductId, componentId, version}).then(response => {
+ dispatch({
+ type: actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES,
+ processesList: response.results
+ });
+ });
+ },
+
+ deleteProcess(dispatch, {process, softwareProductId, componentId}) {
+ return deleteProcess({softwareProductId, processId:process.id, componentId}).then(() => {
+ dispatch({
+ type: actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS,
+ processId: process.id
+ });
+ });
+
+ },
+
+ saveProcess(dispatch, {softwareProductId, componentId, previousProcess, process}) {
+ if (previousProcess) {
+ return putProcess({softwareProductId,componentId, process}).then(() => {
+ if (process.formData && process.formData.name !== previousProcess.artifactName){
+ uploadFileToProcess({softwareProductId, processId: process.id, formData: process.formData, componentId});
+ }
+ dispatch({
+ type: actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS,
+ process
+ });
+ });
+ }
+ else {
+ return postProcess({softwareProductId, componentId, process}).then(response => {
+ if (process.formData) {
+ uploadFileToProcess({softwareProductId, processId: response.value, formData: process.formData, componentId});
+ }
+ dispatch({
+ type: actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS,
+ process: {
+ ...process,
+ id: response.value
+ }
+ });
+ });
+ }
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM,
+ processToDelete: false
+ });
+ },
+
+ openDeleteProcessesConfirm(dispatch, {process} ) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM,
+ processToDelete: process
+ });
+ },
+
+ openEditor(dispatch, process = {}) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN,
+ process
+ });
+ },
+ closeEditor(dispatch) {
+ dispatch({
+ type:actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE
+ });
+ },
+ processEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.processEditor.DATA_CHANGED,
+ deltaData
+ });
+ }
+};
+
+export default SoftwareProductComponentProcessesActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js
new file mode 100644
index 0000000..78a111a
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js
@@ -0,0 +1,34 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null,
+ EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null,
+ DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS: null,
+ SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN: null,
+ SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE: null,
+ FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES: null,
+ SOFTWARE_PRODUCT_PROCESS_DELETE_COMPONENTS_CONFIRM: null,
+ processEditor: {
+ DATA_CHANGED: null
+ }
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js
new file mode 100644
index 0000000..0138023
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditor.js
@@ -0,0 +1,54 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper';
+import SoftwareProductComponentProcessesEditorView from './SoftwareProductComponentProcessesEditorView.jsx';
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductComponents: {componentProcesses = {}}} = softwareProduct;
+ let {processesList = [], processesEditor = {}} = componentProcesses;
+ let {data} = processesEditor;
+
+ let previousData;
+ const processId = data ? data.id : null;
+ if(processId) {
+ previousData = processesList.find(process => process.id === processId);
+ }
+
+ return {
+ data,
+ previousData
+ };
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId, componentId}) => {
+
+ return {
+ onDataChanged: deltaData => SoftwareProductComponentProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}),
+ onCancel: () => SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch),
+ onSubmit: ({previousProcess, process}) => {
+ SoftwareProductComponentProcessesActionHelper.closeEditor(dispatch);
+ SoftwareProductComponentProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, componentId, process});
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductComponentProcessesEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js
new file mode 100644
index 0000000..f859f69
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorReducer.js
@@ -0,0 +1,44 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentProcessesConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_OPEN:
+ return {
+ ...state,
+ data: action.process
+ };
+ case actionTypes.SOFTWARE_PRODUCT_PROCESS_COMPONENTS_EDITOR_CLOSE:
+ return {};
+
+ case actionTypes.processEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx
new file mode 100644
index 0000000..ca6d843
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesEditorView.jsx
@@ -0,0 +1,124 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Dropzone from 'react-dropzone';
+
+
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+
+const SoftwareProductProcessEditorPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ artifactName: React.PropTypes.string
+});
+
+class SoftwareProductProcessesEditorView extends React.Component {
+
+ state = {
+ dragging: false,
+ files: []
+ };
+
+ static propTypes = {
+ data: SoftwareProductProcessEditorPropType,
+ previousData: SoftwareProductProcessEditorPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ onDataChanged: React.PropTypes.func,
+ onSubmit: React.PropTypes.func,
+ onCancel: React.PropTypes.func
+ };
+
+ render() {
+ let {isReadOnlyMode, onCancel, onDataChanged, data = {}} = this.props;
+ let {name, description, artifactName} = data;
+
+ return (
+ <div>
+ <ValidationForm
+ ref='validationForm'
+ isReadOnlyMode={isReadOnlyMode}
+ hasButtons={true}
+ labledButtons={true}
+ onSubmit={ () => this.submit() }
+ onReset={ () => onCancel() }
+ className='vsp-processes-editor'>
+ <div className={`vsp-processes-editor-data${isReadOnlyMode ? ' disabled' : '' }`}>
+ <Dropzone
+ className={`vsp-process-dropzone-view ${this.state.dragging ? 'active-dragging' : ''}`}
+ onDrop={files => this.handleImportSubmit(files)}
+ onDragEnter={() => this.setState({dragging:true})}
+ onDragLeave={() => this.setState({dragging:false})}
+ multiple={false}
+ disableClick={true}
+ ref='processEditorFileInput'
+ name='processEditorFileInput'
+ accept='*.*'>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ onChange={name => onDataChanged({name})}
+ label={i18n('Name')}
+ value={name}
+ validations={{validateName: true, maxLength: 120, required: true}}
+ type='text'/>
+ <ValidationInput
+ label={i18n('Artifacts')}
+ value={artifactName}
+ type='text'
+ disabled/>
+ </div>
+ <div className='col-md-6'>
+ <div className='file-upload-box'>
+ <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
+ <div className='or-text'>{i18n('or')}</div>
+ <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}>
+ <span className='primary-btn-text'>{i18n('Select file')}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <ValidationInput
+ onChange={description => onDataChanged({description})}
+ label={i18n('Notes')}
+ value={description}
+ name='vsp-process-description'
+ className='vsp-process-description'
+ validations={{maxLength: 1000}}
+ type='textarea'/>
+ </Dropzone>
+ </div>
+ </ValidationForm>
+ </div>
+ );
+ }
+
+ submit() {
+ const {data: process, previousData: previousProcess} = this.props;
+ let {files} = this.state;
+ let formData = new FormData();
+ if (files.length) {
+ let file = files[0];
+ formData.append('upload', file);
+ }
+
+ let updatedProcess = {
+ ...process,
+ formData: files.length ? formData : false
+ };
+ this.props.onSubmit({process: updatedProcess, previousProcess});
+ }
+
+
+ handleImportSubmit(files) {
+ let {onDataChanged} = this.props;
+ this.setState({
+ dragging: false,
+ complete: '0',
+ files
+ });
+ onDataChanged({artifactName: files[0].name});
+ }
+}
+
+export default SoftwareProductProcessesEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js
new file mode 100644
index 0000000..5f69328
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesList.js
@@ -0,0 +1,54 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js';
+
+import SoftwareProductComponentsProcessesListView from './SoftwareProductComponentsProcessesListView.jsx';
+
+const mapStateToProps = ({softwareProduct}) => {
+
+ let {softwareProductEditor: {data:currentSoftwareProduct = {}, isValidityData = true}, softwareProductComponents: {componentProcesses = {}}} = softwareProduct;
+ let{processesList = [], processesEditor = {}} = componentProcesses;
+ let {data} = processesEditor;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+
+ return {
+ currentSoftwareProduct,
+ isValidityData,
+ processesList,
+ isDisplayModal: Boolean(data),
+ isModalInEditMode: Boolean(data && data.id),
+ isReadOnlyMode
+ };
+
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId}) => {
+
+ return {
+ onAddProcess: () => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch),
+ onEditProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openEditor(dispatch, process),
+ onDeleteProcessClick: (process) => SoftwareProductComponentProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId})
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductComponentsProcessesListView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js
new file mode 100644
index 0000000..4bb124d
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesListReducer.js
@@ -0,0 +1,37 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductComponentProcessesConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.FETCH_SOFTWARE_PRODUCT_COMPONENTS_PROCESSES:
+ return [...action.processesList];
+ case actionTypes.EDIT_SOFTWARE_PRODUCT_COMPONENTS_PROCESS:
+ const indexForEdit = state.findIndex(process => process.id === action.process.id);
+ return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)];
+ case actionTypes.ADD_SOFTWARE_PRODUCT_COMPONENTS_PROCESS:
+ return [...state, action.process];
+ case actionTypes.DELETE_SOFTWARE_PRODUCT_COMPONENTS_PROCESS:
+ return state.filter(process => process.id !== action.processId);
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx
new file mode 100644
index 0000000..48fa862
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesConfirmationModal.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import SoftwareProductComponentProcessesActionHelper from './SoftwareProductComponentProcessesActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function renderMsg(processToDelete) {
+ let name = processToDelete ? processToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{name}"?', {name});
+ return (
+ <div>
+ <p>{msg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor, softwareProductComponents} = softwareProduct;
+ let {componentProcesses} = softwareProductComponents;
+ let {processToDelete} = componentProcesses;
+ let softwareProductId = softwareProductEditor.data.id;
+ const show = processToDelete !== false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: renderMsg(processToDelete),
+ confirmationDetails: {processToDelete, softwareProductId}
+ };
+};
+
+const mapActionsToProps = (dispatch,{componentId, softwareProductId}) => {
+ return {
+ onConfirmed: ({processToDelete}) => {
+ SoftwareProductComponentProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId, componentId});
+ SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ SoftwareProductComponentProcessesActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx
new file mode 100644
index 0000000..a8b07e9
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentsProcessesListView.jsx
@@ -0,0 +1,125 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import SoftwareProductProcessesEditor from './SoftwareProductComponentProcessesEditor.js';
+import SoftwareProductComponentsProcessesConfirmationModal from './SoftwareProductComponentsProcessesConfirmationModal.jsx';
+
+class SoftwareProductProcessesView extends React.Component {
+
+ state = {
+ localFilter: ''
+ };
+
+ static propTypes = {
+ onAddProcess: React.PropTypes.func,
+ onEditProcessClick: React.PropTypes.func,
+ onDeleteProcessClick: React.PropTypes.func,
+ isDisplayModal: React.PropTypes.bool,
+ isModalInEditMode: React.PropTypes.bool,
+ onStorageSelect: React.PropTypes.func,
+ componentId: React.PropTypes.string,
+ softwareProductId: React.PropTypes.string
+ };
+
+ render() {
+ let { softwareProductId, componentId} = this.props;
+
+ return (
+ <div className='vsp-processes-page'>
+ <div className='software-product-view'>
+ <div className='software-product-landing-view-right-side flex-column'>
+ {this.renderEditor()}
+ {this.renderProcessList()}
+ </div>
+ <SoftwareProductComponentsProcessesConfirmationModal
+ componentId={componentId}
+ softwareProductId={softwareProductId}/>
+ </div>
+ </div>
+ );
+ }
+
+ renderEditor() {
+ let {softwareProductId, componentId, isReadOnlyMode, isDisplayModal, isModalInEditMode} = this.props;
+ return (
+ <Modal show={isDisplayModal} bsSize='large' animation={true}>
+ <Modal.Header>
+ <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body className='edit-process-modal'>
+ <SoftwareProductProcessesEditor
+ componentId={componentId}
+ softwareProductId={softwareProductId}
+ isReadOnlyMode={isReadOnlyMode}/>
+ </Modal.Body>
+ </Modal>
+
+ );
+ }
+
+ renderProcessList() {
+ const {localFilter} = this.state;
+ let {onAddProcess, isReadOnlyMode} = this.props;
+ return (
+ <div className='processes-list'>
+ <ListEditorView
+ plusButtonTitle={i18n('Add Component Process Details')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter Process')}
+ onAdd={onAddProcess}
+ isReadOnlyMode={isReadOnlyMode}
+ onFilter={filter => this.setState({localFilter: filter})}>
+ {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))}
+ </ListEditorView>
+ </div>
+ );
+ }
+
+ renderProcessListItem(process, isReadOnlyMode) {
+ let {id, name, description, artifactName = ''} = process;
+ let {onEditProcessClick, onDeleteProcessClick} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}
+ onSelect={() => onEditProcessClick(process)}
+ onDelete={() => onDeleteProcessClick(process)}>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='name'>{name}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Artifact name')}</div>
+ <div className='artifact-name'>{artifactName}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Notes')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+
+ filterList() {
+ let {processesList} = this.props;
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return processesList.filter(({name = '', description = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return processesList;
+ }
+ }
+}
+
+export default SoftwareProductProcessesView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js
new file mode 100644
index 0000000..fbd3f81
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorage.js
@@ -0,0 +1,48 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+import SoftwareProductComponentsActionHelper from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponentsActionHelper.js';
+import SoftwareProductComponentStorageView from './SoftwareProductComponentStorageView.jsx';
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentVSP}, softwareProductComponents} = softwareProduct;
+ let {componentEditor: {data: componentData , qdata, qschema}} = softwareProductComponents;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentVSP);
+
+ return {
+ componentData,
+ qdata,
+ qschema,
+ isReadOnlyMode
+ };
+};
+
+const mapActionToProps = (dispatch, {softwareProductId, componentId}) => {
+ return {
+ onQDataChanged: ({data}) => SoftwareProductComponentsActionHelper.componentQuestionnaireUpdated(dispatch, {data}),
+ onSubmit: ({qdata}) => { return SoftwareProductComponentsActionHelper.updateSoftwareProductComponentQuestionnaire(dispatch, {softwareProductId, vspComponentId: componentId, qdata});}
+ };
+};
+
+export default connect(mapStateToProps, mapActionToProps, null, {withRef: true}) (SoftwareProductComponentStorageView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx
new file mode 100644
index 0000000..9c9600c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/components/storage/SoftwareProductComponentStorageView.jsx
@@ -0,0 +1,124 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from'nfvo-components/input/validation/ValidationInput.jsx';
+
+
+class SoftwareProductComponentStorageView extends React.Component {
+
+ static propTypes = {
+ componentId: React.PropTypes.string,
+ onQDataChanged: React.PropTypes.func,
+ onSubmit: React.PropTypes.func,
+ isReadOnlyMode: React.PropTypes.bool
+ };
+
+ render() {
+ let {qdata, qschema, onQDataChanged, onSubmit, isReadOnlyMode} = this.props;
+
+ return(
+ <div className='vsp-component-questionnaire-view'>
+ <ValidationForm
+ ref='storageValidationForm'
+ hasButtons={false}
+ onSubmit={() => onSubmit({qdata})}
+ className='component-questionnaire-validation-form'
+ isReadOnlyMode={isReadOnlyMode}
+ onDataChanged={onQDataChanged}
+ data={qdata}
+ schema={qschema}>
+
+ <div className='section-title'>{i18n('Backup')}</div>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <div className='vertical-flex'>
+ <label key='label' className='control-label'>{i18n('Backup Type')}</label>
+ <div className='radio-options-content-row'>
+ <ValidationInput
+ label={i18n('On Site')}
+ type='radiogroup'
+ pointer={'/storage/backup/backupType'}
+ className='radio-field'/>
+ </div>
+ </div>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Backup Solution')}
+ pointer={'/storage/backup/backupSolution'}
+ className='section-field'/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Backup Storage Size (GB)')}
+ pointer={'/storage/backup/backupStorageSize'}
+ className='section-field'/>
+ </div>
+ <ValidationInput
+ type='select'
+ label={i18n('Backup NIC')}
+ pointer={'/storage/backup/backupNIC'}
+ className='section-field'/>
+ </div>
+ </div>
+
+ <div className='section-title'>{i18n('Snapshot Backup')}</div>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Snapshot Frequency (hours)')}
+ pointer={'/storage/snapshotBackup/snapshotFrequency'}
+ className='section-field'/>
+ </div>
+ <div className='empty-two-col' />
+ <div className='empty-col' />
+ </div>
+ </div>
+
+ <div className='section-title'>{i18n('Log Backup')}</div>
+ <div className='rows-section'>
+ <div className='row-flex-components input-row'>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Size of Log Files (GB)')}
+ pointer={'/storage/logBackup/sizeOfLogFiles'}
+ className='section-field'/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Log Retention Period (days)')}
+ pointer={'/storage/logBackup/logRetentionPeriod'}
+ className='section-field'/>
+ </div>
+ <div className='single-col'>
+ <ValidationInput
+ type='text'
+ label={i18n('Log Backup Frequency (days)')}
+ pointer={'/storage/logBackup/logBackupFrequency'}
+ className='section-field'/>
+ </div>
+ <ValidationInput
+ type='text'
+ label={i18n('Log File Location')}
+ pointer={'/storage/logBackup/logFileLocation'}
+ className='section-field'/>
+ </div>
+ </div>
+ </ValidationForm>
+ </div>
+ );
+ }
+
+ save(){
+ return this.refs.storageValidationForm.handleFormSubmit(new Event('dummy'));
+ }
+}
+
+export default SoftwareProductComponentStorageView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js
new file mode 100644
index 0000000..46308f0
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js
@@ -0,0 +1,49 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import SoftwareProductCreationActionHelper from './SoftwareProductCreationActionHelper.js';
+import SoftwareProductCreationView from './SoftwareProductCreationView.jsx';
+
+const mapStateToProps = ({finalizedLicenseModelList, softwareProduct: {softwareProductCreation, softwareProductCategories} }) => {
+ return {
+ data: softwareProductCreation.data,
+ softwareProductCategories,
+ finalizedLicenseModelList
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onDataChanged: deltaData => SoftwareProductCreationActionHelper.changeData(dispatch, {deltaData}),
+ onCancel: () => SoftwareProductCreationActionHelper.resetData(dispatch),
+ onSubmit: (softwareProduct) => {
+ SoftwareProductCreationActionHelper.resetData(dispatch);
+ SoftwareProductCreationActionHelper.createSoftwareProduct(dispatch, {softwareProduct}).then(softwareProductId => {
+ let {vendorId: licenseModelId, licensingVersion} = softwareProduct;
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {softwareProductId, licenseModelId, licensingVersion});
+ });
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductCreationView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js
new file mode 100644
index 0000000..f4e51f1
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationActionHelper.js
@@ -0,0 +1,77 @@
+/*-
+ * ============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=========================================================
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js';
+import {actionTypes} from './SoftwareProductCreationConstants.js';
+
+
+function baseUrl() {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/`;
+}
+
+function createSoftwareProduct(softwareProduct) {
+ return RestAPIUtil.create(baseUrl(), {
+ ...softwareProduct,
+ icon: 'icon',
+ licensingData: {}
+ });
+}
+
+const SoftwareProductCreationActionHelper = {
+
+ open(dispatch) {
+ SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
+ dispatch({
+ type: actionTypes.OPEN
+ });
+ },
+
+ resetData(dispatch) {
+ dispatch({
+ type: actionTypes.RESET_DATA
+ });
+ },
+
+ changeData(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ createSoftwareProduct(dispatch, {softwareProduct}) {
+ return createSoftwareProduct(softwareProduct).then(response => {
+ SoftwareProductActionHelper.addSoftwareProduct(dispatch, {
+ softwareProduct: {
+ ...softwareProduct,
+ id: response.vspId
+ }
+ });
+ return response.vspId;
+ });
+ }
+
+};
+
+export default SoftwareProductCreationActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js
new file mode 100644
index 0000000..0a9cdb9
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationConstants.js
@@ -0,0 +1,27 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ OPEN: null,
+ RESET_DATA: null,
+ DATA_CHANGED: null
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js
new file mode 100644
index 0000000..5e3db09
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationReducer.js
@@ -0,0 +1,44 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductCreationConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.OPEN:
+ return {
+ ...state,
+ data: {},
+ showModal: true
+ };
+ case actionTypes.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ case actionTypes.RESET_DATA:
+ return {};
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx
new file mode 100644
index 0000000..2c8f243
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreationView.jsx
@@ -0,0 +1,123 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+
+import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js';
+
+
+const SoftwareProductPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ category: React.PropTypes.string,
+ subCategory: React.PropTypes.string,
+ vendorId: React.PropTypes.string
+});
+
+class SoftwareProductCreationView extends React.Component {
+
+ static propTypes = {
+ data: SoftwareProductPropType,
+ finalizedLicenseModelList: React.PropTypes.array,
+ softwareProductCategories: React.PropTypes.array,
+ onDataChanged: React.PropTypes.func.isRequired,
+ onSubmit: React.PropTypes.func.isRequired,
+ onCancel: React.PropTypes.func.isRequired
+ };
+
+ render() {
+ let {softwareProductCategories, data = {}, onDataChanged, onCancel} = this.props;
+ let {name, description, vendorId, subCategory} = data;
+
+ const vendorList = this.getVendorList();
+
+ return (
+ <div className='software-product-creation-page'>
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ onSubmit={() => this.submit() }
+ onReset={() => onCancel() }
+ labledButtons={true}>
+ <div className='software-product-form-row'>
+ <div className='software-product-inline-section'>
+ <ValidationInput
+ value={name}
+ label={i18n('Name')}
+ ref='software-product-name'
+ onChange={name => onDataChanged({name})}
+ validations={{validateName: true, maxLength: 25, required: true}}
+ type='text'
+ className='field-section'/>
+ <ValidationInput
+ onEnumChange={vendorId => onDataChanged({vendorId})}
+ value={vendorId}
+ label={i18n('Vendor')}
+ values={vendorList}
+ validations={{required: true}}
+ type='select'
+ className='field-section'/>
+ <ValidationInput
+ label={i18n('Category')}
+ type='select'
+ value={subCategory}
+ onChange={subCategory => this.onSelectSubCategory(subCategory)}
+ validations={{required: true}}
+ className='options-input-category'>
+ <option key='' value=''>{i18n('please select…')}</option>
+ {softwareProductCategories.map(category =>
+ category.subcategories &&
+ <optgroup
+ key={category.name}
+ label={category.name}>{category.subcategories.map(sub =>
+ <option key={sub.uniqueId} value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)}
+ </optgroup>)
+ }
+ </ValidationInput>
+ </div>
+ <div className='software-product-inline-section'>
+ <ValidationInput
+ value={description}
+ label={i18n('Description')}
+ ref='description'
+ onChange={description => onDataChanged({description})}
+ validations={{freeEnglishText: true, maxLength: 1000, required: true}}
+ type='textarea'
+ className='field-section'/>
+ </div>
+ </div>
+ </ValidationForm>
+ </div>
+ );
+ }
+
+ getVendorList() {
+ let {finalizedLicenseModelList} = this.props;
+
+ return [{enum: '', title: i18n('please select...')}].concat(finalizedLicenseModelList.map(vendor => {
+ return {
+ enum: vendor.id,
+ title: vendor.vendorName
+ };
+ }));
+ }
+
+ onSelectSubCategory(subCategory) {
+ let {softwareProductCategories, onDataChanged} = this.props;
+ let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories);
+ onDataChanged({category, subCategory});
+ }
+
+ create(){
+ this.refs.validationForm.handleFormSubmit(new Event('dummy'));
+ }
+
+ submit() {
+ const {data:softwareProduct, finalizedLicenseModelList} = this.props;
+ softwareProduct.vendorName = finalizedLicenseModelList.find(vendor => vendor.id === softwareProduct.vendorId).vendorName;
+ this.props.onSubmit(softwareProduct);
+ }
+}
+
+export default SoftwareProductCreationView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js
new file mode 100644
index 0000000..16a100c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetails.js
@@ -0,0 +1,66 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js';
+import SoftwareProductDetailsView from './SoftwareProductDetailsView.jsx';
+
+export const mapStateToProps = ({finalizedLicenseModelList, softwareProduct, licenseModel: {licenseAgreement, featureGroup}}) => {
+ let {softwareProductEditor: {data: currentSoftwareProduct}, softwareProductCategories, softwareProductQuestionnaire} = softwareProduct;
+ let {licensingData = {}, licensingVersion} = currentSoftwareProduct;
+ let licenseAgreementList = [], filteredFeatureGroupsList = [];
+ if(licensingVersion && licensingVersion !== '') {
+ licenseAgreementList = licenseAgreement.licenseAgreementList;
+ let selectedLicenseAgreement = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement);
+ if (selectedLicenseAgreement) {
+ let featureGroupsList = featureGroup.featureGroupsList.filter(({referencingLicenseAgreements}) => referencingLicenseAgreements.includes(selectedLicenseAgreement.id));
+ if (featureGroupsList.length) {
+ filteredFeatureGroupsList = featureGroupsList.map(featureGroup => ({enum: featureGroup.id, title: featureGroup.name}));
+ }
+ }
+ }
+ let {qdata, qschema} = softwareProductQuestionnaire;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+
+ return {
+ currentSoftwareProduct,
+ softwareProductCategories,
+ licenseAgreementList,
+ featureGroupsList: filteredFeatureGroupsList,
+ finalizedLicenseModelList,
+ qdata,
+ qschema,
+ isReadOnlyMode
+ };
+};
+
+export const mapActionsToProps = (dispatch) => {
+ return {
+ onDataChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorDataChanged(dispatch, {deltaData}),
+ onVendorParamChanged: deltaData => SoftwareProductActionHelper.softwareProductEditorVendorChanged(dispatch, {deltaData}),
+ onQDataChanged: ({data}) => SoftwareProductActionHelper.softwareProductQuestionnaireUpdate(dispatch, {data}),
+ onValidityChanged: isValidityData => SoftwareProductActionHelper.setIsValidityData(dispatch, {isValidityData}),
+ onSubmit: (softwareProduct, qdata) =>{ return SoftwareProductActionHelper.updateSoftwareProduct(dispatch, {softwareProduct, qdata});}
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductDetailsView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js
new file mode 100644
index 0000000..e060706
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsReducer.js
@@ -0,0 +1,63 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.softwareProductEditor.OPEN:
+ return {
+ ...state,
+ data: {}
+ };
+ case actionTypes.softwareProductEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ case actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION:
+ return {
+ ...state,
+ uploadData:action.uploadData
+ };
+ case actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED:
+ return {
+ ...state,
+ isValidityData: action.isValidityData
+ };
+ case actionTypes.softwareProductEditor.CLOSE:
+ return {};
+ case actionTypes.SOFTWARE_PRODUCT_LOADED:
+ return {
+ ...state,
+ data: action.response
+ };
+ case actionTypes.TOGGLE_NAVIGATION_ITEM:
+ return {
+ ...state,
+ mapOfExpandedIds: action.mapOfExpandedIds
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx
new file mode 100644
index 0000000..75a5797
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/details/SoftwareProductDetailsView.jsx
@@ -0,0 +1,264 @@
+import React, {Component, PropTypes} from 'react';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Form from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js';
+
+class SoftwareProductDetails extends Component {
+
+ static propTypes = {
+ vendorName: PropTypes.string,
+ currentSoftwareProduct: PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ description: PropTypes.string,
+ category: PropTypes.string,
+ subCategory: PropTypes.string,
+ vendorId: PropTypes.string,
+ vendorName: PropTypes.string,
+ licensingVersion: PropTypes.string,
+ licensingData: PropTypes.shape({
+ licenceAgreement: PropTypes.string,
+ featureGroups: PropTypes.array
+ })
+ }),
+ softwareProductCategories: PropTypes.array,
+ finalizedLicenseModelList: PropTypes.array,
+ licenseAgreementList: PropTypes.array,
+ featureGroupsList: PropTypes.array,
+ onSubmit: PropTypes.func.isRequired,
+ onDataChanged: PropTypes.func.isRequired,
+ onValidityChanged: PropTypes.func.isRequired,
+ qdata: PropTypes.object.isRequired,
+ qschema: PropTypes.object.isRequired,
+ onQDataChanged: PropTypes.func.isRequired,
+ onVendorParamChanged: PropTypes.func.isRequired
+ };
+
+ state = {
+ licensingVersionsList: []
+ };
+
+ render() {
+ let {softwareProductCategories, finalizedLicenseModelList, onDataChanged, featureGroupsList, licenseAgreementList, currentSoftwareProduct} = this.props;
+ let {name, description, vendorId, licensingVersion, subCategory, licensingData = {}} = currentSoftwareProduct;
+ let licensingVersionsList = this.state.licensingVersionsList.length > 0 ? this.state.licensingVersionsList : this.refreshVendorVersionsList(vendorId);
+ let {qdata, qschema, onQDataChanged} = this.props;
+ let {isReadOnlyMode} = this.props;
+
+ return (
+ <div className='vsp-details-page'>
+ <Form
+ ref='validationForm'
+ className='vsp-general-tab'
+ hasButtons={false}
+ onSubmit={() => this.props.onSubmit(currentSoftwareProduct, qdata)}
+ onValidityChanged={(isValidityData) => this.props.onValidityChanged(isValidityData)}
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='section-title general'>{i18n('General')}</div>
+ <div className='vsp-general-tab-inline-section'>
+ <div className='vsp-general-tab-sub-section'>
+ <ValidationInput
+ label={i18n('Name')}
+ type='text'
+ value={name}
+ onChange={name => onDataChanged({name})}
+ validations={{validateName: true, maxLength: 120, required: true}}
+ className='field-section'/>
+ <ValidationInput
+ label={i18n('Vendor')}
+ type='select'
+ selectedEnum={vendorId}
+ onEnumChange={vendorId => this.onVendorParamChanged({vendorId})}
+ className='field-section'>
+ {finalizedLicenseModelList.map(lm => <option key={lm.id} value={lm.id}>{lm.vendorName}</option>)}
+ </ValidationInput>
+ <div className='input-row'>
+ <ValidationInput
+ label={i18n('Category')}
+ type='select'
+ selectedEnum={subCategory}
+ onEnumChange={subCategory => this.onSelectSubCategory(subCategory)}
+ className='field-section'>
+ {
+ softwareProductCategories.map(category =>
+ category.subcategories &&
+ <optgroup
+ key={category.name}
+ label={category.name}>{category.subcategories.map(sub =>
+ <option
+ key={sub.uniqueId}
+ value={sub.uniqueId}>{`${sub.name} (${category.name})`}</option>)}
+ </optgroup>
+ )
+ }
+ </ValidationInput>
+ </div>
+ </div>
+ <div className='vsp-general-tab-sub-section input-row'>
+ <ValidationInput
+ label={i18n('Description')}
+ type='textarea'
+ value={description}
+ onChange={description => onDataChanged({description})}
+ className='field-section'
+ validations={{required: true}}/>
+ </div>
+ </div>
+ <div className='vsp-general-tab-section licenses'>
+ <div className='section-title'>{i18n('Licenses')}</div>
+ <div className='vsp-general-tab-inline-section input-row'>
+ <ValidationInput
+ onEnumChange={licensingVersion => this.onVendorParamChanged({vendorId, licensingVersion})}
+ selectedEnum={licensingVersion}
+ label={i18n('Licensing Version')}
+ values={licensingVersionsList}
+ type='select'
+ className='field-section'/>
+ <ValidationInput
+ label={i18n('License Agreement')}
+ type='select'
+ selectedEnum={licensingData.licenseAgreement}
+ className='field-section'
+ onEnumChange={(licenseAgreement) => this.onLicensingDataChanged({licenseAgreement, featureGroups: []})}>
+ <option key='placeholder' value=''>{i18n('Select...')}</option>
+ {licenseAgreementList.map(la => <option value={la.id} key={la.id}>{la.name}</option>)}
+ </ValidationInput>
+ </div>
+ <div className='vsp-general-tab-inline-section input-row'>
+ {licensingData.licenseAgreement && (
+ <ValidationInput
+ type='select'
+ isMultiSelect={true}
+ onEnumChange={featureGroups => this.onFeatureGroupsChanged({featureGroups})}
+ multiSelectedEnum={licensingData.featureGroups}
+ name='feature-groups'
+ label={i18n('Feature Groups')}
+ clearable={false}
+ values={featureGroupsList}/>)
+ }
+ </div>
+ </div>
+ </Form>
+ <Form
+ data={qdata}
+ schema={qschema}
+ onDataChanged={onQDataChanged}
+ className='vsp-general-tab'
+ hasButtons={false}
+ isReadOnlyMode={isReadOnlyMode}>
+ <div className='vsp-general-tab-section'>
+ <div className='section-title'> {i18n('Availability')} </div>
+ <div className='vsp-general-tab-inline-section'>
+ <div className='vsp-general-tab-sub-section input-row'>
+ <ValidationInput
+ label={i18n('Use Availability Zones for High Availability')}
+ type='checkbox'
+ pointer='/general/availability/useAvailabilityZonesForHighAvailability'/>
+ </div>
+ </div>
+ <div className='section-title'> {i18n('Regions')} </div>
+ <div className='vsp-general-tab-inline-section'>
+ <div className='vsp-general-tab-sub-section input-row'>
+ <ValidationInput
+ type='select'
+ laebl='Ziv'
+ pointer='/general/regionsData/regions'/>
+ </div>
+ </div>
+ <div className='section-title'> {i18n('Storage Data Replication')} </div>
+ <div className='vsp-general-tab-inline-section'>
+ <div className='vsp-general-tab-sub-section'>
+ <ValidationInput
+ label={i18n('Storage Replication Size (GB)')}
+ type='text'
+ pointer='/general/storageDataReplication/storageReplicationSize'
+ className='field-section'/>
+ <ValidationInput
+ label={i18n('Storage Replication Source')}
+ type='text'
+ pointer='/general/storageDataReplication/storageReplicationSource'
+ className='field-section'/>
+ </div>
+ <div className='vsp-general-tab-sub-section'>
+ <ValidationInput
+ label={i18n('Storage Replication Frequency (minutes)')}
+ type='text'
+ pointer='/general/storageDataReplication/storageReplicationFrequency'
+ className='field-section'/>
+ <ValidationInput
+ label={i18n('Storage Replication Destination')}
+ type='text'
+ pointer='/general/storageDataReplication/storageReplicationDestination'
+ className='field-section'/>
+ </div>
+ </div>
+ </div>
+ </Form>
+ </div>
+ );
+ }
+
+ onVendorParamChanged({vendorId, licensingVersion}) {
+ let {finalizedLicenseModelList, onVendorParamChanged} = this.props;
+ if(!licensingVersion) {
+ const licensingVersionsList = this.refreshVendorVersionsList(vendorId);
+ licensingVersion = licensingVersionsList.length > 0 ? licensingVersionsList[0].enum : '';
+ }
+ let vendorName = finalizedLicenseModelList.find(licenseModelItem => licenseModelItem.id === vendorId).vendorName || '';
+ let deltaData = {
+ vendorId,
+ vendorName,
+ licensingVersion,
+ licensingData: {}
+ };
+ onVendorParamChanged(deltaData);
+ }
+
+ refreshVendorVersionsList(vendorId) {
+ if(!vendorId) {
+ return [];
+ }
+
+ let {finalVersions} = this.props.finalizedLicenseModelList.find(vendor => vendor.id === vendorId);
+
+ let licensingVersionsList = [{
+ enum: '',
+ title: i18n('Select...')
+ }];
+ if(finalVersions) {
+ finalVersions.forEach(version => licensingVersionsList.push({
+ enum: version,
+ title: version
+ }));
+ }
+
+ return licensingVersionsList;
+ }
+
+ onSelectSubCategory(subCategory) {
+ let {softwareProductCategories, onDataChanged} = this.props;
+ let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(subCategory, softwareProductCategories);
+ onDataChanged({category, subCategory});
+ }
+
+ onFeatureGroupsChanged({featureGroups}) {
+ this.onLicensingDataChanged({featureGroups});
+ }
+
+ onLicensingDataChanged(deltaData) {
+ this.props.onDataChanged({
+ licensingData: {
+ ...this.props.currentSoftwareProduct.licensingData,
+ ...deltaData
+ }
+ });
+ }
+
+ save(){
+ return this.refs.validationForm.handleFormSubmit(new Event('dummy'));
+ }
+}
+
+export default SoftwareProductDetails;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
new file mode 100644
index 0000000..7604f58
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
@@ -0,0 +1,97 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js';
+import LandingPageView from './SoftwareProductLandingPageView.jsx';
+
+const mapStateToProps = ({softwareProduct, licenseModel: {licenseAgreement}}) => {
+ let {softwareProductEditor: {data:currentSoftwareProduct = {}}, softwareProductComponents, softwareProductCategories = []} = softwareProduct;
+ let {licensingData = {}} = currentSoftwareProduct;
+ let {licenseAgreementList} = licenseAgreement;
+ let {componentsList} = softwareProductComponents;
+ let licenseAgreementName = licenseAgreementList.find(la => la.id === licensingData.licenseAgreement);
+ if (licenseAgreementName) {
+ licenseAgreementName = licenseAgreementName.name;
+ }
+
+ let categoryName = '', subCategoryName = '', fullCategoryDisplayName = '';
+ const category = softwareProductCategories.find(ca => ca.uniqueId === currentSoftwareProduct.category);
+ if (category) {
+ categoryName = category.name;
+ const subcategories = category.subcategories || [];
+ const subcat = subcategories.find(sc => sc.uniqueId === currentSoftwareProduct.subCategory);
+ subCategoryName = subcat && subcat.name ? subcat.name : '';
+ }
+ fullCategoryDisplayName = `${subCategoryName} (${categoryName})`;
+
+ const isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+
+ return {
+ currentSoftwareProduct: {
+ ...currentSoftwareProduct,
+ licenseAgreementName,
+ fullCategoryDisplayName
+ },
+ isReadOnlyMode,
+ componentsList
+ };
+};
+
+const mapActionsToProps = (dispatch, {version}) => {
+ return {
+ onDetailsSelect: ({id: softwareProductId, vendorId: licenseModelId}) => OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {
+ softwareProductId,
+ licenseModelId
+ }),
+ onAttachmentsSelect: ({id: softwareProductId}) => OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId}),
+ onUpload: (softwareProductId, formData) =>
+ SoftwareProductActionHelper.uploadFile(dispatch, {
+ softwareProductId,
+ formData,
+ failedNotificationTitle: i18n('Upload validation failed')
+ }),
+ onUploadConfirmation: (softwareProductId, formData) =>
+ SoftwareProductActionHelper.uploadConfirmation(dispatch, {
+ softwareProductId,
+ formData,
+ failedNotificationTitle: i18n('Upload validation failed')}),
+
+ onInvalidFileSizeUpload: () => dispatch({
+ type: NotificationConstants.NOTIFY_ERROR,
+ data: {
+ title: i18n('Upload Failed'),
+ msg: i18n('no zip file was uploaded or zip file doesn\'t exist')
+ }
+ }),
+ onComponentSelect: ({id: softwareProductId, componentId}) => {
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {softwareProductId, componentId, version });
+ },
+ /** for the next version */
+ onAddComponent: () => SoftwareProductActionHelper.addComponent(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(LandingPageView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx
new file mode 100644
index 0000000..4a84883
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageUploadConfirmationModal.jsx
@@ -0,0 +1,38 @@
+import {connect} from 'react-redux';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor} = softwareProduct;
+ let {uploadData} = softwareProductEditor;
+ const show = uploadData ? true : false;
+ return {
+ show,
+ title: 'Warning!',
+ type: 'warning',
+ msg: i18n('Upload will erase existing data. Do you want to continue?'),
+ confirmationDetails: {uploadData}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({uploadData}) => {
+ let {softwareProductId, formData, failedNotificationTitle} = uploadData;
+ SoftwareProductActionHelper.uploadFile(dispatch, {
+ softwareProductId,
+ formData,
+ failedNotificationTitle
+ });
+ SoftwareProductActionHelper.hideUploadConfirm(dispatch);
+ },
+ onDeclined: () => {
+ SoftwareProductActionHelper.hideUploadConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx
new file mode 100644
index 0000000..cf7c7a3
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx
@@ -0,0 +1,272 @@
+import React from 'react';
+import classnames from 'classnames';
+import Dropzone from 'react-dropzone';
+
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import FontAwesome from 'react-fontawesome';
+import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx';
+
+
+const SoftwareProductPropType = React.PropTypes.shape({
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ version: React.PropTypes.string,
+ id: React.PropTypes.string,
+ categoryId: React.PropTypes.string,
+ vendorId: React.PropTypes.string,
+ status: React.PropTypes.string,
+ licensingData: React.PropTypes.object,
+ validationData: React.PropTypes.object
+});
+
+const ComponentPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ displayName: React.PropTypes.string,
+ description: React.PropTypes.string
+});
+
+class SoftwareProductLandingPageView extends React.Component {
+
+ state = {
+ localFilter: '',
+ fileName: '',
+ dragging: false,
+ files: []
+ };
+
+ static propTypes = {
+ currentSoftwareProduct: SoftwareProductPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ componentsList: React.PropTypes.arrayOf(ComponentPropType),
+ onDetailsSelect: React.PropTypes.func,
+ onAttachmentsSelect: React.PropTypes.func,
+ onUpload: React.PropTypes.func,
+ onUploadConfirmation: React.PropTypes.func,
+ onInvalidFileSizeUpload: React.PropTypes.func,
+ onComponentSelect: React.PropTypes.func,
+ onAddComponent: React.PropTypes.func
+ };
+
+ render() {
+ let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props;
+ return (
+ <div className='software-product-landing-wrapper'>
+ <Dropzone
+ className={classnames('software-product-landing-view', {'active-dragging': this.state.dragging})}
+ onDrop={files => this.handleImportSubmit(files, isReadOnlyMode)}
+ onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)}
+ onDragLeave={() => this.setState({dragging:false})}
+ multiple={false}
+ disableClick={true}
+ ref='fileInput'
+ name='fileInput'
+ accept='.zip'
+ disabled>
+ <div className='draggable-wrapper'>
+ <div className='software-product-landing-view-top'>
+ <div className='row'>
+ {this.renderProductSummary(currentSoftwareProduct)}
+ {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)}
+ </div>
+ </div>
+ </div>
+ </Dropzone>
+ {
+ componentsList.length > 0 && this.renderComponents()
+ }
+ <SoftwareProductLandingPageUploadConfirmationModal confirmationButtonText={i18n('Continue')}/>
+ </div>
+ );
+ }
+
+ handleOnDragEnter(isReadOnlyMode) {
+ if (!isReadOnlyMode) {
+ this.setState({dragging: true});
+ }
+ }
+
+ renderProductSummary(currentSoftwareProduct) {
+ let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct;
+ let {onDetailsSelect} = this.props;
+ return (
+ <div className='details-panel'>
+ <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div>
+ <div
+ className='software-product-landing-view-top-block clickable'
+ onClick={() => onDetailsSelect(currentSoftwareProduct)}>
+ <div className='details-container'>
+ <div className='single-detail-section title-section'>
+ <div>
+ <div>{name}</div>
+ </div>
+ </div>
+ <div className='multiple-details-section'>
+ <div className='detail-col' >
+ <div className='title'>{i18n('Vendor')}</div>
+ <div className='description'>{vendorName}</div>
+ </div>
+ <div className='detail-col'>
+ <div className='title'>{i18n('Category')}</div>
+ <div className='description'>{fullCategoryDisplayName}</div>
+ </div>
+ <div className='detail-col'>
+ <div className='title extra-large'>{i18n('License Agreement')}</div>
+ <div className='description'>
+ {this.renderLicenseAgreement(licenseAgreementName)}
+ </div>
+ </div>
+ </div>
+ <div className='single-detail-section'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderProductDetails(currentSoftwareProduct, isReadOnlyMode) {
+ let {validationData} = currentSoftwareProduct;
+ let {onAttachmentsSelect} = this.props;
+ let details = {
+ heatTemplates: validationData ? '1' : '0',
+ images: '0',
+ otherArtifacts: '0'
+ };
+
+ return (
+ <div className='details-panel'>
+ <div className='software-product-landing-view-heading-title'>{i18n('Software Product Attachments')}</div>
+ <div className='software-product-landing-view-top-block'>
+ <div
+ className='software-product-landing-view-top-block-col'
+ onClick={() => onAttachmentsSelect(currentSoftwareProduct)}>
+ <div>
+ <div className='attachment-details'>{i18n('HEAT Templates')} (<span
+ className='attachment-details-count'>{details.heatTemplates}</span>)
+ </div>
+ <div className='attachment-details'>{i18n('Images')} (<span
+ className='attachment-details-count'>{details.images}</span>)
+ </div>
+ <div className='attachment-details'>{i18n('Other Artifacts')} (<span
+ className='attachment-details-count'>{details.otherArtifacts}</span>)
+ </div>
+ </div>
+ </div>
+ <div
+ className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}>
+ <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
+ <div className='or-text'>{i18n('or')}</div>
+ <div className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}>
+ <span className='primary-btn-text'>{i18n('Select file')}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderComponents() {
+ const {localFilter} = this.state;
+
+ return (
+ <ListEditorView
+ title={i18n('Virtual Function Components')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter Components')}
+ onFilter={filter => this.setState({localFilter: filter})}>
+ {this.filterList().map(component => this.renderComponentsListItem(component))}
+ </ListEditorView>
+ );
+ }
+
+ renderComponentsListItem(component) {
+ let {id: componentId, name, displayName, description = ''} = component;
+ let {currentSoftwareProduct: {id}, onComponentSelect} = this.props;
+ return (
+ <ListEditorItemView
+ key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()}
+ className='list-editor-item-view'
+ onSelect={() => onComponentSelect({id, componentId})}>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Component')}</div>
+ <div className='name'>{displayName}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Description')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ renderLicenseAgreement(licenseAgreementName) {
+ if (!licenseAgreementName) {
+ return (<FontAwesome name='exclamation-triangle' className='warning-icon'/>);
+ }
+ return (licenseAgreementName);
+ }
+
+
+ filterList() {
+ let {componentsList = []} = this.props;
+
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return componentsList.filter(({displayName = '', description = ''}) => {
+ return escape(displayName).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return componentsList;
+ }
+ }
+
+ handleImportSubmit(files, isReadOnlyMode) {
+ if (isReadOnlyMode) {
+ return;
+ }
+ if (files[0] && files[0].size) {
+ this.setState({
+ fileName: files[0].name,
+ dragging: false,
+ complete: '0',
+ });
+ this.startUploading(files);
+ }
+ else {
+ this.props.onInvalidFileSizeUpload();
+ }
+
+ }
+
+ startUploading(files) {
+ let {onUpload, currentSoftwareProduct, onUploadConfirmation} = this.props;
+
+ let {validationData} = currentSoftwareProduct;
+
+ if (!(files && files.length)) {
+ return;
+ }
+ let file = files[0];
+ let formData = new FormData();
+ formData.append('upload', file);
+ this.refs.fileInput.value = '';
+
+ if (validationData) {
+ onUploadConfirmation(currentSoftwareProduct.id, formData);
+ }else {
+ onUpload(currentSoftwareProduct.id, formData);
+ }
+
+ }
+}
+
+export default SoftwareProductLandingPageView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js
new file mode 100644
index 0000000..dadc777
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworks.js
@@ -0,0 +1,30 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductNetworksView from './SoftwareProductNetworksView.jsx';
+
+export const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductNetworks: {networksList = []}} = softwareProduct;
+ return {networksList};
+};
+
+export default connect(mapStateToProps, null, null, {withRef: true})(SoftwareProductNetworksView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js
new file mode 100644
index 0000000..d0e29bc
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksActionHelper.js
@@ -0,0 +1,47 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductNetworksConstants.js';
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+function baseUrl(svpId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${svpId}/networks`;
+}
+
+
+function fetchNetworksList(softwareProductId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`);
+}
+
+const SoftwareProductNetworksActionHelper = {
+ fetchNetworksList(dispatch, {softwareProductId, version}) {
+ return fetchNetworksList(softwareProductId, version).then(response => {
+ dispatch({
+ type: actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS,
+ networksList: response.results
+ });
+ });
+ }
+};
+
+export default SoftwareProductNetworksActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js
new file mode 100644
index 0000000..d428d21
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksConstants.js
@@ -0,0 +1,25 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ FETCH_SOFTWARE_PRODUCT_NETWORKS: null,
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js
new file mode 100644
index 0000000..0c9c623
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksListReducer.js
@@ -0,0 +1,30 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductNetworksConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.FETCH_SOFTWARE_PRODUCT_NETWORKS:
+ return [...action.networksList];
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx
new file mode 100644
index 0000000..bd47467
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/networks/SoftwareProductNetworksView.jsx
@@ -0,0 +1,73 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+class SoftwareProductNetworksView extends React.Component {
+
+ static propTypes = {
+ networksList: React.PropTypes.arrayOf(React.PropTypes.shape({
+ id: React.PropTypes.string.isRequired,
+ name: React.PropTypes.string.isRequired,
+ dhcp: React.PropTypes.bool.isRequired
+ })).isRequired
+ };
+
+ state = {
+ localFilter: ''
+ };
+
+ render() {
+ const {localFilter} = this.state;
+
+ return (
+ <div className='vsp-networks-page'>
+ <ListEditorView
+ title={i18n('Networks')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter Networks')}
+ onFilter={filter => this.setState({localFilter: filter})}>
+ {this.filterList().map(network => this.renderNetworksListItem(network))}
+ </ListEditorView>
+ </div>
+ );
+ }
+
+ renderNetworksListItem(network) {
+ let {id, name, dhcp} = network;
+ return (
+ <ListEditorItemView
+ key={id}
+ className='list-editor-item-view'
+ isReadOnlyMode={true}>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='name'>{name}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('DHCP')}</div>
+ <div className='artifact-name'>{dhcp ? i18n('YES') : i18n('NO')}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ filterList() {
+ let {networksList} = this.props;
+
+ let {localFilter} = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return networksList.filter(({name = ''}) => {
+ return escape(name).match(filter);
+ });
+ }
+ else {
+ return networksList;
+ }
+ }
+}
+
+export default SoftwareProductNetworksView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js
new file mode 100644
index 0000000..5c3a8da
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcesses.js
@@ -0,0 +1,49 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import VersionControllerUtils from 'nfvo-components/panel/versionController/VersionControllerUtils.js';
+
+import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js';
+import SoftwareProductProcessesView from './SoftwareProductProcessesView.jsx';
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor: {data: currentSoftwareProduct = {}}, softwareProductProcesses: {processesList, processesEditor}} = softwareProduct;
+ let isReadOnlyMode = VersionControllerUtils.isReadOnly(currentSoftwareProduct);
+ let {data} = processesEditor;
+
+ return {
+ currentSoftwareProduct,
+ processesList,
+ isDisplayEditor: Boolean(data),
+ isModalInEditMode: Boolean(data && data.id),
+ isReadOnlyMode
+ };
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId}) => {
+ return {
+ onAddProcess: () => SoftwareProductProcessesActionHelper.openEditor(dispatch),
+ onEditProcess: (process) => SoftwareProductProcessesActionHelper.openEditor(dispatch, process),
+ onDeleteProcess: (process) => SoftwareProductProcessesActionHelper.openDeleteProcessesConfirm(dispatch, {process, softwareProductId})
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(SoftwareProductProcessesView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js
new file mode 100644
index 0000000..df5d08f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesActionHelper.js
@@ -0,0 +1,151 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductProcessesConstants.js';
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+
+function baseUrl(svpId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/vendor-software-products/${svpId}/processes`;
+}
+
+function putProcess(softwareProductId, process) {
+ return RestAPIUtil.save(`${baseUrl(softwareProductId)}/${process.id}`, {
+ name: process.name,
+ description: process.description
+ });
+}
+
+function postProcess(softwareProductId, process) {
+ return RestAPIUtil.create(`${baseUrl(softwareProductId)}`, {
+ name: process.name,
+ description: process.description
+ });
+}
+
+function deleteProcess(softwareProductId, processId) {
+ return RestAPIUtil.destroy(`${baseUrl(softwareProductId)}/${processId}`);
+}
+
+function uploadFileToProcess(softwareProductId, processId, formData)
+{
+ return RestAPIUtil.create(`${baseUrl(softwareProductId)}/${processId}/upload`, formData);
+}
+
+function fetchProcesses(softwareProductId, version) {
+ let versionQuery = version ? `?version=${version}` : '';
+ return RestAPIUtil.fetch(`${baseUrl(softwareProductId)}${versionQuery}`);
+}
+
+
+
+const SoftwareProductActionHelper = {
+
+ fetchProcessesList(dispatch, {softwareProductId, version}) {
+
+ dispatch({
+ type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES,
+ processesList: []
+ });
+
+ return fetchProcesses(softwareProductId, version).then(response => {
+ dispatch({
+ type: actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES,
+ processesList: response.results
+ });
+ });
+ },
+ openEditor(dispatch, process = {}) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN,
+ process
+ });
+ },
+
+ deleteProcess(dispatch, {process, softwareProductId}) {
+ return deleteProcess(softwareProductId, process.id).then(() => {
+ dispatch({
+ type: actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS,
+ processId: process.id
+ });
+ });
+
+ },
+
+ closeEditor(dispatch) {
+ dispatch({
+ type:actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE
+ });
+ },
+
+ processEditorDataChanged(dispatch, {deltaData}) {
+ dispatch({
+ type: actionTypes.processEditor.DATA_CHANGED,
+ deltaData
+ });
+ },
+
+ saveProcess(dispatch, {softwareProductId, previousProcess, process}) {
+ if (previousProcess) {
+ return putProcess(softwareProductId, process).then(() => {
+ if (process.formData){
+ uploadFileToProcess(softwareProductId, process.id, process.formData);
+ }
+ dispatch({
+ type: actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS,
+ process
+ });
+ });
+ }
+ else {
+ return postProcess(softwareProductId, process).then(response => {
+ if (process.formData) {
+ uploadFileToProcess(softwareProductId, response.value, process.formData);
+ }
+ dispatch({
+ type: actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS,
+ process: {
+ ...process,
+ id: response.value
+ }
+ });
+ });
+ }
+ },
+
+ hideDeleteConfirm(dispatch) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM,
+ processToDelete: false
+ });
+ },
+
+ openDeleteProcessesConfirm(dispatch, {process} ) {
+ dispatch({
+ type: actionTypes.SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM,
+ processToDelete: process
+ });
+ }
+
+};
+
+export default SoftwareProductActionHelper;
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx
new file mode 100644
index 0000000..0159352
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConfirmationModal.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import {connect} from 'react-redux';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ConfirmationModalView from 'nfvo-components/confirmations/ConfirmationModalView.jsx';
+import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper.js';
+
+function renderMsg(processToDelete) {
+ let name = processToDelete ? processToDelete.name : '';
+ let msg = i18n('Are you sure you want to delete "{name}"?', {name});
+ return (
+ <div>
+ <p>{msg}</p>
+ </div>
+ );
+};
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductEditor, softwareProductProcesses} = softwareProduct;
+ let {processToDelete} = softwareProductProcesses;
+ let softwareProductId = softwareProductEditor.data.id;
+
+ const show = processToDelete !== false;
+ return {
+ show,
+ title: i18n('Warning!'),
+ type: 'warning',
+ msg: renderMsg(processToDelete),
+ confirmationDetails: {processToDelete, softwareProductId}
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ onConfirmed: ({processToDelete, softwareProductId}) => {
+ SoftwareProductProcessesActionHelper.deleteProcess(dispatch, {process: processToDelete, softwareProductId});
+ SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch);
+ },
+ onDeclined: () => {
+ SoftwareProductProcessesActionHelper.hideDeleteConfirm(dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(ConfirmationModalView);
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js
new file mode 100644
index 0000000..63f3067
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesConstants.js
@@ -0,0 +1,34 @@
+/*-
+ * ============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=========================================================
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ ADD_SOFTWARE_PRODUCT_PROCESS: null,
+ EDIT_SOFTWARE_PRODUCT_PROCESS: null,
+ DELETE_SOFTWARE_PRODUCT_PROCESS: null,
+ SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN: null,
+ SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE: null,
+ FETCH_SOFTWARE_PRODUCT_PROCESSES: null,
+ SOFTWARE_PRODUCT_PROCESS_DELETE_CONFIRM: null,
+ processEditor: {
+ DATA_CHANGED: null
+ }
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js
new file mode 100644
index 0000000..8dc48c5
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditor.js
@@ -0,0 +1,52 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {connect} from 'react-redux';
+import SoftwareProductProcessesActionHelper from './SoftwareProductProcessesActionHelper';
+import SoftwareProductProcessesEditorView from './SoftwareProductProcessesEditorView.jsx';
+
+const mapStateToProps = ({softwareProduct}) => {
+ let {softwareProductProcesses: {processesList, processesEditor}} = softwareProduct;
+ let {data} = processesEditor;
+
+ let previousData;
+ const processId = data ? data.id : null;
+ if(processId) {
+ previousData = processesList.find(process => process.id === processId);
+ }
+
+ return {
+ data,
+ previousData
+ };
+};
+
+const mapActionsToProps = (dispatch, {softwareProductId}) => {
+ return {
+ onDataChanged: deltaData => SoftwareProductProcessesActionHelper.processEditorDataChanged(dispatch, {deltaData}),
+ onSubmit: ({previousProcess, process}) => {
+ SoftwareProductProcessesActionHelper.closeEditor(dispatch);
+ SoftwareProductProcessesActionHelper.saveProcess(dispatch, {softwareProductId, previousProcess, process});
+ },
+ onClose: () => SoftwareProductProcessesActionHelper.closeEditor(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(SoftwareProductProcessesEditorView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js
new file mode 100644
index 0000000..cae25e2
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorReducer.js
@@ -0,0 +1,44 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductProcessesConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_OPEN:
+ return {
+ ...state,
+ data: action.process
+ };
+ case actionTypes.SOFTWARE_PRODUCT_PROCESS_EDITOR_CLOSE:
+ return {};
+
+ case actionTypes.processEditor.DATA_CHANGED:
+ return {
+ ...state,
+ data: {
+ ...state.data,
+ ...action.deltaData
+ }
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx
new file mode 100644
index 0000000..c2c4aff
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesEditorView.jsx
@@ -0,0 +1,122 @@
+import React from 'react';
+import Dropzone from 'react-dropzone';
+import classnames from 'classnames';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import ValidationForm from 'nfvo-components/input/validation/ValidationForm.jsx';
+import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx';
+
+const SoftwareProductProcessEditorPropType = React.PropTypes.shape({
+ id: React.PropTypes.string,
+ name: React.PropTypes.string,
+ description: React.PropTypes.string,
+ artifactName: React.PropTypes.string
+});
+
+class SoftwareProductProcessesEditorView extends React.Component {
+
+ state = {
+ dragging: false,
+ files: []
+ };
+
+ static propTypes = {
+ data: SoftwareProductProcessEditorPropType,
+ previousData: SoftwareProductProcessEditorPropType,
+ isReadOnlyMode: React.PropTypes.bool,
+ onDataChanged: React.PropTypes.func,
+ onSubmit: React.PropTypes.func,
+ onClose: React.PropTypes.func
+ };
+
+ render() {
+ let {data = {}, isReadOnlyMode, onDataChanged, onClose} = this.props;
+ let {name, description, artifactName} = data;
+ return (
+ <ValidationForm
+ ref='validationForm'
+ hasButtons={true}
+ labledButtons={true}
+ isReadOnlyMode={isReadOnlyMode}
+ onSubmit={ () => this.submit() }
+ onReset={ () => onClose() }
+ className='vsp-processes-editor'>
+ <div className={classnames('vsp-processes-editor-data', {'disabled': isReadOnlyMode})}>
+ <Dropzone
+ className={classnames('vsp-process-dropzone-view', {'active-dragging': this.state.dragging})}
+ onDrop={files => this.handleImportSubmit(files)}
+ onDragEnter={() => this.setState({dragging: true})}
+ onDragLeave={() => this.setState({dragging: false})}
+ multiple={false}
+ disableClick={true}
+ ref='processEditorFileInput'
+ name='processEditorFileInput'
+ accept='*.*'>
+ <div className='row'>
+ <div className='col-md-6'>
+ <ValidationInput
+ onChange={name => onDataChanged({name})}
+ label={i18n('Name')}
+ value={name}
+ validations={{validateName: true, maxLength: 120, required: true}}
+ type='text'/>
+ <ValidationInput
+ label={i18n('Artifacts')}
+ value={artifactName}
+ type='text'
+ disabled/>
+ </div>
+ <div className='col-md-6'>
+ <div className='file-upload-box'>
+ <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
+ <div className='or-text'>{i18n('or')}</div>
+ <div className='upload-btn primary-btn' onClick={() => this.refs.processEditorFileInput.open()}>
+ <span className='primary-btn-text'>{i18n('Select file')}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <ValidationInput
+ onChange={description => onDataChanged({description})}
+ label={i18n('Notes')}
+ value={description}
+ name='vsp-process-description'
+ className='vsp-process-description'
+ validations={{maxLength: 1000}}
+ type='textarea'/>
+ </Dropzone>
+ </div>
+ </ValidationForm>
+ );
+ }
+
+ submit() {
+ const {data: process, previousData: previousProcess} = this.props;
+ let {files} = this.state;
+ let formData = false;
+ if (files.length) {
+ let file = files[0];
+ formData = new FormData();
+ formData.append('upload', file);
+ }
+
+ let updatedProcess = {
+ ...process,
+ formData
+ };
+ this.props.onSubmit({process: updatedProcess, previousProcess});
+ }
+
+
+ handleImportSubmit(files) {
+ let {onDataChanged} = this.props;
+ this.setState({
+ dragging: false,
+ complete: '0',
+ files
+ });
+ onDataChanged({artifactName: files[0].name});
+ }
+}
+
+export default SoftwareProductProcessesEditorView;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js
new file mode 100644
index 0000000..619a2db
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesListReducer.js
@@ -0,0 +1,37 @@
+/*-
+ * ============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=========================================================
+ */
+
+import {actionTypes} from './SoftwareProductProcessesConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.FETCH_SOFTWARE_PRODUCT_PROCESSES:
+ return [...action.processesList];
+ case actionTypes.EDIT_SOFTWARE_PRODUCT_PROCESS:
+ const indexForEdit = state.findIndex(process => process.id === action.process.id);
+ return [...state.slice(0, indexForEdit), action.process, ...state.slice(indexForEdit + 1)];
+ case actionTypes.ADD_SOFTWARE_PRODUCT_PROCESS:
+ return [...state, action.process];
+ case actionTypes.DELETE_SOFTWARE_PRODUCT_PROCESS:
+ return state.filter(process => process.id !== action.processId);
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx
new file mode 100644
index 0000000..a2aa3d4
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/processes/SoftwareProductProcessesView.jsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import Modal from 'nfvo-components/modal/Modal.jsx';
+
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
+
+import SoftwareProductProcessesEditor from './SoftwareProductProcessesEditor.js';
+import SoftwareProductProcessesConfirmationModal from './SoftwareProductProcessesConfirmationModal.jsx';
+
+
+class SoftwareProductProcessesView extends React.Component {
+
+ state = {
+ localFilter: ''
+ };
+
+ static propTypes = {
+ onAddProcess: React.PropTypes.func.isRequired,
+ onEditProcess: React.PropTypes.func.isRequired,
+ onDeleteProcess: React.PropTypes.func.isRequired,
+ isDisplayEditor: React.PropTypes.bool.isRequired,
+ isReadOnlyMode: React.PropTypes.bool.isRequired
+ };
+
+ render() {
+ let { currentSoftwareProduct} = this.props;
+ return (
+ <div className='software-product-landing-view-right-side vsp-processes-page'>
+ {this.renderEditor()}
+ {this.renderProcessList()}
+ <SoftwareProductProcessesConfirmationModal softwareProductId={currentSoftwareProduct.id}/>
+ </div>
+ );
+ }
+
+ renderEditor() {
+ let {currentSoftwareProduct: {id}, isModalInEditMode, isReadOnlyMode, isDisplayEditor} = this.props;
+ return (
+
+ <Modal show={isDisplayEditor} bsSize='large' animation={true}>
+ <Modal.Header>
+ <Modal.Title>{isModalInEditMode ? i18n('Edit Process Details') : i18n('Create New Process Details')}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body className='edit-process-modal'>
+ <SoftwareProductProcessesEditor softwareProductId={id} isReadOnlyMode={isReadOnlyMode}/>
+ </Modal.Body>
+ </Modal>
+ );
+ }
+
+ renderProcessList() {
+ const {localFilter} = this.state;
+ let {onAddProcess, isReadOnlyMode} = this.props;
+
+ return (
+ <ListEditorView
+ plusButtonTitle={i18n('Add Process Details')}
+ filterValue={localFilter}
+ placeholder={i18n('Filter Process')}
+ onAdd={onAddProcess}
+ isReadOnlyMode={isReadOnlyMode}
+ onFilter={filter => this.setState({localFilter: filter})}>
+ {this.filterList().map(processes => this.renderProcessListItem(processes, isReadOnlyMode))}
+ </ListEditorView>
+ );
+ }
+
+ renderProcessListItem(process, isReadOnlyMode) {
+ let {id, name, description, artifactName = ''} = process;
+ let {onEditProcess, onDeleteProcess} = this.props;
+ return (
+ <ListEditorItemView
+ key={id}
+ className='list-editor-item-view'
+ isReadOnlyMode={isReadOnlyMode}
+ onSelect={() => onEditProcess(process)}
+ onDelete={() => onDeleteProcess(process)}>
+
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Name')}</div>
+ <div className='name'>{name}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Artifact name')}</div>
+ <div className='artifact-name'>{artifactName}</div>
+ </div>
+ <div className='list-editor-item-view-field'>
+ <div className='title'>{i18n('Notes')}</div>
+ <div className='description'>{description}</div>
+ </div>
+ </ListEditorItemView>
+ );
+ }
+
+ filterList() {
+ let {processesList} = this.props;
+ let {localFilter} = this.state;
+
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return processesList.filter(({name = '', description = ''}) => {
+ return escape(name).match(filter) || escape(description).match(filter);
+ });
+ }
+ else {
+ return processesList;
+ }
+ }
+}
+
+export default SoftwareProductProcessesView;